Enqueue promise jobs from SpiderMonkey callbacks, and execute them in batches. Implement native Promise APIs.

Add SpiderMonkey hooks for enqueuing promise jobs. Start porting various native Promise APIs.
This commit is contained in:
Mátyás Mustoha 2016-07-20 12:47:55 +02:00 committed by Josh Matthews
parent a1091772ec
commit fd778b4240
4 changed files with 154 additions and 20 deletions

View file

@ -22,12 +22,14 @@ use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo};
use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
use devtools_traits::CSSError;
use document_loader::DocumentLoader;
use dom::bindings::callback::ExceptionHandling;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
use dom::bindings::codegen::Bindings::LocationBinding::LocationMethods;
use dom::bindings::codegen::Bindings::PromiseBinding::PromiseJobCallback;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, StringificationBehavior};
use dom::bindings::global::GlobalRef;
use dom::bindings::global::{GlobalRef, global_root_from_object};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root, RootCollection};
use dom::bindings::js::{RootCollectionPtr, RootedReference};
@ -59,8 +61,8 @@ use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::glue::GetWindowProxyClass;
use js::jsapi::{JSAutoCompartment, JSContext, JS_SetWrapObjectCallbacks};
use js::jsapi::{JSTracer, SetWindowProxyClass};
use js::jsapi::{JSAutoCompartment, JSContext, JS_SetWrapObjectCallbacks, HandleObject};
use js::jsapi::{JSTracer, SetWindowProxyClass, SetEnqueuePromiseJobCallback};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use mem::heap_size_of_self_and_children;
@ -91,6 +93,7 @@ use std::borrow::ToOwned;
use std::cell::Cell;
use std::collections::{HashMap, HashSet};
use std::option::Option;
use std::os::raw::c_void;
use std::ptr;
use std::rc::Rc;
use std::result::Result;
@ -315,6 +318,18 @@ impl OpaqueSender<CommonScriptMsg> for Sender<MainThreadScriptMsg> {
}
}
#[allow(unsafe_code)]
unsafe extern "C" fn enqueue_job(_cx: *mut JSContext,
job: HandleObject,
_allocation_site: HandleObject,
_data: *mut c_void) -> bool {
SCRIPT_THREAD_ROOT.with(|root| {
let script_thread = &*root.get().unwrap();
script_thread.enqueue_promise_job(job);
});
true
}
/// Information for an entire page. Pages are top-level browsing contexts and can contain multiple
/// frames.
#[derive(JSTraceable)]
@ -394,6 +409,16 @@ pub struct ScriptThread {
timer_event_port: Receiver<TimerEvent>,
content_process_shutdown_chan: IpcSender<()>,
flushing_job_queue: DOMRefCell<Vec<EnqueuedPromiseCallback>>,
promise_job_queue: DOMRefCell<Vec<EnqueuedPromiseCallback>>,
pending_promise_job_runnable: Cell<bool>,
}
#[derive(JSTraceable)]
struct EnqueuedPromiseCallback {
callback: Rc<PromiseJobCallback>,
pipeline: PipelineId,
}
/// In the event of thread panic, all data on the stack runs its destructor. However, there
@ -541,6 +566,7 @@ impl ScriptThread {
JS_SetWrapObjectCallbacks(runtime.rt(),
&WRAP_CALLBACKS);
SetWindowProxyClass(runtime.rt(), GetWindowProxyClass());
SetEnqueuePromiseJobCallback(runtime.rt(), Some(enqueue_job), ptr::null_mut());
}
// Ask the router to proxy IPC messages from the devtools to us.
@ -599,6 +625,10 @@ impl ScriptThread {
timer_event_port: timer_event_port,
content_process_shutdown_chan: state.content_process_shutdown_chan,
promise_job_queue: DOMRefCell::new(vec![]),
flushing_job_queue: DOMRefCell::new(vec![]),
pending_promise_job_runnable: Cell::new(false),
}
}
@ -2174,6 +2204,40 @@ impl ScriptThread {
location.Reload();
}
}
fn enqueue_promise_job(&self, job: HandleObject) {
let global = unsafe { global_root_from_object(job.get()) };
let pipeline = global.r().pipeline();
self.promise_job_queue.borrow_mut().push(EnqueuedPromiseCallback {
callback: PromiseJobCallback::new(job.get()),
pipeline: pipeline,
});
if !self.pending_promise_job_runnable.get() {
self.pending_promise_job_runnable.set(true);
let _ = self.dom_manipulation_task_source.queue(box FlushPromiseJobs, global.r());
}
}
fn flush_promise_jobs(&self) {
self.pending_promise_job_runnable.set(false);
{
let mut pending_queue = self.promise_job_queue.borrow_mut();
*self.flushing_job_queue.borrow_mut() = pending_queue.drain(..).collect();
}
for job in &*self.flushing_job_queue.borrow() {
if let Some(context) = self.find_child_context(job.pipeline) {
let _ = job.callback.Call_(&*context.active_window(), ExceptionHandling::Report);
}
}
self.flushing_job_queue.borrow_mut().clear();
}
}
struct FlushPromiseJobs;
impl Runnable for FlushPromiseJobs {
fn main_thread_handler(self: Box<FlushPromiseJobs>, script_thread: &ScriptThread) {
script_thread.flush_promise_jobs();
}
}
impl Drop for ScriptThread {