diff options
author | yvt <i@yvt.jp> | 2021-07-10 17:24:27 +0900 |
---|---|---|
committer | yvt <i@yvt.jp> | 2021-07-10 17:55:42 +0900 |
commit | 01a7de50ab1843d85295f9dccad7f4c099e7208c (patch) | |
tree | ee53fb6e8889deb7b880ee969e6c662e6128d210 /components/script/microtask.rs | |
parent | ff8d2cdbbfc7a9dc7f38b7dd47cb350fde39388f (diff) | |
parent | 94b613fbdaa2b98f2179fc0bbda13c64e6fa0d38 (diff) | |
download | servo-01a7de50ab1843d85295f9dccad7f4c099e7208c.tar.gz servo-01a7de50ab1843d85295f9dccad7f4c099e7208c.zip |
Merge remote-tracking branch 'upstream/master' into feat-cow-infra
`tests/wpt/web-platform-tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html`
was reverted to the upstream version.
Diffstat (limited to 'components/script/microtask.rs')
-rw-r--r-- | components/script/microtask.rs | 119 |
1 files changed, 95 insertions, 24 deletions
diff --git a/components/script/microtask.rs b/components/script/microtask.rs index da04d1ed6ab..b97368fcad5 100644 --- a/components/script/microtask.rs +++ b/components/script/microtask.rs @@ -1,54 +1,88 @@ /* 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/. */ + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! Implementation of [microtasks](https://html.spec.whatwg.org/multipage/#microtask) and //! microtask queues. It is up to implementations of event loops to store a queue and //! perform checkpoints at appropriate times, as well as enqueue microtasks as required. -use dom::bindings::callback::ExceptionHandling; -use dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback; -use dom::bindings::js::Root; -use dom::globalscope::GlobalScope; +use crate::dom::bindings::callback::ExceptionHandling; +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback; +use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction; +use crate::dom::bindings::root::DomRoot; +use crate::dom::globalscope::GlobalScope; +use crate::dom::htmlimageelement::ImageElementMicrotask; +use crate::dom::htmlmediaelement::MediaElementMicrotask; +use crate::dom::mutationobserver::MutationObserver; +use crate::script_runtime::{notify_about_rejected_promises, JSContext}; +use crate::script_thread::ScriptThread; +use js::jsapi::{JobQueueIsEmpty, JobQueueMayNotBeEmpty}; use msg::constellation_msg::PipelineId; use std::cell::Cell; use std::mem; use std::rc::Rc; /// A collection of microtasks in FIFO order. -#[derive(JSTraceable, HeapSizeOf, Default)] +#[derive(Default, JSTraceable, MallocSizeOf)] pub struct MicrotaskQueue { /// The list of enqueued microtasks that will be invoked at the next microtask checkpoint. - microtask_queue: DOMRefCell<Vec<Microtask>>, - /// https://html.spec.whatwg.org/multipage/#performing-a-microtask-checkpoint + microtask_queue: DomRefCell<Vec<Microtask>>, + /// <https://html.spec.whatwg.org/multipage/#performing-a-microtask-checkpoint> performing_a_microtask_checkpoint: Cell<bool>, } -#[derive(JSTraceable, HeapSizeOf)] +#[derive(JSTraceable, MallocSizeOf)] pub enum Microtask { Promise(EnqueuedPromiseCallback), + User(UserMicrotask), + MediaElement(MediaElementMicrotask), + ImageElement(ImageElementMicrotask), + CustomElementReaction, + NotifyMutationObservers, +} + +pub trait MicrotaskRunnable { + fn handler(&self) {} } /// A promise callback scheduled to run during the next microtask checkpoint (#4283). -#[derive(JSTraceable, HeapSizeOf)] +#[derive(JSTraceable, MallocSizeOf)] pub struct EnqueuedPromiseCallback { - #[ignore_heap_size_of = "Rc has unclear ownership"] + #[ignore_malloc_size_of = "Rc has unclear ownership"] pub callback: Rc<PromiseJobCallback>, pub pipeline: PipelineId, + pub is_user_interacting: bool, +} + +/// A microtask that comes from a queueMicrotask() Javascript call, +/// identical to EnqueuedPromiseCallback once it's on the queue +#[derive(JSTraceable, MallocSizeOf)] +pub struct UserMicrotask { + #[ignore_malloc_size_of = "Rc has unclear ownership"] + pub callback: Rc<VoidFunction>, + pub pipeline: PipelineId, } impl MicrotaskQueue { /// Add a new microtask to this queue. It will be invoked as part of the next /// microtask checkpoint. - pub fn enqueue(&self, job: Microtask) { + #[allow(unsafe_code)] + pub fn enqueue(&self, job: Microtask, cx: JSContext) { self.microtask_queue.borrow_mut().push(job); + unsafe { JobQueueMayNotBeEmpty(*cx) }; } - /// https://html.spec.whatwg.org/multipage/#perform-a-microtask-checkpoint + /// <https://html.spec.whatwg.org/multipage/#perform-a-microtask-checkpoint> /// Perform a microtask checkpoint, executing all queued microtasks until the queue is empty. - pub fn checkpoint<F>(&self, target_provider: F) - where F: Fn(PipelineId) -> Option<Root<GlobalScope>> + #[allow(unsafe_code)] + pub fn checkpoint<F>( + &self, + cx: JSContext, + target_provider: F, + globalscopes: Vec<DomRoot<GlobalScope>>, + ) where + F: Fn(PipelineId) -> Option<DomRoot<GlobalScope>>, { if self.performing_a_microtask_checkpoint.get() { return; @@ -57,27 +91,64 @@ impl MicrotaskQueue { // Step 1 self.performing_a_microtask_checkpoint.set(true); - // Steps 2-7 + debug!("Now performing a microtask checkpoint"); + + // Steps 2 while !self.microtask_queue.borrow().is_empty() { rooted_vec!(let mut pending_queue); - mem::swap( - &mut *pending_queue, - &mut *self.microtask_queue.borrow_mut()); + mem::swap(&mut *pending_queue, &mut *self.microtask_queue.borrow_mut()); + + for (idx, job) in pending_queue.iter().enumerate() { + if idx == pending_queue.len() - 1 && self.microtask_queue.borrow().is_empty() { + unsafe { JobQueueIsEmpty(*cx) }; + } - for job in pending_queue.iter() { match *job { Microtask::Promise(ref job) => { if let Some(target) = target_provider(job.pipeline) { + let was_interacting = ScriptThread::is_user_interacting(); + ScriptThread::set_user_interacting(job.is_user_interacting); let _ = job.callback.Call_(&*target, ExceptionHandling::Report); + ScriptThread::set_user_interacting(was_interacting); } - } + }, + Microtask::User(ref job) => { + if let Some(target) = target_provider(job.pipeline) { + let _ = job.callback.Call_(&*target, ExceptionHandling::Report); + } + }, + Microtask::MediaElement(ref task) => { + task.handler(); + }, + Microtask::ImageElement(ref task) => { + task.handler(); + }, + Microtask::CustomElementReaction => { + ScriptThread::invoke_backup_element_queue(); + }, + Microtask::NotifyMutationObservers => { + MutationObserver::notify_mutation_observers(); + }, } } } - //TODO: Step 8 - notify about rejected promises + // Step 3 + for global in globalscopes.into_iter() { + notify_about_rejected_promises(&global); + } + + // TODO: Step 4 - Cleanup Indexed Database transactions. - // Step 9 + // Step 5 self.performing_a_microtask_checkpoint.set(false); } + + pub fn empty(&self) -> bool { + self.microtask_queue.borrow().is_empty() + } + + pub fn clear(&self) { + self.microtask_queue.borrow_mut().clear(); + } } |