diff options
author | Josh Matthews <josh@joshmatthews.net> | 2015-10-21 16:42:30 -0400 |
---|---|---|
committer | Josh Matthews <josh@joshmatthews.net> | 2015-11-06 10:41:38 -0500 |
commit | 989e80036e078a19003f0e4eb069671b5222c77b (patch) | |
tree | 3fe8c22b82bd0521d87b4ef63464406da6555c7a /components/script | |
parent | 9fea6d2e46dd917f16a5f1ec0f6484e8164c7a3a (diff) | |
download | servo-989e80036e078a19003f0e4eb069671b5222c77b.tar.gz servo-989e80036e078a19003f0e4eb069671b5222c77b.zip |
Implement cancellable runnables.
Additionally, make image load events cancellable. Resolves #7731.
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/bindings/trace.rs | 3 | ||||
-rw-r--r-- | components/script/dom/htmlimageelement.rs | 9 | ||||
-rw-r--r-- | components/script/dom/window.rs | 19 | ||||
-rw-r--r-- | components/script/script_task.rs | 39 |
4 files changed, 62 insertions, 8 deletions
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index df5c84d3778..48f6ed39032 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -79,6 +79,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use std::rc::Rc; use std::sync::Arc; +use std::sync::atomic::AtomicBool; use std::sync::mpsc::{Receiver, Sender}; use string_cache::{Atom, Namespace}; use style::properties::PropertyDeclarationBlock; @@ -246,7 +247,7 @@ impl<A: JSTraceable, B: JSTraceable> JSTraceable for (A, B) { } -no_jsmanaged_fields!(bool, f32, f64, String, Url); +no_jsmanaged_fields!(bool, f32, f64, String, Url, AtomicBool); no_jsmanaged_fields!(usize, u8, u16, u32, u64); no_jsmanaged_fields!(isize, i8, i16, i32, i64); no_jsmanaged_fields!(Sender<T>); diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 098938c8285..e05a50e6027 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -110,13 +110,16 @@ impl HTMLImageElement { let trusted_node = Trusted::new(window.get_cx(), self, window.script_chan()); let (responder_sender, responder_receiver) = ipc::channel().unwrap(); let script_chan = window.script_chan(); + let wrapper = window.get_runnable_wrapper(); ROUTER.add_route(responder_receiver.to_opaque(), box move |message| { // Return the image via a message to the script task, which marks the element // as dirty and triggers a reflow. let image_response = message.to().unwrap(); - script_chan.send(CommonScriptMsg::RunnableMsg(UpdateReplacedElement, - box ImageResponseHandlerRunnable::new( - trusted_node.clone(), image_response))).unwrap(); + let runnable = ImageResponseHandlerRunnable::new( + trusted_node.clone(), image_response); + let runnable = wrapper.wrap_runnable(runnable); + script_chan.send(CommonScriptMsg::RunnableMsg( + UpdateReplacedElement, runnable)).unwrap(); }); image_cache.request_image(img_url, diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 9682b89cba6..a485d881533 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -53,7 +53,7 @@ use num::traits::ToPrimitive; use page::Page; use profile_traits::mem; use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64}; -use script_task::{ScriptChan, ScriptPort, MainThreadScriptMsg}; +use script_task::{ScriptChan, ScriptPort, MainThreadScriptMsg, RunnableWrapper}; use script_task::{SendableMainThreadScriptChan, MainThreadScriptChan, MainThreadTimerEventChan}; use script_traits::{TimerEventChan, TimerEventId, TimerEventRequest, TimerSource}; use selectors::parser::PseudoElement; @@ -66,6 +66,7 @@ use std::ffi::CString; use std::io::{Write, stderr, stdout}; use std::rc::Rc; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::TryRecvError::{Disconnected, Empty}; use std::sync::mpsc::{Sender, channel}; use string_cache::Atom; @@ -209,7 +210,11 @@ pub struct Window { /// The current state of the window object current_state: Cell<WindowState>, - current_viewport: Cell<Rect<Au>> + current_viewport: Cell<Rect<Au>>, + + /// A flag to prevent async events from attempting to interact with this window. + #[ignore_heap_size_of = "defined in std"] + ignore_further_async_events: Arc<AtomicBool>, } impl Window { @@ -219,6 +224,7 @@ impl Window { *self.js_runtime.borrow_for_script_deallocation() = None; self.browsing_context.set(None); self.current_state.set(WindowState::Zombie); + self.ignore_further_async_events.store(true, Ordering::Relaxed); } } @@ -781,6 +787,12 @@ impl<'a, T: Reflectable> ScriptHelpers for &'a T { } impl Window { + pub fn get_runnable_wrapper(&self) -> RunnableWrapper { + RunnableWrapper { + cancelled: self.ignore_further_async_events.clone() + } + } + pub fn clear_js_runtime(&self) { self.Document().upcast::<Node>().teardown(); @@ -798,6 +810,7 @@ impl Window { self.current_state.set(WindowState::Zombie); *self.js_runtime.borrow_mut() = None; self.browsing_context.set(None); + self.ignore_further_async_events.store(true, Ordering::Relaxed); } /// https://drafts.csswg.org/cssom-view/#dom-window-scroll @@ -1260,6 +1273,8 @@ impl Window { devtools_markers: DOMRefCell::new(HashSet::new()), devtools_wants_updates: Cell::new(false), webdriver_script_chan: DOMRefCell::new(None), + + ignore_further_async_events: Arc::new(AtomicBool::new(false)), }; WindowBinding::Wrap(runtime.cx(), win) diff --git a/components/script/script_task.rs b/components/script/script_task.rs index ac67e4a5996..dda6faaa85b 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -95,6 +95,7 @@ use std::option::Option; use std::ptr; use std::rc::Rc; use std::result::Result; +use std::sync::atomic::{Ordering, AtomicBool}; use std::sync::mpsc::{Receiver, Select, Sender, channel}; use std::sync::{Arc, Mutex}; use string_cache::Atom; @@ -158,7 +159,38 @@ impl InProgressLoad { } } +/// Encapsulated state required to create cancellable runnables from non-script threads. +pub struct RunnableWrapper { + pub cancelled: Arc<AtomicBool>, +} + +impl RunnableWrapper { + pub fn wrap_runnable<T: Runnable + Send + 'static>(&self, runnable: T) -> Box<Runnable + Send> { + box CancellableRunnable { + cancelled: self.cancelled.clone(), + inner: box runnable, + } + } +} + +/// A runnable that can be discarded by toggling a shared flag. +pub struct CancellableRunnable<T: Runnable + Send> { + cancelled: Arc<AtomicBool>, + inner: Box<T>, +} + +impl<T: Runnable + Send> Runnable for CancellableRunnable<T> { + fn is_cancelled(&self) -> bool { + self.cancelled.load(Ordering::Relaxed) + } + + fn handler(self: Box<CancellableRunnable<T>>) { + self.inner.handler() + } +} + pub trait Runnable { + fn is_cancelled(&self) -> bool { false } fn handler(self: Box<Self>); } @@ -990,10 +1022,13 @@ impl ScriptTask { runnable.handler(self), MainThreadScriptMsg::DocumentLoadsComplete(id) => self.handle_loads_complete(id), - MainThreadScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) => + MainThreadScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) => { // The category of the runnable is ignored by the pattern, however // it is still respected by profiling (see categorize_msg). - runnable.handler(), + if !runnable.is_cancelled() { + runnable.handler() + } + } MainThreadScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => LiveDOMReferences::cleanup(addr), MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => |