diff --git a/components/script/document_collection.rs b/components/script/document_collection.rs index 0dc4ce87bb4..d9c28f6b7db 100644 --- a/components/script/document_collection.rs +++ b/components/script/document_collection.rs @@ -55,8 +55,12 @@ impl DocumentCollection { pipeline_id: PipelineId, browsing_context_id: BrowsingContextId, ) -> Option> { - self.find_document(pipeline_id) - .and_then(|doc| doc.find_iframe(browsing_context_id)) + self.find_document(pipeline_id).and_then(|document| { + document + .iframes() + .get(browsing_context_id) + .map(|iframe| iframe.element.as_rooted()) + }) } pub fn iter(&self) -> DocumentsIter<'_> { @@ -83,9 +87,6 @@ impl DocumentCollection { /// /// [update-the-rendering]: https://html.spec.whatwg.org/multipage/#update-the-rendering pub(crate) fn documents_in_order(&self) -> Vec { - // TODO: This is a fairly expensive operation, because iterating iframes requires walking - // the *entire* DOM for a document. Instead this should be cached and marked as dirty when - // the DOM of a document changes or documents are added or removed from our set. DocumentTree::new(self).documents_in_order() } } @@ -140,7 +141,8 @@ impl DocumentTree { let mut tree = DocumentTree::default(); for (id, document) in documents.iter() { let children: Vec = document - .iter_iframes() + .iframes() + .iter() .filter_map(|iframe| iframe.pipeline_id()) .filter(|iframe_pipeline_id| documents.find_document(*iframe_pipeline_id).is_some()) .collect(); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 5dfcedda5b9..577b21d7dc2 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -15,7 +15,6 @@ use std::sync::{LazyLock, Mutex}; use std::time::{Duration, Instant}; use base::cross_process_instant::CrossProcessInstant; -use base::id::BrowsingContextId; use canvas_traits::webgl::{self, WebGLContextId, WebGLMsg}; use chrono::Local; use content_security_policy::{self as csp, CspList}; @@ -185,6 +184,7 @@ use crate::dom::window::Window; use crate::dom::windowproxy::WindowProxy; use crate::dom::xpathevaluator::XPathEvaluator; use crate::fetch::FetchCanceller; +use crate::iframe_collection::IFrameCollection; use crate::network_listener::{NetworkListener, PreInvoke}; use crate::realms::{enter_realm, AlreadyInRealm, InRealm}; use crate::script_runtime::{CanGc, CommonScriptMsg, ScriptThreadEventCategory}; @@ -294,6 +294,8 @@ pub struct Document { scripts: MutNullableDom, anchors: MutNullableDom, applets: MutNullableDom, + /// Information about the `` in this [`Document`]. + iframes: RefCell, /// Lock use for style attributes and author-origin stylesheet objects in this document. /// Can be acquired once for accessing many objects. #[no_trace] @@ -2251,9 +2253,12 @@ impl Document { } // Step 9 if !recursive_flag { - for iframe in self.iter_iframes() { + // `prompt_to_unload` might cause futher modifications to the DOM so collecting here prevents + // a double borrow if the `IFrameCollection` needs to be validated again. + let iframes: Vec<_> = self.iframes().iter().collect(); + for iframe in &iframes { // TODO: handle the case of cross origin iframes. - let document = document_from_node(&*iframe); + let document = document_from_node(&**iframe); can_unload = document.prompt_to_unload(true, can_gc); if !document.salvageable() { self.salvageable.set(false); @@ -2320,9 +2325,12 @@ impl Document { // Step 13 if !recursive_flag { - for iframe in self.iter_iframes() { + // `unload` might cause futher modifications to the DOM so collecting here prevents + // a double borrow if the `IFrameCollection` needs to be validated again. + let iframes: Vec<_> = self.iframes().iter().collect(); + for iframe in &iframes { // TODO: handle the case of cross origin iframes. - let document = document_from_node(&*iframe); + let document = document_from_node(&**iframe); document.unload(true, can_gc); if !document.salvageable() { self.salvageable.set(false); @@ -2677,7 +2685,7 @@ impl Document { self.loader.borrow_mut().inhibit_events(); // Step 1. - for iframe in self.iter_iframes() { + for iframe in self.iframes().iter() { if let Some(document) = iframe.GetContentDocument() { // TODO: abort the active documents of every child browsing context. document.abort(can_gc); @@ -2726,20 +2734,18 @@ impl Document { self.current_parser.get() } - /// Iterate over all iframes in the document. - pub fn iter_iframes(&self) -> impl Iterator> { - self.upcast::() - .traverse_preorder(ShadowIncluding::Yes) - .filter_map(DomRoot::downcast::) + /// A reference to the [`IFrameCollection`] of this [`Document`], holding information about + /// `