зеркало из https://github.com/mozilla/gecko-dev.git
servo: Merge #16295 - Root nodes for the duration of their CSS transitions (from jdm:transition-safety); r=nox
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 Source-Repo: https://github.com/servo/servo Source-Revision: fa251ec96b445b9ba8439d76e05870a88c2caa0f --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : 1a087905ac9a835498215ff10f73105f5a4c1e45
This commit is contained in:
Родитель
1ee94c2366
Коммит
96dc842167
|
@ -9,7 +9,9 @@ use flow::{self, Flow};
|
|||
use gfx::display_list::OpaqueNode;
|
||||
use ipc_channel::ipc::IpcSender;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use opaque_node::OpaqueNodeMethods;
|
||||
use script_traits::{AnimationState, ConstellationControlMsg, LayoutMsg as ConstellationMsg};
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::mpsc::Receiver;
|
||||
use style::animation::{Animation, update_style_for_animation};
|
||||
|
@ -24,6 +26,7 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
|
|||
script_chan: &IpcSender<ConstellationControlMsg>,
|
||||
running_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
|
||||
expired_animations: &mut HashMap<OpaqueNode, Vec<Animation>>,
|
||||
mut newly_transitioning_nodes: Option<&mut Vec<UntrustedNodeAddress>>,
|
||||
new_animations_receiver: &Receiver<Animation>,
|
||||
pipeline_id: PipelineId,
|
||||
timer: &Timer) {
|
||||
|
@ -71,7 +74,7 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
|
|||
let mut animations_still_running = vec![];
|
||||
for mut running_animation in running_animations.drain(..) {
|
||||
let still_running = !running_animation.is_expired() && match running_animation {
|
||||
Animation::Transition(_, _, started_at, ref frame, _expired) => {
|
||||
Animation::Transition(_, started_at, ref frame, _expired) => {
|
||||
now < started_at + frame.duration
|
||||
}
|
||||
Animation::Keyframes(_, _, ref mut state) => {
|
||||
|
@ -86,8 +89,8 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
|
|||
continue
|
||||
}
|
||||
|
||||
if let Animation::Transition(_, unsafe_node, _, ref frame, _) = running_animation {
|
||||
script_chan.send(ConstellationControlMsg::TransitionEnd(unsafe_node,
|
||||
if let Animation::Transition(node, _, ref frame, _) = running_animation {
|
||||
script_chan.send(ConstellationControlMsg::TransitionEnd(node.to_untrusted_node_address(),
|
||||
frame.property_animation
|
||||
.property_name().into(),
|
||||
frame.duration))
|
||||
|
@ -112,6 +115,17 @@ pub fn update_animation_state(constellation_chan: &IpcSender<ConstellationMsg>,
|
|||
|
||||
// Add new running animations.
|
||||
for new_running_animation in new_running_animations {
|
||||
if new_running_animation.is_transition() {
|
||||
match newly_transitioning_nodes {
|
||||
Some(ref mut nodes) => {
|
||||
nodes.push(new_running_animation.node().to_untrusted_node_address());
|
||||
}
|
||||
None => {
|
||||
warn!("New transition encountered from compositor-initiated layout.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
running_animations.entry(*new_running_animation.node())
|
||||
.or_insert_with(Vec::new)
|
||||
.push(new_running_animation)
|
||||
|
|
|
@ -15,6 +15,7 @@ use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
|
|||
use opaque_node::OpaqueNodeMethods;
|
||||
use parking_lot::RwLock;
|
||||
use script_layout_interface::{PendingImage, PendingImageState};
|
||||
use script_traits::UntrustedNodeAddress;
|
||||
use servo_url::ServoUrl;
|
||||
use std::borrow::{Borrow, BorrowMut};
|
||||
use std::cell::{RefCell, RefMut};
|
||||
|
@ -96,7 +97,11 @@ pub struct LayoutContext<'a> {
|
|||
|
||||
/// A list of in-progress image loads to be shared with the script thread.
|
||||
/// A None value means that this layout was not initiated by the script thread.
|
||||
pub pending_images: Option<Mutex<Vec<PendingImage>>>
|
||||
pub pending_images: Option<Mutex<Vec<PendingImage>>>,
|
||||
|
||||
/// A list of nodes that have just initiated a CSS transition.
|
||||
/// A None value means that this layout was not initiated by the script thread.
|
||||
pub newly_transitioning_nodes: Option<Mutex<Vec<UntrustedNodeAddress>>>,
|
||||
}
|
||||
|
||||
impl<'a> Drop for LayoutContext<'a> {
|
||||
|
|
|
@ -17,7 +17,6 @@ use inline::LAST_FRAGMENT_OF_ELEMENT;
|
|||
use ipc_channel::ipc::IpcSender;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use opaque_node::OpaqueNodeMethods;
|
||||
use script_layout_interface::PendingImage;
|
||||
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse};
|
||||
use script_layout_interface::rpc::{HitTestResponse, LayoutRPC};
|
||||
use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse};
|
||||
|
@ -28,7 +27,6 @@ use script_traits::LayoutMsg as ConstellationMsg;
|
|||
use script_traits::UntrustedNodeAddress;
|
||||
use sequential;
|
||||
use std::cmp::{min, max};
|
||||
use std::mem;
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use style::computed_values;
|
||||
|
@ -89,9 +87,6 @@ pub struct LayoutThreadData {
|
|||
/// Index in a text fragment. We need this do determine the insertion point.
|
||||
pub text_index_response: TextIndexResponse,
|
||||
|
||||
/// A list of images requests that need to be initiated.
|
||||
pub pending_images: Vec<PendingImage>,
|
||||
|
||||
/// A queued response for the list of nodes at a given point.
|
||||
pub nodes_from_point_response: Vec<UntrustedNodeAddress>,
|
||||
}
|
||||
|
@ -198,12 +193,6 @@ impl LayoutRPC for LayoutRPCImpl {
|
|||
let rw_data = rw_data.lock().unwrap();
|
||||
rw_data.text_index_response.clone()
|
||||
}
|
||||
|
||||
fn pending_images(&self) -> Vec<PendingImage> {
|
||||
let &LayoutRPCImpl(ref rw_data) = self;
|
||||
let mut rw_data = rw_data.lock().unwrap();
|
||||
mem::replace(&mut rw_data.pending_images, vec![])
|
||||
}
|
||||
}
|
||||
|
||||
struct UnioningFragmentBorderBoxIterator {
|
||||
|
|
|
@ -81,7 +81,8 @@ use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
|
|||
use profile_traits::time::{self, TimerMetadata, profile};
|
||||
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
|
||||
use script::layout_wrapper::{ServoLayoutElement, ServoLayoutDocument, ServoLayoutNode};
|
||||
use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow};
|
||||
use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType};
|
||||
use script_layout_interface::message::{ScriptReflow, ReflowComplete};
|
||||
use script_layout_interface::reporter::CSSErrorReporter;
|
||||
use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse};
|
||||
use script_layout_interface::rpc::TextIndexResponse;
|
||||
|
@ -292,6 +293,37 @@ impl LayoutThreadFactory for LayoutThread {
|
|||
}
|
||||
}
|
||||
|
||||
struct ScriptReflowResult {
|
||||
script_reflow: ScriptReflow,
|
||||
result: RefCell<Option<ReflowComplete>>,
|
||||
}
|
||||
|
||||
impl Deref for ScriptReflowResult {
|
||||
type Target = ScriptReflow;
|
||||
fn deref(&self) -> &ScriptReflow {
|
||||
&self.script_reflow
|
||||
}
|
||||
}
|
||||
|
||||
impl ScriptReflowResult {
|
||||
fn new(script_reflow: ScriptReflow) -> ScriptReflowResult {
|
||||
ScriptReflowResult {
|
||||
script_reflow: script_reflow,
|
||||
result: RefCell::new(Some(Default::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for ScriptReflowResult {
|
||||
fn drop(&mut self) {
|
||||
self.script_reflow.script_join_chan.send(
|
||||
self.result
|
||||
.borrow_mut()
|
||||
.take()
|
||||
.unwrap()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// The `LayoutThread` `rw_data` lock must remain locked until the first reflow,
|
||||
/// as RPC calls don't make sense until then. Use this in combination with
|
||||
/// `LayoutThread::lock_rw_data` and `LayoutThread::return_rw_data`.
|
||||
|
@ -476,7 +508,6 @@ impl LayoutThread {
|
|||
margin_style_response: MarginStyleResponse::empty(),
|
||||
stacking_context_scroll_offsets: HashMap::new(),
|
||||
text_index_response: TextIndexResponse(None),
|
||||
pending_images: vec![],
|
||||
nodes_from_point_response: vec![],
|
||||
})),
|
||||
error_reporter: CSSErrorReporter {
|
||||
|
@ -513,7 +544,7 @@ impl LayoutThread {
|
|||
// Create a layout context for use in building display lists, hit testing, &c.
|
||||
fn build_layout_context<'a>(&'a self,
|
||||
guards: StylesheetGuards<'a>,
|
||||
request_images: bool,
|
||||
script_initiated_layout: bool,
|
||||
snapshot_map: &'a SnapshotMap)
|
||||
-> LayoutContext<'a> {
|
||||
let thread_local_style_context_creation_data =
|
||||
|
@ -537,7 +568,8 @@ impl LayoutThread {
|
|||
image_cache: self.image_cache.clone(),
|
||||
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
|
||||
webrender_image_cache: self.webrender_image_cache.clone(),
|
||||
pending_images: if request_images { Some(Mutex::new(vec![])) } else { None },
|
||||
pending_images: if script_initiated_layout { Some(Mutex::new(vec![])) } else { None },
|
||||
newly_transitioning_nodes: if script_initiated_layout { Some(Mutex::new(vec![])) } else { None },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,10 +646,11 @@ impl LayoutThread {
|
|||
Box<LayoutRPC + Send>).unwrap();
|
||||
},
|
||||
Msg::Reflow(data) => {
|
||||
let mut data = ScriptReflowResult::new(data);
|
||||
profile(time::ProfilerCategory::LayoutPerform,
|
||||
self.profiler_metadata(),
|
||||
self.time_profiler_chan.clone(),
|
||||
|| self.handle_reflow(&data, possibly_locked_rw_data));
|
||||
|| self.handle_reflow(&mut data, possibly_locked_rw_data));
|
||||
},
|
||||
Msg::TickAnimations => self.tick_all_animations(possibly_locked_rw_data),
|
||||
Msg::SetStackingContextScrollStates(new_scroll_states) => {
|
||||
|
@ -953,7 +986,7 @@ impl LayoutThread {
|
|||
|
||||
/// The high-level routine that performs layout threads.
|
||||
fn handle_reflow<'a, 'b>(&mut self,
|
||||
data: &ScriptReflow,
|
||||
data: &mut ScriptReflowResult,
|
||||
possibly_locked_rw_data: &mut RwData<'a, 'b>) {
|
||||
let document = unsafe { ServoLayoutNode::new(&data.document) };
|
||||
let document = document.as_document().unwrap();
|
||||
|
@ -1238,18 +1271,26 @@ impl LayoutThread {
|
|||
|
||||
self.respond_to_query_if_necessary(&data.query_type,
|
||||
&mut *rw_data,
|
||||
&mut layout_context);
|
||||
&mut layout_context,
|
||||
data.result.borrow_mut().as_mut().unwrap());
|
||||
}
|
||||
|
||||
fn respond_to_query_if_necessary(&self,
|
||||
query_type: &ReflowQueryType,
|
||||
rw_data: &mut LayoutThreadData,
|
||||
context: &mut LayoutContext) {
|
||||
context: &mut LayoutContext,
|
||||
reflow_result: &mut ReflowComplete) {
|
||||
let pending_images = match context.pending_images {
|
||||
Some(ref pending) => std_mem::replace(&mut *pending.lock().unwrap(), vec![]),
|
||||
None => vec![],
|
||||
};
|
||||
rw_data.pending_images = pending_images;
|
||||
reflow_result.pending_images = pending_images;
|
||||
|
||||
let newly_transitioning_nodes = match context.newly_transitioning_nodes {
|
||||
Some(ref nodes) => std_mem::replace(&mut *nodes.lock().unwrap(), vec![]),
|
||||
None => vec![],
|
||||
};
|
||||
reflow_result.newly_transitioning_nodes = newly_transitioning_nodes;
|
||||
|
||||
let mut root_flow = match self.root_flow.borrow().clone() {
|
||||
Some(root_flow) => root_flow,
|
||||
|
@ -1426,6 +1467,7 @@ impl LayoutThread {
|
|||
&mut *rw_data,
|
||||
&mut layout_context);
|
||||
assert!(layout_context.pending_images.is_none());
|
||||
assert!(layout_context.newly_transitioning_nodes.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1436,14 +1478,24 @@ impl LayoutThread {
|
|||
document: Option<&ServoLayoutDocument>,
|
||||
rw_data: &mut LayoutThreadData,
|
||||
context: &mut LayoutContext) {
|
||||
// Kick off animations if any were triggered, expire completed ones.
|
||||
animation::update_animation_state(&self.constellation_chan,
|
||||
&self.script_chan,
|
||||
&mut *self.running_animations.write(),
|
||||
&mut *self.expired_animations.write(),
|
||||
&self.new_animations_receiver,
|
||||
self.id,
|
||||
&self.timer);
|
||||
{
|
||||
let mut newly_transitioning_nodes = context
|
||||
.newly_transitioning_nodes
|
||||
.as_ref()
|
||||
.map(|nodes| nodes.lock().unwrap());
|
||||
let newly_transitioning_nodes = newly_transitioning_nodes
|
||||
.as_mut()
|
||||
.map(|nodes| &mut **nodes);
|
||||
// Kick off animations if any were triggered, expire completed ones.
|
||||
animation::update_animation_state(&self.constellation_chan,
|
||||
&self.script_chan,
|
||||
&mut *self.running_animations.write(),
|
||||
&mut *self.expired_animations.write(),
|
||||
newly_transitioning_nodes,
|
||||
&self.new_animations_receiver,
|
||||
self.id,
|
||||
&self.timer);
|
||||
}
|
||||
|
||||
profile(time::ProfilerCategory::LayoutRestyleDamagePropagation,
|
||||
self.profiler_metadata(),
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -47,7 +47,7 @@ use dom::globalscope::GlobalScope;
|
|||
use dom::htmlanchorelement::HTMLAnchorElement;
|
||||
use dom::htmliframeelement::{HTMLIFrameElement, NavigationType};
|
||||
use dom::mutationobserver::MutationObserver;
|
||||
use dom::node::{Node, NodeDamage, window_from_node};
|
||||
use dom::node::{Node, NodeDamage, window_from_node, from_untrusted_node_address};
|
||||
use dom::serviceworker::TrustedServiceWorkerAddress;
|
||||
use dom::serviceworkerregistration::ServiceWorkerRegistration;
|
||||
use dom::servoparser::{ParserContext, ServoParser};
|
||||
|
@ -69,7 +69,6 @@ use js::jsapi::{JSAutoCompartment, JSContext, JS_SetWrapObjectCallbacks};
|
|||
use js::jsapi::{JSTracer, SetWindowProxyClass};
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::rust::Runtime;
|
||||
use layout_wrapper::ServoLayoutNode;
|
||||
use mem::heap_size_of_self_and_children;
|
||||
use microtask::{MicrotaskQueue, Microtask};
|
||||
use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespace};
|
||||
|
@ -109,7 +108,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
|||
use std::sync::mpsc::{Receiver, Select, Sender, channel};
|
||||
use std::thread;
|
||||
use style::context::ReflowGoal;
|
||||
use style::dom::{TNode, UnsafeNode};
|
||||
use style::thread_state;
|
||||
use task_source::dom_manipulation::{DOMManipulationTask, DOMManipulationTaskSource};
|
||||
use task_source::file_reading::FileReadingTaskSource;
|
||||
|
@ -490,6 +488,10 @@ pub struct ScriptThread {
|
|||
/// A list of pipelines containing documents that finished loading all their blocking
|
||||
/// resources during a turn of the event loop.
|
||||
docs_with_no_blocking_loads: DOMRefCell<HashSet<JS<Document>>>,
|
||||
|
||||
/// A list of nodes with in-progress CSS transitions, which roots them for the duration
|
||||
/// of the transition.
|
||||
transitioning_nodes: DOMRefCell<Vec<JS<Node>>>,
|
||||
}
|
||||
|
||||
/// In the event of thread panic, all data on the stack runs its destructor. However, there
|
||||
|
@ -574,6 +576,17 @@ impl ScriptThreadFactory for ScriptThread {
|
|||
}
|
||||
|
||||
impl ScriptThread {
|
||||
pub unsafe fn note_newly_transitioning_nodes(nodes: Vec<UntrustedNodeAddress>) {
|
||||
SCRIPT_THREAD_ROOT.with(|root| {
|
||||
let script_thread = &*root.get().unwrap();
|
||||
let js_runtime = script_thread.js_runtime.rt();
|
||||
let new_nodes = nodes
|
||||
.into_iter()
|
||||
.map(|n| JS::from_ref(&*from_untrusted_node_address(js_runtime, n)));
|
||||
script_thread.transitioning_nodes.borrow_mut().extend(new_nodes);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_mutation_observer(observer: &MutationObserver) {
|
||||
SCRIPT_THREAD_ROOT.with(|root| {
|
||||
let script_thread = unsafe { &*root.get().unwrap() };
|
||||
|
@ -742,6 +755,8 @@ impl ScriptThread {
|
|||
webvr_thread: state.webvr_thread,
|
||||
|
||||
docs_with_no_blocking_loads: Default::default(),
|
||||
|
||||
transitioning_nodes: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1602,11 +1617,29 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
/// Handles firing of transition events.
|
||||
#[allow(unsafe_code)]
|
||||
fn handle_transition_event(&self, unsafe_node: UnsafeNode, name: String, duration: f64) {
|
||||
let node = unsafe { ServoLayoutNode::from_unsafe(&unsafe_node) };
|
||||
let node = unsafe { node.get_jsmanaged().get_for_script() };
|
||||
let window = window_from_node(node);
|
||||
fn handle_transition_event(&self, unsafe_node: UntrustedNodeAddress, name: String, duration: f64) {
|
||||
let js_runtime = self.js_runtime.rt();
|
||||
let node = unsafe {
|
||||
from_untrusted_node_address(js_runtime, unsafe_node)
|
||||
};
|
||||
|
||||
let idx = self.transitioning_nodes
|
||||
.borrow()
|
||||
.iter()
|
||||
.position(|n| &**n as *const _ == &*node as *const _);
|
||||
match idx {
|
||||
Some(idx) => {
|
||||
self.transitioning_nodes.borrow_mut().remove(idx);
|
||||
}
|
||||
None => {
|
||||
// If no index is found, we can't know whether this node is safe to use.
|
||||
// It's better not to fire a DOM event than crash.
|
||||
warn!("Ignoring transition end notification for unknown node.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let window = window_from_node(&*node);
|
||||
|
||||
// Not quite the right thing - see #13865.
|
||||
node.dirty(NodeDamage::NodeStyleDamaged);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use {OpaqueStyleAndLayoutData, TrustedNodeAddress};
|
||||
use {OpaqueStyleAndLayoutData, TrustedNodeAddress, PendingImage};
|
||||
use app_units::Au;
|
||||
use euclid::point::Point2D;
|
||||
use euclid::rect::Rect;
|
||||
|
@ -12,7 +12,7 @@ use msg::constellation_msg::PipelineId;
|
|||
use net_traits::image_cache::ImageCache;
|
||||
use profile_traits::mem::ReportsChan;
|
||||
use rpc::LayoutRPC;
|
||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg};
|
||||
use script_traits::{ConstellationControlMsg, LayoutControlMsg, UntrustedNodeAddress};
|
||||
use script_traits::{LayoutMsg as ConstellationMsg, StackingContextScrollState, WindowSizeData};
|
||||
use servo_url::ServoUrl;
|
||||
use std::sync::Arc;
|
||||
|
@ -109,6 +109,15 @@ pub struct Reflow {
|
|||
pub page_clip_rect: Rect<Au>,
|
||||
}
|
||||
|
||||
/// Information derived from a layout pass that needs to be returned to the script thread.
|
||||
#[derive(Default)]
|
||||
pub struct ReflowComplete {
|
||||
/// The list of images that were encountered that are in progress.
|
||||
pub pending_images: Vec<PendingImage>,
|
||||
/// The list of nodes that initiated a CSS transition.
|
||||
pub newly_transitioning_nodes: Vec<UntrustedNodeAddress>,
|
||||
}
|
||||
|
||||
/// Information needed for a script-initiated reflow.
|
||||
pub struct ScriptReflow {
|
||||
/// General reflow data.
|
||||
|
@ -122,19 +131,13 @@ pub struct ScriptReflow {
|
|||
/// The current window size.
|
||||
pub window_size: WindowSizeData,
|
||||
/// The channel that we send a notification to.
|
||||
pub script_join_chan: Sender<()>,
|
||||
pub script_join_chan: Sender<ReflowComplete>,
|
||||
/// The type of query if any to perform during this reflow.
|
||||
pub query_type: ReflowQueryType,
|
||||
/// The number of objects in the dom #10110
|
||||
pub dom_count: u32,
|
||||
}
|
||||
|
||||
impl Drop for ScriptReflow {
|
||||
fn drop(&mut self) {
|
||||
self.script_join_chan.send(()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NewLayoutThreadInfo {
|
||||
pub id: PipelineId,
|
||||
pub url: ServoUrl,
|
||||
|
|
|
@ -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 http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use PendingImage;
|
||||
use app_units::Au;
|
||||
use euclid::point::Point2D;
|
||||
use euclid::rect::Rect;
|
||||
|
@ -38,8 +37,6 @@ pub trait LayoutRPC {
|
|||
fn offset_parent(&self) -> OffsetParentResponse;
|
||||
/// Query layout for the resolve values of the margin properties for an element.
|
||||
fn margin_style(&self) -> MarginStyleResponse;
|
||||
/// Requests the list of not-yet-loaded images that were encountered in the last reflow.
|
||||
fn pending_images(&self) -> Vec<PendingImage>;
|
||||
/// Requests the list of nodes from the given point.
|
||||
fn nodes_from_point_response(&self) -> Vec<UntrustedNodeAddress>;
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ use std::collections::HashMap;
|
|||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
use std::sync::mpsc::{Receiver, Sender};
|
||||
use style_traits::{CSSPixel, UnsafeNode};
|
||||
use style_traits::CSSPixel;
|
||||
use webdriver_msg::{LoadStatus, WebDriverScriptCommand};
|
||||
use webrender_traits::ClipId;
|
||||
use webvr_traits::{WebVREvent, WebVRMsg};
|
||||
|
@ -274,7 +274,7 @@ pub enum ConstellationControlMsg {
|
|||
/// Notifies script thread that all animations are done
|
||||
TickAllAnimations(PipelineId),
|
||||
/// Notifies the script thread of a transition end
|
||||
TransitionEnd(UnsafeNode, String, f64),
|
||||
TransitionEnd(UntrustedNodeAddress, String, f64),
|
||||
/// Notifies the script thread that a new Web font has been loaded, and thus the page should be
|
||||
/// reflowed.
|
||||
WebFontLoaded(PipelineId),
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
use Atom;
|
||||
use bezier::Bezier;
|
||||
use context::SharedStyleContext;
|
||||
use dom::{OpaqueNode, UnsafeNode};
|
||||
use dom::OpaqueNode;
|
||||
use euclid::point::Point2D;
|
||||
use font_metrics::FontMetricsProvider;
|
||||
use keyframes::{KeyframesStep, KeyframesStepValue};
|
||||
|
@ -188,7 +188,7 @@ pub enum Animation {
|
|||
/// the f64 field is the start time as returned by `time::precise_time_s()`.
|
||||
///
|
||||
/// The `bool` field is werther this animation should no longer run.
|
||||
Transition(OpaqueNode, UnsafeNode, f64, AnimationFrame, bool),
|
||||
Transition(OpaqueNode, f64, AnimationFrame, bool),
|
||||
/// A keyframes animation is identified by a name, and can have a
|
||||
/// node-dependent state (i.e. iteration count, etc.).
|
||||
Keyframes(OpaqueNode, Atom, KeyframesAnimationState),
|
||||
|
@ -200,7 +200,7 @@ impl Animation {
|
|||
pub fn mark_as_expired(&mut self) {
|
||||
debug_assert!(!self.is_expired());
|
||||
match *self {
|
||||
Animation::Transition(_, _, _, _, ref mut expired) => *expired = true,
|
||||
Animation::Transition(_, _, _, ref mut expired) => *expired = true,
|
||||
Animation::Keyframes(_, _, ref mut state) => state.expired = true,
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ impl Animation {
|
|||
#[inline]
|
||||
pub fn is_expired(&self) -> bool {
|
||||
match *self {
|
||||
Animation::Transition(_, _, _, _, expired) => expired,
|
||||
Animation::Transition(_, _, _, expired) => expired,
|
||||
Animation::Keyframes(_, _, ref state) => state.expired,
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ impl Animation {
|
|||
#[inline]
|
||||
pub fn node(&self) -> &OpaqueNode {
|
||||
match *self {
|
||||
Animation::Transition(ref node, _, _, _, _) => node,
|
||||
Animation::Transition(ref node, _, _, _) => node,
|
||||
Animation::Keyframes(ref node, _, _) => node,
|
||||
}
|
||||
}
|
||||
|
@ -231,6 +231,15 @@ impl Animation {
|
|||
Animation::Keyframes(_, _, ref state) => state.is_paused(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this animation is a transition.
|
||||
#[inline]
|
||||
pub fn is_transition(&self) -> bool {
|
||||
match *self {
|
||||
Animation::Transition(..) => true,
|
||||
Animation::Keyframes(..) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -402,7 +411,6 @@ impl PropertyAnimation {
|
|||
#[cfg(feature = "servo")]
|
||||
pub fn start_transitions_if_applicable(new_animations_sender: &Sender<Animation>,
|
||||
opaque_node: OpaqueNode,
|
||||
unsafe_node: UnsafeNode,
|
||||
old_style: &ComputedValues,
|
||||
new_style: &mut Arc<ComputedValues>,
|
||||
timer: &Timer,
|
||||
|
@ -436,7 +444,7 @@ pub fn start_transitions_if_applicable(new_animations_sender: &Sender<Animation>
|
|||
let start_time =
|
||||
now + (box_style.transition_delay_mod(i).seconds() as f64);
|
||||
new_animations_sender
|
||||
.send(Animation::Transition(opaque_node, unsafe_node, start_time, AnimationFrame {
|
||||
.send(Animation::Transition(opaque_node, start_time, AnimationFrame {
|
||||
duration: box_style.transition_duration_mod(i).seconds() as f64,
|
||||
property_animation: property_animation,
|
||||
}, /* is_expired = */ false)).unwrap();
|
||||
|
@ -589,7 +597,7 @@ pub fn update_style_for_animation(context: &SharedStyleContext,
|
|||
debug_assert!(!animation.is_expired());
|
||||
|
||||
match *animation {
|
||||
Animation::Transition(_, _, start_time, ref frame, _) => {
|
||||
Animation::Transition(_, start_time, ref frame, _) => {
|
||||
debug!("update_style_for_animation: transition found");
|
||||
let now = context.timer.seconds();
|
||||
let mut new_style = (*style).clone();
|
||||
|
@ -767,7 +775,7 @@ pub fn complete_expired_transitions(node: OpaqueNode, style: &mut Arc<ComputedVa
|
|||
if let Some(ref animations) = animations_to_expire {
|
||||
for animation in *animations {
|
||||
// TODO: support animation-fill-mode
|
||||
if let Animation::Transition(_, _, _, ref frame, _) = *animation {
|
||||
if let Animation::Transition(_, _, ref frame, _) = *animation {
|
||||
frame.property_animation.update(Arc::make_mut(style), 1.0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -745,7 +745,6 @@ trait PrivateMatchMethods: TElement {
|
|||
animation::start_transitions_if_applicable(
|
||||
new_animations_sender,
|
||||
this_opaque,
|
||||
self.as_node().to_unsafe(),
|
||||
&**values,
|
||||
new_values,
|
||||
&shared_context.timer,
|
||||
|
@ -843,7 +842,7 @@ trait PrivateMatchMethods: TElement {
|
|||
running_animation,
|
||||
style,
|
||||
font_metrics);
|
||||
if let Animation::Transition(_, _, _, ref frame, _) = *running_animation {
|
||||
if let Animation::Transition(_, _, ref frame, _) = *running_animation {
|
||||
possibly_expired_animations.push(frame.property_animation.clone())
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче