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:
Josh Matthews 2017-05-15 15:00:19 -05:00
Родитель 1ee94c2366
Коммит 96dc842167
13 изменённых файлов: 207 добавлений и 90 удалений

Просмотреть файл

@ -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())
}
}