servo: Merge #18573 - Introduce TaskOnce (from servo:TASKS); r=SimonSapin
authorAnthony Ramine <n.oxyde@gmail.com>
Wed, 20 Sep 2017 05:17:12 -0500
changeset 381876 3ed409890cbb3010f216e4def893cc8a5e4e7339
parent 381875 658b68ee4d0ae1b6c5ffb44ed38c310f50d4b283
child 381877 905ddac6491cca547135660ad0abdc75380002b4
push id32542
push userkwierso@gmail.com
push dateWed, 20 Sep 2017 21:07:55 +0000
treeherdermozilla-central@319a34bea9e4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersSimonSapin
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #18573 - Introduce TaskOnce (from servo:TASKS); r=SimonSapin Source-Repo: https://github.com/servo/servo Source-Revision: 44c6bd9fa9594e7fd2a6576a4eab501a5838d113
servo/components/script/dom/bindings/refcounted.rs
servo/components/script/dom/bluetooth.rs
servo/components/script/dom/document.rs
servo/components/script/dom/element.rs
servo/components/script/dom/event.rs
servo/components/script/dom/eventsource.rs
servo/components/script/dom/filereader.rs
servo/components/script/dom/htmldetailselement.rs
servo/components/script/dom/htmlformelement.rs
servo/components/script/dom/htmliframeelement.rs
servo/components/script/dom/htmlimageelement.rs
servo/components/script/dom/htmlmediaelement.rs
servo/components/script/dom/serviceworker.rs
servo/components/script/dom/storage.rs
servo/components/script/dom/websocket.rs
servo/components/script/dom/window.rs
servo/components/script/dom/worker.rs
servo/components/script/dom/workerglobalscope.rs
servo/components/script/dom/worklet.rs
servo/components/script/network_listener.rs
servo/components/script/script_runtime.rs
servo/components/script/script_thread.rs
servo/components/script/serviceworkerjob.rs
servo/components/script/task.rs
servo/components/script/task_source/dom_manipulation.rs
servo/components/script/task_source/file_reading.rs
servo/components/script/task_source/mod.rs
servo/components/script/task_source/networking.rs
servo/components/script/task_source/performance_timeline.rs
servo/components/script/task_source/user_interaction.rs
--- a/servo/components/script/dom/bindings/refcounted.rs
+++ b/servo/components/script/dom/bindings/refcounted.rs
@@ -35,17 +35,17 @@ use libc;
 use std::cell::RefCell;
 use std::collections::hash_map::Entry::{Occupied, Vacant};
 use std::collections::hash_map::HashMap;
 use std::hash::Hash;
 use std::marker::PhantomData;
 use std::os;
 use std::rc::Rc;
 use std::sync::{Arc, Weak};
-use task::Task;
+use task::TaskOnce;
 
 
 #[allow(missing_docs)]  // FIXME
 mod dummy {  // Attributes don’t apply through the macro.
     use std::cell::RefCell;
     use std::rc::Rc;
     use super::LiveDOMReferences;
     thread_local!(pub static LIVE_REFERENCES: Rc<RefCell<Option<LiveDOMReferences>>> =
@@ -117,30 +117,30 @@ impl TrustedPromise {
                 Vacant(_) => unreachable!(),
             };
             promise
         })
     }
 
     /// A task which will reject the promise.
     #[allow(unrooted_must_root)]
-    pub fn reject_task(self, error: Error) -> impl Send + Task {
+    pub fn reject_task(self, error: Error) -> impl TaskOnce {
         let this = self;
         task!(reject_promise: move || {
             debug!("Rejecting promise.");
             let this = this.root();
             let cx = this.global().get_cx();
             let _ac = JSAutoCompartment::new(cx, this.reflector().get_jsobject().get());
             this.reject_error(cx, error);
         })
     }
 
     /// A task which will resolve the promise.
     #[allow(unrooted_must_root)]
-    pub fn resolve_task<T>(self, value: T) -> impl Send + Task
+    pub fn resolve_task<T>(self, value: T) -> impl TaskOnce
     where
         T: ToJSValConvertible + Send,
     {
         let this = self;
         task!(resolve_promise: move || {
             debug!("Resolving promise.");
             let this = this.root();
             let cx = this.global().get_cx();
--- a/servo/components/script/dom/bluetooth.rs
+++ b/servo/components/script/dom/bluetooth.rs
@@ -35,17 +35,17 @@ use ipc_channel::router::ROUTER;
 use js::conversions::ConversionResult;
 use js::jsapi::{JSAutoCompartment, JSContext, JSObject};
 use js::jsval::{ObjectValue, UndefinedValue};
 use std::cell::Ref;
 use std::collections::HashMap;
 use std::rc::Rc;
 use std::str::FromStr;
 use std::sync::{Arc, Mutex};
-use task::Task;
+use task::TaskOnce;
 
 const KEY_CONVERSION_ERROR: &'static str = "This `manufacturerData` key can not be parsed as unsigned short:";
 const FILTER_EMPTY_ERROR: &'static str = "'filters' member, if present, must be nonempty to find any devices.";
 const FILTER_ERROR: &'static str = "A filter must restrict the devices in some way.";
 const MANUFACTURER_DATA_ERROR: &'static str = "'manufacturerData', if present, must be non-empty to filter devices.";
 const MASK_LENGTH_ERROR: &'static str = "`mask`, if present, must have the same length as `dataPrefix`.";
 // 248 is the maximum number of UTF-8 code units in a Bluetooth Device Name.
 const MAX_DEVICE_NAME_LENGTH: usize = 248;
@@ -224,28 +224,27 @@ pub fn response_async<T: AsyncBluetoothL
         receiver: Trusted::new(receiver),
     }));
     ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
         struct ListenerTask<T: AsyncBluetoothListener + DomObject> {
             context: Arc<Mutex<BluetoothContext<T>>>,
             action: BluetoothResponseResult,
         }
 
-        impl<T> Task for ListenerTask<T>
+        impl<T> TaskOnce for ListenerTask<T>
         where
             T: AsyncBluetoothListener + DomObject,
         {
-            fn run(self: Box<Self>) {
-                let this = *self;
-                let mut context = this.context.lock().unwrap();
-                context.response(this.action);
+            fn run_once(self) {
+                let mut context = self.context.lock().unwrap();
+                context.response(self.action);
             }
         }
 
-        let task = box ListenerTask {
+        let task = ListenerTask {
             context: context.clone(),
             action: message.to().unwrap(),
         };
 
         let result = task_source.queue_unconditionally(task);
         if let Err(err) = result {
             warn!("failed to deliver network data: {:?}", err);
         }
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -1711,17 +1711,17 @@ impl Document {
         assert!(!self.loader.borrow().events_inhibited());
         self.loader.borrow_mut().inhibit_events();
 
         // The rest will ever run only once per document.
         // Step 7.
         debug!("Document loads are complete.");
         let document = Trusted::new(self);
         self.window.dom_manipulation_task_source().queue(
-            box task!(fire_load_event: move || {
+            task!(fire_load_event: move || {
                 let document = document.root();
                 let window = document.window();
                 if !window.is_alive() {
                     return;
                 }
 
                 // Step 7.1.
                 document.set_ready_state(DocumentReadyState::Complete);
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -114,17 +114,17 @@ use style::properties::longhands::{self,
 use style::rule_tree::CascadeLevel;
 use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser};
 use style::selector_parser::extended_filtering;
 use style::shared_lock::{SharedRwLock, Locked};
 use style::thread_state;
 use style::values::{CSSFloat, Either};
 use style::values::{specified, computed};
 use stylesheet_loader::StylesheetOwner;
-use task::Task;
+use task::TaskOnce;
 
 // TODO: Update focus state when the top-level browsing context gains or loses system focus,
 // and when the element enters or leaves a browsing context container.
 // https://html.spec.whatwg.org/multipage/#selector-focus
 
 #[dom_struct]
 pub struct Element {
     node: Node,
@@ -3042,19 +3042,19 @@ impl ElementPerformFullscreenEnter {
         box ElementPerformFullscreenEnter {
             element: element,
             promise: promise,
             error: error,
         }
     }
 }
 
-impl Task for ElementPerformFullscreenEnter {
+impl TaskOnce for ElementPerformFullscreenEnter {
     #[allow(unrooted_must_root)]
-    fn run(self: Box<Self>) {
+    fn run_once(self) {
         let element = self.element.root();
         let document = document_from_node(element.r());
 
         // Step 7.1
         if self.error || !element.fullscreen_element_ready_check() {
             // JSAutoCompartment needs to be manually made.
             // Otherwise, Servo will crash.
             let promise = self.promise.root();
@@ -3095,19 +3095,19 @@ impl ElementPerformFullscreenExit {
     pub fn new(element: Trusted<Element>, promise: TrustedPromise) -> Box<ElementPerformFullscreenExit> {
         box ElementPerformFullscreenExit {
             element: element,
             promise: promise,
         }
     }
 }
 
-impl Task for ElementPerformFullscreenExit {
+impl TaskOnce for ElementPerformFullscreenExit {
     #[allow(unrooted_must_root)]
-    fn run(self: Box<Self>) {
+    fn run_once(self) {
         let element = self.element.root();
         let document = document_from_node(element.r());
         // TODO Step 9.1-5
         // Step 9.6
         element.set_fullscreen_state(false);
 
         document.window().reflow(ReflowGoal::ForDisplay,
                                  ReflowQueryType::NoQuery,
--- a/servo/components/script/dom/event.rs
+++ b/servo/components/script/dom/event.rs
@@ -18,17 +18,17 @@ use dom::eventtarget::{CompiledEventList
 use dom::globalscope::GlobalScope;
 use dom::node::Node;
 use dom::virtualmethods::vtable_for;
 use dom::window::Window;
 use dom_struct::dom_struct;
 use servo_atoms::Atom;
 use std::cell::Cell;
 use std::default::Default;
-use task::Task;
+use task::TaskOnce;
 use time;
 
 #[dom_struct]
 pub struct Event {
     reflector_: Reflector,
     current_target: MutNullableJS<EventTarget>,
     target: MutNullableJS<EventTarget>,
     type_: DOMRefCell<Atom>,
@@ -383,33 +383,33 @@ pub enum EventStatus {
 // https://dom.spec.whatwg.org/#concept-event-fire
 pub struct EventTask {
     pub target: Trusted<EventTarget>,
     pub name: Atom,
     pub bubbles: EventBubbles,
     pub cancelable: EventCancelable,
 }
 
-impl Task for EventTask {
-    fn run(self: Box<Self>) {
+impl TaskOnce for EventTask {
+    fn run_once(self) {
         let target = self.target.root();
         let bubbles = self.bubbles;
         let cancelable = self.cancelable;
         target.fire_event_with_params(self.name, bubbles, cancelable);
     }
 }
 
 // https://html.spec.whatwg.org/multipage/#fire-a-simple-event
 pub struct SimpleEventTask {
     pub target: Trusted<EventTarget>,
     pub name: Atom,
 }
 
-impl Task for SimpleEventTask {
-    fn run(self: Box<Self>) {
+impl TaskOnce for SimpleEventTask {
+    fn run_once(self) {
         let target = self.target.root();
         target.fire_event(self.name);
     }
 }
 
 // See dispatch_event.
 // https://dom.spec.whatwg.org/#concept-event-dispatch
 fn dispatch_to_listeners(event: &Event, target: &EventTarget, event_path: &[&EventTarget]) {
--- a/servo/components/script/dom/eventsource.rs
+++ b/servo/components/script/dom/eventsource.rs
@@ -95,17 +95,17 @@ impl EventSourceContext {
         let event_source = self.event_source.root();
         if self.gen_id != event_source.generation_id.get() {
             return;
         }
         let global = event_source.global();
         let event_source = self.event_source.clone();
         // FIXME(nox): Why are errors silenced here?
         let _ = global.networking_task_source().queue(
-            box task!(announce_the_event_source_connection: move || {
+            task!(announce_the_event_source_connection: move || {
                 let event_source = event_source.root();
                 if event_source.ready_state.get() != ReadyState::Closed {
                     event_source.ready_state.set(ReadyState::Open);
                     event_source.upcast::<EventTarget>().fire_event(atom!("open"));
                 }
             }),
             &global,
         );
@@ -116,17 +116,17 @@ impl EventSourceContext {
         let event_source = self.event_source.root();
         if self.gen_id != event_source.generation_id.get() {
             return;
         }
         let global = event_source.global();
         let event_source = self.event_source.clone();
         // FIXME(nox): Why are errors silenced here?
         let _ = global.networking_task_source().queue(
-            box task!(fail_the_event_source_connection: move || {
+            task!(fail_the_event_source_connection: move || {
                 let event_source = event_source.root();
                 if event_source.ready_state.get() != ReadyState::Closed {
                     event_source.ready_state.set(ReadyState::Closed);
                     event_source.upcast::<EventTarget>().fire_event(atom!("error"));
                 }
             }),
             &global,
         );
@@ -140,17 +140,17 @@ impl EventSourceContext {
             return;
         }
 
         let trusted_event_source = self.event_source.clone();
         let action_sender = self.action_sender.clone();
         let global = event_source.global();
         // FIXME(nox): Why are errors silenced here?
         let _ = global.networking_task_source().queue(
-            box task!(reestablish_the_event_source_onnection: move || {
+            task!(reestablish_the_event_source_onnection: move || {
                 let event_source = trusted_event_source.root();
 
                 // Step 1.1.
                 if event_source.ready_state.get() == ReadyState::Closed {
                     return;
                 }
 
                 // Step 1.2.
@@ -237,17 +237,17 @@ impl EventSourceContext {
         self.data.clear();
 
         // Step 8.
         let global = event_source.global();
         let event_source = self.event_source.clone();
         let event = Trusted::new(&*event);
         // FIXME(nox): Why are errors silenced here?
         let _ = global.networking_task_source().queue(
-            box task!(dispatch_the_event_source_event: move || {
+            task!(dispatch_the_event_source_event: move || {
                 let event_source = event_source.root();
                 if event_source.ready_state.get() != ReadyState::Closed {
                     event.root().upcast::<Event>().fire(&event_source.upcast());
                 }
             }),
             &global,
         );
     }
--- a/servo/components/script/dom/filereader.rs
+++ b/servo/components/script/dom/filereader.rs
@@ -411,16 +411,16 @@ fn perform_annotated_read_operation(
     data: ReadMetaData,
     blob_contents: Arc<Vec<u8>>,
     filereader: TrustedFileReader,
     task_source: FileReadingTaskSource,
     canceller: TaskCanceller,
 ) {
     // Step 4
     let task = FileReadingTask::ProcessRead(filereader.clone(), gen_id);
-    task_source.queue_with_canceller(box task, &canceller).unwrap();
+    task_source.queue_with_canceller(task, &canceller).unwrap();
 
     let task = FileReadingTask::ProcessReadData(filereader.clone(), gen_id);
-    task_source.queue_with_canceller(box task, &canceller).unwrap();
+    task_source.queue_with_canceller(task, &canceller).unwrap();
 
     let task = FileReadingTask::ProcessReadEOF(filereader, gen_id, data, blob_contents);
-    task_source.queue_with_canceller(box task, &canceller).unwrap();
+    task_source.queue_with_canceller(task, &canceller).unwrap();
 }
--- a/servo/components/script/dom/htmldetailselement.rs
+++ b/servo/components/script/dom/htmldetailselement.rs
@@ -65,17 +65,17 @@ impl VirtualMethods for HTMLDetailsEleme
         if attr.local_name() == &local_name!("open") {
             let counter = self.toggle_counter.get() + 1;
             self.toggle_counter.set(counter);
 
             let window = window_from_node(self);
             let this = Trusted::new(self);
             // FIXME(nox): Why are errors silenced here?
             let _ = window.dom_manipulation_task_source().queue(
-                box task!(details_notification_task_steps: move || {
+                task!(details_notification_task_steps: move || {
                     let this = this.root();
                     if counter == this.toggle_counter.get() {
                         this.upcast::<EventTarget>().fire_event(atom!("toggle"));
                     }
                 }),
                 window.upcast(),
             );
         }
--- a/servo/components/script/dom/htmlformelement.rs
+++ b/servo/components/script/dom/htmlformelement.rs
@@ -435,17 +435,17 @@ impl HTMLFormElement {
         // generation ID is the same as its own generation ID.
         let generation_id = GenerationId(self.generation_id.get().0 + 1);
         self.generation_id.set(generation_id);
 
         // Step 2.
         let pipeline_id = window.upcast::<GlobalScope>().pipeline_id();
         let script_chan = window.main_thread_script_chan().clone();
         let this = Trusted::new(self);
-        let task = box task!(navigate_to_form_planned_navigation: move || {
+        let task = task!(navigate_to_form_planned_navigation: move || {
             if generation_id != this.root().generation_id.get() {
                 return;
             }
             script_chan.send(MainThreadScriptMsg::Navigate(
                 pipeline_id,
                 load_data,
                 false,
             )).unwrap();
--- a/servo/components/script/dom/htmliframeelement.rs
+++ b/servo/components/script/dom/htmliframeelement.rs
@@ -231,17 +231,17 @@ impl HTMLIFrameElement {
         let window = window_from_node(self);
 
         // https://github.com/whatwg/html/issues/490
         if mode == ProcessingMode::FirstTime && !self.upcast::<Element>().has_attribute(&local_name!("src")) {
             let this = Trusted::new(self);
             let pipeline_id = self.pipeline_id().unwrap();
             // FIXME(nox): Why are errors silenced here?
             let _ = window.dom_manipulation_task_source().queue(
-                box task!(iframe_load_event_steps: move || {
+                task!(iframe_load_event_steps: move || {
                     this.root().iframe_load_event_steps(pipeline_id);
                 }),
                 window.upcast(),
             );
             return;
         }
 
         let url = self.get_url();
--- a/servo/components/script/dom/htmlimageelement.rs
+++ b/servo/components/script/dom/htmlimageelement.rs
@@ -169,17 +169,17 @@ impl HTMLImageElement {
             ROUTER.add_route(responder_receiver.to_opaque(), box move |message| {
                 debug!("Got image {:?}", message);
                 // Return the image via a message to the script thread, which marks
                 // the element as dirty and triggers a reflow.
                 let element = trusted_node.clone();
                 let image = message.to().unwrap();
                 // FIXME(nox): Why are errors silenced here?
                 let _ = task_source.queue_with_canceller(
-                    box task!(process_image_response: move || {
+                    task!(process_image_response: move || {
                         let element = element.root();
                         // Ignore any image response for a previous request that has been discarded.
                         if generation == element.generation.get() {
                             element.process_image_response(image);
                         }
                     }),
                     &task_canceller,
                 );
@@ -420,17 +420,17 @@ impl HTMLImageElement {
                 // Step 8.
                 // TODO: Handle pixel density.
                 src
             },
             None => {
                 // Step 9.
                 // FIXME(nox): Why are errors silenced here?
                 let _ = task_source.queue(
-                    box task!(image_null_source_error: move || {
+                    task!(image_null_source_error: move || {
                         let this = this.root();
                         {
                             let mut current_request =
                                 this.current_request.borrow_mut();
                             current_request.source_url = None;
                             current_request.parsed_url = None;
                         }
                         if this.upcast::<Element>().has_attribute(&local_name!("src")) {
@@ -446,17 +446,17 @@ impl HTMLImageElement {
                 );
                 return;
             },
         };
         // Step 10.
         let target = Trusted::new(self.upcast::<EventTarget>());
         // FIXME(nox): Why are errors silenced here?
         let _ = task_source.queue(
-            box task!(fire_progress_event: move || {
+            task!(fire_progress_event: move || {
                 let target = target.root();
 
                 let event = ProgressEvent::new(
                     &target.global(),
                     atom!("loadstart"),
                     EventBubbles::DoesNotBubble,
                     EventCancelable::NotCancelable,
                     false,
@@ -475,17 +475,17 @@ impl HTMLImageElement {
                     // Step 12
                 self.prepare_image_request(&url, &src);
             },
             Err(_) => {
                 // Step 11.1-11.5.
                 let src = String::from(src);
                 // FIXME(nox): Why are errors silenced here?
                 let _ = task_source.queue(
-                    box task!(image_selected_source_error: move || {
+                    task!(image_selected_source_error: move || {
                         let this = this.root();
                         {
                             let mut current_request =
                                 this.current_request.borrow_mut();
                             current_request.source_url = Some(src.into());
                         }
                         this.upcast::<EventTarget>().fire_event(atom!("error"));
                         this.upcast::<EventTarget>().fire_event(atom!("loadend"));
@@ -544,17 +544,17 @@ impl HTMLImageElement {
                     self.abort_request(State::CompletelyAvailable, ImageRequestPhase::Pending);
                     let mut current_request = self.current_request.borrow_mut();
                     current_request.final_url = Some(url);
                     current_request.image = Some(image.clone());
                     current_request.metadata = Some(metadata);
                     let this = Trusted::new(self);
                     let src = String::from(src);
                     let _ = window.dom_manipulation_task_source().queue(
-                        box task!(image_load_event: move || {
+                        task!(image_load_event: move || {
                             let this = this.root();
                             {
                                 let mut current_request =
                                     this.current_request.borrow_mut();
                                 current_request.parsed_url = Some(img_url);
                                 current_request.source_url = Some(src.into());
                             }
                             // TODO: restart animation, if set.
--- a/servo/components/script/dom/htmlmediaelement.rs
+++ b/servo/components/script/dom/htmlmediaelement.rs
@@ -149,17 +149,17 @@ impl HTMLMediaElement {
             // FIXME(nox): Take pending play promises and let promises be the
             // result.
 
             // Step 2.3.
             let window = window_from_node(self);
             let target = Trusted::new(self.upcast::<EventTarget>());
             // FIXME(nox): Why are errors silenced here?
             let _ = window.dom_manipulation_task_source().queue(
-                box task!(internal_pause_steps: move || {
+                task!(internal_pause_steps: move || {
                     let target = target.root();
 
                     // Step 2.3.1.
                     target.fire_event(atom!("timeupdate"));
 
                     // Step 2.3.2.
                     target.fire_event(atom!("pause"));
 
@@ -181,17 +181,17 @@ impl HTMLMediaElement {
         // Step 1.
         // TODO(nox): Take pending play promises and let promises be the result.
 
         // Step 2.
         let target = Trusted::new(self.upcast::<EventTarget>());
         let window = window_from_node(self);
         // FIXME(nox): Why are errors silenced here?
         let _ = window.dom_manipulation_task_source().queue(
-            box task!(notify_about_playing: move || {
+            task!(notify_about_playing: move || {
                 let target = target.root();
 
                 // Step 2.1.
                 target.fire_event(atom!("playing"));
 
                 // Step 2.2.
                 // FIXME(nox): Resolve pending play promises with promises.
             }),
@@ -490,17 +490,17 @@ impl HTMLMediaElement {
     /// Queues the [dedicated media source failure steps][steps].
     ///
     /// [steps]: https://html.spec.whatwg.org/multipage/#dedicated-media-source-failure-steps
     fn queue_dedicated_media_source_failure_steps(&self) {
         let this = Trusted::new(self);
         let window = window_from_node(self);
         // FIXME(nox): Why are errors silenced here?
         let _ = window.dom_manipulation_task_source().queue(
-            box task!(dedicated_media_source_failure_steps: move || {
+            task!(dedicated_media_source_failure_steps: move || {
                 let this = this.root();
 
                 // Step 1.
                 this.error.set(Some(&*MediaError::new(
                     &window_from_node(&*this),
                     MEDIA_ERR_SRC_NOT_SUPPORTED,
                 )));
 
--- a/servo/components/script/dom/serviceworker.rs
+++ b/servo/components/script/dom/serviceworker.rs
@@ -14,17 +14,17 @@ use dom::bindings::str::USVString;
 use dom::bindings::structuredclone::StructuredCloneData;
 use dom::eventtarget::EventTarget;
 use dom::globalscope::GlobalScope;
 use dom_struct::dom_struct;
 use js::jsapi::{HandleValue, JSContext};
 use script_traits::{ScriptMsg, DOMMessage};
 use servo_url::ServoUrl;
 use std::cell::Cell;
-use task::Task;
+use task::TaskOnce;
 
 pub type TrustedServiceWorkerAddress = Trusted<ServiceWorker>;
 
 #[dom_struct]
 pub struct ServiceWorker {
     eventtarget: EventTarget,
     script_url: DOMRefCell<String>,
     scope_url: ServoUrl,
@@ -99,15 +99,14 @@ impl ServiceWorkerMethods for ServiceWor
 
     // https://w3c.github.io/ServiceWorker/#service-worker-container-onerror-attribute
     event_handler!(error, GetOnerror, SetOnerror);
 
     // https://w3c.github.io/ServiceWorker/#ref-for-service-worker-onstatechange-attribute-1
     event_handler!(statechange, GetOnstatechange, SetOnstatechange);
 }
 
-impl Task for SimpleWorkerErrorHandler<ServiceWorker> {
+impl TaskOnce for SimpleWorkerErrorHandler<ServiceWorker> {
     #[allow(unrooted_must_root)]
-    fn run(self: Box<Self>) {
-        let this = *self;
-        ServiceWorker::dispatch_simple_error(this.addr);
+    fn run_once(self) {
+        ServiceWorker::dispatch_simple_error(self.addr);
     }
 }
--- a/servo/components/script/dom/storage.rs
+++ b/servo/components/script/dom/storage.rs
@@ -162,17 +162,17 @@ impl Storage {
         url: ServoUrl,
         key: Option<String>,
         old_value: Option<String>,
         new_value: Option<String>,
     ) {
         let global = self.global();
         let this = Trusted::new(self);
         global.as_window().dom_manipulation_task_source().queue(
-            box task!(send_storage_notification: move || {
+            task!(send_storage_notification: move || {
                 let this = this.root();
                 let global = this.global();
                 let event = StorageEvent::new(
                     global.as_window(),
                     atom!("storage"),
                     EventBubbles::DoesNotBubble,
                     EventCancelable::NotCancelable,
                     key.map(DOMString::from),
--- a/servo/components/script/dom/websocket.rs
+++ b/servo/components/script/dom/websocket.rs
@@ -32,17 +32,17 @@ use net_traits::MessageData;
 use script_runtime::CommonScriptMsg;
 use script_runtime::ScriptThreadEventCategory::WebSocketEvent;
 use servo_url::ServoUrl;
 use std::ascii::AsciiExt;
 use std::borrow::ToOwned;
 use std::cell::Cell;
 use std::ptr;
 use std::thread;
-use task::{Task, TaskCanceller};
+use task::{TaskOnce, TaskCanceller};
 use task_source::TaskSource;
 use task_source::networking::NetworkingTaskSource;
 
 #[derive(Clone, Copy, Debug, HeapSizeOf, JSTraceable, PartialEq)]
 enum WebSocketRequestState {
     Connecting = 0,
     Open = 1,
     Closing = 2,
@@ -69,31 +69,31 @@ mod close_code {
 
 pub fn close_the_websocket_connection(
     address: Trusted<WebSocket>,
     task_source: &NetworkingTaskSource,
     canceller: &TaskCanceller,
     code: Option<u16>,
     reason: String,
 ) {
-    let close_task = box CloseTask {
+    let close_task = CloseTask {
         address: address,
         failed: false,
         code: code,
         reason: Some(reason),
     };
     task_source.queue_with_canceller(close_task, &canceller).unwrap();
 }
 
 pub fn fail_the_websocket_connection(
     address: Trusted<WebSocket>,
     task_source: &NetworkingTaskSource,
     canceller: &TaskCanceller,
 ) {
-    let close_task = box CloseTask {
+    let close_task = CloseTask {
         address: address,
         failed: true,
         code: Some(close_code::ABNORMAL),
         reason: None,
     };
     task_source.queue_with_canceller(close_task, &canceller).unwrap();
 }
 
@@ -201,24 +201,24 @@ impl WebSocket {
         *ws.sender.borrow_mut() = Some(dom_action_sender);
 
         let task_source = global.networking_task_source();
         let canceller = global.task_canceller();
         thread::spawn(move || {
             while let Ok(event) = dom_event_receiver.recv() {
                 match event {
                     WebSocketNetworkEvent::ConnectionEstablished { protocol_in_use } => {
-                        let open_thread = box ConnectionEstablishedTask {
+                        let open_thread = ConnectionEstablishedTask {
                             address: address.clone(),
                             protocol_in_use,
                         };
                         task_source.queue_with_canceller(open_thread, &canceller).unwrap();
                     },
                     WebSocketNetworkEvent::MessageReceived(message) => {
-                        let message_thread = box MessageReceivedTask {
+                        let message_thread = MessageReceivedTask {
                             address: address.clone(),
                             message: message,
                         };
                         task_source.queue_with_canceller(message_thread, &canceller).unwrap();
                     },
                     WebSocketNetworkEvent::Fail => {
                         fail_the_websocket_connection(address.clone(),
                             &task_source, &canceller);
@@ -392,19 +392,19 @@ impl WebSocketMethods for WebSocket {
 
 /// Task queued when *the WebSocket connection is established*.
 /// https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol:concept-websocket-established
 struct ConnectionEstablishedTask {
     address: Trusted<WebSocket>,
     protocol_in_use: Option<String>,
 }
 
-impl Task for ConnectionEstablishedTask {
+impl TaskOnce for ConnectionEstablishedTask {
     /// https://html.spec.whatwg.org/multipage/#feedback-from-the-protocol:concept-websocket-established
-    fn run(self: Box<Self>) {
+    fn run_once(self) {
         let ws = self.address.root();
 
         // Step 1.
         ws.ready_state.set(WebSocketRequestState::Open);
 
         // Step 2: Extensions.
         // TODO: Set extensions to extensions in use.
 
@@ -417,39 +417,39 @@ impl Task for ConnectionEstablishedTask 
         ws.upcast().fire_event(atom!("open"));
     }
 }
 
 struct BufferedAmountTask {
     address: Trusted<WebSocket>,
 }
 
-impl Task for BufferedAmountTask {
+impl TaskOnce for BufferedAmountTask {
     // See https://html.spec.whatwg.org/multipage/#dom-websocket-bufferedamount
     //
     // To be compliant with standards, we need to reset bufferedAmount only when the event loop
     // reaches step 1.  In our implementation, the bytes will already have been sent on a background
     // thread.
-    fn run(self: Box<Self>) {
+    fn run_once(self) {
         let ws = self.address.root();
 
         ws.buffered_amount.set(0);
         ws.clearing_buffer.set(false);
     }
 }
 
 struct CloseTask {
     address: Trusted<WebSocket>,
     failed: bool,
     code: Option<u16>,
     reason: Option<String>,
 }
 
-impl Task for CloseTask {
-    fn run(self: Box<Self>) {
+impl TaskOnce for CloseTask {
+    fn run_once(self) {
         let ws = self.address.root();
 
         if ws.ready_state.get() == WebSocketRequestState::Closed {
             // Do nothing if already closed.
             return;
         }
 
         // Perform _the WebSocket connection is closed_ steps.
@@ -478,19 +478,19 @@ impl Task for CloseTask {
     }
 }
 
 struct MessageReceivedTask {
     address: Trusted<WebSocket>,
     message: MessageData,
 }
 
-impl Task for MessageReceivedTask {
+impl TaskOnce for MessageReceivedTask {
     #[allow(unsafe_code)]
-    fn run(self: Box<Self>) {
+    fn run_once(self) {
         let ws = self.address.root();
         debug!("MessageReceivedTask::handler({:p}): readyState={:?}", &*ws,
                ws.ready_state.get());
 
         // Step 1.
         if ws.ready_state.get() != WebSocketRequestState::Open {
             return;
         }
--- a/servo/components/script/dom/window.rs
+++ b/servo/components/script/dom/window.rs
@@ -1972,17 +1972,17 @@ fn debug_reflow_events(id: PipelineId, g
 impl Window {
     // https://html.spec.whatwg.org/multipage/#dom-window-postmessage step 7.
     pub fn post_message(
         &self,
         target_origin: Option<ImmutableOrigin>,
         serialize_with_transfer_result: StructuredCloneData,
     ) {
         let this = Trusted::new(self);
-        let task = box task!(post_serialised_message: move || {
+        let task = task!(post_serialised_message: move || {
             let this = this.root();
 
             // Step 7.1.
             if let Some(target_origin) = target_origin {
                 if !target_origin.same_origin(this.Document().origin()) {
                     return;
                 }
             }
@@ -2007,12 +2007,12 @@ impl Window {
                 this.upcast(),
                 message_clone.handle(),
             );
         });
         // FIXME(nox): Why are errors silenced here?
         // TODO(#12718): Use the "posted message task source".
         let _ = self.script_chan.send(CommonScriptMsg::Task(
             ScriptThreadEventCategory::DomEvent,
-            self.task_canceller().wrap_task(task),
+            box self.task_canceller().wrap_task(task),
         ));
     }
 }
--- a/servo/components/script/dom/worker.rs
+++ b/servo/components/script/dom/worker.rs
@@ -23,17 +23,17 @@ use dom_struct::dom_struct;
 use ipc_channel::ipc;
 use js::jsapi::{HandleValue, JSAutoCompartment, JSContext};
 use js::jsval::UndefinedValue;
 use script_traits::WorkerScriptLoadOrigin;
 use std::cell::Cell;
 use std::sync::{Arc, Mutex};
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::mpsc::{Sender, channel};
-use task::Task;
+use task::TaskOnce;
 
 pub type TrustedWorkerAddress = Trusted<Worker>;
 
 // https://html.spec.whatwg.org/multipage/#worker
 #[dom_struct]
 pub struct Worker {
     eventtarget: EventTarget,
     #[ignore_heap_size_of = "Defined in std"]
@@ -170,15 +170,14 @@ impl WorkerMethods for Worker {
 
     // https://html.spec.whatwg.org/multipage/#handler-worker-onmessage
     event_handler!(message, GetOnmessage, SetOnmessage);
 
     // https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onerror
     event_handler!(error, GetOnerror, SetOnerror);
 }
 
-impl Task for SimpleWorkerErrorHandler<Worker> {
+impl TaskOnce for SimpleWorkerErrorHandler<Worker> {
     #[allow(unrooted_must_root)]
-    fn run(self: Box<Self>) {
-        let this = *self;
-        Worker::dispatch_simple_error(this.addr);
+    fn run_once(self) {
+        Worker::dispatch_simple_error(self.addr);
     }
 }
--- a/servo/components/script/dom/workerglobalscope.rs
+++ b/servo/components/script/dom/workerglobalscope.rs
@@ -383,17 +383,17 @@ impl WorkerGlobalScope {
         } else {
             panic!("need to implement a sender for SharedWorker/ServiceWorker")
         }
     }
 
     pub fn process_event(&self, msg: CommonScriptMsg) {
         match msg {
             CommonScriptMsg::Task(_, task) => {
-                task.run()
+                task.run_box()
             },
             CommonScriptMsg::CollectReports(reports_chan) => {
                 let cx = self.get_cx();
                 let path_seg = format!("url({})", self.get_url());
                 let reports = get_reports(cx, path_seg);
                 reports_chan.send(reports);
             },
         }
--- a/servo/components/script/dom/worklet.rs
+++ b/servo/components/script/dom/worklet.rs
@@ -64,17 +64,17 @@ use std::sync::atomic::AtomicIsize;
 use std::sync::atomic::Ordering;
 use std::sync::mpsc;
 use std::sync::mpsc::Receiver;
 use std::sync::mpsc::Sender;
 use std::thread;
 use style::thread_state;
 use swapper::Swapper;
 use swapper::swapper;
-use task::Task;
+use task::TaskBox;
 use uuid::Uuid;
 
 // Magic numbers
 const WORKLET_THREAD_POOL_SIZE: u32 = 3;
 const MIN_GC_THRESHOLD: u32 = 1_000_000;
 
 #[dom_struct]
 /// https://drafts.css-houdini.org/worklets/#worklet
@@ -640,18 +640,19 @@ impl WorkletThread {
                                                        credentials,
                                                        pending_tasks_struct,
                                                        promise)
             }
         }
     }
 
     /// Run a task in the main script thread.
-    fn run_in_script_thread<T>(&self, task: T) where
-        T: 'static + Send + Task,
+    fn run_in_script_thread<T>(&self, task: T)
+    where
+        T: TaskBox + 'static,
     {
         let msg = CommonScriptMsg::Task(ScriptThreadEventCategory::WorkletEvent, box task);
         let msg = MainThreadScriptMsg::Common(msg);
         self.global_init.to_script_thread_sender.send(msg).expect("Worklet thread outlived script thread.");
     }
 }
 
 /// An executor of worklet tasks
--- a/servo/components/script/network_listener.rs
+++ b/servo/components/script/network_listener.rs
@@ -1,29 +1,29 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 net_traits::{Action, FetchResponseListener, FetchResponseMsg};
 use std::sync::{Arc, Mutex};
-use task::{Task, TaskCanceller};
+use task::{TaskCanceller, TaskOnce};
 use task_source::TaskSource;
 use task_source::networking::NetworkingTaskSource;
 
 /// An off-thread sink for async network event tasks. All such events are forwarded to
 /// a target thread, where they are invoked on the provided context object.
 pub struct NetworkListener<Listener: PreInvoke + Send + 'static> {
     pub context: Arc<Mutex<Listener>>,
     pub task_source: NetworkingTaskSource,
     pub canceller: Option<TaskCanceller>,
 }
 
 impl<Listener: PreInvoke + Send + 'static> NetworkListener<Listener> {
     pub fn notify<A: Action<Listener> + Send + 'static>(&self, action: A) {
-        let task = box ListenerTask {
+        let task = ListenerTask {
             context: self.context.clone(),
             action: action,
         };
         let result = if let Some(ref canceller) = self.canceller {
             self.task_source.queue_with_canceller(task, canceller)
         } else {
             self.task_source.queue_unconditionally(task)
         };
@@ -50,21 +50,20 @@ pub trait PreInvoke {
 }
 
 /// A task for moving the async network events between threads.
 struct ListenerTask<A: Action<Listener> + Send + 'static, Listener: PreInvoke + Send> {
     context: Arc<Mutex<Listener>>,
     action: A,
 }
 
-impl<A, Listener> Task for ListenerTask<A, Listener>
+impl<A, Listener> TaskOnce for ListenerTask<A, Listener>
 where
     A: Action<Listener> + Send + 'static,
     Listener: PreInvoke + Send,
 {
-    fn run(self: Box<Self>) {
-        let this = *self;
-        let mut context = this.context.lock().unwrap();
+    fn run_once(self) {
+        let mut context = self.context.lock().unwrap();
         if context.should_invoke() {
-            this.action.process(&mut *context);
+            self.action.process(&mut *context);
         }
     }
 }
--- a/servo/components/script/script_runtime.rs
+++ b/servo/components/script/script_runtime.rs
@@ -30,26 +30,26 @@ use std::cell::Cell;
 use std::fmt;
 use std::io::{Write, stdout};
 use std::marker::PhantomData;
 use std::os;
 use std::os::raw::c_void;
 use std::panic::AssertUnwindSafe;
 use std::ptr;
 use style::thread_state;
-use task::Task;
+use task::TaskBox;
 use time::{Tm, now};
 
 /// Common messages used to control the event loops in both the script and the worker
 pub enum CommonScriptMsg {
     /// Requests that the script thread measure its memory usage. The results are sent back via the
     /// supplied channel.
     CollectReports(ReportsChan),
     /// Generic message that encapsulates event handling.
-    Task(ScriptThreadEventCategory, Box<Task + Send>),
+    Task(ScriptThreadEventCategory, Box<TaskBox>),
 }
 
 impl fmt::Debug for CommonScriptMsg {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             CommonScriptMsg::CollectReports(_) => write!(f, "CollectReports(...)"),
             CommonScriptMsg::Task(ref category, ref task) => {
                 f.debug_tuple("Task").field(category).field(task).finish()
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -1253,17 +1253,17 @@ impl ScriptThread {
         match msg {
             MainThreadScriptMsg::Navigate(parent_pipeline_id, load_data, replace) => {
                 self.handle_navigate(parent_pipeline_id, None, load_data, replace)
             },
             MainThreadScriptMsg::ExitWindow(id) => {
                 self.handle_exit_window_msg(id)
             },
             MainThreadScriptMsg::Common(CommonScriptMsg::Task(_, task)) => {
-                task.run()
+                task.run_box()
             }
             MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(chan)) => {
                 self.collect_reports(chan)
             },
             MainThreadScriptMsg::WorkletLoaded(pipeline_id) => {
                 self.handle_worklet_loaded(pipeline_id)
             },
             MainThreadScriptMsg::RegisterPaintWorklet {
--- a/servo/components/script/serviceworkerjob.rs
+++ b/servo/components/script/serviceworkerjob.rs
@@ -270,17 +270,17 @@ fn settle_job_promise(global: &GlobalSco
 }
 
 #[allow(unrooted_must_root)]
 fn queue_settle_promise_for_job(job: &Job, settle: SettleType, task_source: &DOMManipulationTaskSource) {
     let global = job.client.global();
     let promise = TrustedPromise::new(job.promise.clone());
     // FIXME(nox): Why are errors silenced here?
     let _ = task_source.queue(
-        box task!(settle_promise_for_job: move || {
+        task!(settle_promise_for_job: move || {
             let promise = promise.root();
             settle_job_promise(&promise.global(), &promise, settle)
         }),
         &*global,
     );
 }
 
 // https://w3c.github.io/ServiceWorker/#reject-job-promise-algorithm
--- a/servo/components/script/task.rs
+++ b/servo/components/script/task.rs
@@ -8,87 +8,110 @@ use std::fmt;
 use std::intrinsics;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool, Ordering};
 
 macro_rules! task {
     ($name:ident: move || $body:tt) => {{
         #[allow(non_camel_case_types)]
         struct $name<F>(F);
-        impl<F> ::task::Task for $name<F>
+        impl<F> ::task::TaskOnce for $name<F>
         where
-            F: ::std::ops::FnOnce(),
+            F: ::std::ops::FnOnce() + Send,
         {
             fn name(&self) -> &'static str {
                 stringify!($name)
             }
 
-            fn run(self: Box<Self>) {
+            fn run_once(self) {
                 (self.0)();
             }
         }
         $name(move || $body)
     }};
 }
 
 /// A task that can be run. The name method is for profiling purposes.
-pub trait Task {
+pub trait TaskOnce: Send {
     #[allow(unsafe_code)]
-    fn name(&self) -> &'static str { unsafe { intrinsics::type_name::<Self>() } }
-    fn run(self: Box<Self>);
+    fn name(&self) -> &'static str {
+        unsafe { intrinsics::type_name::<Self>() }
+    }
+
+    fn run_once(self);
 }
 
-impl fmt::Debug for Task + Send {
+/// A boxed version of `TaskOnce`.
+pub trait TaskBox: Send {
+    fn name(&self) -> &'static str;
+
+    fn run_box(self: Box<Self>);
+}
+
+impl<T> TaskBox for T
+where
+    T: TaskOnce,
+{
+    fn name(&self) -> &'static str {
+        TaskOnce::name(self)
+    }
+
+    fn run_box(self: Box<Self>) {
+        self.run_once()
+    }
+}
+
+impl fmt::Debug for TaskBox {
     fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
         fmt.debug_tuple(self.name()).field(&format_args!("...")).finish()
     }
 }
 
 /// Encapsulated state required to create cancellable tasks from non-script threads.
 pub struct TaskCanceller {
     pub cancelled: Option<Arc<AtomicBool>>,
 }
 
 impl TaskCanceller {
     /// Returns a wrapped `task` that will be cancelled if the `TaskCanceller`
     /// says so.
-    pub fn wrap_task<T>(&self, task: Box<T>) -> Box<Task + Send>
+    pub fn wrap_task<T>(&self, task: T) -> impl TaskOnce
     where
-        T: Send + Task + 'static,
+        T: TaskOnce,
     {
-        box CancellableTask {
+        CancellableTask {
             cancelled: self.cancelled.clone(),
             inner: task,
         }
     }
 }
 
 /// A task that can be cancelled by toggling a shared flag.
-pub struct CancellableTask<T: Send + Task> {
+pub struct CancellableTask<T: TaskOnce> {
     cancelled: Option<Arc<AtomicBool>>,
-    inner: Box<T>,
+    inner: T,
 }
 
 impl<T> CancellableTask<T>
 where
-    T: Send + Task,
+    T: TaskOnce,
 {
     fn is_cancelled(&self) -> bool {
         self.cancelled.as_ref().map_or(false, |cancelled| {
             cancelled.load(Ordering::SeqCst)
         })
     }
 }
 
-impl<T> Task for CancellableTask<T>
+impl<T> TaskOnce for CancellableTask<T>
 where
-    T: Send + Task,
+    T: TaskOnce,
 {
     fn name(&self) -> &'static str {
         self.inner.name()
     }
 
-    fn run(self: Box<Self>) {
+    fn run_once(self) {
         if !self.is_cancelled() {
-            self.inner.run()
+            self.inner.run_once()
         }
     }
 }
--- a/servo/components/script/task_source/dom_manipulation.rs
+++ b/servo/components/script/task_source/dom_manipulation.rs
@@ -8,59 +8,59 @@ use dom::event::{EventBubbles, EventCanc
 use dom::eventtarget::EventTarget;
 use dom::window::Window;
 use script_runtime::{CommonScriptMsg, ScriptThreadEventCategory};
 use script_thread::MainThreadScriptMsg;
 use servo_atoms::Atom;
 use std::fmt;
 use std::result::Result;
 use std::sync::mpsc::Sender;
-use task::{Task, TaskCanceller};
+use task::{TaskCanceller, TaskOnce};
 use task_source::TaskSource;
 
 #[derive(Clone, JSTraceable)]
 pub struct DOMManipulationTaskSource(pub Sender<MainThreadScriptMsg>);
 
 impl fmt::Debug for DOMManipulationTaskSource {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "DOMManipulationTaskSource(...)")
     }
 }
 
 impl TaskSource for DOMManipulationTaskSource {
     fn queue_with_canceller<T>(
         &self,
-        msg: Box<T>,
+        task: T,
         canceller: &TaskCanceller,
     ) -> Result<(), ()>
     where
-        T: Task + Send + 'static,
+        T: TaskOnce + 'static,
     {
         let msg = MainThreadScriptMsg::Common(CommonScriptMsg::Task(
             ScriptThreadEventCategory::ScriptEvent,
-            canceller.wrap_task(msg),
+            box canceller.wrap_task(task),
         ));
         self.0.send(msg).map_err(|_| ())
     }
 }
 
 impl DOMManipulationTaskSource {
     pub fn queue_event(&self,
                        target: &EventTarget,
                        name: Atom,
                        bubbles: EventBubbles,
                        cancelable: EventCancelable,
                        window: &Window) {
         let target = Trusted::new(target);
-        let task = box EventTask {
+        let task = EventTask {
             target: target,
             name: name,
             bubbles: bubbles,
             cancelable: cancelable,
         };
         let _ = self.queue(task, window.upcast());
     }
 
     pub fn queue_simple_event(&self, target: &EventTarget, name: Atom, window: &Window) {
         let target = Trusted::new(target);
-        let _ = self.queue(box SimpleEventTask { target, name }, window.upcast());
+        let _ = self.queue(SimpleEventTask { target, name }, window.upcast());
     }
 }
--- a/servo/components/script/task_source/file_reading.rs
+++ b/servo/components/script/task_source/file_reading.rs
@@ -1,46 +1,46 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 dom::domexception::DOMErrorName;
 use dom::filereader::{FileReader, TrustedFileReader, GenerationId, ReadMetaData};
 use script_runtime::{CommonScriptMsg, ScriptThreadEventCategory, ScriptChan};
 use std::sync::Arc;
-use task::{Task, TaskCanceller};
+use task::{TaskCanceller, TaskOnce};
 use task_source::TaskSource;
 
 #[derive(JSTraceable)]
 pub struct FileReadingTaskSource(pub Box<ScriptChan + Send + 'static>);
 
 impl Clone for FileReadingTaskSource {
     fn clone(&self) -> FileReadingTaskSource {
         FileReadingTaskSource(self.0.clone())
     }
 }
 
 impl TaskSource for FileReadingTaskSource {
     fn queue_with_canceller<T>(
         &self,
-        msg: Box<T>,
+        task: T,
         canceller: &TaskCanceller,
     ) -> Result<(), ()>
     where
-        T: Send + Task + 'static,
+        T: TaskOnce + 'static,
     {
         self.0.send(CommonScriptMsg::Task(
             ScriptThreadEventCategory::FileRead,
-            canceller.wrap_task(msg),
+            box canceller.wrap_task(task),
         ))
     }
 }
 
-impl Task for FileReadingTask {
-    fn run(self: Box<Self>) {
+impl TaskOnce for FileReadingTask {
+    fn run_once(self) {
         self.handle_task();
     }
 }
 
 #[allow(dead_code)]
 pub enum FileReadingTask {
     ProcessRead(TrustedFileReader, GenerationId),
     ProcessReadData(TrustedFileReader, GenerationId),
--- a/servo/components/script/task_source/mod.rs
+++ b/servo/components/script/task_source/mod.rs
@@ -6,23 +6,26 @@ pub mod dom_manipulation;
 pub mod file_reading;
 pub mod history_traversal;
 pub mod networking;
 pub mod performance_timeline;
 pub mod user_interaction;
 
 use dom::globalscope::GlobalScope;
 use std::result::Result;
-use task::{Task, TaskCanceller};
+use task::{TaskCanceller, TaskOnce};
 
 pub trait TaskSource {
     fn queue_with_canceller<T>(
         &self,
-        msg: Box<T>,
+        task: T,
         canceller: &TaskCanceller,
     ) -> Result<(), ()>
     where
-        T: Send + Task + 'static;
+        T: TaskOnce + 'static;
 
-    fn queue<T: Task + Send + 'static>(&self, msg: Box<T>, global: &GlobalScope) -> Result<(), ()> {
-        self.queue_with_canceller(msg, &global.task_canceller())
+    fn queue<T>(&self, task: T, global: &GlobalScope) -> Result<(), ()>
+    where
+        T: TaskOnce + 'static,
+    {
+        self.queue_with_canceller(task, &global.task_canceller())
     }
 }
--- a/servo/components/script/task_source/networking.rs
+++ b/servo/components/script/task_source/networking.rs
@@ -1,43 +1,46 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * 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 script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
-use task::{Task, TaskCanceller};
+use task::{TaskCanceller, TaskOnce};
 use task_source::TaskSource;
 
 #[derive(JSTraceable)]
 pub struct NetworkingTaskSource(pub Box<ScriptChan + Send + 'static>);
 
 impl Clone for NetworkingTaskSource {
     fn clone(&self) -> NetworkingTaskSource {
         NetworkingTaskSource(self.0.clone())
     }
 }
 
 impl TaskSource for NetworkingTaskSource {
     fn queue_with_canceller<T>(
         &self,
-        msg: Box<T>,
+        task: T,
         canceller: &TaskCanceller,
     ) -> Result<(), ()>
     where
-        T: Send + Task + 'static,
+        T: TaskOnce + 'static,
     {
         self.0.send(CommonScriptMsg::Task(
             ScriptThreadEventCategory::NetworkEvent,
-            canceller.wrap_task(msg),
+            box canceller.wrap_task(task),
         ))
     }
 }
 
 impl NetworkingTaskSource {
     /// This queues a task that will not be cancelled when its associated
     /// global scope gets destroyed.
-    pub fn queue_unconditionally<T>(&self, msg: Box<T>) -> Result<(), ()>
+    pub fn queue_unconditionally<T>(&self, task: T) -> Result<(), ()>
     where
-        T: Task + Send + 'static,
+        T: TaskOnce + 'static,
     {
-        self.0.send(CommonScriptMsg::Task(ScriptThreadEventCategory::NetworkEvent, msg))
+        self.0.send(CommonScriptMsg::Task(
+            ScriptThreadEventCategory::NetworkEvent,
+            box task,
+        ))
     }
 }
--- a/servo/components/script/task_source/performance_timeline.rs
+++ b/servo/components/script/task_source/performance_timeline.rs
@@ -6,17 +6,17 @@
 //     a low priority task and it should be processed during idle periods.
 //     We are currently treating this task queue as a normal priority queue.
 
 use dom::bindings::refcounted::Trusted;
 use dom::globalscope::GlobalScope;
 use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
 use std::fmt;
 use std::result::Result;
-use task::{Task, TaskCanceller};
+use task::{TaskCanceller, TaskOnce};
 use task_source::TaskSource;
 
 #[derive(JSTraceable)]
 pub struct PerformanceTimelineTaskSource(pub Box<ScriptChan + Send + 'static>);
 
 impl Clone for PerformanceTimelineTaskSource {
     fn clone(&self) -> PerformanceTimelineTaskSource {
         PerformanceTimelineTaskSource(self.0.clone())
@@ -27,34 +27,34 @@ impl fmt::Debug for PerformanceTimelineT
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "PerformanceTimelineTaskSource(...)")
     }
 }
 
 impl TaskSource for PerformanceTimelineTaskSource {
     fn queue_with_canceller<T>(
         &self,
-        msg: Box<T>,
+        task: T,
         canceller: &TaskCanceller,
     ) -> Result<(), ()>
     where
-        T: Send + Task + 'static,
+        T: TaskOnce + 'static,
     {
         let msg = CommonScriptMsg::Task(
             ScriptThreadEventCategory::PerformanceTimelineTask,
-            canceller.wrap_task(msg)
+            box canceller.wrap_task(task)
         );
         self.0.send(msg).map_err(|_| ())
     }
 }
 
 impl PerformanceTimelineTaskSource {
     pub fn queue_notification(&self, global: &GlobalScope) {
         let owner = Trusted::new(&*global.performance());
         // FIXME(nox): Why are errors silenced here?
         let _ = self.queue(
-            box task!(notify_performance_observers: move || {
+            task!(notify_performance_observers: move || {
                 owner.root().notify_observers();
             }),
             global,
         );
     }
 }
--- a/servo/components/script/task_source/user_interaction.rs
+++ b/servo/components/script/task_source/user_interaction.rs
@@ -8,49 +8,49 @@ use dom::event::{EventBubbles, EventCanc
 use dom::eventtarget::EventTarget;
 use dom::window::Window;
 use script_runtime::{CommonScriptMsg, ScriptThreadEventCategory};
 use script_thread::MainThreadScriptMsg;
 use servo_atoms::Atom;
 use std::fmt;
 use std::result::Result;
 use std::sync::mpsc::Sender;
-use task::{Task, TaskCanceller};
+use task::{TaskCanceller, TaskOnce};
 use task_source::TaskSource;
 
 #[derive(Clone, JSTraceable)]
 pub struct UserInteractionTaskSource(pub Sender<MainThreadScriptMsg>);
 
 impl fmt::Debug for UserInteractionTaskSource {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "UserInteractionTaskSource(...)")
     }
 }
 
 impl TaskSource for UserInteractionTaskSource {
     fn queue_with_canceller<T>(
         &self,
-        msg: Box<T>,
+        task: T,
         canceller: &TaskCanceller,
     ) -> Result<(), ()>
     where
-        T: Task + Send + 'static,
+        T: TaskOnce + 'static,
     {
         let msg = MainThreadScriptMsg::Common(CommonScriptMsg::Task(
             ScriptThreadEventCategory::InputEvent,
-            canceller.wrap_task(msg),
+            box canceller.wrap_task(task),
         ));
         self.0.send(msg).map_err(|_| ())
     }
 }
 
 impl UserInteractionTaskSource {
     pub fn queue_event(&self,
                        target: &EventTarget,
                        name: Atom,
                        bubbles: EventBubbles,
                        cancelable: EventCancelable,
                        window: &Window) {
         let target = Trusted::new(target);
-        let task = box EventTask { target, name, bubbles, cancelable };
+        let task = EventTask { target, name, bubbles, cancelable };
         let _ = self.queue(task, window.upcast());
     }
 }