mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Prevent JS execution and layout operations while DOM in inconsistent state.
This commit is contained in:
parent
231a37be24
commit
14b0de30db
4 changed files with 40 additions and 0 deletions
|
@ -4,12 +4,15 @@
|
||||||
|
|
||||||
//! Base classes to work with IDL callbacks.
|
//! 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::error::{report_pending_exception, Error, Fallible};
|
||||||
|
use crate::dom::bindings::inheritance::Castable;
|
||||||
use crate::dom::bindings::reflector::DomObject;
|
use crate::dom::bindings::reflector::DomObject;
|
||||||
use crate::dom::bindings::root::{Dom, DomRoot};
|
use crate::dom::bindings::root::{Dom, DomRoot};
|
||||||
use crate::dom::bindings::settings_stack::{AutoEntryScript, AutoIncumbentScript};
|
use crate::dom::bindings::settings_stack::{AutoEntryScript, AutoIncumbentScript};
|
||||||
use crate::dom::bindings::utils::AsCCharPtrPtr;
|
use crate::dom::bindings::utils::AsCCharPtrPtr;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
|
use crate::dom::window::Window;
|
||||||
use js::jsapi::Heap;
|
use js::jsapi::Heap;
|
||||||
use js::jsapi::JSAutoCompartment;
|
use js::jsapi::JSAutoCompartment;
|
||||||
use js::jsapi::{AddRawValueRoot, IsCallable, JSContext, JSObject};
|
use js::jsapi::{AddRawValueRoot, IsCallable, JSContext, JSObject};
|
||||||
|
@ -242,6 +245,9 @@ impl CallSetup {
|
||||||
#[allow(unrooted_must_root)]
|
#[allow(unrooted_must_root)]
|
||||||
pub fn new<T: CallbackContainer>(callback: &T, handling: ExceptionHandling) -> CallSetup {
|
pub fn new<T: CallbackContainer>(callback: &T, handling: ExceptionHandling) -> CallSetup {
|
||||||
let global = unsafe { GlobalScope::from_object(callback.callback()) };
|
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 cx = global.get_cx();
|
||||||
|
|
||||||
let aes = AutoEntryScript::new(&global);
|
let aes = AutoEntryScript::new(&global);
|
||||||
|
|
|
@ -410,6 +410,8 @@ pub struct Document {
|
||||||
responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>,
|
responsive_images: DomRefCell<Vec<Dom<HTMLImageElement>>>,
|
||||||
/// Number of redirects for the document load
|
/// Number of redirects for the document load
|
||||||
redirect_count: Cell<u16>,
|
redirect_count: Cell<u16>,
|
||||||
|
///
|
||||||
|
script_and_layout_blockers: Cell<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(JSTraceable, MallocSizeOf)]
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
|
@ -2695,9 +2697,27 @@ impl Document {
|
||||||
fired_unload: Cell::new(false),
|
fired_unload: Cell::new(false),
|
||||||
responsive_images: Default::default(),
|
responsive_images: Default::default(),
|
||||||
redirect_count: Cell::new(0),
|
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
|
// https://dom.spec.whatwg.org/#dom-document-document
|
||||||
pub fn Constructor(window: &Window) -> Fallible<DomRoot<Document>> {
|
pub fn Constructor(window: &Window) -> Fallible<DomRoot<Document>> {
|
||||||
let doc = window.Document();
|
let doc = window.Document();
|
||||||
|
|
|
@ -1504,8 +1504,11 @@ impl Node {
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#concept-node-adopt
|
// https://dom.spec.whatwg.org/#concept-node-adopt
|
||||||
pub fn adopt(node: &Node, document: &Document) {
|
pub fn adopt(node: &Node, document: &Document) {
|
||||||
|
document.add_script_and_layout_blocker();
|
||||||
|
|
||||||
// Step 1.
|
// Step 1.
|
||||||
let old_doc = node.owner_doc();
|
let old_doc = node.owner_doc();
|
||||||
|
old_doc.add_script_and_layout_blocker();
|
||||||
// Step 2.
|
// Step 2.
|
||||||
node.remove_self();
|
node.remove_self();
|
||||||
// Step 3.
|
// Step 3.
|
||||||
|
@ -1530,6 +1533,9 @@ impl Node {
|
||||||
vtable_for(&descendant).adopting_steps(&old_doc);
|
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
|
// https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
|
||||||
|
@ -1685,6 +1691,7 @@ impl Node {
|
||||||
child: Option<&Node>,
|
child: Option<&Node>,
|
||||||
suppress_observers: SuppressObserver,
|
suppress_observers: SuppressObserver,
|
||||||
) {
|
) {
|
||||||
|
node.owner_doc().add_script_and_layout_blocker();
|
||||||
debug_assert!(&*node.owner_doc() == &*parent.owner_doc());
|
debug_assert!(&*node.owner_doc() == &*parent.owner_doc());
|
||||||
debug_assert!(child.map_or(true, |child| Some(parent) == child.GetParentNode().r()));
|
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);
|
MutationObserver::queue_a_mutation_record(&parent, mutation);
|
||||||
}
|
}
|
||||||
|
node.owner_doc().remove_script_and_layout_blocker();
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#concept-node-replace-all
|
// https://dom.spec.whatwg.org/#concept-node-replace-all
|
||||||
pub fn replace_all(node: Option<&Node>, parent: &Node) {
|
pub fn replace_all(node: Option<&Node>, parent: &Node) {
|
||||||
|
parent.owner_doc().add_script_and_layout_blocker();
|
||||||
// Step 1.
|
// Step 1.
|
||||||
if let Some(node) = node {
|
if let Some(node) = node {
|
||||||
Node::adopt(node, &*parent.owner_doc());
|
Node::adopt(node, &*parent.owner_doc());
|
||||||
|
@ -1819,6 +1828,7 @@ impl Node {
|
||||||
};
|
};
|
||||||
MutationObserver::queue_a_mutation_record(&parent, mutation);
|
MutationObserver::queue_a_mutation_record(&parent, mutation);
|
||||||
}
|
}
|
||||||
|
parent.owner_doc().remove_script_and_layout_blocker();
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#concept-node-pre-remove
|
// https://dom.spec.whatwg.org/#concept-node-pre-remove
|
||||||
|
@ -1839,6 +1849,7 @@ impl Node {
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#concept-node-remove
|
// https://dom.spec.whatwg.org/#concept-node-remove
|
||||||
fn remove(node: &Node, parent: &Node, suppress_observers: SuppressObserver) {
|
fn remove(node: &Node, parent: &Node, suppress_observers: SuppressObserver) {
|
||||||
|
parent.owner_doc().add_script_and_layout_blocker();
|
||||||
assert!(
|
assert!(
|
||||||
node.GetParentNode()
|
node.GetParentNode()
|
||||||
.map_or(false, |node_parent| &*node_parent == parent)
|
.map_or(false, |node_parent| &*node_parent == parent)
|
||||||
|
@ -1884,6 +1895,7 @@ impl Node {
|
||||||
};
|
};
|
||||||
MutationObserver::queue_a_mutation_record(&parent, mutation);
|
MutationObserver::queue_a_mutation_record(&parent, mutation);
|
||||||
}
|
}
|
||||||
|
parent.owner_doc().remove_script_and_layout_blocker();
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#concept-node-clone
|
// https://dom.spec.whatwg.org/#concept-node-clone
|
||||||
|
|
|
@ -1361,6 +1361,7 @@ impl Window {
|
||||||
/// Returns true if layout actually happened, false otherwise.
|
/// Returns true if layout actually happened, false otherwise.
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
pub fn force_reflow(&self, reflow_goal: ReflowGoal, reason: ReflowReason) -> bool {
|
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
|
// Check if we need to unsuppress reflow. Note that this needs to be
|
||||||
// *before* any early bailouts, or reflow might never be unsuppresed!
|
// *before* any early bailouts, or reflow might never be unsuppresed!
|
||||||
match reason {
|
match reason {
|
||||||
|
@ -1497,6 +1498,7 @@ impl Window {
|
||||||
/// may happen in the only case a query reflow may bail out, that is, if the
|
/// 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.
|
/// viewport size is not present). See #11223 for an example of that.
|
||||||
pub fn reflow(&self, reflow_goal: ReflowGoal, reason: ReflowReason) -> bool {
|
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 for_display = reflow_goal == ReflowGoal::Full;
|
||||||
|
|
||||||
let mut issued_reflow = false;
|
let mut issued_reflow = false;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue