From 97f544aa206efe9db4a05cba8303ca3e8151e2af Mon Sep 17 00:00:00 2001 From: Jonathan Schwender <55576758+jschwe@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:54:16 +0800 Subject: [PATCH] dom: Optimize IFrameCollection::validate (#38196) The `IFrameCollection` was previously rebuilt too often. This PR tries to address the todo, to only rebuild the IFrameCollection when necessary. Testing: `CSS Selector Invalidation: :has() invalidation should not be O(n^2)` wpt-test changed status from timeout to failed. Fixes: #38131 (IFrameCollection::validate accounts for 30% of script time in DOM heavy scenarios) Co-authored-by: sharpshooter_pt Signed-off-by: Jonathan Schwender --- components/script/dom/document.rs | 6 ++++- components/script/dom/htmliframeelement.rs | 11 +++++++- components/script/iframe_collection.rs | 27 +++++++++++-------- .../invalidation/has-complexity.html.ini | 16 ++++++++++- 4 files changed, 46 insertions(+), 14 deletions(-) diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 89f323983eb..6a956c6ec78 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -3349,6 +3349,10 @@ impl Document { self.iframes.borrow_mut() } + pub(crate) fn invalidate_iframes_collection(&self) { + self.iframes.borrow_mut().invalidate(); + } + pub(crate) fn get_dom_interactive(&self) -> Option { self.dom_interactive.get() } @@ -4145,7 +4149,7 @@ impl Document { scripts: Default::default(), anchors: Default::default(), applets: Default::default(), - iframes: Default::default(), + iframes: RefCell::new(IFrameCollection::new()), style_shared_lock: { /// Per-process shared lock for author-origin stylesheets /// diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index be2641a0fee..ee6cd312e2e 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -43,7 +43,7 @@ use crate::dom::element::{ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; -use crate::dom::node::{Node, NodeDamage, NodeTraits, UnbindContext}; +use crate::dom::node::{BindContext, Node, NodeDamage, NodeTraits, UnbindContext}; use crate::dom::trustedhtml::TrustedHTML; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::windowproxy::WindowProxy; @@ -813,6 +813,13 @@ impl VirtualMethods for HTMLIFrameElement { } } + fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) { + if let Some(s) = self.super_type() { + s.bind_to_tree(context, can_gc); + } + self.owner_document().invalidate_iframes_collection(); + } + fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) { self.super_type().unwrap().unbind_from_tree(context, can_gc); @@ -865,5 +872,7 @@ impl VirtualMethods for HTMLIFrameElement { // a new iframe. Without this, the constellation gets very // confused. self.destroy_nested_browsing_context(); + + self.owner_document().invalidate_iframes_collection(); } } diff --git a/components/script/iframe_collection.rs b/components/script/iframe_collection.rs index 7626a190d81..b8828cd920c 100644 --- a/components/script/iframe_collection.rs +++ b/components/script/iframe_collection.rs @@ -2,7 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use std::cell::Cell; use std::default::Default; use base::id::BrowsingContextId; @@ -29,25 +28,31 @@ pub(crate) struct IFrame { #[derive(Default, JSTraceable, MallocSizeOf)] #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] pub(crate) struct IFrameCollection { - /// The version of the [`Document`] that this collection refers to. When the versions - /// do not match, the collection will need to be rebuilt. - document_version: Cell, /// The `