diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2017-05-15 15:00:19 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-05-15 15:00:19 -0500 |
commit | fa251ec96b445b9ba8439d76e05870a88c2caa0f (patch) | |
tree | d4fe49b542c7585a7f9acec508082c8e82f391ef /components/script/dom | |
parent | dfb939629616490af4248c58ec3675244dc10e27 (diff) | |
parent | b0bf2b4bad636acfba66d55571b417ebae795408 (diff) | |
download | servo-fa251ec96b445b9ba8439d76e05870a88c2caa0f.tar.gz servo-fa251ec96b445b9ba8439d76e05870a88c2caa0f.zip |
Auto merge of #16295 - jdm:transition-safety, r=nox
Root nodes for the duration of their CSS transitions
This ensures that we can pass a node address as part of the asynchronous
transition end notification, making it safe to fire the corresponding
DOM event on the node from the script thread. Without explicitly rooting
this node when the transition starts, we risk the node being GCed before
the transition is complete.
---
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #14972
- [X] There are tests for these changes
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16295)
<!-- Reviewable:end -->
Diffstat (limited to 'components/script/dom')
-rw-r--r-- | components/script/dom/document.rs | 26 | ||||
-rw-r--r-- | components/script/dom/node.rs | 18 | ||||
-rw-r--r-- | components/script/dom/window.rs | 23 |
3 files changed, 42 insertions, 25 deletions
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 2f0d9dcfde3..bce898e6347 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -826,6 +826,7 @@ impl Document { } } + #[allow(unsafe_code)] pub fn handle_mouse_event(&self, js_runtime: *mut JSRuntime, button: MouseButton, @@ -841,7 +842,9 @@ impl Document { let node = match self.window.hit_test_query(client_point, false) { Some(node_address) => { debug!("node address is {:?}", node_address); - node::from_untrusted_node_address(js_runtime, node_address) + unsafe { + node::from_untrusted_node_address(js_runtime, node_address) + } }, None => return, }; @@ -988,13 +991,16 @@ impl Document { *self.last_click_info.borrow_mut() = Some((now, click_pos)); } + #[allow(unsafe_code)] pub fn handle_touchpad_pressure_event(&self, js_runtime: *mut JSRuntime, client_point: Point2D<f32>, pressure: f32, phase_now: TouchpadPressurePhase) { let node = match self.window.hit_test_query(client_point, false) { - Some(node_address) => node::from_untrusted_node_address(js_runtime, node_address), + Some(node_address) => unsafe { + node::from_untrusted_node_address(js_runtime, node_address) + }, None => return }; @@ -1089,6 +1095,7 @@ impl Document { event.fire(target); } + #[allow(unsafe_code)] pub fn handle_mouse_move_event(&self, js_runtime: *mut JSRuntime, client_point: Option<Point2D<f32>>, @@ -1104,7 +1111,7 @@ impl Document { }; let maybe_new_target = self.window.hit_test_query(client_point, true).and_then(|address| { - let node = node::from_untrusted_node_address(js_runtime, address); + let node = unsafe { node::from_untrusted_node_address(js_runtime, address) }; node.inclusive_ancestors() .filter_map(Root::downcast::<Element>) .next() @@ -1186,6 +1193,7 @@ impl Document { ReflowReason::MouseEvent); } + #[allow(unsafe_code)] pub fn handle_touch_event(&self, js_runtime: *mut JSRuntime, event_type: TouchEventType, @@ -1202,7 +1210,9 @@ impl Document { }; let node = match self.window.hit_test_query(point, false) { - Some(node_address) => node::from_untrusted_node_address(js_runtime, node_address), + Some(node_address) => unsafe { + node::from_untrusted_node_address(js_runtime, node_address) + }, None => return TouchEventResult::Processed(false), }; let el = match node.downcast::<Element>() { @@ -3480,7 +3490,9 @@ impl DocumentMethods for Document { Some(untrusted_node_address) => { let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) }; - let node = node::from_untrusted_node_address(js_runtime, untrusted_node_address); + let node = unsafe { + node::from_untrusted_node_address(js_runtime, untrusted_node_address) + }; let parent_node = node.GetParentNode().unwrap(); let element_ref = node.downcast::<Element>().unwrap_or_else(|| { parent_node.downcast::<Element>().unwrap() @@ -3515,7 +3527,9 @@ impl DocumentMethods for Document { // Step 1 and Step 3 let mut elements: Vec<Root<Element>> = self.nodes_from_point(point).iter() .flat_map(|&untrusted_node_address| { - let node = node::from_untrusted_node_address(js_runtime, untrusted_node_address); + let node = unsafe { + node::from_untrusted_node_address(js_runtime, untrusted_node_address) + }; Root::downcast::<Element>(node) }).collect(); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 81a06850757..6d16cac6731 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -927,20 +927,18 @@ fn first_node_not_in<I>(mut nodes: I, not_in: &[NodeOrString]) -> Option<Root<No /// If the given untrusted node address represents a valid DOM node in the given runtime, /// returns it. #[allow(unsafe_code)] -pub fn from_untrusted_node_address(_runtime: *mut JSRuntime, candidate: UntrustedNodeAddress) +pub unsafe fn from_untrusted_node_address(_runtime: *mut JSRuntime, candidate: UntrustedNodeAddress) -> Root<Node> { - unsafe { - // https://github.com/servo/servo/issues/6383 - let candidate: uintptr_t = mem::transmute(candidate.0); + // https://github.com/servo/servo/issues/6383 + let candidate: uintptr_t = mem::transmute(candidate.0); // let object: *mut JSObject = jsfriendapi::bindgen::JS_GetAddressableObject(runtime, // candidate); - let object: *mut JSObject = mem::transmute(candidate); - if object.is_null() { - panic!("Attempted to create a `JS<Node>` from an invalid pointer!") - } - let boxed_node = conversions::private_from_object(object) as *const Node; - Root::from_ref(&*boxed_node) + let object: *mut JSObject = mem::transmute(candidate); + if object.is_null() { + panic!("Attempted to create a `JS<Node>` from an invalid pointer!") } + let boxed_node = conversions::private_from_object(object) as *const Node; + Root::from_ref(&*boxed_node) } #[allow(unsafe_code)] diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 993749b00c2..4317d2890b7 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -76,7 +76,7 @@ use script_layout_interface::rpc::{MarginStyleResponse, NodeScrollRootIdResponse use script_layout_interface::rpc::{ResolvedStyleResponse, TextIndexResponse}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThreadEventCategory}; use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, Runnable, RunnableWrapper}; -use script_thread::{SendableMainThreadScriptChan, ImageCacheMsg}; +use script_thread::{SendableMainThreadScriptChan, ImageCacheMsg, ScriptThread}; use script_traits::{ConstellationControlMsg, LoadData, MozBrowserEvent, UntrustedNodeAddress}; use script_traits::{DocumentState, TimerEvent, TimerEventId}; use script_traits::{ScriptMsg as ConstellationMsg, TimerSchedulerMsg, WindowSizeData, WindowSizeType}; @@ -1150,6 +1150,7 @@ impl Window { /// off-main-thread layout. /// /// Returns true if layout actually happened, false otherwise. + #[allow(unsafe_code)] pub fn force_reflow(&self, goal: ReflowGoal, query_type: ReflowQueryType, @@ -1213,16 +1214,16 @@ impl Window { debug!("script: layout forked"); - match join_port.try_recv() { + let complete = match join_port.try_recv() { Err(Empty) => { info!("script: waiting on layout"); - join_port.recv().unwrap(); + join_port.recv().unwrap() } - Ok(_) => {} + Ok(reflow_complete) => reflow_complete, Err(Disconnected) => { panic!("Layout thread failed while script was waiting for a result."); } - } + }; debug!("script: layout joined"); @@ -1236,12 +1237,11 @@ impl Window { self.emit_timeline_marker(marker.end()); } - let pending_images = self.layout_rpc.pending_images(); - for image in pending_images { + for image in complete.pending_images { let id = image.id; let js_runtime = self.js_runtime.borrow(); let js_runtime = js_runtime.as_ref().unwrap(); - let node = from_untrusted_node_address(js_runtime.rt(), image.node); + let node = unsafe { from_untrusted_node_address(js_runtime.rt(), image.node) }; if let PendingImageState::Unrequested(ref url) = image.state { fetch_image_for_layout(url.clone(), &*node, id, self.image_cache.clone()); @@ -1261,6 +1261,10 @@ impl Window { } } + unsafe { + ScriptThread::note_newly_transitioning_nodes(complete.newly_transitioning_nodes); + } + true } @@ -1455,6 +1459,7 @@ impl Window { DOMString::from(resolved) } + #[allow(unsafe_code)] pub fn offset_parent_query(&self, node: TrustedNodeAddress) -> (Option<Root<Element>>, Rect<Au>) { if !self.reflow(ReflowGoal::ForScriptQuery, ReflowQueryType::OffsetParentQuery(node), @@ -1466,7 +1471,7 @@ impl Window { let js_runtime = self.js_runtime.borrow(); let js_runtime = js_runtime.as_ref().unwrap(); let element = response.node_address.and_then(|parent_node_address| { - let node = from_untrusted_node_address(js_runtime.rt(), parent_node_address); + let node = unsafe { from_untrusted_node_address(js_runtime.rt(), parent_node_address) }; Root::downcast(node) }); (element, response.rect) |