blitz_dom/
document.rs

1use crate::events::handle_event;
2use crate::layout::construct::collect_layout_children;
3use crate::mutator::ViewportMut;
4use crate::node::{ImageData, NodeSpecificData, RasterImageData, Status, TextBrush};
5use crate::stylo_to_cursor_icon::stylo_to_cursor_icon;
6use crate::traversal::{AncestorTraverser, TreeTraverser};
7use crate::util::{ImageType, resolve_url};
8use crate::{DocumentMutator, ElementNodeData, Node, NodeData, TextNodeData};
9use app_units::Au;
10use blitz_traits::events::UiEvent;
11use blitz_traits::navigation::{DummyNavigationProvider, NavigationProvider};
12use blitz_traits::net::{DummyNetProvider, SharedProvider};
13use blitz_traits::shell::{DummyShellProvider, ShellProvider};
14use blitz_traits::{ColorScheme, Devtools, Viewport};
15use blitz_traits::{DomEvent, HitResult};
16use cursor_icon::CursorIcon;
17use markup5ever::local_name;
18use parley::FontContext;
19use peniko::{Blob, kurbo};
20use string_cache::Atom;
21use style::attr::{AttrIdentifier, AttrValue};
22use style::data::{ElementData, ElementStyles};
23use style::properties::ComputedValues;
24use style::properties::style_structs::Font;
25use style::values::GenericAtomIdent;
26use style::values::computed::Overflow;
27// use quadtree_rs::Quadtree;
28use crate::net::{Resource, StylesheetLoader};
29use selectors::{Element, matching::QuirksMode};
30use slab::Slab;
31use std::any::Any;
32use std::collections::{BTreeMap, Bound, HashMap, HashSet};
33use std::ops::{Deref, DerefMut};
34use std::sync::Arc;
35use std::sync::atomic::{AtomicUsize, Ordering};
36use style::media_queries::MediaType;
37use style::queries::values::PrefersColorScheme;
38use style::selector_parser::ServoElementSnapshot;
39use style::servo::media_queries::FontMetricsProvider;
40use style::servo_arc::Arc as ServoArc;
41use style::{
42    dom::{TDocument, TNode},
43    media_queries::{Device, MediaList},
44    selector_parser::SnapshotMap,
45    shared_lock::{SharedRwLock, StylesheetGuards},
46    stylesheets::{AllowImportRules, DocumentStyleSheet, Origin, Stylesheet, UrlExtraData},
47    stylist::Stylist,
48};
49use taffy::AvailableSpace;
50use url::Url;
51
52pub trait Document: Deref<Target = BaseDocument> + DerefMut + 'static {
53    fn poll(&mut self, cx: std::task::Context) -> bool {
54        // Default implementation does nothing
55        let _ = cx;
56        false
57    }
58
59    fn handle_event(&mut self, event: UiEvent) {
60        // Default implementation does nothing
61        let _ = event;
62    }
63
64    fn as_any_mut(&mut self) -> &mut dyn Any;
65
66    fn id(&self) -> usize;
67}
68
69// TODO: implement a proper font metrics provider
70#[derive(Debug, Clone)]
71struct DummyFontMetricsProvider;
72impl FontMetricsProvider for DummyFontMetricsProvider {
73    fn query_font_metrics(
74        &self,
75        _vertical: bool,
76        _font: &Font,
77        _base_size: style::values::computed::CSSPixelLength,
78        _flags: style::values::computed::font::QueryFontMetricsFlags,
79    ) -> style::font_metrics::FontMetrics {
80        Default::default()
81    }
82
83    fn base_size_for_generic(
84        &self,
85        generic: style::values::computed::font::GenericFontFamily,
86    ) -> style::values::computed::Length {
87        let size = match generic {
88            style::values::computed::font::GenericFontFamily::Monospace => 13.0,
89            _ => 16.0,
90        };
91        style::values::computed::Length::from(Au::from_f32_px(size))
92    }
93}
94
95pub struct BaseDocument {
96    id: usize,
97
98    /// A bump-backed tree
99    ///
100    /// Both taffy and stylo traits are implemented for this.
101    /// We pin the tree to a guarantee to the nodes it creates that the tree is stable in memory.
102    ///
103    /// There is no way to create the tree - publicly or privately - that would invalidate that invariant.
104    pub nodes: Box<Slab<Node>>,
105
106    // TODO: encapsulate and make private again
107    pub guard: SharedRwLock,
108
109    /// The styling engine of firefox
110    pub(crate) stylist: Stylist,
111
112    // caching for the stylist
113    pub(crate) snapshots: SnapshotMap,
114
115    // TODO: encapsulate and make private again
116    pub nodes_to_id: HashMap<String, usize>,
117
118    /// Base url for resolving linked resources (stylesheets, images, fonts, etc)
119    pub base_url: Option<url::Url>,
120
121    // /// The quadtree we use for hit-testing
122    // pub(crate) quadtree: Quadtree<u64, usize>,
123
124    // Viewport details such as the dimensions, HiDPI scale, and zoom factor,
125    pub(crate) viewport: Viewport,
126    // Devtool settings. Currently used to render debug overlays
127    pub(crate) devtool_settings: Devtools,
128
129    // Scroll within our viewport
130    pub(crate) viewport_scroll: kurbo::Point,
131
132    /// Stylesheets added by the useragent
133    /// where the key is the hashed CSS
134    pub(crate) ua_stylesheets: HashMap<String, DocumentStyleSheet>,
135
136    pub(crate) nodes_to_stylesheet: BTreeMap<usize, DocumentStyleSheet>,
137
138    /// A Parley font context
139    pub font_ctx: parley::FontContext,
140
141    /// A Parley layout context
142    pub layout_ctx: parley::LayoutContext<TextBrush>,
143
144    /// The node which is currently hovered (if any)
145    pub(crate) hover_node_id: Option<usize>,
146    /// The node which is currently focussed (if any)
147    pub(crate) focus_node_id: Option<usize>,
148    /// The node which is currently active (if any)
149    pub(crate) active_node_id: Option<usize>,
150    /// The node which recieved a mousedown event (if any)
151    pub(crate) mousedown_node_id: Option<usize>,
152
153    pub changed: HashSet<usize>,
154
155    /// A map from control node ID's to their associated forms node ID's
156    pub controls_to_form: HashMap<usize, usize>,
157
158    /// Network provider. Can be used to fetch assets.
159    pub net_provider: SharedProvider<Resource>,
160
161    /// Navigation provider. Can be used to navigate to a new page (bubbles up the event
162    /// on e.g. clicking a Link)
163    pub navigation_provider: Arc<dyn NavigationProvider>,
164
165    /// Shell provider. Can be used to request a redraw or set the cursor icon
166    pub shell_provider: Arc<dyn ShellProvider>,
167}
168
169pub(crate) fn make_device(viewport: &Viewport) -> Device {
170    let width = viewport.window_size.0 as f32 / viewport.scale();
171    let height = viewport.window_size.1 as f32 / viewport.scale();
172    let viewport_size = euclid::Size2D::new(width, height);
173    let device_pixel_ratio = euclid::Scale::new(viewport.scale());
174
175    Device::new(
176        MediaType::screen(),
177        selectors::matching::QuirksMode::NoQuirks,
178        viewport_size,
179        device_pixel_ratio,
180        Box::new(DummyFontMetricsProvider),
181        ComputedValues::initial_values_with_font_override(Font::initial_values()),
182        match viewport.color_scheme {
183            ColorScheme::Light => PrefersColorScheme::Light,
184            ColorScheme::Dark => PrefersColorScheme::Dark,
185        },
186    )
187}
188
189impl BaseDocument {
190    pub fn handle_event<F: FnMut(DomEvent)>(&mut self, event: &mut DomEvent, dispatch_event: F) {
191        handle_event(self, event, dispatch_event)
192    }
193    pub fn as_any_mut(&mut self) -> &mut dyn Any {
194        self
195    }
196}
197
198impl BaseDocument {
199    pub fn new(viewport: Viewport) -> Self {
200        Self::with_font_ctx(viewport, parley::FontContext::default())
201    }
202
203    pub fn with_font_ctx(viewport: Viewport, mut font_ctx: FontContext) -> Self {
204        static ID_GENERATOR: AtomicUsize = AtomicUsize::new(1);
205
206        let id = ID_GENERATOR.fetch_add(1, Ordering::SeqCst);
207        let device = make_device(&viewport);
208        let stylist = Stylist::new(device, QuirksMode::NoQuirks);
209        let snapshots = SnapshotMap::new();
210        let nodes = Box::new(Slab::new());
211        let guard = SharedRwLock::new();
212        let nodes_to_id = HashMap::new();
213
214        // Make sure we turn on stylo features
215        style_config::set_bool("layout.flexbox.enabled", true);
216        style_config::set_bool("layout.grid.enabled", true);
217        style_config::set_bool("layout.legacy_layout", true);
218        style_config::set_bool("layout.unimplemented", true);
219        style_config::set_bool("layout.columns.enabled", true);
220
221        font_ctx
222            .collection
223            .register_fonts(Blob::new(Arc::new(crate::BULLET_FONT) as _), None);
224
225        let mut doc = Self {
226            id,
227            guard,
228            nodes,
229            stylist,
230            snapshots,
231            nodes_to_id,
232            viewport,
233            devtool_settings: Devtools::default(),
234            viewport_scroll: kurbo::Point::ZERO,
235            base_url: None,
236            // quadtree: Quadtree::new(20),
237            ua_stylesheets: HashMap::new(),
238            nodes_to_stylesheet: BTreeMap::new(),
239            font_ctx,
240            layout_ctx: parley::LayoutContext::new(),
241
242            hover_node_id: None,
243            focus_node_id: None,
244            active_node_id: None,
245            mousedown_node_id: None,
246            changed: HashSet::new(),
247            controls_to_form: HashMap::new(),
248            net_provider: Arc::new(DummyNetProvider),
249            navigation_provider: Arc::new(DummyNavigationProvider),
250            shell_provider: Arc::new(DummyShellProvider),
251        };
252
253        // Initialise document with root Document node
254        doc.create_node(NodeData::Document);
255
256        // Stylo data on the root node container is needed to render the node
257        let stylo_element_data = ElementData {
258            styles: ElementStyles {
259                primary: Some(
260                    ComputedValues::initial_values_with_font_override(Font::initial_values())
261                        .to_arc(),
262                ),
263                ..Default::default()
264            },
265            ..Default::default()
266        };
267        *doc.root_node().stylo_element_data.borrow_mut() = Some(stylo_element_data);
268
269        doc
270    }
271
272    /// Set the Document's networking provider
273    pub fn set_net_provider(&mut self, net_provider: SharedProvider<Resource>) {
274        self.net_provider = net_provider;
275    }
276
277    /// Set the Document's navigation provider
278    pub fn set_navigation_provider(&mut self, navigation_provider: Arc<dyn NavigationProvider>) {
279        self.navigation_provider = navigation_provider;
280    }
281
282    /// Set the Document's shell provider
283    pub fn set_shell_provider(&mut self, shell_provider: Arc<dyn ShellProvider>) {
284        self.shell_provider = shell_provider;
285    }
286
287    /// Set base url for resolving linked resources (stylesheets, images, fonts, etc)
288    pub fn set_base_url(&mut self, url: &str) {
289        self.base_url = Some(Url::parse(url).unwrap());
290    }
291
292    pub fn guard(&self) -> &SharedRwLock {
293        &self.guard
294    }
295
296    pub fn tree(&self) -> &Slab<Node> {
297        &self.nodes
298    }
299
300    pub fn id(&self) -> usize {
301        self.id
302    }
303
304    pub fn get_node(&self, node_id: usize) -> Option<&Node> {
305        self.nodes.get(node_id)
306    }
307
308    pub fn get_node_mut(&mut self, node_id: usize) -> Option<&mut Node> {
309        self.nodes.get_mut(node_id)
310    }
311
312    pub fn get_focussed_node_id(&self) -> Option<usize> {
313        self.focus_node_id
314            .or(self.try_root_element().map(|el| el.id))
315    }
316
317    pub fn mutate<'doc>(&'doc mut self) -> DocumentMutator<'doc> {
318        DocumentMutator::new(self)
319    }
320
321    /// Find the label's bound input elements:
322    /// the element id referenced by the "for" attribute of a given label element
323    /// or the first input element which is nested in the label
324    /// Note that although there should only be one bound element,
325    /// we return all possibilities instead of just the first
326    /// in order to allow the caller to decide which one is correct
327    pub fn label_bound_input_element(&self, label_node_id: usize) -> Option<&Node> {
328        let label_element = self.nodes[label_node_id].element_data()?;
329        if let Some(target_element_dom_id) = label_element.attr(local_name!("for")) {
330            TreeTraverser::new(self)
331                .filter_map(|id| {
332                    let node = self.get_node(id)?;
333                    let element_data = node.element_data()?;
334                    if element_data.name.local != local_name!("input") {
335                        return None;
336                    }
337                    let id = element_data.id.as_ref()?;
338                    if *id == *target_element_dom_id {
339                        Some(node)
340                    } else {
341                        None
342                    }
343                })
344                .next()
345        } else {
346            TreeTraverser::new_with_root(self, label_node_id)
347                .filter_map(|child_id| {
348                    let node = self.get_node(child_id)?;
349                    let element_data = node.element_data()?;
350                    if element_data.name.local == local_name!("input") {
351                        Some(node)
352                    } else {
353                        None
354                    }
355                })
356                .next()
357        }
358    }
359
360    pub fn toggle_checkbox(el: &mut ElementNodeData) -> bool {
361        let Some(is_checked) = el.checkbox_input_checked_mut() else {
362            return false;
363        };
364        *is_checked = !*is_checked;
365
366        *is_checked
367    }
368
369    pub fn toggle_radio(&mut self, radio_set_name: String, target_radio_id: usize) {
370        for i in 0..self.nodes.len() {
371            let node = &mut self.nodes[i];
372            if let Some(node_data) = node.data.downcast_element_mut() {
373                if node_data.attr(local_name!("name")) == Some(&radio_set_name) {
374                    let was_clicked = i == target_radio_id;
375                    let Some(is_checked) = node_data.checkbox_input_checked_mut() else {
376                        continue;
377                    };
378                    *is_checked = was_clicked;
379                }
380            }
381        }
382    }
383
384    pub fn root_node(&self) -> &Node {
385        &self.nodes[0]
386    }
387
388    pub fn try_root_element(&self) -> Option<&Node> {
389        TDocument::as_node(&self.root_node()).first_element_child()
390    }
391
392    pub fn root_element(&self) -> &Node {
393        TDocument::as_node(&self.root_node())
394            .first_element_child()
395            .unwrap()
396            .as_element()
397            .unwrap()
398    }
399
400    pub fn create_node(&mut self, node_data: NodeData) -> usize {
401        let slab_ptr = self.nodes.as_mut() as *mut Slab<Node>;
402        let guard = self.guard.clone();
403
404        let entry = self.nodes.vacant_entry();
405        let id = entry.key();
406        entry.insert(Node::new(slab_ptr, id, guard, node_data));
407
408        // Mark the new node as changed.
409        self.changed.insert(id);
410        id
411    }
412
413    pub fn create_text_node(&mut self, text: &str) -> usize {
414        let content = text.to_string();
415        let data = NodeData::Text(TextNodeData::new(content));
416        self.create_node(data)
417    }
418
419    pub fn deep_clone_node(&mut self, node_id: usize) -> usize {
420        // Load existing node
421        let node = &self.nodes[node_id];
422        let data = node.data.clone();
423        let children = node.children.clone();
424
425        // Create new node
426        let new_node_id = self.create_node(data);
427
428        // Recursively clone children
429        let new_children: Vec<usize> = children
430            .into_iter()
431            .map(|child_id| self.deep_clone_node(child_id))
432            .collect();
433        for &child_id in &new_children {
434            self.nodes[child_id].parent = Some(new_node_id);
435        }
436        self.nodes[new_node_id].children = new_children;
437
438        new_node_id
439    }
440
441    pub fn insert_before(&mut self, node_id: usize, inserted_node_ids: &[usize]) {
442        // let count = inserted_node_ids.len();
443
444        // self.print_tree();
445
446        let node = &self.nodes[node_id];
447
448        let parent_id = node.parent.unwrap();
449        let parent = &mut self.nodes[parent_id];
450        let node_child_idx = parent
451            .children
452            .iter()
453            .position(|id| *id == node_id)
454            .unwrap();
455
456        // Mark the node's parent as changed.
457        self.changed.insert(parent_id);
458
459        let mut children = std::mem::take(&mut parent.children);
460        children.splice(
461            node_child_idx..node_child_idx,
462            inserted_node_ids.iter().copied(),
463        );
464
465        // Update parent values
466        let mut child_idx = node_child_idx;
467        while child_idx < children.len() {
468            let child_id = children[child_idx];
469            let node = &mut self.nodes[child_id];
470            node.parent = Some(parent_id);
471            child_idx += 1;
472        }
473
474        self.nodes[parent_id].children = children;
475    }
476
477    pub fn append(&mut self, node_id: usize, appended_node_ids: &[usize]) {
478        let node = &self.nodes[node_id];
479        let parent_id = node.parent.unwrap();
480        self.nodes[parent_id]
481            .children
482            .extend_from_slice(appended_node_ids);
483
484        // Update parent values
485        for &child_id in appended_node_ids {
486            self.nodes[child_id].parent = Some(parent_id);
487        }
488    }
489
490    /// Remove the node from it's parent but don't drop it
491    pub fn remove_node(&mut self, node_id: usize) {
492        let node = &mut self.nodes[node_id];
493
494        // Update child_idx values
495        if let Some(parent_id) = node.parent.take() {
496            let parent = &mut self.nodes[parent_id];
497            parent.children.retain(|id| *id != node_id);
498        }
499    }
500
501    pub fn remove_and_drop_node(&mut self, node_id: usize) -> Option<Node> {
502        fn remove_node_ignoring_parent(doc: &mut BaseDocument, node_id: usize) -> Option<Node> {
503            let node = doc.nodes.try_remove(node_id);
504            if let Some(node) = &node {
505                for &child in &node.children {
506                    remove_node_ignoring_parent(doc, child);
507                }
508            }
509            node
510        }
511
512        let node = remove_node_ignoring_parent(self, node_id);
513
514        // Update child_idx values
515        if let Some(parent_id) = node.as_ref().and_then(|node| node.parent) {
516            let parent = &mut self.nodes[parent_id];
517            parent.children.retain(|id| *id != node_id);
518        }
519
520        node
521    }
522
523    pub fn resolve_url(&self, raw: &str) -> url::Url {
524        resolve_url(&self.base_url, raw).unwrap_or_else(|| {
525            panic!(
526                "to be able to resolve {raw} with the base_url: {base_url:?}",
527                base_url = self.base_url
528            )
529        })
530    }
531
532    pub fn print_tree(&self) {
533        crate::util::walk_tree(0, self.root_node());
534    }
535
536    pub fn print_subtree(&self, node_id: usize) {
537        crate::util::walk_tree(0, &self.nodes[node_id]);
538    }
539
540    pub fn process_style_element(&mut self, target_id: usize) {
541        let css = self.nodes[target_id].text_content();
542        let css = html_escape::decode_html_entities(&css);
543        let sheet = self.make_stylesheet(&css, Origin::Author);
544        self.add_stylesheet_for_node(sheet, target_id);
545    }
546
547    pub fn remove_user_agent_stylesheet(&mut self, contents: &str) {
548        if let Some(sheet) = self.ua_stylesheets.remove(contents) {
549            self.stylist.remove_stylesheet(sheet, &self.guard.read());
550        }
551    }
552
553    pub fn add_user_agent_stylesheet(&mut self, css: &str) {
554        let sheet = self.make_stylesheet(css, Origin::UserAgent);
555        self.ua_stylesheets.insert(css.to_string(), sheet.clone());
556        self.stylist.append_stylesheet(sheet, &self.guard.read());
557    }
558
559    pub fn make_stylesheet(&self, css: impl AsRef<str>, origin: Origin) -> DocumentStyleSheet {
560        let data = Stylesheet::from_str(
561            css.as_ref(),
562            UrlExtraData::from(self.base_url.clone().unwrap_or_else(|| {
563                "data:text/css;charset=utf-8;base64,"
564                    .parse::<Url>()
565                    .unwrap()
566            })),
567            origin,
568            ServoArc::new(self.guard.wrap(MediaList::empty())),
569            self.guard.clone(),
570            Some(&StylesheetLoader(self.id, self.net_provider.clone())),
571            None,
572            QuirksMode::NoQuirks,
573            AllowImportRules::Yes,
574        );
575
576        DocumentStyleSheet(ServoArc::new(data))
577    }
578
579    pub fn upsert_stylesheet_for_node(&mut self, node_id: usize) {
580        let raw_styles = self.nodes[node_id].text_content();
581        let sheet = self.make_stylesheet(raw_styles, Origin::Author);
582        self.add_stylesheet_for_node(sheet, node_id);
583    }
584
585    pub fn add_stylesheet_for_node(&mut self, stylesheet: DocumentStyleSheet, node_id: usize) {
586        let old = self.nodes_to_stylesheet.insert(node_id, stylesheet.clone());
587
588        if let Some(old) = old {
589            self.stylist.remove_stylesheet(old, &self.guard.read())
590        }
591
592        // TODO: Nodes could potentially get reused so ordering by node_id might be wrong.
593        let insertion_point = self
594            .nodes_to_stylesheet
595            .range((Bound::Excluded(node_id), Bound::Unbounded))
596            .next()
597            .map(|(_, sheet)| sheet);
598
599        if let Some(insertion_point) = insertion_point {
600            self.stylist.insert_stylesheet_before(
601                stylesheet,
602                insertion_point.clone(),
603                &self.guard.read(),
604            )
605        } else {
606            self.stylist
607                .append_stylesheet(stylesheet, &self.guard.read())
608        }
609    }
610
611    pub fn load_resource(&mut self, resource: Resource) {
612        match resource {
613            Resource::Css(node_id, css) => {
614                self.add_stylesheet_for_node(css, node_id);
615            }
616            Resource::Image(node_id, kind, width, height, image_data) => {
617                let node = self.get_node_mut(node_id).unwrap();
618
619                match kind {
620                    ImageType::Image => {
621                        node.element_data_mut().unwrap().node_specific_data =
622                            NodeSpecificData::Image(Box::new(ImageData::Raster(
623                                RasterImageData::new(width, height, image_data),
624                            )));
625
626                        // Clear layout cache
627                        node.cache.clear();
628                    }
629                    ImageType::Background(idx) => {
630                        if let Some(Some(bg_image)) = node
631                            .element_data_mut()
632                            .and_then(|el| el.background_images.get_mut(idx))
633                        {
634                            bg_image.status = Status::Ok;
635                            bg_image.image =
636                                ImageData::Raster(RasterImageData::new(width, height, image_data))
637                        }
638                    }
639                }
640            }
641            #[cfg(feature = "svg")]
642            Resource::Svg(node_id, kind, tree) => {
643                let node = self.get_node_mut(node_id).unwrap();
644
645                match kind {
646                    ImageType::Image => {
647                        node.element_data_mut().unwrap().node_specific_data =
648                            NodeSpecificData::Image(Box::new(ImageData::Svg(tree)));
649
650                        // Clear layout cache
651                        node.cache.clear();
652                    }
653                    ImageType::Background(idx) => {
654                        if let Some(Some(bg_image)) = node
655                            .element_data_mut()
656                            .and_then(|el| el.background_images.get_mut(idx))
657                        {
658                            bg_image.status = Status::Ok;
659                            bg_image.image = ImageData::Svg(tree);
660                        }
661                    }
662                }
663            }
664            Resource::Font(bytes) => {
665                // TODO: Implement FontInfoOveride
666                // TODO: Investigate eliminating double-box
667                self.font_ctx
668                    .collection
669                    .register_fonts(Blob::new(Arc::new(bytes)) as _, None);
670            }
671            Resource::None => {
672                // Do nothing
673            }
674            _ => {}
675        }
676    }
677
678    pub fn snapshot_node(&mut self, node_id: usize) {
679        let node = &mut self.nodes[node_id];
680        let opaque_node_id = TNode::opaque(&&*node);
681        node.has_snapshot = true;
682        node.snapshot_handled
683            .store(false, std::sync::atomic::Ordering::SeqCst);
684
685        // TODO: handle invalidations other than hover
686        if let Some(_existing_snapshot) = self.snapshots.get_mut(&opaque_node_id) {
687            // Do nothing
688            // TODO: update snapshot
689        } else {
690            let attrs: Option<Vec<_>> = node.attrs().map(|attrs| {
691                attrs
692                    .iter()
693                    .map(|attr| {
694                        let ident = AttrIdentifier {
695                            local_name: GenericAtomIdent(attr.name.local.clone()),
696                            name: GenericAtomIdent(attr.name.local.clone()),
697                            namespace: GenericAtomIdent(attr.name.ns.clone()),
698                            prefix: None,
699                        };
700
701                        let value = if attr.name.local == local_name!("id") {
702                            AttrValue::Atom(Atom::from(&*attr.value))
703                        } else if attr.name.local == local_name!("class") {
704                            let classes = attr
705                                .value
706                                .split_ascii_whitespace()
707                                .map(Atom::from)
708                                .collect();
709                            AttrValue::TokenList(attr.value.clone(), classes)
710                        } else {
711                            AttrValue::String(attr.value.clone())
712                        };
713
714                        (ident, value)
715                    })
716                    .collect()
717            });
718
719            let changed_attrs = attrs
720                .as_ref()
721                .map(|attrs| attrs.iter().map(|attr| attr.0.name.clone()).collect())
722                .unwrap_or_default();
723
724            self.snapshots.insert(
725                opaque_node_id,
726                ServoElementSnapshot {
727                    state: Some(node.element_state),
728                    attrs,
729                    changed_attrs,
730                    class_changed: true,
731                    id_changed: true,
732                    other_attributes_changed: true,
733                },
734            );
735        }
736    }
737
738    pub fn snapshot_node_and(&mut self, node_id: usize, cb: impl FnOnce(&mut Node)) {
739        self.snapshot_node(node_id);
740        cb(&mut self.nodes[node_id]);
741    }
742
743    /// Restyle the tree and then relayout it
744    pub fn resolve(&mut self) {
745        if TDocument::as_node(&&self.nodes[0])
746            .first_element_child()
747            .is_none()
748        {
749            println!("No DOM - not resolving");
750            return;
751        }
752
753        // we need to resolve stylist first since it will need to drive our layout bits
754        self.resolve_stylist();
755
756        // Fix up tree for layout (insert anonymous blocks as necessary, etc)
757        self.resolve_layout_children();
758
759        // Merge stylo into taffy
760        self.flush_styles_to_layout(self.root_element().id);
761
762        // Next we resolve layout with the data resolved by stlist
763        self.resolve_layout();
764    }
765
766    // Takes (x, y) co-ordinates (relative to the )
767    pub fn hit(&self, x: f32, y: f32) -> Option<HitResult> {
768        if TDocument::as_node(&&self.nodes[0])
769            .first_element_child()
770            .is_none()
771        {
772            println!("No DOM - not resolving");
773            return None;
774        }
775
776        self.root_element().hit(x, y)
777    }
778
779    /// If the node is non-anonymous then returns the node's id
780    /// Else find's the first non-anonymous ancester of the node
781    pub fn non_anon_ancestor_if_anon(&self, mut node_id: usize) -> usize {
782        loop {
783            let node = &self.nodes[node_id];
784
785            if !node.is_anonymous() {
786                return node.id;
787            }
788
789            let Some(parent_id) = node.layout_parent.get() else {
790                // Shouldn't be reachable unless invalid node_id is passed
791                // as root node is always non-anonymous
792                panic!("Node does not exist or does not have a non-anonymous parent");
793            };
794
795            node_id = parent_id;
796        }
797    }
798
799    pub fn iter_children_mut(
800        &mut self,
801        node_id: usize,
802        mut cb: impl FnMut(usize, &mut BaseDocument),
803    ) {
804        let children = std::mem::take(&mut self.nodes[node_id].children);
805        for child_id in children.iter().cloned() {
806            cb(child_id, self);
807        }
808        self.nodes[node_id].children = children;
809    }
810
811    pub fn iter_subtree_mut(
812        &mut self,
813        node_id: usize,
814        mut cb: impl FnMut(usize, &mut BaseDocument),
815    ) {
816        cb(node_id, self);
817        iter_subtree_mut_inner(self, node_id, &mut cb);
818        fn iter_subtree_mut_inner(
819            doc: &mut BaseDocument,
820            node_id: usize,
821            cb: &mut impl FnMut(usize, &mut BaseDocument),
822        ) {
823            let children = std::mem::take(&mut doc.nodes[node_id].children);
824            for child_id in children.iter().cloned() {
825                cb(child_id, doc);
826                iter_subtree_mut_inner(doc, child_id, cb);
827            }
828            doc.nodes[node_id].children = children;
829        }
830    }
831
832    pub fn iter_children_and_pseudos_mut(
833        &mut self,
834        node_id: usize,
835        mut cb: impl FnMut(usize, &mut BaseDocument),
836    ) {
837        let before = self.nodes[node_id].before.take();
838        if let Some(before_node_id) = before {
839            cb(before_node_id, self)
840        }
841        self.nodes[node_id].before = before;
842
843        self.iter_children_mut(node_id, &mut cb);
844
845        let after = self.nodes[node_id].after.take();
846        if let Some(after_node_id) = after {
847            cb(after_node_id, self)
848        }
849        self.nodes[node_id].after = after;
850    }
851
852    pub fn next_node(&self, start: &Node, mut filter: impl FnMut(&Node) -> bool) -> Option<usize> {
853        let start_id = start.id;
854        let mut node = start;
855        let mut look_in_children = true;
856        loop {
857            // Next is first child
858            let next = if look_in_children && !node.children.is_empty() {
859                let node_id = node.children[0];
860                &self.nodes[node_id]
861            }
862            // Next is next sibling or parent
863            else if let Some(parent) = node.parent_node() {
864                let self_idx = parent
865                    .children
866                    .iter()
867                    .position(|id| *id == node.id)
868                    .unwrap();
869                // Next is next sibling
870                if let Some(sibling_id) = parent.children.get(self_idx + 1) {
871                    look_in_children = true;
872                    &self.nodes[*sibling_id]
873                }
874                // Next is parent
875                else {
876                    look_in_children = false;
877                    node = parent;
878                    continue;
879                }
880            }
881            // Continue search from the root
882            else {
883                look_in_children = true;
884                self.root_node()
885            };
886
887            if filter(next) {
888                return Some(next.id);
889            } else if next.id == start_id {
890                return None;
891            }
892
893            node = next;
894        }
895    }
896
897    pub fn node_layout_ancestors(&self, node_id: usize) -> Vec<usize> {
898        let mut ancestors = Vec::with_capacity(12);
899        let mut maybe_id = Some(node_id);
900        while let Some(id) = maybe_id {
901            ancestors.push(id);
902            maybe_id = self.nodes[id].layout_parent.get();
903        }
904        ancestors.reverse();
905        ancestors
906    }
907
908    pub fn maybe_node_layout_ancestors(&self, node_id: Option<usize>) -> Vec<usize> {
909        node_id
910            .map(|id| self.node_layout_ancestors(id))
911            .unwrap_or_default()
912    }
913
914    pub fn focus_next_node(&mut self) -> Option<usize> {
915        let focussed_node_id = self.get_focussed_node_id()?;
916        let id = self.next_node(&self.nodes[focussed_node_id], |node| node.is_focussable())?;
917        self.set_focus_to(id);
918        Some(id)
919    }
920
921    /// Clear the focussed node
922    pub fn clear_focus(&mut self) {
923        if let Some(id) = self.focus_node_id {
924            self.snapshot_node_and(id, |node| node.blur());
925            self.focus_node_id = None;
926        }
927    }
928
929    pub fn set_mousedown_node_id(&mut self, node_id: Option<usize>) {
930        self.mousedown_node_id = node_id;
931    }
932    pub fn set_focus_to(&mut self, focus_node_id: usize) -> bool {
933        if Some(focus_node_id) == self.focus_node_id {
934            return false;
935        }
936
937        println!("Focussed node {focus_node_id}");
938
939        // Remove focus from the old node
940        if let Some(id) = self.focus_node_id {
941            self.snapshot_node_and(id, |node| node.blur());
942        }
943
944        // Focus the new node
945        self.snapshot_node_and(focus_node_id, |node| node.focus());
946
947        self.focus_node_id = Some(focus_node_id);
948
949        true
950    }
951
952    pub fn active_node(&mut self) -> bool {
953        let Some(hover_node_id) = self.get_hover_node_id() else {
954            return false;
955        };
956
957        if let Some(active_node_id) = self.active_node_id {
958            if active_node_id == hover_node_id {
959                return true;
960            }
961            self.unactive_node();
962        }
963
964        let active_node_id = Some(hover_node_id);
965
966        let node_path = self.maybe_node_layout_ancestors(active_node_id);
967        for &id in node_path.iter() {
968            self.snapshot_node_and(id, |node| node.active());
969        }
970
971        self.active_node_id = active_node_id;
972
973        true
974    }
975
976    pub fn unactive_node(&mut self) -> bool {
977        let Some(active_node_id) = self.active_node_id.take() else {
978            return false;
979        };
980
981        let node_path = self.maybe_node_layout_ancestors(Some(active_node_id));
982        for &id in node_path.iter() {
983            self.snapshot_node_and(id, |node| node.unactive());
984        }
985
986        true
987    }
988
989    pub fn set_hover_to(&mut self, x: f32, y: f32) -> bool {
990        let hit = self.hit(x, y);
991        let hover_node_id = hit.map(|hit| hit.node_id);
992
993        // Return early if the new node is the same as the already-hovered node
994        if hover_node_id == self.hover_node_id {
995            return false;
996        }
997
998        let old_node_path = self.maybe_node_layout_ancestors(self.hover_node_id);
999        let new_node_path = self.maybe_node_layout_ancestors(hover_node_id);
1000        let same_count = old_node_path
1001            .iter()
1002            .zip(&new_node_path)
1003            .take_while(|(o, n)| o == n)
1004            .count();
1005        for &id in old_node_path.iter().skip(same_count) {
1006            self.snapshot_node_and(id, |node| node.unhover());
1007        }
1008        for &id in new_node_path.iter().skip(same_count) {
1009            self.snapshot_node_and(id, |node| node.hover());
1010        }
1011
1012        self.hover_node_id = hover_node_id;
1013
1014        // Update the cursor
1015        let cursor = self.get_cursor().unwrap_or_default();
1016        self.shell_provider.set_cursor(cursor);
1017
1018        // Request redraw
1019        self.shell_provider.request_redraw();
1020
1021        true
1022    }
1023
1024    pub fn get_hover_node_id(&self) -> Option<usize> {
1025        self.hover_node_id
1026    }
1027
1028    pub fn set_viewport(&mut self, viewport: Viewport) {
1029        self.viewport = viewport;
1030        self.set_stylist_device(make_device(&self.viewport));
1031        self.scroll_viewport_by(0.0, 0.0); // Clamp scroll offset
1032    }
1033
1034    pub fn viewport(&self) -> &Viewport {
1035        &self.viewport
1036    }
1037
1038    pub fn viewport_mut(&mut self) -> ViewportMut<'_> {
1039        ViewportMut::new(self)
1040    }
1041
1042    pub fn zoom_by(&mut self, increment: f32) {
1043        *self.viewport.zoom_mut() += increment;
1044        self.set_viewport(self.viewport.clone());
1045    }
1046
1047    pub fn zoom_to(&mut self, zoom: f32) {
1048        *self.viewport.zoom_mut() = zoom;
1049        self.set_viewport(self.viewport.clone());
1050    }
1051
1052    pub fn get_viewport(&self) -> Viewport {
1053        self.viewport.clone()
1054    }
1055
1056    pub fn devtools(&self) -> &Devtools {
1057        &self.devtool_settings
1058    }
1059
1060    pub fn devtools_mut(&mut self) -> &mut Devtools {
1061        &mut self.devtool_settings
1062    }
1063
1064    /// Update the device and reset the stylist to process the new size
1065    pub fn set_stylist_device(&mut self, device: Device) {
1066        let origins = {
1067            let guard = &self.guard;
1068            let guards = StylesheetGuards {
1069                author: &guard.read(),
1070                ua_or_user: &guard.read(),
1071            };
1072            self.stylist.set_device(device, &guards)
1073        };
1074        self.stylist.force_stylesheet_origins_dirty(origins);
1075    }
1076
1077    pub fn stylist_device(&mut self) -> &Device {
1078        self.stylist.device()
1079    }
1080
1081    /// Ensure that the layout_children field is populated for all nodes
1082    pub fn resolve_layout_children(&mut self) {
1083        resolve_layout_children_recursive(self, self.root_node().id);
1084
1085        fn resolve_layout_children_recursive(doc: &mut BaseDocument, node_id: usize) {
1086            // if doc.nodes[node_id].layout_children.borrow().is_none() {
1087            let mut layout_children = Vec::new();
1088            let mut anonymous_block: Option<usize> = None;
1089            collect_layout_children(doc, node_id, &mut layout_children, &mut anonymous_block);
1090
1091            // Recurse into newly collected layout children
1092            for child_id in layout_children.iter().copied() {
1093                resolve_layout_children_recursive(doc, child_id);
1094                doc.nodes[child_id].layout_parent.set(Some(node_id));
1095            }
1096
1097            *doc.nodes[node_id].layout_children.borrow_mut() = Some(layout_children.clone());
1098            *doc.nodes[node_id].paint_children.borrow_mut() = Some(layout_children);
1099            // }
1100        }
1101    }
1102
1103    /// Walk the nodes now that they're properly styled and transfer their styles to the taffy style system
1104    /// Ideally we could just break apart the styles into ECS bits, but alas
1105    ///
1106    /// Todo: update taffy to use an associated type instead of slab key
1107    /// Todo: update taffy to support traited styles so we don't even need to rely on taffy for storage
1108    pub fn resolve_layout(&mut self) {
1109        let size = self.stylist.device().au_viewport_size();
1110
1111        let available_space = taffy::Size {
1112            width: AvailableSpace::Definite(size.width.to_f32_px()),
1113            height: AvailableSpace::Definite(size.height.to_f32_px()),
1114        };
1115
1116        let root_element_id = taffy::NodeId::from(self.root_element().id);
1117
1118        // println!("\n\nRESOLVE LAYOUT\n===========\n");
1119
1120        taffy::compute_root_layout(self, root_element_id, available_space);
1121        taffy::round_layout(self, root_element_id);
1122
1123        // println!("\n\n");
1124        // taffy::print_tree(self, root_node_id)
1125    }
1126
1127    pub fn set_document(&mut self, _content: String) {}
1128
1129    pub fn add_element(&mut self) {}
1130
1131    pub fn get_cursor(&self) -> Option<CursorIcon> {
1132        // todo: cache this on the node itself
1133        let node = &self.nodes[self.get_hover_node_id()?];
1134
1135        let style = node.primary_styles()?;
1136        let keyword = stylo_to_cursor_icon(style.clone_cursor().keyword);
1137
1138        // Return cursor from style if it is non-auto
1139        if keyword != CursorIcon::Default {
1140            return Some(keyword);
1141        }
1142
1143        // Return text cursor for text nodes and text inputs
1144        if node.is_text_node()
1145            || node
1146                .element_data()
1147                .is_some_and(|e| e.text_input_data().is_some())
1148        {
1149            return Some(CursorIcon::Text);
1150        }
1151
1152        // Use "pointer" cursor if any ancestor is a link
1153        let mut maybe_node = Some(node);
1154        while let Some(node) = maybe_node {
1155            if node.is_link() {
1156                return Some(CursorIcon::Pointer);
1157            }
1158
1159            maybe_node = node.layout_parent.get().map(|node_id| node.with(node_id));
1160        }
1161
1162        // Else fallback to default cursor
1163        Some(CursorIcon::Default)
1164    }
1165
1166    /// Scroll a node by given x and y
1167    /// Will bubble scrolling up to parent node once it can no longer scroll further
1168    /// If we're already at the root node, bubbles scrolling up to the viewport
1169    pub fn scroll_node_by(&mut self, node_id: usize, x: f64, y: f64) {
1170        let Some(node) = self.nodes.get_mut(node_id) else {
1171            return;
1172        };
1173
1174        let is_html_or_body = node.data.downcast_element().is_some_and(|e| {
1175            let tag = &e.name.local;
1176            tag == "html" || tag == "body"
1177        });
1178
1179        let (can_x_scroll, can_y_scroll) = node
1180            .primary_styles()
1181            .map(|styles| {
1182                (
1183                    matches!(styles.clone_overflow_x(), Overflow::Scroll | Overflow::Auto),
1184                    matches!(styles.clone_overflow_y(), Overflow::Scroll | Overflow::Auto)
1185                        || (styles.clone_overflow_y() == Overflow::Visible && is_html_or_body),
1186                )
1187            })
1188            .unwrap_or((false, false));
1189
1190        let new_x = node.scroll_offset.x - x;
1191        let new_y = node.scroll_offset.y - y;
1192
1193        let mut bubble_x = 0.0;
1194        let mut bubble_y = 0.0;
1195
1196        let scroll_width = node.final_layout.scroll_width() as f64;
1197        let scroll_height = node.final_layout.scroll_height() as f64;
1198
1199        // If we're past our scroll bounds, transfer remainder of scrolling to parent/viewport
1200        if !can_x_scroll {
1201            bubble_x = x
1202        } else if new_x < 0.0 {
1203            bubble_x = -new_x;
1204            node.scroll_offset.x = 0.0;
1205        } else if new_x > scroll_width {
1206            bubble_x = scroll_width - new_x;
1207            node.scroll_offset.x = scroll_width;
1208        } else {
1209            node.scroll_offset.x = new_x;
1210        }
1211
1212        if !can_y_scroll {
1213            bubble_y = y
1214        } else if new_y < 0.0 {
1215            bubble_y = -new_y;
1216            node.scroll_offset.y = 0.0;
1217        } else if new_y > scroll_height {
1218            bubble_y = scroll_height - new_y;
1219            node.scroll_offset.y = scroll_height;
1220        } else {
1221            node.scroll_offset.y = new_y;
1222        }
1223
1224        if bubble_x != 0.0 || bubble_y != 0.0 {
1225            if let Some(parent) = node.parent {
1226                self.scroll_node_by(parent, bubble_x, bubble_y);
1227            } else {
1228                self.scroll_viewport_by(bubble_x, bubble_y);
1229            }
1230        }
1231    }
1232
1233    /// Scroll the viewport by the given values
1234    pub fn scroll_viewport_by(&mut self, x: f64, y: f64) {
1235        let content_size = self.root_element().final_layout.size;
1236        let new_scroll = (self.viewport_scroll.x - x, self.viewport_scroll.y - y);
1237        let window_width = self.viewport.window_size.0 as f64 / self.viewport.scale() as f64;
1238        let window_height = self.viewport.window_size.1 as f64 / self.viewport.scale() as f64;
1239        self.viewport_scroll.x = f64::max(
1240            0.0,
1241            f64::min(new_scroll.0, content_size.width as f64 - window_width),
1242        );
1243        self.viewport_scroll.y = f64::max(
1244            0.0,
1245            f64::min(new_scroll.1, content_size.height as f64 - window_height),
1246        )
1247    }
1248
1249    pub fn viewport_scroll(&self) -> kurbo::Point {
1250        self.viewport_scroll
1251    }
1252
1253    pub fn set_viewport_scroll(&mut self, scroll: kurbo::Point) {
1254        self.viewport_scroll = scroll;
1255    }
1256
1257    pub fn visit<F>(&self, mut visit: F)
1258    where
1259        F: FnMut(usize, &Node),
1260    {
1261        TreeTraverser::new(self).for_each(|node_id| visit(node_id, &self.nodes[node_id]));
1262    }
1263
1264    /// Collect the nodes into a chain by traversing upwards
1265    pub fn node_chain(&self, node_id: usize) -> Vec<usize> {
1266        let mut chain = Vec::with_capacity(16);
1267        chain.push(node_id);
1268        chain.extend(
1269            AncestorTraverser::new(self, node_id).filter(|id| self.nodes[*id].is_element()),
1270        );
1271        chain
1272    }
1273}
1274
1275impl AsRef<BaseDocument> for BaseDocument {
1276    fn as_ref(&self) -> &BaseDocument {
1277        self
1278    }
1279}
1280
1281impl AsMut<BaseDocument> for BaseDocument {
1282    fn as_mut(&mut self) -> &mut BaseDocument {
1283        self
1284    }
1285}