1use crate::events::{DragMode, ScrollAnimationState, handle_dom_event};
2use crate::font_metrics::BlissFontMetricsProvider;
3use crate::layout::construct::ConstructionTask;
4use crate::layout::damage::ALL_DAMAGE;
5use crate::mutator::ViewportMut;
6use crate::net::{
7 Resource, ResourceHandler, ResourceLoadResponse, StylesheetHandler, StylesheetLoader,
8};
9use crate::node::{ImageData, NodeFlags, RasterImageData, SpecialElementData, Status, TextBrush};
10use crate::selection::TextSelection;
11use crate::stylo_to_cursor_icon::stylo_to_cursor_icon;
12use crate::traversal::TreeTraverser;
13use crate::url::DocumentUrl;
14use crate::util::ImageType;
15use crate::{
16 DEFAULT_CSS, DocumentConfig, DocumentMutator, DummyHtmlParserProvider, ElementData,
17 EventDriver, HtmlParserProvider, Node, NodeData, NoopEventHandler, TextNodeData,
18};
19use bliss_traits::devtools::DevtoolSettings;
20use bliss_traits::events::{
21 BlissScrollEvent, DomEvent, DomEventData, EventSink, HitResult, UiEvent,
22};
23use bliss_traits::navigation::{DummyNavigationProvider, NavigationProvider};
24use bliss_traits::net::{DummyNetProvider, NetProvider, Request};
25use bliss_traits::shell::{ColorScheme, DummyShellProvider, ShellProvider, Viewport};
26use cursor_icon::CursorIcon;
27use linebender_resource_handle::Blob;
28use markup5ever::local_name;
29use parley::{FontContext, PlainEditorDriver};
30use selectors::{Element, matching::QuirksMode};
31use slab::Slab;
32use std::any::Any;
33use std::cell::RefCell;
34use std::collections::{BTreeMap, Bound, HashMap, HashSet};
35use std::ops::{Deref, DerefMut};
36use std::rc::Rc;
37use std::str::FromStr;
38use std::sync::atomic::{AtomicUsize, Ordering};
39use std::sync::mpsc::{Receiver, Sender, channel};
40use std::sync::{Arc, Mutex, MutexGuard, RwLockReadGuard, RwLockWriteGuard};
41use std::task::Context as TaskContext;
42use std::time::Instant;
43use style::Atom;
44use style::animation::DocumentAnimationSet;
45use style::attr::{AttrIdentifier, AttrValue};
46use style::data::{ElementData as StyloElementData, ElementStyles};
47use style::media_queries::MediaType;
48use style::properties::ComputedValues;
49use style::properties::style_structs::Font;
50use style::queries::values::PrefersColorScheme;
51use style::selector_parser::ServoElementSnapshot;
52use style::servo_arc::Arc as ServoArc;
53use style::values::GenericAtomIdent;
54use style::values::computed::Overflow;
55use style::{
56 dom::{TDocument, TNode},
57 media_queries::{Device, MediaList},
58 selector_parser::SnapshotMap,
59 shared_lock::{SharedRwLock, StylesheetGuards},
60 stylesheets::{AllowImportRules, DocumentStyleSheet, Origin, Stylesheet},
61 stylist::Stylist,
62};
63use url::Url;
64
65#[cfg(feature = "parallel-construct")]
66use thread_local::ThreadLocal;
67
68pub enum DocGuard<'a> {
69 Ref(&'a BaseDocument),
70 RefCell(std::cell::Ref<'a, BaseDocument>),
71 RwLock(RwLockReadGuard<'a, BaseDocument>),
72 Mutex(MutexGuard<'a, BaseDocument>),
73}
74
75impl Deref for DocGuard<'_> {
76 type Target = BaseDocument;
77 #[inline(always)]
78 fn deref(&self) -> &Self::Target {
79 match self {
80 Self::Ref(base_document) => base_document,
81 Self::RefCell(refcell_guard) => refcell_guard,
82 Self::RwLock(rw_lock_read_guard) => rw_lock_read_guard,
83 Self::Mutex(mutex_guard) => mutex_guard,
84 }
85 }
86}
87
88pub enum DocGuardMut<'a> {
89 Ref(&'a mut BaseDocument),
90 RefCell(std::cell::RefMut<'a, BaseDocument>),
91 RwLock(RwLockWriteGuard<'a, BaseDocument>),
92 Mutex(MutexGuard<'a, BaseDocument>),
93}
94
95impl Deref for DocGuardMut<'_> {
96 type Target = BaseDocument;
97 #[inline(always)]
98 fn deref(&self) -> &Self::Target {
99 match self {
100 Self::Ref(base_document) => base_document,
101 Self::RefCell(refcell_guard) => refcell_guard,
102 Self::RwLock(rw_lock_read_guard) => rw_lock_read_guard,
103 Self::Mutex(mutex_guard) => mutex_guard,
104 }
105 }
106}
107
108impl DerefMut for DocGuardMut<'_> {
109 #[inline(always)]
110 fn deref_mut(&mut self) -> &mut Self::Target {
111 match self {
112 Self::Ref(base_document) => base_document,
113 Self::RefCell(refcell_guard) => &mut *refcell_guard,
114 Self::RwLock(rw_lock_read_guard) => &mut *rw_lock_read_guard,
115 Self::Mutex(mutex_guard) => &mut *mutex_guard,
116 }
117 }
118}
119
120pub trait Document: Any + 'static {
123 fn inner(&self) -> DocGuard<'_>;
124 fn inner_mut(&mut self) -> DocGuardMut<'_>;
125
126 fn handle_ui_event(&mut self, event: UiEvent) {
128 let mut doc = self.inner_mut();
129 let sink = doc.event_sink.clone();
130 match &sink {
133 Some(s) if doc.events_enabled => {
134 let mut driver =
135 EventDriver::with_event_sink(&mut *doc, NoopEventHandler, s.as_ref());
136 driver.handle_ui_event(event);
137 }
138 _ => {
139 let mut driver = EventDriver::new(&mut *doc, NoopEventHandler);
140 driver.handle_ui_event(event);
141 }
142 }
143 }
144
145 fn poll(&mut self, task_context: Option<TaskContext>) -> bool {
147 let _ = task_context;
149 false
150 }
151
152 fn id(&self) -> usize {
154 self.inner().id
155 }
156}
157
158pub struct PlainDocument(pub BaseDocument);
159impl Document for PlainDocument {
160 fn inner(&self) -> DocGuard<'_> {
161 DocGuard::Ref(&self.0)
162 }
163 fn inner_mut(&mut self) -> DocGuardMut<'_> {
164 DocGuardMut::Ref(&mut self.0)
165 }
166}
167
168impl Document for BaseDocument {
169 fn inner(&self) -> DocGuard<'_> {
170 DocGuard::Ref(self)
171 }
172 fn inner_mut(&mut self) -> DocGuardMut<'_> {
173 DocGuardMut::Ref(self)
174 }
175}
176
177impl Document for Rc<RefCell<BaseDocument>> {
178 fn inner(&self) -> DocGuard<'_> {
179 DocGuard::RefCell(self.borrow())
180 }
181
182 fn inner_mut(&mut self) -> DocGuardMut<'_> {
183 DocGuardMut::RefCell(self.borrow_mut())
184 }
185}
186
187pub enum DocumentEvent {
188 ResourceLoad(ResourceLoadResponse),
189}
190
191pub struct BaseDocument {
192 id: usize,
194
195 pub(crate) url: DocumentUrl,
198 pub(crate) devtool_settings: DevtoolSettings,
200 pub(crate) viewport: Viewport,
202 pub(crate) viewport_scroll: crate::Point<f64>,
204
205 pub(crate) tx: Sender<DocumentEvent>,
207 pub(crate) rx: Option<Receiver<DocumentEvent>>,
209
210 pub(crate) nodes: Box<Slab<Node>>,
215
216 pub(crate) stylist: Stylist,
219 pub(crate) animations: DocumentAnimationSet,
220 pub(crate) guard: SharedRwLock,
222 pub(crate) snapshots: SnapshotMap,
224
225 pub(crate) font_ctx: Arc<Mutex<parley::FontContext>>,
228 #[cfg(feature = "parallel-construct")]
229 pub(crate) thread_font_contexts: ThreadLocal<RefCell<Box<FontContext>>>,
231 pub(crate) layout_ctx: parley::LayoutContext<TextBrush>,
233
234 pub(crate) hover_node_id: Option<usize>,
236 pub(crate) hover_node_is_text: bool,
238 pub(crate) focus_node_id: Option<usize>,
240 pub(crate) active_node_id: Option<usize>,
242 pub(crate) mousedown_node_id: Option<usize>,
244 pub(crate) last_mousedown_time: Option<Instant>,
246 pub(crate) mousedown_position: taffy::Point<f32>,
248 pub(crate) click_count: u16,
250 pub(crate) drag_mode: DragMode,
252 pub(crate) scroll_animation: ScrollAnimationState,
254
255 pub(crate) text_selection: TextSelection,
257
258 pub(crate) has_active_animations: bool,
261 pub(crate) has_canvas: bool,
263 pub(crate) subdoc_is_animating: bool,
265
266 pub(crate) nodes_to_id: HashMap<String, usize>,
268 pub(crate) nodes_to_stylesheet: BTreeMap<usize, DocumentStyleSheet>,
270 pub(crate) ua_stylesheets: HashMap<String, DocumentStyleSheet>,
273 pub(crate) controls_to_form: HashMap<usize, usize>,
275 pub(crate) sub_document_nodes: HashSet<usize>,
277 pub(crate) changed_nodes: HashSet<usize>,
279 pub(crate) deferred_construction_nodes: Vec<ConstructionTask>,
281
282 pub(crate) image_cache: HashMap<String, ImageData>,
285
286 pub(crate) pending_images: HashMap<String, Vec<(usize, ImageType)>>,
290
291 pub(crate) event_listeners: HashMap<(usize, String), Vec<u64>>,
296
297 pub(crate) shadow_scoped_sheets: HashMap<usize, Vec<DocumentStyleSheet>>,
302
303 pub(crate) shadow_cascade_data: HashMap<usize, Box<style::stylist::CascadeData>>,
307
308 pub net_provider: Arc<dyn NetProvider>,
311 pub navigation_provider: Arc<dyn NavigationProvider>,
314 pub shell_provider: Arc<dyn ShellProvider>,
316 pub html_parser_provider: Arc<dyn HtmlParserProvider>,
318
319 pub(crate) script_engine: Option<crate::script::BoxedScriptEngine>,
321
322 pub(crate) event_sink: Option<Arc<dyn EventSink>>,
324
325 pub(crate) events_enabled: bool,
328}
329
330pub(crate) fn make_device(viewport: &Viewport, font_ctx: Arc<Mutex<FontContext>>) -> Device {
331 let width = viewport.window_size.0 as f32 / viewport.scale();
332 let height = viewport.window_size.1 as f32 / viewport.scale();
333 let viewport_size = euclid::Size2D::new(width, height);
334 let device_pixel_ratio = euclid::Scale::new(viewport.scale());
335
336 Device::new(
337 MediaType::screen(),
338 selectors::matching::QuirksMode::NoQuirks,
339 viewport_size,
340 device_pixel_ratio,
341 Box::new(BlissFontMetricsProvider { font_ctx }),
342 ComputedValues::initial_values_with_font_override(Font::initial_values()),
343 match viewport.color_scheme {
344 ColorScheme::Light => PrefersColorScheme::Light,
345 ColorScheme::Dark => PrefersColorScheme::Dark,
346 },
347 )
348}
349
350impl BaseDocument {
351 pub fn new(config: DocumentConfig) -> Self {
353 static ID_GENERATOR: AtomicUsize = AtomicUsize::new(1);
354
355 let id = ID_GENERATOR.fetch_add(1, Ordering::SeqCst);
356
357 let font_ctx = config
358 .font_ctx
359 .unwrap_or_else(|| {
365 let mut font_ctx = FontContext::default();
373 font_ctx
374 .collection
375 .register_fonts(Blob::new(Arc::new(crate::BULLET_FONT) as _), None);
376 font_ctx
377 });
378 let font_ctx = Arc::new(Mutex::new(font_ctx));
379
380 let viewport = config.viewport.unwrap_or_default();
381 let device = make_device(&viewport, font_ctx.clone());
382 let stylist = Stylist::new(device, QuirksMode::NoQuirks);
383 let snapshots = SnapshotMap::new();
384 let nodes = Box::new(Slab::new());
385 let guard = SharedRwLock::new();
386 let nodes_to_id = HashMap::new();
387
388 style_config::set_bool("layout.flexbox.enabled", true);
390 style_config::set_bool("layout.grid.enabled", true);
391 style_config::set_bool("layout.legacy_layout", true);
392 style_config::set_bool("layout.unimplemented", true);
393 style_config::set_bool("layout.columns.enabled", true);
394
395 let base_url = config
396 .base_url
397 .and_then(|url| DocumentUrl::from_str(&url).ok())
398 .unwrap_or_default();
399
400 let net_provider = config
401 .net_provider
402 .unwrap_or_else(|| Arc::new(DummyNetProvider));
403 let navigation_provider = config
404 .navigation_provider
405 .unwrap_or_else(|| Arc::new(DummyNavigationProvider));
406 let shell_provider = config
407 .shell_provider
408 .unwrap_or_else(|| Arc::new(DummyShellProvider));
409 let html_parser_provider = config
410 .html_parser_provider
411 .unwrap_or_else(|| Arc::new(DummyHtmlParserProvider));
412
413 let (tx, rx) = channel();
414
415 let mut doc = Self {
416 id,
417 tx,
418 rx: Some(rx),
419
420 guard,
421 nodes,
422 stylist,
423 animations: DocumentAnimationSet::default(),
424 snapshots,
425 nodes_to_id,
426 viewport,
427 devtool_settings: DevtoolSettings::default(),
428 viewport_scroll: crate::Point::ZERO,
429 url: base_url,
430 ua_stylesheets: HashMap::new(),
431 nodes_to_stylesheet: BTreeMap::new(),
432 font_ctx,
433 #[cfg(feature = "parallel-construct")]
434 thread_font_contexts: ThreadLocal::new(),
435 layout_ctx: parley::LayoutContext::new(),
436
437 hover_node_id: None,
438 hover_node_is_text: false,
439 focus_node_id: None,
440 active_node_id: None,
441 mousedown_node_id: None,
442 has_active_animations: false,
443 subdoc_is_animating: false,
444 has_canvas: false,
445 sub_document_nodes: HashSet::new(),
446 changed_nodes: HashSet::new(),
447 deferred_construction_nodes: Vec::new(),
448 image_cache: HashMap::new(),
449 pending_images: HashMap::new(),
450 event_listeners: HashMap::new(),
451 shadow_scoped_sheets: HashMap::new(),
452 shadow_cascade_data: HashMap::new(),
453 controls_to_form: HashMap::new(),
454 net_provider,
455 navigation_provider,
456 shell_provider,
457 html_parser_provider,
458 script_engine: None,
459 event_sink: None,
460 events_enabled: false,
461 last_mousedown_time: None,
462 mousedown_position: taffy::Point::ZERO,
463 click_count: 0,
464 drag_mode: DragMode::None,
465 scroll_animation: ScrollAnimationState::None,
466 text_selection: TextSelection::default(),
467 };
468
469 doc.create_node(NodeData::Document);
471 doc.root_node_mut().flags.insert(NodeFlags::IS_IN_DOCUMENT);
472
473 match config.ua_stylesheets {
474 Some(stylesheets) => {
475 for ss in &stylesheets {
476 doc.add_user_agent_stylesheet(ss);
477 }
478 }
479 None => doc.add_user_agent_stylesheet(DEFAULT_CSS),
480 }
481
482 let stylo_element_data = StyloElementData {
484 styles: ElementStyles {
485 primary: Some(
486 ComputedValues::initial_values_with_font_override(Font::initial_values())
487 .to_arc(),
488 ),
489 ..Default::default()
490 },
491 ..Default::default()
492 };
493 *doc.root_node().stylo_element_data.borrow_mut() = Some(stylo_element_data);
494
495 doc
496 }
497
498 pub fn set_net_provider(&mut self, net_provider: Arc<dyn NetProvider>) {
500 self.net_provider = net_provider;
501 }
502
503 pub fn set_navigation_provider(&mut self, navigation_provider: Arc<dyn NavigationProvider>) {
505 self.navigation_provider = navigation_provider;
506 }
507
508 pub fn set_shell_provider(&mut self, shell_provider: Arc<dyn ShellProvider>) {
510 self.shell_provider = shell_provider;
511 }
512
513 pub fn set_html_parser_provider(&mut self, html_parser_provider: Arc<dyn HtmlParserProvider>) {
515 self.html_parser_provider = html_parser_provider;
516 }
517
518 pub fn set_script_engine(&mut self, mut engine: crate::script::BoxedScriptEngine) {
520 engine.init(self);
521 self.script_engine = Some(engine);
522 }
523
524 pub fn set_event_sink(&mut self, sink: Arc<dyn EventSink>) {
526 self.event_sink = Some(sink);
527 }
528
529 pub fn set_events_enabled(&mut self, enabled: bool) {
532 self.events_enabled = enabled;
533 }
534
535 pub fn get_event_listeners(&self, node_id: usize, event_name: &str) -> &[u64] {
538 self.event_listeners
539 .get(&(node_id, event_name.to_string()))
540 .map(|v| v.as_slice())
541 .unwrap_or(&[])
542 }
543
544 pub fn execute_script(
546 &mut self,
547 code: &str,
548 language: crate::script::ScriptLanguage,
549 context: &crate::script::ExecutionContext,
550 ) -> Result<crate::script::ScriptValue, crate::script::ScriptError> {
551 if let Some(engine) = &mut self.script_engine {
552 engine.execute(code, language, context)
553 } else {
554 Err(crate::script::ScriptError::UnsupportedLanguage(format!(
555 "{:?}",
556 language
557 )))
558 }
559 }
560
561 pub fn poll_script_engine(&mut self) -> Result<bool, crate::script::ScriptError> {
563 if let Some(engine) = &mut self.script_engine {
564 engine.tick()
565 } else {
566 Ok(false)
567 }
568 }
569
570 pub fn set_script_error_handler(
572 &mut self,
573 callback: Option<crate::script::ScriptErrorCallback>,
574 ) {
575 if let Some(engine) = &mut self.script_engine {
576 engine.set_error_handler(callback);
577 }
578 }
579
580 pub fn handle_ui_event_with_script(&mut self, event: &DomEvent) -> crate::script::EventHandled {
582 if let Some(engine) = &mut self.script_engine {
583 engine.handle_event(event)
584 } else {
585 crate::script::EventHandled::Propagate
586 }
587 }
588
589 pub fn set_base_url(&mut self, url: &str) {
591 self.url = DocumentUrl::from(Url::parse(url).unwrap_or_else(|_| {
592 Url::parse("about:blank").unwrap()
593 }));
594 }
595
596 pub fn guard(&self) -> &SharedRwLock {
597 &self.guard
598 }
599
600 pub fn tree(&self) -> &Slab<Node> {
601 &self.nodes
602 }
603
604 pub fn id(&self) -> usize {
605 self.id
606 }
607
608 pub fn get_node(&self, node_id: usize) -> Option<&Node> {
609 self.nodes.get(node_id)
610 }
611
612 pub fn get_node_mut(&mut self, node_id: usize) -> Option<&mut Node> {
613 self.nodes.get_mut(node_id)
614 }
615
616 pub fn get_focussed_node_id(&self) -> Option<usize> {
617 self.focus_node_id
618 .or(self.try_root_element().map(|el| el.id))
619 }
620
621 pub fn mutate<'doc>(&'doc mut self) -> DocumentMutator<'doc> {
622 DocumentMutator::new(self)
623 }
624
625 pub fn handle_dom_event<F: FnMut(DomEvent)>(
626 &mut self,
627 event: &mut DomEvent,
628 dispatch_event: F,
629 ) {
630 handle_dom_event(self, event, dispatch_event)
631 }
632
633 pub fn as_any_mut(&mut self) -> &mut dyn Any {
634 self
635 }
636
637 pub fn label_bound_input_element(&self, label_node_id: usize) -> Option<&Node> {
644 let label_element = self.get_node(label_node_id)?.element_data()?;
645 if let Some(target_element_dom_id) = label_element.attr(local_name!("for")) {
646 TreeTraverser::new(self)
647 .filter_map(|id| {
648 let node = self.get_node(id)?;
649 let element_data = node.element_data()?;
650 if element_data.name.local != local_name!("input") {
651 return None;
652 }
653 let id = element_data.id.as_ref()?;
654 if *id == *target_element_dom_id {
655 Some(node)
656 } else {
657 None
658 }
659 })
660 .next()
661 } else {
662 TreeTraverser::new_with_root(self, label_node_id)
663 .filter_map(|child_id| {
664 let node = self.get_node(child_id)?;
665 let element_data = node.element_data()?;
666 if element_data.name.local == local_name!("input") {
667 Some(node)
668 } else {
669 None
670 }
671 })
672 .next()
673 }
674 }
675
676 pub fn toggle_checkbox(el: &mut ElementData) -> bool {
677 let Some(is_checked) = el.checkbox_input_checked_mut() else {
678 return false;
679 };
680 *is_checked = !*is_checked;
681
682 *is_checked
683 }
684
685 pub fn toggle_radio(&mut self, radio_set_name: String, target_radio_id: usize) {
686 for (i, node) in self.nodes.iter_mut() {
687 if let Some(node_data) = node.data.downcast_element_mut() {
688 if node_data.attr(local_name!("name")) == Some(&radio_set_name) {
689 let was_clicked = i == target_radio_id;
690 let Some(is_checked) = node_data.checkbox_input_checked_mut() else {
691 continue;
692 };
693 *is_checked = was_clicked;
694 }
695 }
696 }
697 }
698
699 pub fn set_style_property(&mut self, node_id: usize, name: &str, value: &str) {
700 let Some(node) = self.nodes.get_mut(node_id) else {
701 return;
702 };
703 let Some(el) = node.element_data_mut() else {
706 return;
707 };
708 el.set_style_property(name, value, &self.guard, self.url.url_extra_data());
709 }
710
711 pub fn remove_style_property(&mut self, node_id: usize, name: &str) {
712 let Some(node) = self.nodes.get_mut(node_id) else {
713 return;
714 };
715 let Some(el) = node.element_data_mut() else {
716 return;
717 };
718 el.remove_style_property(name, &self.guard, self.url.url_extra_data());
719 }
720
721 pub fn set_sub_document(&mut self, node_id: usize, sub_document: Box<dyn Document>) {
722 let Some(node) = self.get_node_mut(node_id) else {
723 return;
724 };
725 let Some(el) = node.element_data_mut() else {
726 return;
727 };
728 el.set_sub_document(sub_document);
729 self.sub_document_nodes.insert(node_id);
730 }
731
732 pub fn remove_sub_document(&mut self, node_id: usize) {
733 let Some(node) = self.get_node_mut(node_id) else {
734 self.sub_document_nodes.remove(&node_id);
735 return;
736 };
737 let Some(el) = node.element_data_mut() else {
738 self.sub_document_nodes.remove(&node_id);
739 return;
740 };
741 el.remove_sub_document();
742 self.sub_document_nodes.remove(&node_id);
743 }
744
745 pub fn root_node(&self) -> &Node {
746 &self.nodes[0]
747 }
748
749 pub fn root_node_mut(&mut self) -> &mut Node {
750 &mut self.nodes[0]
751 }
752
753 pub fn root_element(&self) -> Option<&Node> {
754 TDocument::as_node(&self.root_node()).first_element_child()
755 }
756
757 pub fn try_root_element(&self) -> Option<&Node> {
763 self.root_element()
764 }
765
766 pub fn create_node(&mut self, node_data: NodeData) -> usize {
767 let slab_ptr = self.nodes.as_ref() as *const Slab<Node>;
769 let doc_ptr = self as *const BaseDocument;
770 let guard = self.guard.clone();
771
772 let entry = self.nodes.vacant_entry();
773 let id = entry.key();
774 entry.insert(Node::new(slab_ptr, doc_ptr, id, guard, node_data));
775
776 self.changed_nodes.insert(id);
778 id
779 }
780
781 pub(crate) fn drop_node_ignoring_parent(&mut self, node_id: usize) -> Option<Node> {
782 let mut node = self.nodes.try_remove(node_id);
783 if let Some(node) = &mut node {
784 if let Some(before) = node.before {
785 self.drop_node_ignoring_parent(before);
786 }
787 if let Some(after) = node.after {
788 self.drop_node_ignoring_parent(after);
789 }
790
791 for &child in &node.children {
792 self.drop_node_ignoring_parent(child);
793 }
794 }
795 node
796 }
797
798 pub fn has_changes(&self) -> bool {
800 !self.changed_nodes.is_empty()
801 }
802
803 pub fn create_text_node(&mut self, text: &str) -> usize {
804 let content = text.to_string();
805 let data = NodeData::Text(TextNodeData::new(content));
806 self.create_node(data)
807 }
808
809 pub fn deep_clone_node(&mut self, node_id: usize) -> usize {
821 let Some(node) = self.get_node(node_id) else {
822 debug_assert!(
823 false,
824 "deep_clone_node: stale root node_id={node_id}"
825 );
826 return usize::MAX;
827 };
828 let data = node.data.clone();
829 let children = node.children.clone();
830
831 let new_node_id = self.create_node(data);
832
833 let mut new_children = Vec::with_capacity(children.len());
836 for child_id in children {
837 let cloned = self.deep_clone_node(child_id);
838 if cloned == usize::MAX {
839 for &drop_id in &new_children {
841 self.drop_node_ignoring_parent(drop_id);
842 }
843 self.drop_node_ignoring_parent(new_node_id);
844 return usize::MAX;
845 }
846 new_children.push(child_id);
847 }
848
849 for &child_id in &new_children {
852 match self.get_node_mut(child_id) {
853 Some(child) => child.parent = Some(new_node_id),
854 None => {
855 debug_assert!(
856 false,
857 "deep_clone_node: vanished child_id={child_id}"
858 );
859 return usize::MAX;
860 }
861 }
862 }
863 match self.get_node_mut(new_node_id) {
864 Some(new_node) => new_node.children = new_children,
865 None => {
866 debug_assert!(
867 false,
868 "deep_clone_node: vanished new_node_id={new_node_id}"
869 );
870 return usize::MAX;
871 }
872 }
873
874 new_node_id
875 }
876
877 pub(crate) fn remove_and_drop_pe(&mut self, node_id: usize) -> Option<Node> {
878 fn remove_pe_ignoring_parent(doc: &mut BaseDocument, node_id: usize) -> Option<Node> {
879 let mut node = doc.nodes.try_remove(node_id);
880 if let Some(node) = &mut node {
881 for &child in &node.children {
882 remove_pe_ignoring_parent(doc, child);
883 }
884 }
885 node
886 }
887
888 let node = remove_pe_ignoring_parent(self, node_id);
889
890 if let Some(parent_id) = node.as_ref().and_then(|node| node.parent) {
892 let parent = &mut self.nodes[parent_id];
893 parent.children.retain(|id| *id != node_id);
894 }
895
896 node
897 }
898
899 pub(crate) fn resolve_url(&self, raw: &str) -> Option<url::Url> {
900 let result = self.url.resolve_relative(raw);
901 #[cfg(feature = "tracing")]
902 if result.is_none() {
903 tracing::warn!("Failed to resolve URL '{raw}' with base {:?}", *self.url);
904 }
905 result
906 }
907
908 pub fn print_tree(&self) {
909 crate::util::walk_tree(0, self.root_node());
910 }
911
912 pub fn print_subtree(&self, node_id: usize) {
913 crate::util::walk_tree(0, &self.nodes[node_id]);
914 }
915
916 pub fn reload_resource_by_href(&mut self, href_to_reload: &str) {
917 for &node_id in self.nodes_to_stylesheet.keys() {
918 let Some(node) = self.get_node(node_id) else {
919 debug_assert!(
920 false,
921 "reload_resource_by_href: stale node_id={node_id} (still in nodes_to_stylesheet)"
922 );
923 continue;
924 };
925 let Some(element) = node.element_data() else {
926 continue;
927 };
928
929 if element.name.local == local_name!("link") {
930 if let Some(href) = element.attr(local_name!("href")) {
931 if href == href_to_reload {
933 let Some(resolved_href) = self.resolve_url(href) else {
934 continue;
935 };
936 self.net_provider.fetch(
937 self.id(),
938 Request::get(resolved_href.clone()),
939 ResourceHandler::boxed(
940 self.tx.clone(),
941 self.id,
942 Some(node_id),
943 self.shell_provider.clone(),
944 StylesheetHandler {
945 source_url: resolved_href,
946 guard: self.guard.clone(),
947 net_provider: self.net_provider.clone(),
948 },
949 ),
950 );
951 }
952 }
953 }
954 }
955 }
956
957 pub fn process_style_element(&mut self, target_id: usize) {
958 let Some(node) = self.get_node(target_id) else {
959 debug_assert!(
960 false,
961 "process_style_element called with stale target_id={target_id}"
962 );
963 return;
964 };
965 let css = node.text_content();
966 let css = html_escape::decode_html_entities(&css);
967 let sheet = self.make_stylesheet(&css, Origin::Author);
968
969 if let Some(shadow_root_id) = self.find_containing_shadow_root(target_id) {
974 self.shadow_scoped_sheets
975 .entry(shadow_root_id)
976 .or_default()
977 .push(sheet);
978 self.shadow_cascade_data.remove(&shadow_root_id);
980 return;
981 }
982
983 self.add_stylesheet_for_node(sheet, target_id);
984 }
985
986 fn find_containing_shadow_root(&self, node_id: usize) -> Option<usize> {
991 let Some(start) = self.get_node(node_id) else {
992 debug_assert!(
993 false,
994 "find_containing_shadow_root: stale node_id={node_id}"
995 );
996 return None;
997 };
998 let mut current = start.parent;
999 while let Some(parent_id) = current {
1000 let Some(parent) = self.get_node(parent_id) else {
1001 debug_assert!(
1002 false,
1003 "find_containing_shadow_root: stale parent_id={parent_id}"
1004 );
1005 return None;
1006 };
1007 if matches!(parent.data, NodeData::ShadowRoot { .. }) {
1008 return Some(parent_id);
1009 }
1010 current = parent.parent;
1011 }
1012 None
1013 }
1014
1015 pub fn remove_user_agent_stylesheet(&mut self, contents: &str) {
1016 if let Some(sheet) = self.ua_stylesheets.remove(contents) {
1017 self.stylist.remove_stylesheet(sheet, &self.guard.read());
1018 }
1019 }
1020
1021 pub fn add_user_agent_stylesheet(&mut self, css: &str) {
1022 let sheet = self.make_stylesheet(css, Origin::UserAgent);
1023 self.ua_stylesheets.insert(css.to_string(), sheet.clone());
1024 self.stylist.append_stylesheet(sheet, &self.guard.read());
1025 }
1026
1027 pub fn make_stylesheet(&self, css: impl AsRef<str>, origin: Origin) -> DocumentStyleSheet {
1028 let data = Stylesheet::from_str(
1029 css.as_ref(),
1030 self.url.url_extra_data(),
1031 origin,
1032 ServoArc::new(self.guard.wrap(MediaList::empty())),
1033 self.guard.clone(),
1034 Some(&StylesheetLoader {
1035 tx: self.tx.clone(),
1036 doc_id: self.id,
1037 net_provider: self.net_provider.clone(),
1038 shell_provider: self.shell_provider.clone(),
1039 }),
1040 None,
1041 QuirksMode::NoQuirks,
1042 AllowImportRules::Yes,
1043 );
1044
1045 DocumentStyleSheet(ServoArc::new(data))
1046 }
1047
1048 pub fn upsert_stylesheet_for_node(&mut self, node_id: usize) {
1049 let Some(node) = self.get_node(node_id) else {
1050 return;
1051 };
1052 let raw_styles = node.text_content();
1053 let sheet = self.make_stylesheet(raw_styles, Origin::Author);
1054 self.add_stylesheet_for_node(sheet, node_id);
1055 }
1056
1057 pub fn add_stylesheet_for_node(&mut self, stylesheet: DocumentStyleSheet, node_id: usize) {
1058 let old = self.nodes_to_stylesheet.insert(node_id, stylesheet.clone());
1059
1060 if let Some(old) = old {
1061 self.stylist.remove_stylesheet(old, &self.guard.read())
1062 }
1063
1064 crate::net::fetch_font_face(
1066 self.tx.clone(),
1067 self.id,
1068 Some(node_id),
1069 &stylesheet.0,
1070 &self.net_provider,
1071 &self.shell_provider,
1072 &self.guard.read(),
1073 );
1074
1075 let Some(node) = self.get_node_mut(node_id) else {
1077 return;
1078 };
1079 let Some(element) = node.element_data_mut() else {
1080 debug_assert!(
1081 false,
1082 "add_stylesheet_for_node: node_id={node_id} is not an element"
1083 );
1084 return;
1085 };
1086 element.special_data = SpecialElementData::Stylesheet(stylesheet.clone());
1087
1088 let insertion_point = self
1090 .nodes_to_stylesheet
1091 .range((Bound::Excluded(node_id), Bound::Unbounded))
1092 .next()
1093 .map(|(_, sheet)| sheet);
1094
1095 if let Some(insertion_point) = insertion_point {
1096 self.stylist.insert_stylesheet_before(
1097 stylesheet,
1098 insertion_point.clone(),
1099 &self.guard.read(),
1100 )
1101 } else {
1102 self.stylist
1103 .append_stylesheet(stylesheet, &self.guard.read())
1104 }
1105 }
1106
1107 pub fn handle_messages(&mut self) {
1108 let rx = self.rx.take().unwrap();
1111
1112 while let Ok(msg) = rx.try_recv() {
1113 self.handle_message(msg);
1114 }
1115
1116 self.rx = Some(rx);
1118 }
1119
1120 pub fn handle_message(&mut self, msg: DocumentEvent) {
1121 match msg {
1122 DocumentEvent::ResourceLoad(resource) => self.load_resource(resource),
1123 }
1124 }
1125
1126 pub fn load_resource(&mut self, res: ResourceLoadResponse) {
1127 let Ok(resource) = res.result else {
1128 return;
1130 };
1131
1132 match resource {
1133 Resource::Css(css) => {
1134 let node_id = res.node_id.unwrap();
1135 self.add_stylesheet_for_node(css, node_id);
1136 }
1137 Resource::Image(_kind, width, height, image_data) => {
1138 let image = ImageData::Raster(RasterImageData::new(width, height, image_data));
1140
1141 let Some(url) = res.resolved_url.as_ref() else {
1142 return;
1143 };
1144
1145 let waiting_nodes = self.pending_images.remove(url).unwrap_or_default();
1147
1148 #[cfg(feature = "tracing")]
1149 tracing::info!(
1150 "Image {url} loaded, applying to {} nodes",
1151 waiting_nodes.len()
1152 );
1153
1154 self.image_cache.insert(url.clone(), image.clone());
1156
1157 for (node_id, image_type) in waiting_nodes {
1159 let Some(node) = self.get_node_mut(node_id) else {
1160 continue;
1161 };
1162
1163 match image_type {
1164 ImageType::Image => {
1165 node.element_data_mut().unwrap().special_data =
1166 SpecialElementData::Image(Box::new(image.clone()));
1167
1168 node.cache.clear();
1170 node.insert_damage(ALL_DAMAGE);
1171 }
1172 ImageType::Background(idx) => {
1173 if let Some(Some(bg_image)) = node
1174 .element_data_mut()
1175 .and_then(|el| el.background_images.get_mut(idx))
1176 {
1177 bg_image.status = Status::Ok;
1178 bg_image.image = image.clone();
1179 }
1180 }
1181 }
1182 }
1183 }
1184 #[cfg(feature = "svg")]
1185 Resource::Svg(_kind, tree) => {
1186 let image = ImageData::Svg(tree);
1188
1189 let Some(url) = res.resolved_url.as_ref() else {
1190 return;
1191 };
1192
1193 let waiting_nodes = self.pending_images.remove(url).unwrap_or_default();
1195
1196 #[cfg(feature = "tracing")]
1197 tracing::info!(
1198 "SVG {url} loaded, applying to {} nodes",
1199 waiting_nodes.len()
1200 );
1201
1202 self.image_cache.insert(url.clone(), image.clone());
1204
1205 for (node_id, image_type) in waiting_nodes {
1207 let Some(node) = self.get_node_mut(node_id) else {
1208 continue;
1209 };
1210
1211 match image_type {
1212 ImageType::Image => {
1213 node.element_data_mut().unwrap().special_data =
1214 SpecialElementData::Image(Box::new(image.clone()));
1215
1216 node.cache.clear();
1218 node.insert_damage(ALL_DAMAGE);
1219 }
1220 ImageType::Background(idx) => {
1221 if let Some(Some(bg_image)) = node
1222 .element_data_mut()
1223 .and_then(|el| el.background_images.get_mut(idx))
1224 {
1225 bg_image.status = Status::Ok;
1226 bg_image.image = image.clone();
1227 }
1228 }
1229 }
1230 }
1231 }
1232 Resource::Font(bytes) => {
1233 let font = Blob::new(Arc::new(bytes));
1234
1235 let mut global_font_ctx = self.font_ctx.lock().unwrap_or_else(|e| e.into_inner());
1238 global_font_ctx
1239 .collection
1240 .register_fonts(font.clone(), None);
1241
1242 #[cfg(feature = "parallel-construct")]
1243 {
1244 rayon::broadcast(|_ctx| {
1245 let mut font_ctx = self
1246 .thread_font_contexts
1247 .get_or(|| RefCell::new(Box::new(global_font_ctx.clone())))
1248 .borrow_mut();
1249 font_ctx.collection.register_fonts(font.clone(), None);
1250 });
1251 }
1252 drop(global_font_ctx);
1253
1254 self.invalidate_inline_contexts();
1256 }
1257 Resource::None => {
1258 }
1260 }
1261 }
1262
1263 pub fn snapshot_node(&mut self, node_id: usize) {
1264 let Some(node) = self.nodes.get_mut(node_id) else {
1265 return;
1266 };
1267 let opaque_node_id = TNode::opaque(&&*node);
1268 node.has_snapshot = true;
1269 node.snapshot_handled
1270 .store(false, std::sync::atomic::Ordering::SeqCst);
1271
1272 if let Some(_existing_snapshot) = self.snapshots.get_mut(&opaque_node_id) {
1274 } else {
1277 let attrs: Option<Vec<_>> = node.attrs().map(|attrs| {
1278 attrs
1279 .iter()
1280 .map(|attr| {
1281 let ident = AttrIdentifier {
1282 local_name: GenericAtomIdent(attr.name.local.clone()),
1283 name: GenericAtomIdent(attr.name.local.clone()),
1284 namespace: GenericAtomIdent(attr.name.ns.clone()),
1285 prefix: None,
1286 };
1287
1288 let value = if attr.name.local == local_name!("id") {
1289 AttrValue::Atom(Atom::from(&*attr.value))
1290 } else if attr.name.local == local_name!("class") {
1291 let classes = attr
1292 .value
1293 .split_ascii_whitespace()
1294 .map(Atom::from)
1295 .collect();
1296 AttrValue::TokenList(attr.value.clone(), classes)
1297 } else {
1298 AttrValue::String(attr.value.clone())
1299 };
1300
1301 (ident, value)
1302 })
1303 .collect()
1304 });
1305
1306 let changed_attrs = attrs
1307 .as_ref()
1308 .map(|attrs| attrs.iter().map(|attr| attr.0.name.clone()).collect())
1309 .unwrap_or_default();
1310
1311 self.snapshots.insert(
1312 opaque_node_id,
1313 ServoElementSnapshot {
1314 state: Some(node.element_state),
1315 attrs,
1316 changed_attrs,
1317 class_changed: true,
1318 id_changed: true,
1319 other_attributes_changed: true,
1320 },
1321 );
1322 }
1323 }
1324
1325 pub fn snapshot_node_and(&mut self, node_id: usize, cb: impl FnOnce(&mut Node)) {
1326 self.snapshot_node(node_id);
1327 let Some(node) = self.nodes.get_mut(node_id) else {
1328 return;
1329 };
1330 cb(node);
1331 }
1332
1333 pub fn hit(&self, x: f32, y: f32) -> Option<HitResult> {
1335 self.root_element()?.hit(x, y)
1338 }
1339
1340 pub fn focus_next_node(&mut self) -> Option<usize> {
1341 let focussed_node_id = self.get_focussed_node_id()?;
1342 let focussed_node = self.get_node(focussed_node_id)?;
1343 let id = self
1344 .next_node(focussed_node, |node| node.is_focussable())
1345 .or_else(|| self.next_node(self.root_node(), |node| node.is_focussable()))?;
1347 self.set_focus_to(id);
1348 Some(id)
1349 }
1350
1351 pub fn focus_prev_node(&mut self) -> Option<usize> {
1353 let focussed_node_id = self.get_focussed_node_id()?;
1354 let focussed_node = self.get_node(focussed_node_id)?;
1355 let id = self
1356 .prev_node(focussed_node, |node| node.is_focussable())
1357 .or_else(|| self.prev_node(self.root_node(), |node| node.is_focussable()))?;
1359 self.set_focus_to(id);
1360 Some(id)
1361 }
1362
1363 pub fn clear_focus(&mut self) {
1365 if let Some(id) = self.focus_node_id {
1366 let shell_provider = self.shell_provider.clone();
1367 self.snapshot_node_and(id, |node| node.blur(shell_provider));
1368 self.focus_node_id = None;
1369 }
1370 }
1371
1372 pub fn set_mousedown_node_id(&mut self, node_id: Option<usize>) {
1373 self.mousedown_node_id = node_id;
1374 }
1375 pub fn set_focus_to(&mut self, focus_node_id: usize) -> bool {
1376 if Some(focus_node_id) == self.focus_node_id {
1377 return false;
1378 }
1379
1380 #[cfg(feature = "tracing")]
1381 tracing::info!("Focussed node {focus_node_id}");
1382
1383 let shell_provider = self.shell_provider.clone();
1384
1385 if let Some(id) = self.focus_node_id {
1387 self.snapshot_node_and(id, |node| node.blur(shell_provider.clone()));
1388 }
1389
1390 self.snapshot_node_and(focus_node_id, |node| node.focus(shell_provider));
1392
1393 self.focus_node_id = Some(focus_node_id);
1394
1395 true
1396 }
1397
1398 pub fn active_node(&mut self) -> bool {
1399 let Some(hover_node_id) = self.get_hover_node_id() else {
1400 return false;
1401 };
1402
1403 if let Some(active_node_id) = self.active_node_id {
1404 if active_node_id == hover_node_id {
1405 return true;
1406 }
1407 self.unactive_node();
1408 }
1409
1410 let active_node_id = Some(hover_node_id);
1411
1412 let node_path = self.maybe_node_layout_ancestors(active_node_id);
1413 for &id in node_path.iter() {
1414 self.snapshot_node_and(id, |node| node.active());
1415 }
1416
1417 self.active_node_id = active_node_id;
1418
1419 true
1420 }
1421
1422 pub fn unactive_node(&mut self) -> bool {
1423 let Some(active_node_id) = self.active_node_id.take() else {
1424 return false;
1425 };
1426
1427 let node_path = self.maybe_node_layout_ancestors(Some(active_node_id));
1428 for &id in node_path.iter() {
1429 self.snapshot_node_and(id, |node| node.unactive());
1430 }
1431
1432 true
1433 }
1434
1435 pub fn set_hover_to(&mut self, x: f32, y: f32) -> bool {
1436 let hit = self.hit(x, y);
1437 let hover_node_id = hit.map(|hit| hit.node_id);
1438 let new_is_text = hit.map(|hit| hit.is_text).unwrap_or(false);
1439
1440 if hover_node_id == self.hover_node_id {
1442 return false;
1443 }
1444
1445 let old_node_path = self.maybe_node_layout_ancestors(self.hover_node_id);
1446 let new_node_path = self.maybe_node_layout_ancestors(hover_node_id);
1447 let same_count = old_node_path
1448 .iter()
1449 .zip(&new_node_path)
1450 .take_while(|(o, n)| o == n)
1451 .count();
1452 for &id in old_node_path.iter().skip(same_count) {
1453 self.snapshot_node_and(id, |node| node.unhover());
1454 }
1455 for &id in new_node_path.iter().skip(same_count) {
1456 self.snapshot_node_and(id, |node| node.hover());
1457 }
1458
1459 self.hover_node_id = hover_node_id;
1460 self.hover_node_is_text = new_is_text;
1461
1462 let cursor = self.get_cursor().unwrap_or_default();
1464 self.shell_provider.set_cursor(cursor);
1465
1466 self.shell_provider.request_redraw();
1468
1469 true
1470 }
1471
1472 pub fn clear_hover(&mut self) -> bool {
1473 let Some(hover_node_id) = self.hover_node_id else {
1474 return false;
1475 };
1476
1477 let old_node_path = self.maybe_node_layout_ancestors(Some(hover_node_id));
1478 for &id in old_node_path.iter() {
1479 self.snapshot_node_and(id, |node| node.unhover());
1480 }
1481
1482 self.hover_node_id = None;
1483 self.hover_node_is_text = false;
1484
1485 let cursor = self.get_cursor().unwrap_or_default();
1487 self.shell_provider.set_cursor(cursor);
1488
1489 self.shell_provider.request_redraw();
1491
1492 true
1493 }
1494
1495 pub fn get_hover_node_id(&self) -> Option<usize> {
1496 self.hover_node_id
1497 }
1498
1499 pub fn set_viewport(&mut self, viewport: Viewport) {
1500 let scale_has_changed = viewport.scale_f64() != self.viewport.scale_f64();
1501 self.viewport = viewport;
1502 self.set_stylist_device(make_device(&self.viewport, self.font_ctx.clone()));
1503 self.scroll_viewport_by(0.0, 0.0); if scale_has_changed {
1506 self.invalidate_inline_contexts();
1507 self.shell_provider.request_redraw();
1508 }
1509 }
1510
1511 pub fn viewport(&self) -> &Viewport {
1512 &self.viewport
1513 }
1514
1515 pub fn viewport_mut(&mut self) -> ViewportMut<'_> {
1516 ViewportMut::new(self)
1517 }
1518
1519 pub fn zoom_by(&mut self, increment: f32) {
1520 *self.viewport.zoom_mut() += increment;
1521 self.set_viewport(self.viewport.clone());
1522 }
1523
1524 pub fn zoom_to(&mut self, zoom: f32) {
1525 *self.viewport.zoom_mut() = zoom;
1526 self.set_viewport(self.viewport.clone());
1527 }
1528
1529 pub fn get_viewport(&self) -> Viewport {
1530 self.viewport.clone()
1531 }
1532
1533 pub fn devtools(&self) -> &DevtoolSettings {
1534 &self.devtool_settings
1535 }
1536
1537 pub fn devtools_mut(&mut self) -> &mut DevtoolSettings {
1538 &mut self.devtool_settings
1539 }
1540
1541 pub fn is_animating(&self) -> bool {
1542 self.has_canvas
1543 | self.has_active_animations
1544 | self.subdoc_is_animating
1545 | (self.scroll_animation != ScrollAnimationState::None)
1546 }
1547
1548 pub fn set_stylist_device(&mut self, device: Device) {
1550 let origins = {
1551 let guard = &self.guard;
1552 let guards = StylesheetGuards {
1553 author: &guard.read(),
1554 ua_or_user: &guard.read(),
1555 };
1556 self.stylist.set_device(device, &guards)
1557 };
1558 self.stylist.force_stylesheet_origins_dirty(origins);
1559 }
1560
1561 pub fn stylist_device(&mut self) -> &Device {
1562 self.stylist.device()
1563 }
1564
1565 pub fn get_cursor(&self) -> Option<CursorIcon> {
1566 let node = self.get_node(self.get_hover_node_id()?)?;
1567
1568 if let Some(subdoc) = node.subdoc().map(|doc| doc.inner()) {
1569 return subdoc.get_cursor();
1570 }
1571
1572 let style = node.primary_styles()?;
1573 let keyword = stylo_to_cursor_icon(style.clone_cursor().keyword);
1574
1575 if keyword != CursorIcon::Default {
1577 return Some(keyword);
1578 }
1579
1580 if node
1582 .element_data()
1583 .is_some_and(|e| e.text_input_data().is_some())
1584 {
1585 return Some(CursorIcon::Text);
1586 }
1587
1588 let mut maybe_node = Some(node);
1590 while let Some(node) = maybe_node {
1591 if node.is_link() {
1592 return Some(CursorIcon::Pointer);
1593 }
1594
1595 maybe_node = node.layout_parent.get().map(|node_id| node.with(node_id));
1596 }
1597
1598 if self.hover_node_is_text {
1600 return Some(CursorIcon::Text);
1601 }
1602
1603 Some(CursorIcon::Default)
1605 }
1606
1607 pub fn scroll_node_by<F: FnMut(DomEvent)>(
1608 &mut self,
1609 node_id: usize,
1610 x: f64,
1611 y: f64,
1612 dispatch_event: F,
1613 ) {
1614 self.scroll_node_by_has_changed(node_id, x, y, dispatch_event);
1615 }
1616
1617 pub fn scroll_node_by_has_changed<F: FnMut(DomEvent)>(
1621 &mut self,
1622 node_id: usize,
1623 x: f64,
1624 y: f64,
1625 mut dispatch_event: F,
1626 ) -> bool {
1627 let Some(node) = self.nodes.get_mut(node_id) else {
1628 return false;
1629 };
1630
1631 let is_html_or_body = node.data.downcast_element().is_some_and(|e| {
1632 let tag = &e.name.local;
1633 tag == "html" || tag == "body"
1634 });
1635
1636 let (can_x_scroll, can_y_scroll) = node
1637 .primary_styles()
1638 .map(|styles| {
1639 (
1640 matches!(styles.clone_overflow_x(), Overflow::Scroll | Overflow::Auto),
1641 matches!(styles.clone_overflow_y(), Overflow::Scroll | Overflow::Auto)
1642 || (styles.clone_overflow_y() == Overflow::Visible && is_html_or_body),
1643 )
1644 })
1645 .unwrap_or((false, false));
1646
1647 let initial = node.scroll_offset;
1648 let new_x = node.scroll_offset.x - x;
1649 let new_y = node.scroll_offset.y - y;
1650
1651 let mut bubble_x = 0.0;
1652 let mut bubble_y = 0.0;
1653
1654 let scroll_width = node.final_layout.scroll_width() as f64;
1655 let scroll_height = node.final_layout.scroll_height() as f64;
1656
1657 if let Some(mut sub_doc) = node.subdoc_mut().map(|doc| doc.inner_mut()) {
1659 let has_changed = if let Some(hover_node_id) = sub_doc.get_hover_node_id() {
1660 sub_doc.scroll_node_by_has_changed(hover_node_id, x, y, dispatch_event)
1661 } else {
1662 sub_doc.scroll_viewport_by_has_changed(x, y)
1663 };
1664
1665 return has_changed;
1667 }
1668
1669 if !can_x_scroll {
1671 bubble_x = x
1672 } else if new_x < 0.0 {
1673 bubble_x = -new_x;
1674 node.scroll_offset.x = 0.0;
1675 } else if new_x > scroll_width {
1676 bubble_x = scroll_width - new_x;
1677 node.scroll_offset.x = scroll_width;
1678 } else {
1679 node.scroll_offset.x = new_x;
1680 }
1681
1682 if !can_y_scroll {
1683 bubble_y = y
1684 } else if new_y < 0.0 {
1685 bubble_y = -new_y;
1686 node.scroll_offset.y = 0.0;
1687 } else if new_y > scroll_height {
1688 bubble_y = scroll_height - new_y;
1689 node.scroll_offset.y = scroll_height;
1690 } else {
1691 node.scroll_offset.y = new_y;
1692 }
1693
1694 let has_changed = node.scroll_offset != initial;
1695
1696 if has_changed {
1697 let layout = node.final_layout;
1698 let event = BlissScrollEvent {
1699 scroll_top: node.scroll_offset.y,
1700 scroll_left: node.scroll_offset.x,
1701 scroll_width: layout.scroll_width() as i32,
1702 scroll_height: layout.scroll_height() as i32,
1703 client_width: layout.size.width as i32,
1704 client_height: layout.size.height as i32,
1705 };
1706
1707 dispatch_event(DomEvent::new(node_id, DomEventData::Scroll(event)));
1708 }
1709
1710 if bubble_x != 0.0 || bubble_y != 0.0 {
1711 if let Some(parent) = node.parent {
1712 return self.scroll_node_by_has_changed(parent, bubble_x, bubble_y, dispatch_event)
1713 | has_changed;
1714 } else {
1715 return self.scroll_viewport_by_has_changed(bubble_x, bubble_y) | has_changed;
1716 }
1717 }
1718
1719 has_changed
1720 }
1721
1722 pub fn scroll_viewport_by(&mut self, x: f64, y: f64) {
1723 self.scroll_viewport_by_has_changed(x, y);
1724 }
1725
1726 pub fn scroll_viewport_by_has_changed(&mut self, x: f64, y: f64) -> bool {
1728 let content_size = self.root_element().map(|r| r.final_layout.size).unwrap_or_default();
1731 let new_scroll = (self.viewport_scroll.x - x, self.viewport_scroll.y - y);
1732 let window_width = self.viewport.window_size.0 as f64 / self.viewport.scale() as f64;
1733 let window_height = self.viewport.window_size.1 as f64 / self.viewport.scale() as f64;
1734
1735 let initial = self.viewport_scroll;
1736 self.viewport_scroll.x = f64::max(
1737 0.0,
1738 f64::min(new_scroll.0, content_size.width as f64 - window_width),
1739 );
1740 self.viewport_scroll.y = f64::max(
1741 0.0,
1742 f64::min(new_scroll.1, content_size.height as f64 - window_height),
1743 );
1744
1745 self.viewport_scroll != initial
1746 }
1747
1748 pub fn scroll_by(
1749 &mut self,
1750 anchor_node_id: Option<usize>,
1751 scroll_x: f64,
1752 scroll_y: f64,
1753 dispatch_event: &mut dyn FnMut(DomEvent),
1754 ) -> bool {
1755 if let Some(anchor_node_id) = anchor_node_id {
1756 self.scroll_node_by_has_changed(anchor_node_id, scroll_x, scroll_y, dispatch_event)
1757 } else {
1758 self.scroll_viewport_by_has_changed(scroll_x, scroll_y)
1759 }
1760 }
1761
1762 pub fn viewport_scroll(&self) -> crate::Point<f64> {
1763 self.viewport_scroll
1764 }
1765
1766 pub fn set_viewport_scroll(&mut self, scroll: crate::Point<f64>) {
1767 self.viewport_scroll = scroll;
1768 }
1769
1770 pub fn get_client_bounding_rect(&self, node_id: usize) -> Option<BoundingRect> {
1772 let node = self.get_node(node_id)?;
1773 let pos = node.absolute_position(0.0, 0.0);
1774
1775 Some(BoundingRect {
1776 x: pos.x as f64 - self.viewport_scroll.x,
1777 y: pos.y as f64 - self.viewport_scroll.y,
1778 width: node.unrounded_layout.size.width as f64,
1779 height: node.unrounded_layout.size.height as f64,
1780 })
1781 }
1782
1783 pub fn find_title_node(&self) -> Option<&Node> {
1784 TreeTraverser::new(self)
1785 .find(|node_id| {
1786 self.nodes[*node_id]
1787 .data
1788 .is_element_with_tag_name(&local_name!("title"))
1789 })
1790 .map(|node_id| &self.nodes[node_id])
1791 }
1792
1793 pub fn with_text_input(
1794 &mut self,
1795 node_id: usize,
1796 cb: impl FnOnce(PlainEditorDriver<TextBrush>),
1797 ) {
1798 let Some(node) = self.nodes.get_mut(node_id) else {
1799 return;
1800 };
1801
1802 if let Some(text_input) = node
1803 .element_data_mut()
1804 .and_then(|el| el.text_input_data_mut())
1805 {
1806 let mut font_ctx = self.font_ctx.lock().unwrap_or_else(|e| e.into_inner());
1807 let layout_ctx = &mut self.layout_ctx;
1808 let driver = text_input.editor.driver(&mut font_ctx, layout_ctx);
1809 cb(driver)
1810 }
1811 }
1812
1813 pub(crate) fn compute_has_canvas(&self) -> bool {
1814 TreeTraverser::new(self).any(|node_id| {
1815 let node = &self.nodes[node_id];
1816 let Some(element) = node.element_data() else {
1817 return false;
1818 };
1819 if element.name.local == local_name!("canvas") && element.has_attr(local_name!("src")) {
1820 return true;
1821 }
1822
1823 false
1824 })
1825 }
1826
1827 pub fn find_text_position(&self, x: f32, y: f32) -> Option<(usize, usize)> {
1833 let hit = self.hit(x, y)?;
1834 let hit_node = self.get_node(hit.node_id)?;
1835 let inline_root = hit_node.inline_root_ancestor()?;
1836 let byte_offset = inline_root.text_offset_at_point(hit.x, hit.y)?;
1837 Some((inline_root.id, byte_offset))
1838 }
1839
1840 pub fn set_text_selection(
1842 &mut self,
1843 anchor_node: usize,
1844 anchor_offset: usize,
1845 focus_node: usize,
1846 focus_offset: usize,
1847 ) {
1848 self.text_selection =
1849 TextSelection::new(anchor_node, anchor_offset, focus_node, focus_offset);
1850
1851 if let (Some(parent), Some(idx)) = self.anonymous_block_location(anchor_node) {
1853 self.text_selection
1854 .anchor
1855 .set_anonymous(parent, idx, anchor_offset);
1856 }
1857 if let (Some(parent), Some(idx)) = self.anonymous_block_location(focus_node) {
1858 self.text_selection
1859 .focus
1860 .set_anonymous(parent, idx, focus_offset);
1861 }
1862 }
1863
1864 fn anonymous_block_location(&self, node_id: usize) -> (Option<usize>, Option<usize>) {
1867 let Some(node) = self.get_node(node_id) else {
1868 return (None, None);
1869 };
1870
1871 if !node.is_anonymous() {
1872 return (None, None);
1873 }
1874
1875 let Some(parent_id) = node.parent else {
1876 return (None, None);
1877 };
1878
1879 let Some(parent) = self.get_node(parent_id) else {
1880 return (Some(parent_id), None);
1881 };
1882
1883 let layout_children = parent.layout_children.borrow();
1884 let Some(children) = layout_children.as_ref() else {
1885 return (Some(parent_id), None);
1886 };
1887
1888 let mut anon_index = 0;
1890 for &child_id in children.iter() {
1891 if child_id == node_id {
1892 return (Some(parent_id), Some(anon_index));
1893 }
1894 if self.get_node(child_id).is_some_and(|n| n.is_anonymous()) {
1895 anon_index += 1;
1896 }
1897 }
1898
1899 (Some(parent_id), None)
1900 }
1901
1902 pub fn clear_text_selection(&mut self) {
1904 self.text_selection.clear();
1905 }
1906
1907 pub fn update_selection_focus(&mut self, focus_node: usize, focus_offset: usize) {
1909 if let (Some(parent), Some(idx)) = self.anonymous_block_location(focus_node) {
1911 self.text_selection
1912 .focus
1913 .set_anonymous(parent, idx, focus_offset);
1914 } else {
1915 self.text_selection.set_focus(focus_node, focus_offset);
1916 }
1917 }
1918
1919 pub fn extend_text_selection_to_point(&mut self, x: f32, y: f32) -> bool {
1922 if !self.text_selection.anchor.is_some() {
1923 return false;
1924 }
1925
1926 if let Some((node, offset)) = self.find_text_position(x, y) {
1927 self.update_selection_focus(node, offset);
1928 self.shell_provider.request_redraw();
1929 true
1930 } else {
1931 false
1932 }
1933 }
1934
1935 fn find_anonymous_block_by_index(
1937 &self,
1938 parent_id: usize,
1939 target_index: usize,
1940 ) -> Option<usize> {
1941 let parent = self.get_node(parent_id)?;
1942 let layout_children = parent.layout_children.borrow();
1943 let children = layout_children.as_ref()?;
1944
1945 children
1946 .iter()
1947 .filter(|&&child_id| self.get_node(child_id).is_some_and(|n| n.is_anonymous()))
1948 .nth(target_index)
1949 .copied()
1950 }
1951
1952 pub fn has_text_selection(&self) -> bool {
1954 self.text_selection.is_active()
1955 }
1956
1957 pub fn get_selected_text(&self) -> Option<String> {
1959 let ranges = self.get_text_selection_ranges();
1960 if ranges.is_empty() {
1961 return None;
1962 }
1963
1964 let mut result = String::new();
1965 for (node_id, start, end) in &ranges {
1966 let node = self.get_node(*node_id)?;
1967 let element_data = node.element_data()?;
1968 let inline_layout = element_data.inline_layout_data.as_ref()?;
1969
1970 if *end > inline_layout.text.len() {
1971 continue;
1972 }
1973
1974 if !result.is_empty() {
1975 result.push(' ');
1976 }
1977 result.push_str(&inline_layout.text[*start..*end]);
1978 }
1979
1980 if result.is_empty() {
1981 None
1982 } else {
1983 Some(result)
1984 }
1985 }
1986
1987 pub fn get_text_selection_ranges(&self) -> Vec<(usize, usize, usize)> {
1990 let lookup = |parent_id, idx| self.find_anonymous_block_by_index(parent_id, idx);
1991
1992 let anchor_node = match self.text_selection.anchor.resolve_node_id(lookup) {
1993 Some(id) => id,
1994 None => return Vec::new(),
1995 };
1996 let focus_node = match self.text_selection.focus.resolve_node_id(lookup) {
1997 Some(id) => id,
1998 None => return Vec::new(),
1999 };
2000
2001 if anchor_node == focus_node {
2003 let start = self
2004 .text_selection
2005 .anchor
2006 .offset
2007 .min(self.text_selection.focus.offset);
2008 let end = self
2009 .text_selection
2010 .anchor
2011 .offset
2012 .max(self.text_selection.focus.offset);
2013
2014 if start == end {
2015 return Vec::new();
2016 }
2017 return vec![(anchor_node, start, end)];
2018 }
2019
2020 let inline_roots = self.collect_inline_roots_in_range(anchor_node, focus_node);
2022 if inline_roots.is_empty() {
2023 return Vec::new();
2024 }
2025
2026 let first_in_roots = inline_roots[0];
2029
2030 let (first_node, first_offset, last_node, last_offset) =
2031 if first_in_roots == anchor_node || (first_in_roots != focus_node) {
2032 (
2034 anchor_node,
2035 self.text_selection.anchor.offset,
2036 focus_node,
2037 self.text_selection.focus.offset,
2038 )
2039 } else {
2040 (
2042 focus_node,
2043 self.text_selection.focus.offset,
2044 anchor_node,
2045 self.text_selection.anchor.offset,
2046 )
2047 };
2048
2049 let mut ranges = Vec::with_capacity(inline_roots.len());
2050
2051 for &node_id in &inline_roots {
2052 let Some(node) = self.get_node(node_id) else {
2053 continue;
2054 };
2055 let Some(element_data) = node.element_data() else {
2056 continue;
2057 };
2058 let Some(inline_layout) = element_data.inline_layout_data.as_ref() else {
2059 continue;
2060 };
2061
2062 let text_len = inline_layout.text.len();
2063
2064 if node_id == first_node && node_id == last_node {
2065 let start = first_offset.min(last_offset);
2066 let end = first_offset.max(last_offset);
2067 if start < end && end <= text_len {
2068 ranges.push((node_id, start, end));
2069 }
2070 } else if node_id == first_node {
2071 if first_offset < text_len {
2072 ranges.push((node_id, first_offset, text_len));
2073 }
2074 } else if node_id == last_node {
2075 if last_offset > 0 && last_offset <= text_len {
2076 ranges.push((node_id, 0, last_offset));
2077 }
2078 } else if text_len > 0 {
2079 ranges.push((node_id, 0, text_len));
2080 }
2081 }
2082
2083 ranges
2084 }
2085}
2086
2087pub struct BoundingRect {
2088 pub x: f64,
2089 pub y: f64,
2090 pub width: f64,
2091 pub height: f64,
2092}
2093
2094impl AsRef<BaseDocument> for BaseDocument {
2095 fn as_ref(&self) -> &BaseDocument {
2096 self
2097 }
2098}
2099
2100impl AsMut<BaseDocument> for BaseDocument {
2101 fn as_mut(&mut self) -> &mut BaseDocument {
2102 self
2103 }
2104}