diff options
-rw-r--r-- | components/script/dom/bindings/callback.rs | 6 | ||||
-rw-r--r-- | components/script/dom/document.rs | 20 | ||||
-rw-r--r-- | components/script/dom/node.rs | 12 | ||||
-rw-r--r-- | components/script/dom/window.rs | 2 |
4 files changed, 40 insertions, 0 deletions
diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs index 2cd289d29c9..88bb02ce44e 100644 --- a/components/script/dom/bindings/callback.rs +++ b/components/script/dom/bindings/callback.rs @@ -4,12 +4,15 @@ //! Base classes to work with IDL callbacks. +use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use crate::dom::bindings::error::{report_pending_exception, Error, Fallible}; +use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::settings_stack::{AutoEntryScript, AutoIncumbentScript}; use crate::dom::bindings::utils::AsCCharPtrPtr; use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use js::jsapi::Heap; use js::jsapi::JSAutoCompartment; use js::jsapi::{AddRawValueRoot, IsCallable, JSContext, JSObject}; @@ -242,6 +245,9 @@ impl CallSetup { #[allow(unrooted_must_root)] pub fn new<T: CallbackContainer>(callback: &T, handling: ExceptionHandling) -> CallSetup { let global = unsafe { GlobalScope::from_object(callback.callback()) }; + if let Some(window) = global.downcast::<Window>() { + window.Document().ensure_safe_to_run_script_or_layout(); + } let cx = global.get_cx(); let aes = AutoEntryScript::new(&global); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 5ee154658d7..709305f697a 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -410,6 +410,8 @@ pub struct Document { responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>, /// Number of redirects for the document load redirect_count: Cell<u16>, + /// + script_and_layout_blockers: Cell<u32>, } #[derive(JSTraceable, MallocSizeOf)] @@ -2695,9 +2697,27 @@ impl Document { fired_unload: Cell::new(false), responsive_images: Default::default(), redirect_count: Cell::new(0), + script_and_layout_blockers: Cell::new(0), } } + pub fn add_script_and_layout_blocker(&self) { + self.script_and_layout_blockers.set( + self.script_and_layout_blockers.get() + 1 + ); + } + + pub fn remove_script_and_layout_blocker(&self) { + assert!(self.script_and_layout_blockers.get() > 0); + self.script_and_layout_blockers.set( + self.script_and_layout_blockers.get() - 1 + ); + } + + pub fn ensure_safe_to_run_script_or_layout(&self) { + assert_eq!(self.script_and_layout_blockers.get(), 0); + } + // https://dom.spec.whatwg.org/#dom-document-document pub fn Constructor(window: &Window) -> Fallible<DomRoot<Document>> { let doc = window.Document(); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index c059137b21b..ff6374a8b99 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -1504,8 +1504,11 @@ impl Node { // https://dom.spec.whatwg.org/#concept-node-adopt pub fn adopt(node: &Node, document: &Document) { + document.add_script_and_layout_blocker(); + // Step 1. let old_doc = node.owner_doc(); + old_doc.add_script_and_layout_blocker(); // Step 2. node.remove_self(); // Step 3. @@ -1530,6 +1533,9 @@ impl Node { vtable_for(&descendant).adopting_steps(&old_doc); } } + + old_doc.remove_script_and_layout_blocker(); + document.remove_script_and_layout_blocker(); } // https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity @@ -1685,6 +1691,7 @@ impl Node { child: Option<&Node>, suppress_observers: SuppressObserver, ) { + node.owner_doc().add_script_and_layout_blocker(); debug_assert!(&*node.owner_doc() == &*parent.owner_doc()); debug_assert!(child.map_or(true, |child| Some(parent) == child.GetParentNode().r())); @@ -1774,10 +1781,12 @@ impl Node { }; MutationObserver::queue_a_mutation_record(&parent, mutation); } + node.owner_doc().remove_script_and_layout_blocker(); } // https://dom.spec.whatwg.org/#concept-node-replace-all pub fn replace_all(node: Option<&Node>, parent: &Node) { + parent.owner_doc().add_script_and_layout_blocker(); // Step 1. if let Some(node) = node { Node::adopt(node, &*parent.owner_doc()); @@ -1819,6 +1828,7 @@ impl Node { }; MutationObserver::queue_a_mutation_record(&parent, mutation); } + parent.owner_doc().remove_script_and_layout_blocker(); } // https://dom.spec.whatwg.org/#concept-node-pre-remove @@ -1839,6 +1849,7 @@ impl Node { // https://dom.spec.whatwg.org/#concept-node-remove fn remove(node: &Node, parent: &Node, suppress_observers: SuppressObserver) { + parent.owner_doc().add_script_and_layout_blocker(); assert!( node.GetParentNode() .map_or(false, |node_parent| &*node_parent == parent) @@ -1884,6 +1895,7 @@ impl Node { }; MutationObserver::queue_a_mutation_record(&parent, mutation); } + parent.owner_doc().remove_script_and_layout_blocker(); } // https://dom.spec.whatwg.org/#concept-node-clone diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 785404454bd..639d4677993 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1361,6 +1361,7 @@ impl Window { /// Returns true if layout actually happened, false otherwise. #[allow(unsafe_code)] pub fn force_reflow(&self, reflow_goal: ReflowGoal, reason: ReflowReason) -> bool { + self.Document().ensure_safe_to_run_script_or_layout(); // Check if we need to unsuppress reflow. Note that this needs to be // *before* any early bailouts, or reflow might never be unsuppresed! match reason { @@ -1497,6 +1498,7 @@ impl Window { /// may happen in the only case a query reflow may bail out, that is, if the /// viewport size is not present). See #11223 for an example of that. pub fn reflow(&self, reflow_goal: ReflowGoal, reason: ReflowReason) -> bool { + self.Document().ensure_safe_to_run_script_or_layout(); let for_display = reflow_goal == ReflowGoal::Full; let mut issued_reflow = false; |