aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/bindings/codegen/Bindings.conf4
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py106
-rw-r--r--components/script/dom/bindings/codegen/Configuration.py15
-rw-r--r--components/script/dom/bindings/error.rs12
-rw-r--r--components/script/dom/bindings/global.rs30
-rw-r--r--components/script/dom/bindings/js.rs25
-rw-r--r--components/script/dom/bindings/mozmap.rs1
-rw-r--r--components/script/dom/bindings/refcounted.rs94
-rw-r--r--components/script/dom/bindings/trace.rs5
-rw-r--r--components/script/dom/document.rs13
-rw-r--r--components/script/dom/extendableevent.rs2
-rw-r--r--components/script/dom/headers.rs22
-rw-r--r--components/script/dom/htmllinkelement.rs4
-rw-r--r--components/script/dom/htmlscriptelement.rs106
-rw-r--r--components/script/dom/htmlselectelement.rs47
-rw-r--r--components/script/dom/mod.rs2
-rw-r--r--components/script/dom/promise.rs288
-rw-r--r--components/script/dom/promisenativehandler.rs49
-rw-r--r--components/script/dom/request.rs20
-rw-r--r--components/script/dom/testbinding.rs106
-rw-r--r--components/script/dom/webglrenderingcontext.rs51
-rw-r--r--components/script/dom/webidls/HTMLScriptElement.webidl2
-rw-r--r--components/script/dom/webidls/HTMLSelectElement.webidl4
-rw-r--r--components/script/dom/webidls/Headers.webidl3
-rw-r--r--components/script/dom/webidls/Promise.webidl16
-rw-r--r--components/script/dom/webidls/PromiseNativeHandler.webidl13
-rw-r--r--components/script/dom/webidls/TestBinding.webidl15
-rw-r--r--components/script/dom/webidls/WebGLRenderingContext.webidl2
-rw-r--r--components/script/dom/workerglobalscope.rs39
-rw-r--r--components/script/dom/xmlhttprequest.rs75
30 files changed, 1026 insertions, 145 deletions
diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf
index 44d835589c4..434450d2bcc 100644
--- a/components/script/dom/bindings/codegen/Bindings.conf
+++ b/components/script/dom/bindings/codegen/Bindings.conf
@@ -14,6 +14,10 @@
DOMInterfaces = {
+'Promise': {
+ 'spiderMonkeyInterface': True,
+},
+
'Range': {
'weakReferenceable': True,
},
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
index ca7901a56e3..cc6e1660fba 100644
--- a/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -658,7 +658,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
# failureCode will prevent pending exceptions from being set in cases when
# they really should be!
if exceptionCode is None:
- exceptionCode = "return false;"
+ exceptionCode = "return false;\n"
if failureCode is None:
failOrPropagate = "throw_type_error(cx, &error);\n%s" % exceptionCode
@@ -797,25 +797,69 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
descriptorType = descriptor.argumentType
templateBody = ""
- if descriptor.interface.isConsequential():
- raise TypeError("Consequential interface %s being used as an "
- "argument" % descriptor.interface.identifier.name)
-
- if failureCode is None:
- substitutions = {
- "sourceDescription": sourceDescription,
- "interface": descriptor.interface.identifier.name,
- "exceptionCode": exceptionCode,
- }
- unwrapFailureCode = string.Template(
- 'throw_type_error(cx, "${sourceDescription} does not '
- 'implement interface ${interface}.");\n'
- '${exceptionCode}').substitute(substitutions)
+ isPromise = descriptor.interface.identifier.name == "Promise"
+ if isPromise:
+ # Per spec, what we're supposed to do is take the original
+ # Promise.resolve and call it with the original Promise as this
+ # value to make a Promise out of whatever value we actually have
+ # here. The question is which global we should use. There are
+ # a couple cases to consider:
+ #
+ # 1) Normal call to API with a Promise argument. This is a case the
+ # spec covers, and we should be using the current Realm's
+ # Promise. That means the current compartment.
+ # 2) Promise return value from a callback or callback interface.
+ # This is in theory a case the spec covers but in practice it
+ # really doesn't define behavior here because it doesn't define
+ # what Realm we're in after the callback returns, which is when
+ # the argument conversion happens. We will use the current
+ # compartment, which is the compartment of the callable (which
+ # may itself be a cross-compartment wrapper itself), which makes
+ # as much sense as anything else. In practice, such an API would
+ # once again be providing a Promise to signal completion of an
+ # operation, which would then not be exposed to anyone other than
+ # our own implementation code.
+ templateBody = fill(
+ """
+ { // Scope for our JSAutoCompartment.
+
+ rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx));
+ let promiseGlobal = global_root_from_object_maybe_wrapped(globalObj.handle().get());
+
+ rooted!(in(cx) let mut valueToResolve = $${val}.get());
+ if !JS_WrapValue(cx, valueToResolve.handle_mut()) {
+ $*{exceptionCode}
+ }
+ match Promise::Resolve(promiseGlobal.r(), cx, valueToResolve.handle()) {
+ Ok(value) => value,
+ Err(error) => {
+ throw_dom_exception(cx, promiseGlobal.r(), error);
+ $*{exceptionCode}
+ }
+ }
+ }
+ """,
+ exceptionCode=exceptionCode)
else:
- unwrapFailureCode = failureCode
+ if descriptor.interface.isConsequential():
+ raise TypeError("Consequential interface %s being used as an "
+ "argument" % descriptor.interface.identifier.name)
+
+ if failureCode is None:
+ substitutions = {
+ "sourceDescription": sourceDescription,
+ "interface": descriptor.interface.identifier.name,
+ "exceptionCode": exceptionCode,
+ }
+ unwrapFailureCode = string.Template(
+ 'throw_type_error(cx, "${sourceDescription} does not '
+ 'implement interface ${interface}.");\n'
+ '${exceptionCode}').substitute(substitutions)
+ else:
+ unwrapFailureCode = failureCode
- templateBody = unwrapCastableObject(
- descriptor, "${val}", unwrapFailureCode, conversionFunction)
+ templateBody = unwrapCastableObject(
+ descriptor, "${val}", unwrapFailureCode, conversionFunction)
declType = CGGeneric(descriptorType)
if type.nullable():
@@ -4842,13 +4886,12 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
" let this = UnwrapProxy(proxy);\n" +
" let this = &*this;\n" +
CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
- " return true;\n" +
+ " return (*opresult).succeed();\n" +
"}\n")
elif self.descriptor.operations['IndexedGetter']:
set += ("if get_array_index_from_id(cx, id).is_some() {\n" +
- " return false;\n" +
- " //return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
- "}\n") % self.descriptor.name
+ " return (*opresult).failNoIndexedSetter();\n" +
+ "}\n")
namedSetter = self.descriptor.operations['NamedSetter']
if namedSetter:
@@ -4864,15 +4907,10 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
else:
set += ("if RUST_JSID_IS_STRING(id) {\n" +
CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
- " if (found) {\n"
- # TODO(Issue 5876)
- " //return js::IsInNonStrictPropertySet(cx)\n" +
- " // ? opresult.succeed()\n" +
- " // : ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, \"${name}\");\n" +
- " return (*opresult).succeed();\n" +
- " }\n" +
- " return (*opresult).succeed();\n"
- "}\n") % (self.descriptor.name, self.descriptor.name)
+ " if result.is_some() {\n"
+ " return (*opresult).failNoNamedSetter();\n"
+ " }\n"
+ "}\n")
set += "return proxyhandler::define_property(%s);" % ", ".join(a.name for a in self.args)
return set
@@ -5378,6 +5416,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::jsapi::AutoIdVector',
'js::jsapi::Call',
'js::jsapi::CallArgs',
+ 'js::jsapi::CurrentGlobalOrNull',
'js::jsapi::FreeOp',
'js::jsapi::GetPropertyKeys',
'js::jsapi::GetWellKnownSymbol',
@@ -5443,12 +5482,15 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::jsapi::JS_SetProperty',
'js::jsapi::JS_SetReservedSlot',
'js::jsapi::JS_SplicePrototype',
+ 'js::jsapi::JS_WrapValue',
'js::jsapi::MutableHandle',
'js::jsapi::MutableHandleObject',
'js::jsapi::MutableHandleValue',
'js::jsapi::ObjectOpResult',
'js::jsapi::PropertyDescriptor',
+ 'js::jsapi::RootedId',
'js::jsapi::RootedObject',
+ 'js::jsapi::RootedString',
'js::jsapi::SymbolCode',
'js::jsapi::jsid',
'js::jsval::JSVal',
@@ -5478,6 +5520,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'dom::bindings::constant::ConstantVal',
'dom::bindings::global::GlobalRef',
'dom::bindings::global::global_root_from_object',
+ 'dom::bindings::global::global_root_from_object_maybe_wrapped',
'dom::bindings::global::global_root_from_reflector',
'dom::bindings::interface::ConstructorClassHook',
'dom::bindings::interface::InterfaceConstructorBehavior',
@@ -5496,6 +5539,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'dom::bindings::js::JS',
'dom::bindings::js::OptionalRootedReference',
'dom::bindings::js::Root',
+ 'dom::bindings::js::RootedRcReference',
'dom::bindings::js::RootedReference',
'dom::bindings::namespace::NamespaceObjectClass',
'dom::bindings::namespace::create_namespace_object',
diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py
index 6e71bd4bd00..9a1120769c8 100644
--- a/components/script/dom/bindings/codegen/Configuration.py
+++ b/components/script/dom/bindings/codegen/Configuration.py
@@ -194,9 +194,17 @@ class Descriptor(DescriptorProvider):
typeName = desc.get('nativeType', nativeTypeDefault)
- # Callback types do not use JS smart pointers, so we should not use the
+ spiderMonkeyInterface = desc.get('spiderMonkeyInterface', False)
+
+ # Callback and SpiderMonkey types do not use JS smart pointers, so we should not use the
# built-in rooting mechanisms for them.
- if self.interface.isCallback():
+ if spiderMonkeyInterface:
+ self.needsRooting = False
+ self.returnType = 'Rc<%s>' % typeName
+ self.argumentType = '&%s' % typeName
+ self.nativeType = typeName
+ pathDefault = 'dom::types::%s' % typeName
+ elif self.interface.isCallback():
self.needsRooting = False
ty = 'dom::bindings::codegen::Bindings::%sBinding::%s' % (ifaceName, ifaceName)
pathDefault = ty
@@ -225,7 +233,8 @@ class Descriptor(DescriptorProvider):
# them as having a concrete descendant.
self.concrete = (not self.interface.isCallback() and
not self.interface.isNamespace() and
- not self.interface.getExtendedAttribute("Abstract"))
+ not self.interface.getExtendedAttribute("Abstract") and
+ not spiderMonkeyInterface)
self.hasUnforgeableMembers = (self.concrete and
any(MemberIsUnforgeable(m, self) for m in
self.interface.members))
diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs
index c9cbeb09f21..1c0d49c4976 100644
--- a/components/script/dom/bindings/error.rs
+++ b/components/script/dom/bindings/error.rs
@@ -19,6 +19,7 @@ use js::jsapi::JS_ErrorFromException;
use js::jsapi::JS_GetPendingException;
use js::jsapi::JS_IsExceptionPending;
use js::jsapi::JS_SetPendingException;
+use js::jsapi::MutableHandleValue;
use js::jsval::UndefinedValue;
use libc::c_uint;
use std::slice::from_raw_parts;
@@ -266,3 +267,14 @@ pub unsafe fn throw_invalid_this(cx: *mut JSContext, proto_id: u16) {
proto_id_to_name(proto_id));
throw_type_error(cx, &error);
}
+
+impl Error {
+ /// Convert this error value to a JS value, consuming it in the process.
+ pub unsafe fn to_jsval(self, cx: *mut JSContext, global: GlobalRef, rval: MutableHandleValue) {
+ assert!(!JS_IsExceptionPending(cx));
+ throw_dom_exception(cx, global, self);
+ assert!(JS_IsExceptionPending(cx));
+ assert!(JS_GetPendingException(cx, rval));
+ JS_ClearPendingException(cx);
+ }
+}
diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs
index 9452972f774..9b4125bdc6a 100644
--- a/components/script/dom/bindings/global.rs
+++ b/components/script/dom/bindings/global.rs
@@ -18,13 +18,14 @@ use dom::window::{self, ScriptHelpers};
use dom::workerglobalscope::WorkerGlobalScope;
use ipc_channel::ipc::IpcSender;
use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
+use js::glue::{IsWrapper, UnwrapObject};
use js::jsapi::{CurrentGlobalOrNull, GetGlobalForObjectCrossCompartment};
use js::jsapi::{JSContext, JSObject, JS_GetClass, MutableHandleValue};
use js::jsapi::HandleValue;
use msg::constellation_msg::PipelineId;
use net_traits::{CoreResourceThread, IpcSend, ResourceThreads};
use profile_traits::{mem, time};
-use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort};
+use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, EnqueuedPromiseCallback};
use script_thread::{MainThreadScriptChan, RunnableWrapper, ScriptThread};
use script_traits::{MsDuration, ScriptMsg as ConstellationMsg, TimerEventRequest};
use task_source::dom_manipulation::DOMManipulationTaskSource;
@@ -289,6 +290,23 @@ impl<'a> GlobalRef<'a> {
}
}
+ /// Enqueue a promise callback for subsequent execution.
+ pub fn enqueue_promise_job(&self, job: EnqueuedPromiseCallback) {
+ match *self {
+ GlobalRef::Window(_) => ScriptThread::enqueue_promise_job(job, *self),
+ GlobalRef::Worker(ref worker) => worker.enqueue_promise_job(job),
+ }
+ }
+
+ /// Start the process of executing the pending promise callbacks. They will be invoked
+ /// in FIFO order, synchronously, at some point in the future.
+ pub fn flush_promise_jobs(&self) {
+ match *self {
+ GlobalRef::Window(_) => ScriptThread::flush_promise_jobs(*self),
+ GlobalRef::Worker(ref worker) => worker.flush_promise_jobs(),
+ }
+ }
+
/// https://html.spec.whatwg.org/multipage/#report-the-error
pub fn report_an_error(&self, error_info: ErrorInfo, value: HandleValue) {
match *self {
@@ -356,3 +374,13 @@ pub unsafe fn global_root_from_context(cx: *mut JSContext) -> GlobalRoot {
let global = CurrentGlobalOrNull(cx);
global_root_from_global(global)
}
+
+/// Returns the global object of the realm that the given JS object was created in,
+/// after unwrapping any wrappers.
+pub unsafe fn global_root_from_object_maybe_wrapped(mut obj: *mut JSObject) -> GlobalRoot {
+ if IsWrapper(obj) {
+ obj = UnwrapObject(obj, /* stopAtWindowProxy = */ 0);
+ assert!(!obj.is_null());
+ }
+ global_root_from_object(obj)
+}
diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs
index a7ecc975129..1fd127b3b28 100644
--- a/components/script/dom/bindings/js.rs
+++ b/components/script/dom/bindings/js.rs
@@ -43,6 +43,7 @@ use std::intrinsics::type_name;
use std::mem;
use std::ops::Deref;
use std::ptr;
+use std::rc::Rc;
use style::thread_state;
/// A traced reference to a DOM object
@@ -269,6 +270,12 @@ impl MutHeapJSVal {
debug_assert!(thread_state::get().is_script());
unsafe { (*self.val.get()).get() }
}
+
+ /// Get the underlying unsafe pointer to the contained value.
+ pub unsafe fn get_unsafe(&self) -> *mut JSVal {
+ debug_assert!(thread_state::get().is_script());
+ (*self.val.get()).get_unsafe()
+ }
}
@@ -439,6 +446,18 @@ impl<T: Reflectable> LayoutJS<T> {
}
}
+/// Get an `&T` out of a `Rc<T>`
+pub trait RootedRcReference<T> {
+ /// Obtain a safe reference to the wrapped non-JS owned value.
+ fn r(&self) -> &T;
+}
+
+impl<T: Reflectable> RootedRcReference<T> for Rc<T> {
+ fn r(&self) -> &T {
+ &*self
+ }
+}
+
/// Get an `Option<&T>` out of an `Option<Root<T>>`
pub trait RootedReference<T> {
/// Obtain a safe optional reference to the wrapped JS owned-value that
@@ -446,6 +465,12 @@ pub trait RootedReference<T> {
fn r(&self) -> Option<&T>;
}
+impl<T: Reflectable> RootedReference<T> for Option<Rc<T>> {
+ fn r(&self) -> Option<&T> {
+ self.as_ref().map(|root| &**root)
+ }
+}
+
impl<T: Reflectable> RootedReference<T> for Option<Root<T>> {
fn r(&self) -> Option<&T> {
self.as_ref().map(|root| root.r())
diff --git a/components/script/dom/bindings/mozmap.rs b/components/script/dom/bindings/mozmap.rs
index 557ba0662a0..d44114cd057 100644
--- a/components/script/dom/bindings/mozmap.rs
+++ b/components/script/dom/bindings/mozmap.rs
@@ -23,6 +23,7 @@ use std::collections::HashMap;
use std::ops::Deref;
/// The `MozMap` (open-ended dictionary) type.
+#[derive(Clone)]
pub struct MozMap<T> {
map: HashMap<DOMString, T>,
}
diff --git a/components/script/dom/bindings/refcounted.rs b/components/script/dom/bindings/refcounted.rs
index ea4c47b1148..41cc60b0210 100644
--- a/components/script/dom/bindings/refcounted.rs
+++ b/components/script/dom/bindings/refcounted.rs
@@ -26,6 +26,7 @@ use core::nonzero::NonZero;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::bindings::trace::trace_reflector;
+use dom::promise::Promise;
use js::jsapi::JSTracer;
use libc;
use std::cell::RefCell;
@@ -34,6 +35,7 @@ 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};
@@ -58,6 +60,63 @@ impl TrustedReference {
}
}
+/// A safe wrapper around a DOM Promise object that can be shared among threads for use
+/// in asynchronous operations. The underlying DOM object is guaranteed to live at least
+/// as long as the last outstanding `TrustedPromise` instance. These values cannot be cloned,
+/// only created from existing Rc<Promise> values.
+pub struct TrustedPromise {
+ dom_object: *const Promise,
+ owner_thread: *const libc::c_void,
+}
+
+unsafe impl Send for TrustedPromise {}
+
+impl TrustedPromise {
+ /// Create a new `TrustedPromise` instance from an existing DOM object. The object will
+ /// be prevented from being GCed for the duration of the resulting `TrustedPromise` object's
+ /// lifetime.
+ #[allow(unrooted_must_root)]
+ pub fn new(promise: Rc<Promise>) -> TrustedPromise {
+ LIVE_REFERENCES.with(|ref r| {
+ let r = r.borrow();
+ let live_references = r.as_ref().unwrap();
+ let ptr = &*promise as *const Promise;
+ live_references.addref_promise(promise);
+ TrustedPromise {
+ dom_object: ptr,
+ owner_thread: (&*live_references) as *const _ as *const libc::c_void,
+ }
+ })
+ }
+
+ /// Obtain a usable DOM Promise from a pinned `TrustedPromise` value. Fails if used on
+ /// a different thread than the original value from which this `TrustedPromise` was
+ /// obtained.
+ #[allow(unrooted_must_root)]
+ pub fn root(self) -> Rc<Promise> {
+ LIVE_REFERENCES.with(|ref r| {
+ let r = r.borrow();
+ let live_references = r.as_ref().unwrap();
+ assert!(self.owner_thread == (&*live_references) as *const _ as *const libc::c_void);
+ // Borrow-check error requires the redundant `let promise = ...; promise` here.
+ let promise = match live_references.promise_table.borrow_mut().entry(self.dom_object) {
+ Occupied(mut entry) => {
+ let promise = {
+ let promises = entry.get_mut();
+ promises.pop().expect("rooted promise list unexpectedly empty")
+ };
+ if entry.get().is_empty() {
+ entry.remove();
+ }
+ promise
+ }
+ Vacant(_) => unreachable!(),
+ };
+ promise
+ })
+ }
+}
+
/// A safe wrapper around a raw pointer to a DOM object that can be
/// shared among threads for use in asynchronous operations. The underlying
/// DOM object is guaranteed to live at least as long as the last outstanding
@@ -117,9 +176,11 @@ impl<T: Reflectable> Clone for Trusted<T> {
/// The set of live, pinned DOM objects that are currently prevented
/// from being garbage collected due to outstanding references.
+#[allow(unrooted_must_root)]
pub struct LiveDOMReferences {
// keyed on pointer to Rust DOM object
- table: RefCell<HashMap<*const libc::c_void, Weak<TrustedReference>>>,
+ reflectable_table: RefCell<HashMap<*const libc::c_void, Weak<TrustedReference>>>,
+ promise_table: RefCell<HashMap<*const Promise, Vec<Rc<Promise>>>>,
}
impl LiveDOMReferences {
@@ -127,13 +188,20 @@ impl LiveDOMReferences {
pub fn initialize() {
LIVE_REFERENCES.with(|ref r| {
*r.borrow_mut() = Some(LiveDOMReferences {
- table: RefCell::new(HashMap::new()),
+ reflectable_table: RefCell::new(HashMap::new()),
+ promise_table: RefCell::new(HashMap::new()),
})
});
}
+ #[allow(unrooted_must_root)]
+ fn addref_promise(&self, promise: Rc<Promise>) {
+ let mut table = self.promise_table.borrow_mut();
+ table.entry(&*promise).or_insert(vec![]).push(promise)
+ }
+
fn addref<T: Reflectable>(&self, ptr: *const T) -> Arc<TrustedReference> {
- let mut table = self.table.borrow_mut();
+ let mut table = self.reflectable_table.borrow_mut();
let capacity = table.capacity();
let len = table.len();
if (0 < capacity) && (capacity <= len) {
@@ -173,17 +241,27 @@ fn remove_nulls<K: Eq + Hash + Clone, V> (table: &mut HashMap<K, Weak<V>>) {
}
/// A JSTraceDataOp for tracing reflectors held in LIVE_REFERENCES
+#[allow(unrooted_must_root)]
pub unsafe extern "C" fn trace_refcounted_objects(tracer: *mut JSTracer,
_data: *mut os::raw::c_void) {
info!("tracing live refcounted references");
LIVE_REFERENCES.with(|ref r| {
let r = r.borrow();
let live_references = r.as_ref().unwrap();
- let mut table = live_references.table.borrow_mut();
- remove_nulls(&mut table);
- for obj in table.keys() {
- let reflectable = &*(*obj as *const Reflector);
- trace_reflector(tracer, "refcounted", reflectable);
+ {
+ let mut table = live_references.reflectable_table.borrow_mut();
+ remove_nulls(&mut table);
+ for obj in table.keys() {
+ let reflectable = &*(*obj as *const Reflector);
+ trace_reflector(tracer, "refcounted", reflectable);
+ }
+ }
+
+ {
+ let table = live_references.promise_table.borrow_mut();
+ for promise in table.keys() {
+ trace_reflector(tracer, "refcounted", (**promise).reflector());
+ }
}
});
}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 67b8832efed..53d141d114c 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -36,7 +36,7 @@ use devtools_traits::CSSError;
use devtools_traits::WorkerId;
use dom::abstractworker::SharedRt;
use dom::bindings::js::{JS, Root};
-use dom::bindings::refcounted::Trusted;
+use dom::bindings::refcounted::{Trusted, TrustedPromise};
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::bindings::str::{DOMString, USVString};
use dom::bindings::utils::WindowProxyHandler;
@@ -156,7 +156,7 @@ impl<T: JSTraceable> JSTraceable for Rc<T> {
}
}
-impl<T: JSTraceable> JSTraceable for Box<T> {
+impl<T: JSTraceable + ?Sized> JSTraceable for Box<T> {
fn trace(&self, trc: *mut JSTracer) {
(**self).trace(trc)
}
@@ -303,6 +303,7 @@ no_jsmanaged_fields!(Metadata);
no_jsmanaged_fields!(NetworkError);
no_jsmanaged_fields!(Atom, Namespace, QualName);
no_jsmanaged_fields!(Trusted<T: Reflectable>);
+no_jsmanaged_fields!(TrustedPromise);
no_jsmanaged_fields!(PropertyDeclarationBlock);
no_jsmanaged_fields!(HashSet<T>);
// These three are interdependent, if you plan to put jsmanaged data
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 15cb50aa466..2cca84a625a 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -95,9 +95,10 @@ use js::jsapi::JS_GetRuntime;
use msg::constellation_msg::{ALT, CONTROL, SHIFT, SUPER};
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
-use net_traits::{AsyncResponseTarget, IpcSend, PendingAsyncLoad};
+use net_traits::{AsyncResponseTarget, FetchResponseMsg, IpcSend, PendingAsyncLoad};
use net_traits::CookieSource::NonHTTP;
use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl};
+use net_traits::request::RequestInit;
use net_traits::response::HttpsState;
use num_traits::ToPrimitive;
use origin::Origin;
@@ -1433,6 +1434,14 @@ impl Document {
loader.load_async(load, listener, self, referrer_policy);
}
+ pub fn fetch_async(&self, load: LoadType,
+ request: RequestInit,
+ fetch_target: IpcSender<FetchResponseMsg>,
+ referrer_policy: Option<ReferrerPolicy>) {
+ let mut loader = self.loader.borrow_mut();
+ loader.fetch_async(load, request, fetch_target, self, referrer_policy);
+ }
+
pub fn finish_load(&self, load: LoadType) {
debug!("Document got finish_load: {:?}", load);
// The parser might need the loader, so restrict the lifetime of the borrow.
@@ -1520,7 +1529,7 @@ impl Document {
}
/// https://html.spec.whatwg.org/multipage/#the-end step 5 and the latter parts of
- /// https://html.spec.whatwg.org/multipage/#prepare-a-script 15.d and 15.e.
+ /// https://html.spec.whatwg.org/multipage/#prepare-a-script 20.d and 20.e.
pub fn process_asap_scripts(&self) {
// Execute the first in-order asap-executed script if it's ready, repeat as required.
// Re-borrowing the list for each step because it can also be borrowed under execute.
diff --git a/components/script/dom/extendableevent.rs b/components/script/dom/extendableevent.rs
index 76756b40c71..ffb6c3baad2 100644
--- a/components/script/dom/extendableevent.rs
+++ b/components/script/dom/extendableevent.rs
@@ -51,7 +51,7 @@ impl ExtendableEvent {
}
// https://w3c.github.io/ServiceWorker/#wait-until-method
- pub fn WaitUntil(&self, _cx: *mut JSContext, val: HandleValue) -> ErrorResult {
+ pub fn WaitUntil(&self, _cx: *mut JSContext, _val: HandleValue) -> ErrorResult {
// Step 1
if !self.extensions_allowed {
return Err(Error::InvalidState);
diff --git a/components/script/dom/headers.rs b/components/script/dom/headers.rs
index b977110955c..4b832121654 100644
--- a/components/script/dom/headers.rs
+++ b/components/script/dom/headers.rs
@@ -3,10 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::cell::DOMRefCell;
-use dom::bindings::codegen::Bindings::HeadersBinding;
-use dom::bindings::codegen::Bindings::HeadersBinding::HeadersMethods;
-use dom::bindings::codegen::Bindings::HeadersBinding::HeadersWrap;
-use dom::bindings::codegen::UnionTypes::HeadersOrByteStringSequenceSequence;
+use dom::bindings::codegen::Bindings::HeadersBinding::{HeadersInit, HeadersMethods, HeadersWrap};
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::iterable::Iterable;
@@ -51,7 +48,7 @@ impl Headers {
}
// https://fetch.spec.whatwg.org/#dom-headers
- pub fn Constructor(global: GlobalRef, init: Option<HeadersBinding::HeadersInit>)
+ pub fn Constructor(global: GlobalRef, init: Option<HeadersInit>)
-> Fallible<Root<Headers>> {
let dom_headers_new = Headers::new(global);
try!(dom_headers_new.fill(init));
@@ -169,10 +166,10 @@ impl HeadersMethods for Headers {
impl Headers {
// https://fetch.spec.whatwg.org/#concept-headers-fill
- pub fn fill(&self, filler: Option<HeadersBinding::HeadersInit>) -> ErrorResult {
+ pub fn fill(&self, filler: Option<HeadersInit>) -> ErrorResult {
match filler {
// Step 1
- Some(HeadersOrByteStringSequenceSequence::Headers(h)) => {
+ Some(HeadersInit::Headers(h)) => {
for header in h.header_list.borrow().iter() {
try!(self.Append(
ByteString::new(Vec::from(header.name())),
@@ -182,7 +179,7 @@ impl Headers {
Ok(())
},
// Step 2
- Some(HeadersOrByteStringSequenceSequence::ByteStringSequenceSequence(v)) => {
+ Some(HeadersInit::ByteStringSequenceSequence(v)) => {
for mut seq in v {
if seq.len() == 2 {
let val = seq.pop().unwrap();
@@ -196,7 +193,14 @@ impl Headers {
}
Ok(())
},
- // Step 3 TODO constructor for when init is an open-ended dictionary
+ Some(HeadersInit::ByteStringMozMap(m)) => {
+ for (key, value) in m.iter() {
+ let key_vec = key.as_ref().to_string().into();
+ let headers_key = ByteString::new(key_vec);
+ try!(self.Append(headers_key, value.clone()));
+ }
+ Ok(())
+ },
None => Ok(()),
}
}
diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs
index bbffecaaf6a..1c84fbba784 100644
--- a/components/script/dom/htmllinkelement.rs
+++ b/components/script/dom/htmllinkelement.rs
@@ -368,7 +368,9 @@ impl HTMLLinkElementMethods for HTMLLinkElement {
make_getter!(Rel, "rel");
// https://html.spec.whatwg.org/multipage/#dom-link-rel
- make_setter!(SetRel, "rel");
+ fn SetRel(&self, rel: DOMString) {
+ self.upcast::<Element>().set_tokenlist_attribute(&atom!("rel"), rel);
+ }
// https://html.spec.whatwg.org/multipage/#dom-link-media
make_getter!(Media, "media");
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index 11f4451ad50..566b69967d4 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -5,6 +5,7 @@
use document_loader::LoadType;
use dom::attr::Attr;
use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding;
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
@@ -14,6 +15,7 @@ use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, Root};
use dom::bindings::js::RootedReference;
use dom::bindings::refcounted::Trusted;
+use dom::bindings::reflector::Reflectable;
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::element::{AttributeMutation, Element, ElementCreator};
@@ -29,7 +31,8 @@ use html5ever::tree_builder::NextParserState;
use ipc_channel::ipc;
use ipc_channel::router::ROUTER;
use js::jsval::UndefinedValue;
-use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError};
+use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError};
+use net_traits::request::{CORSSettings, CredentialsMode, Destination, RequestInit, RequestMode, Type as RequestType};
use network_listener::{NetworkListener, PreInvoke};
use std::ascii::AsciiExt;
use std::cell::Cell;
@@ -151,9 +154,17 @@ struct ScriptContext {
status: Result<(), NetworkError>
}
-impl AsyncResponseListener for ScriptContext {
- fn headers_available(&mut self, metadata: Result<Metadata, NetworkError>) {
- self.metadata = metadata.ok();
+impl FetchResponseListener for ScriptContext {
+ fn process_request_body(&mut self) {} // TODO(KiChjang): Perhaps add custom steps to perform fetch here?
+
+ fn process_request_eof(&mut self) {} // TODO(KiChjang): Perhaps add custom steps to perform fetch here?
+
+ fn process_response(&mut self,
+ metadata: Result<FetchMetadata, NetworkError>) {
+ self.metadata = metadata.ok().map(|meta| match meta {
+ FetchMetadata::Unfiltered(m) => m,
+ FetchMetadata::Filtered { unsafe_, .. } => unsafe_
+ });
let status_code = self.metadata.as_ref().and_then(|m| {
match m.status {
@@ -169,18 +180,17 @@ impl AsyncResponseListener for ScriptContext {
};
}
- fn data_available(&mut self, payload: Vec<u8>) {
+ fn process_response_chunk(&mut self, mut chunk: Vec<u8>) {
if self.status.is_ok() {
- let mut payload = payload;
- self.data.append(&mut payload);
+ self.data.append(&mut chunk);
}
}
/// https://html.spec.whatwg.org/multipage/#fetch-a-classic-script
/// step 4-9
- fn response_complete(&mut self, status: Result<(), NetworkError>) {
+ fn process_response_eof(&mut self, response: Result<(), NetworkError>) {
// Step 5.
- let load = status.and(self.status.clone()).map(|_| {
+ let load = response.and(self.status.clone()).map(|_| {
let metadata = self.metadata.take().unwrap();
// Step 6.
@@ -210,8 +220,38 @@ impl PreInvoke for ScriptContext {}
/// https://html.spec.whatwg.org/multipage/#fetch-a-classic-script
fn fetch_a_classic_script(script: &HTMLScriptElement,
url: Url,
+ cors_setting: Option<CORSSettings>,
character_encoding: EncodingRef) {
- // TODO(#9186): use the fetch infrastructure.
+ let doc = document_from_node(script);
+
+ // Step 1, 2.
+ let request = RequestInit {
+ url: url.clone(),
+ type_: RequestType::Script,
+ destination: Destination::Script,
+ // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
+ // Step 1
+ mode: match cors_setting {
+ Some(_) => RequestMode::CORSMode,
+ None => RequestMode::NoCORS,
+ },
+ // https://html.spec.whatwg.org/multipage/#create-a-potential-cors-request
+ // Step 3-4
+ credentials_mode: match cors_setting {
+ Some(CORSSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
+ _ => CredentialsMode::Include,
+ },
+ origin: doc.url().clone(),
+ pipeline_id: Some(script.global().r().pipeline_id()),
+ // FIXME: Set to true for now, discussion in https://github.com/whatwg/fetch/issues/381
+ same_origin_data: true,
+ referrer_url: Some(doc.url().clone()),
+ referrer_policy: doc.get_referrer_policy(),
+ .. RequestInit::default()
+ };
+
+ // TODO: Step 3, Add custom steps to perform fetch
+
let context = Arc::new(Mutex::new(ScriptContext {
elem: Trusted::new(script),
character_encoding: character_encoding,
@@ -229,14 +269,11 @@ fn fetch_a_classic_script(script: &HTMLScriptElement,
script_chan: doc.window().networking_task_source(),
wrapper: Some(doc.window().get_runnable_wrapper()),
};
- let response_target = AsyncResponseTarget {
- sender: action_sender,
- };
+
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
- listener.notify_action(message.to().unwrap());
+ listener.notify_fetch(message.to().unwrap());
});
-
- doc.load_async(LoadType::Script(url), response_target, None);
+ doc.fetch_async(LoadType::Script(url), request, action_sender, None);
}
impl HTMLScriptElement {
@@ -322,7 +359,13 @@ impl HTMLScriptElement {
.and_then(|charset| encoding_from_whatwg_label(&charset.value()))
.unwrap_or_else(|| doc.encoding());
- // TODO: Step 14: CORS.
+ // Step 14.
+ let cors_setting = match self.GetCrossOrigin() {
+ Some(ref s) if *s == "anonymous" => Some(CORSSettings::Anonymous),
+ Some(ref s) if *s == "use-credentials" => Some(CORSSettings::UseCredentials),
+ None => None,
+ _ => unreachable!()
+ };
// TODO: Step 15: Nonce.
@@ -354,10 +397,11 @@ impl HTMLScriptElement {
};
// Step 18.6.
- fetch_a_classic_script(self, url, encoding);
+ fetch_a_classic_script(self, url, cors_setting, encoding);
true
},
+ // TODO: Step 19.
None => false,
};
@@ -652,6 +696,32 @@ impl HTMLScriptElementMethods for HTMLScriptElement {
// https://html.spec.whatwg.org/multipage/#dom-script-htmlfor
make_setter!(SetHtmlFor, "for");
+ // https://html.spec.whatwg.org/multipage/#dom-script-crossorigin
+ fn GetCrossOrigin(&self) -> Option<DOMString> {
+ let element = self.upcast::<Element>();
+ let attr = element.get_attribute(&ns!(), &atom!("crossorigin"));
+
+ if let Some(mut val) = attr.map(|v| v.Value()) {
+ val.make_ascii_lowercase();
+ if val == "anonymous" || val == "use-credentials" {
+ return Some(val);
+ }
+ return Some(DOMString::from("anonymous"));
+ }
+ None
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-script-crossorigin
+ fn SetCrossOrigin(&self, value: Option<DOMString>) {
+ let element = self.upcast::<Element>();
+ match value {
+ Some(val) => element.set_string_attribute(&atom!("crossorigin"), val),
+ None => {
+ element.remove_attribute(&ns!(), &atom!("crossorigin"));
+ }
+ }
+ }
+
// https://html.spec.whatwg.org/multipage/#dom-script-text
fn Text(&self) -> DOMString {
Node::collect_text_contents(self.upcast::<Node>().children())
diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs
index 222dff3e52c..6cf63c7e630 100644
--- a/components/script/dom/htmlselectelement.rs
+++ b/components/script/dom/htmlselectelement.rs
@@ -6,6 +6,7 @@ use dom::attr::Attr;
use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
use dom::bindings::codegen::Bindings::HTMLSelectElementBinding;
use dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods;
+use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::UnionTypes::HTMLElementOrLong;
use dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement;
use dom::bindings::inheritance::Castable;
@@ -17,7 +18,7 @@ use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormDatumValue, FormControl, FormDatum, HTMLFormElement};
use dom::htmloptionelement::HTMLOptionElement;
-use dom::node::{Node, UnbindContext, window_from_node};
+use dom::node::{document_from_node, Node, UnbindContext, window_from_node};
use dom::nodelist::NodeList;
use dom::validation::Validatable;
use dom::validitystate::ValidityState;
@@ -183,6 +184,50 @@ impl HTMLSelectElementMethods for HTMLSelectElement {
fn Labels(&self) -> Root<NodeList> {
self.upcast::<HTMLElement>().labels()
}
+
+ // https://html.spec.whatwg.org/multipage/#dom-select-length
+ fn SetLength(&self, value: u32) {
+ let length = self.Length();
+ let node = self.upcast::<Node>();
+ if value < length { // truncate the number of option elements
+ let mut iter = node.rev_children().take((length - value) as usize);
+ while let Some(child) = iter.next() {
+ if let Err(e) = node.RemoveChild(&child) {
+ warn!("Error removing child of HTMLSelectElement: {:?}", e);
+ }
+ }
+ } else if value > length { // add new blank option elements
+ let document = document_from_node(self);
+ for _ in 0..(value - length) {
+ let element = HTMLOptionElement::new(atom!("option"), None, &document.upcast());
+ if let Err(e) = node.AppendChild(element.upcast()) {
+ warn!("error appending child of HTMLSelectElement: {:?}", e);
+ }
+ }
+ }
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-select-length
+ fn Length(&self) -> u32 {
+ self.upcast::<Node>()
+ .traverse_preorder()
+ .filter_map(Root::downcast::<HTMLOptionElement>)
+ .count() as u32
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-select-item
+ fn Item(&self, index: u32) -> Option<Root<Element>> {
+ self.upcast::<Node>()
+ .traverse_preorder()
+ .filter_map(Root::downcast::<HTMLOptionElement>)
+ .nth(index as usize)
+ .map(|item| Root::from_ref(item.upcast()))
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-select-item
+ fn IndexedGetter(&self, index: u32) -> Option<Root<Element>> {
+ self.Item(index)
+ }
}
impl VirtualMethods for HTMLSelectElement {
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index 5f6b4fffbdc..24951c0c075 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -373,6 +373,8 @@ pub mod pluginarray;
pub mod popstateevent;
pub mod processinginstruction;
pub mod progressevent;
+pub mod promise;
+pub mod promisenativehandler;
pub mod radionodelist;
pub mod range;
pub mod request;
diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs
new file mode 100644
index 00000000000..5fd1ef6f85e
--- /dev/null
+++ b/components/script/dom/promise.rs
@@ -0,0 +1,288 @@
+/* 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/. */
+
+//! Native representation of JS Promise values.
+//!
+//! This implementation differs from the traditional Rust DOM object, because the reflector
+//! is provided by SpiderMonkey and has no knowledge of an associated native representation
+//! (ie. dom::Promise). This means that native instances use native reference counting (Rc)
+//! to ensure that no memory is leaked, which means that there can be multiple instances of
+//! native Promise values that refer to the same JS value yet are distinct native objects
+//! (ie. address equality for the native objects is meaningless).
+
+use dom::bindings::callback::CallbackContainer;
+use dom::bindings::codegen::Bindings::PromiseBinding::AnyCallback;
+use dom::bindings::conversions::root_from_object;
+use dom::bindings::error::{Error, Fallible};
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::MutHeapJSVal;
+use dom::bindings::reflector::{Reflectable, MutReflectable, Reflector};
+use dom::promisenativehandler::PromiseNativeHandler;
+use js::conversions::ToJSValConvertible;
+use js::jsapi::{CallOriginalPromiseResolve, CallOriginalPromiseReject, CallOriginalPromiseThen};
+use js::jsapi::{JSAutoCompartment, CallArgs, JS_GetFunctionObject, JS_NewFunction};
+use js::jsapi::{JSContext, HandleValue, HandleObject, IsPromiseObject, GetFunctionNativeReserved};
+use js::jsapi::{JS_ClearPendingException, JSObject, AddRawValueRoot, RemoveRawValueRoot};
+use js::jsapi::{MutableHandleObject, NewPromiseObject, ResolvePromise, RejectPromise};
+use js::jsapi::{SetFunctionNativeReserved, NewFunctionWithReserved, AddPromiseReactions};
+use js::jsval::{JSVal, UndefinedValue, ObjectValue, Int32Value};
+use std::ptr;
+use std::rc::Rc;
+
+#[dom_struct]
+pub struct Promise {
+ reflector: Reflector,
+ /// Since Promise values are natively reference counted without the knowledge of
+ /// the SpiderMonkey GC, an explicit root for the reflector is stored while any
+ /// native instance exists. This ensures that the reflector will never be GCed
+ /// while native code could still interact with its native representation.
+ #[ignore_heap_size_of = "SM handles JS values"]
+ permanent_js_root: MutHeapJSVal,
+}
+
+/// Private helper to enable adding new methods to Rc<Promise>.
+trait PromiseHelper {
+ #[allow(unsafe_code)]
+ unsafe fn initialize(&self, cx: *mut JSContext);
+}
+
+impl PromiseHelper for Rc<Promise> {
+ #[allow(unsafe_code)]
+ unsafe fn initialize(&self, cx: *mut JSContext) {
+ let obj = self.reflector().get_jsobject();
+ self.permanent_js_root.set(ObjectValue(&**obj));
+ assert!(AddRawValueRoot(cx,
+ self.permanent_js_root.get_unsafe(),
+ b"Promise::root\0" as *const _ as *const _));
+ }
+}
+
+impl Drop for Promise {
+ #[allow(unsafe_code)]
+ fn drop(&mut self) {
+ let cx = self.global().r().get_cx();
+ unsafe {
+ RemoveRawValueRoot(cx, self.permanent_js_root.get_unsafe());
+ }
+ }
+}
+
+impl Promise {
+ #[allow(unsafe_code)]
+ pub fn new(global: GlobalRef) -> Rc<Promise> {
+ let cx = global.get_cx();
+ rooted!(in(cx) let mut obj = ptr::null_mut());
+ unsafe {
+ Promise::create_js_promise(cx, HandleObject::null(), obj.handle_mut());
+ Promise::new_with_js_promise(obj.handle(), cx)
+ }
+ }
+
+ #[allow(unsafe_code, unrooted_must_root)]
+ pub fn duplicate(&self) -> Rc<Promise> {
+ let cx = self.global().r().get_cx();
+ unsafe {
+ Promise::new_with_js_promise(self.reflector().get_jsobject(), cx)
+ }
+ }
+
+ #[allow(unsafe_code, unrooted_must_root)]
+ unsafe fn new_with_js_promise(obj: HandleObject, cx: *mut JSContext) -> Rc<Promise> {
+ assert!(IsPromiseObject(obj));
+ let mut promise = Promise {
+ reflector: Reflector::new(),
+ permanent_js_root: MutHeapJSVal::new(),
+ };
+ promise.init_reflector(obj.get());
+ let promise = Rc::new(promise);
+ promise.initialize(cx);
+ promise
+ }
+
+ #[allow(unsafe_code)]
+ unsafe fn create_js_promise(cx: *mut JSContext, proto: HandleObject, obj: MutableHandleObject) {
+ let do_nothing_func = JS_NewFunction(cx, Some(do_nothing_promise_executor), /* nargs = */ 2,
+ /* flags = */ 0, ptr::null());
+ assert!(!do_nothing_func.is_null());
+ rooted!(in(cx) let do_nothing_obj = JS_GetFunctionObject(do_nothing_func));
+ assert!(!do_nothing_obj.is_null());
+ obj.set(NewPromiseObject(cx, do_nothing_obj.handle(), proto));
+ assert!(!obj.is_null());
+ }
+
+ #[allow(unrooted_must_root, unsafe_code)]
+ pub fn Resolve(global: GlobalRef,
+ cx: *mut JSContext,
+ value: HandleValue) -> Fallible<Rc<Promise>> {
+ let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get());
+ rooted!(in(cx) let p = unsafe { CallOriginalPromiseResolve(cx, value) });
+ assert!(!p.handle().is_null());
+ unsafe {
+ Ok(Promise::new_with_js_promise(p.handle(), cx))
+ }
+ }
+
+ #[allow(unrooted_must_root, unsafe_code)]
+ pub fn Reject(global: GlobalRef,
+ cx: *mut JSContext,
+ value: HandleValue) -> Fallible<Rc<Promise>> {
+ let _ac = JSAutoCompartment::new(cx, global.reflector().get_jsobject().get());
+ rooted!(in(cx) let p = unsafe { CallOriginalPromiseReject(cx, value) });
+ assert!(!p.handle().is_null());
+ unsafe {
+ Ok(Promise::new_with_js_promise(p.handle(), cx))
+ }
+ }
+
+ #[allow(unsafe_code)]
+ pub fn resolve_native<T>(&self, cx: *mut JSContext, val: &T) where T: ToJSValConvertible {
+ rooted!(in(cx) let mut v = UndefinedValue());
+ unsafe {
+ val.to_jsval(cx, v.handle_mut());
+ }
+ self.resolve(cx, v.handle());
+ }
+
+ #[allow(unrooted_must_root, unsafe_code)]
+ pub fn resolve(&self, cx: *mut JSContext, value: HandleValue) {
+ unsafe {
+ if !ResolvePromise(cx, self.promise_obj(), value) {
+ JS_ClearPendingException(cx);
+ }
+ }
+ }
+
+ #[allow(unsafe_code)]
+ pub fn reject_native<T>(&self, cx: *mut JSContext, val: &T) where T: ToJSValConvertible {
+ rooted!(in(cx) let mut v = UndefinedValue());
+ unsafe {
+ val.to_jsval(cx, v.handle_mut());
+ }
+ self.reject(cx, v.handle());
+ }
+
+ #[allow(unsafe_code)]
+ pub fn reject_error(&self, cx: *mut JSContext, error: Error) {
+ rooted!(in(cx) let mut v = UndefinedValue());
+ unsafe {
+ error.to_jsval(cx, self.global().r(), v.handle_mut());
+ }
+ self.reject(cx, v.handle());
+ }
+
+ #[allow(unrooted_must_root, unsafe_code)]
+ pub fn reject(&self,
+ cx: *mut JSContext,
+ value: HandleValue) {
+ unsafe {
+ if !RejectPromise(cx, self.promise_obj(), value) {
+ JS_ClearPendingException(cx);
+ }
+ }
+ }
+
+ #[allow(unrooted_must_root, unsafe_code)]
+ pub fn then(&self,
+ cx: *mut JSContext,
+ _callee: HandleObject,
+ cb_resolve: AnyCallback,
+ cb_reject: AnyCallback,
+ result: MutableHandleObject) {
+ let promise = self.promise_obj();
+ rooted!(in(cx) let resolve = cb_resolve.callback());
+ rooted!(in(cx) let reject = cb_reject.callback());
+ unsafe {
+ rooted!(in(cx) let res =
+ CallOriginalPromiseThen(cx, promise, resolve.handle(), reject.handle()));
+ result.set(*res);
+ }
+ }
+
+ #[allow(unsafe_code)]
+ fn promise_obj(&self) -> HandleObject {
+ let obj = self.reflector().get_jsobject();
+ unsafe {
+ assert!(IsPromiseObject(obj));
+ }
+ obj
+ }
+
+ #[allow(unsafe_code)]
+ pub fn append_native_handler(&self, handler: &PromiseNativeHandler) {
+ let global = self.global();
+ let cx = global.r().get_cx();
+ rooted!(in(cx) let resolve_func =
+ create_native_handler_function(cx,
+ handler.reflector().get_jsobject(),
+ NativeHandlerTask::Resolve));
+
+ rooted!(in(cx) let reject_func =
+ create_native_handler_function(cx,
+ handler.reflector().get_jsobject(),
+ NativeHandlerTask::Reject));
+
+ unsafe {
+ let ok = AddPromiseReactions(cx,
+ self.promise_obj(),
+ resolve_func.handle(),
+ reject_func.handle());
+ assert!(ok);
+ }
+ }
+}
+
+#[allow(unsafe_code)]
+unsafe extern fn do_nothing_promise_executor(_cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
+ let args = CallArgs::from_vp(vp, argc);
+ *args.rval() = UndefinedValue();
+ true
+}
+
+const SLOT_NATIVEHANDLER: usize = 0;
+const SLOT_NATIVEHANDLER_TASK: usize = 1;
+
+#[derive(PartialEq)]
+enum NativeHandlerTask {
+ Resolve = 0,
+ Reject = 1,
+}
+
+#[allow(unsafe_code)]
+unsafe extern fn native_handler_callback(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
+ let args = CallArgs::from_vp(vp, argc);
+ rooted!(in(cx) let v = *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER));
+ assert!(v.get().is_object());
+
+ let handler = root_from_object::<PromiseNativeHandler>(v.to_object())
+ .ok().expect("unexpected value for native handler in promise native handler callback");
+
+ rooted!(in(cx) let v = *GetFunctionNativeReserved(args.callee(), SLOT_NATIVEHANDLER_TASK));
+ match v.to_int32() {
+ v if v == NativeHandlerTask::Resolve as i32 => handler.resolved_callback(cx, args.get(0)),
+ v if v == NativeHandlerTask::Reject as i32 => handler.rejected_callback(cx, args.get(0)),
+ _ => panic!("unexpected native handler task value"),
+ };
+
+ true
+}
+
+#[allow(unsafe_code)]
+fn create_native_handler_function(cx: *mut JSContext,
+ holder: HandleObject,
+ task: NativeHandlerTask) -> *mut JSObject {
+ unsafe {
+ let func = NewFunctionWithReserved(cx, Some(native_handler_callback), 1, 0, ptr::null());
+ assert!(!func.is_null());
+
+ rooted!(in(cx) let obj = JS_GetFunctionObject(func));
+ assert!(!obj.is_null());
+ SetFunctionNativeReserved(obj.get(),
+ SLOT_NATIVEHANDLER,
+ &ObjectValue(&**holder));
+ SetFunctionNativeReserved(obj.get(),
+ SLOT_NATIVEHANDLER_TASK,
+ &Int32Value(task as i32));
+ obj.get()
+ }
+}
diff --git a/components/script/dom/promisenativehandler.rs b/components/script/dom/promisenativehandler.rs
new file mode 100644
index 00000000000..603122556ef
--- /dev/null
+++ b/components/script/dom/promisenativehandler.rs
@@ -0,0 +1,49 @@
+/* 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::bindings::codegen::Bindings::PromiseNativeHandlerBinding;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::Root;
+use dom::bindings::reflector::{Reflector, reflect_dom_object};
+use dom::bindings::trace::JSTraceable;
+use heapsize::HeapSizeOf;
+use js::jsapi::{JSContext, HandleValue};
+
+pub trait Callback: JSTraceable + HeapSizeOf {
+ fn callback(&self, cx: *mut JSContext, v: HandleValue);
+}
+
+#[dom_struct]
+pub struct PromiseNativeHandler {
+ reflector: Reflector,
+ resolve: Option<Box<Callback>>,
+ reject: Option<Box<Callback>>,
+}
+
+impl PromiseNativeHandler {
+ pub fn new(global: GlobalRef,
+ resolve: Option<Box<Callback>>,
+ reject: Option<Box<Callback>>)
+ -> Root<PromiseNativeHandler> {
+ reflect_dom_object(box PromiseNativeHandler {
+ reflector: Reflector::new(),
+ resolve: resolve,
+ reject: reject,
+ }, global, PromiseNativeHandlerBinding::Wrap)
+ }
+
+ fn callback(callback: &Option<Box<Callback>>, cx: *mut JSContext, v: HandleValue) {
+ if let Some(ref callback) = *callback {
+ callback.callback(cx, v)
+ }
+ }
+
+ pub fn resolved_callback(&self, cx: *mut JSContext, v: HandleValue) {
+ PromiseNativeHandler::callback(&self.resolve, cx, v)
+ }
+
+ pub fn rejected_callback(&self, cx: *mut JSContext, v: HandleValue) {
+ PromiseNativeHandler::callback(&self.reject, cx, v)
+ }
+}
diff --git a/components/script/dom/request.rs b/components/script/dom/request.rs
index e8eb62c3f05..149c61c96ee 100644
--- a/components/script/dom/request.rs
+++ b/components/script/dom/request.rs
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::cell::DOMRefCell;
+use dom::bindings::codegen::Bindings::HeadersBinding::HeadersInit;
use dom::bindings::codegen::Bindings::RequestBinding;
use dom::bindings::codegen::Bindings::RequestBinding::ReferrerPolicy;
use dom::bindings::codegen::Bindings::RequestBinding::RequestCache;
@@ -14,7 +15,6 @@ use dom::bindings::codegen::Bindings::RequestBinding::RequestMethods;
use dom::bindings::codegen::Bindings::RequestBinding::RequestMode;
use dom::bindings::codegen::Bindings::RequestBinding::RequestRedirect;
use dom::bindings::codegen::Bindings::RequestBinding::RequestType;
-use dom::bindings::codegen::UnionTypes::HeadersOrByteStringSequenceSequence;
use dom::bindings::error::{Error, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, MutNullableHeap, Root};
@@ -312,7 +312,7 @@ impl Request {
// Step 28
if let Some(possible_header) = init.headers.as_ref() {
- if let &HeadersOrByteStringSequenceSequence::Headers(ref init_headers) = possible_header {
+ if let &HeadersInit::Headers(ref init_headers) = possible_header {
headers_copy = init_headers.clone();
}
}
@@ -337,7 +337,7 @@ impl Request {
}
// Step 31
- try!(r.Headers().fill(Some(HeadersOrByteStringSequenceSequence::Headers(headers_copy))));
+ try!(r.Headers().fill(Some(HeadersInit::Headers(headers_copy))));
// Step 32
let input_body = if let RequestInfo::Request(ref input_request) = input {
@@ -796,13 +796,15 @@ impl Into<RequestRedirect> for NetTraitsRequestRedirect {
}
}
-impl Clone for HeadersOrByteStringSequenceSequence {
- fn clone(&self) -> HeadersOrByteStringSequenceSequence {
+impl Clone for HeadersInit {
+ fn clone(&self) -> HeadersInit {
match self {
- &HeadersOrByteStringSequenceSequence::Headers(ref h) =>
- HeadersOrByteStringSequenceSequence::Headers(h.clone()),
- &HeadersOrByteStringSequenceSequence::ByteStringSequenceSequence(ref b) =>
- HeadersOrByteStringSequenceSequence::ByteStringSequenceSequence(b.clone()),
+ &HeadersInit::Headers(ref h) =>
+ HeadersInit::Headers(h.clone()),
+ &HeadersInit::ByteStringSequenceSequence(ref b) =>
+ HeadersInit::ByteStringSequenceSequence(b.clone()),
+ &HeadersInit::ByteStringMozMap(ref m) =>
+ HeadersInit::ByteStringMozMap(m.clone()),
}
}
}
diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs
index 566ff2d9375..6f7d7baf770 100644
--- a/components/script/dom/testbinding.rs
+++ b/components/script/dom/testbinding.rs
@@ -5,9 +5,10 @@
// check-tidy: no specs after this line
use core::nonzero::NonZero;
+use dom::bindings::callback::ExceptionHandling;
use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
-use dom::bindings::codegen::Bindings::TestBindingBinding;
+use dom::bindings::codegen::Bindings::TestBindingBinding::{self, SimpleCallback};
use dom::bindings::codegen::Bindings::TestBindingBinding::{TestBindingMethods, TestDictionary};
use dom::bindings::codegen::Bindings::TestBindingBinding::{TestDictionaryDefaults, TestEnum};
use dom::bindings::codegen::UnionTypes;
@@ -19,22 +20,27 @@ use dom::bindings::codegen::UnionTypes::{EventOrUSVString, HTMLElementOrLong, Lo
use dom::bindings::codegen::UnionTypes::{HTMLElementOrUnsignedLongOrStringOrBoolean, LongSequenceOrBoolean};
use dom::bindings::codegen::UnionTypes::{StringOrLongSequence, StringOrStringSequence, StringSequenceOrUnsignedLong};
use dom::bindings::codegen::UnionTypes::{StringOrUnsignedLong, StringOrBoolean, UnsignedLongOrBoolean};
-use dom::bindings::error::Fallible;
-use dom::bindings::global::GlobalRef;
+use dom::bindings::error::{Error, Fallible};
+use dom::bindings::global::{GlobalRef, global_root_from_context};
use dom::bindings::js::Root;
use dom::bindings::mozmap::MozMap;
use dom::bindings::num::Finite;
+use dom::bindings::refcounted::TrustedPromise;
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::{ByteString, DOMString, USVString};
use dom::bindings::weakref::MutableWeakRef;
use dom::blob::{Blob, BlobImpl};
+use dom::promise::Promise;
+use dom::promisenativehandler::{PromiseNativeHandler, Callback};
use dom::url::URL;
-use js::jsapi::{HandleObject, HandleValue, JSContext, JSObject};
+use js::jsapi::{HandleObject, HandleValue, JSContext, JSObject, JSAutoCompartment};
use js::jsapi::{JS_NewPlainObject, JS_NewUint8ClampedArray};
use js::jsval::{JSVal, NullValue};
+use script_traits::MsDuration;
use std::borrow::ToOwned;
use std::ptr;
use std::rc::Rc;
+use timers::OneshotTimerCallback;
use util::prefs::PREFS;
#[dom_struct]
@@ -647,6 +653,81 @@ impl TestBindingMethods for TestBinding {
fn ReceiveMozMapOfMozMaps(&self) -> MozMap<MozMap<i32>> { MozMap::new() }
fn ReceiveAnyMozMap(&self) -> MozMap<JSVal> { MozMap::new() }
+ #[allow(unrooted_must_root)]
+ fn ReturnResolvedPromise(&self, cx: *mut JSContext, v: HandleValue) -> Fallible<Rc<Promise>> {
+ Promise::Resolve(self.global().r(), cx, v)
+ }
+
+ #[allow(unrooted_must_root)]
+ fn ReturnRejectedPromise(&self, cx: *mut JSContext, v: HandleValue) -> Fallible<Rc<Promise>> {
+ Promise::Reject(self.global().r(), cx, v)
+ }
+
+ fn PromiseResolveNative(&self, cx: *mut JSContext, p: &Promise, v: HandleValue) {
+ p.resolve(cx, v);
+ }
+
+ fn PromiseRejectNative(&self, cx: *mut JSContext, p: &Promise, v: HandleValue) {
+ p.reject(cx, v);
+ }
+
+ fn PromiseRejectWithTypeError(&self, p: &Promise, s: USVString) {
+ p.reject_error(self.global().r().get_cx(), Error::Type(s.0));
+ }
+
+ #[allow(unrooted_must_root)]
+ fn ResolvePromiseDelayed(&self, p: &Promise, value: DOMString, delay: u64) {
+ let promise = p.duplicate();
+ let cb = TestBindingCallback {
+ promise: TrustedPromise::new(promise),
+ value: value,
+ };
+ let _ = self.global().r().schedule_callback(OneshotTimerCallback::TestBindingCallback(cb),
+ MsDuration::new(delay));
+ }
+
+ #[allow(unrooted_must_root)]
+ fn PromiseNativeHandler(&self,
+ resolve: Option<Rc<SimpleCallback>>,
+ reject: Option<Rc<SimpleCallback>>) -> Rc<Promise> {
+ let global = self.global();
+ let handler = PromiseNativeHandler::new(global.r(),
+ resolve.map(SimpleHandler::new),
+ reject.map(SimpleHandler::new));
+ let p = Promise::new(global.r());
+ p.append_native_handler(&handler);
+ return p;
+
+ #[derive(JSTraceable, HeapSizeOf)]
+ struct SimpleHandler {
+ #[ignore_heap_size_of = "Rc has unclear ownership semantics"]
+ handler: Rc<SimpleCallback>,
+ }
+ impl SimpleHandler {
+ fn new(callback: Rc<SimpleCallback>) -> Box<Callback> {
+ box SimpleHandler { handler: callback }
+ }
+ }
+ impl Callback for SimpleHandler {
+ #[allow(unsafe_code)]
+ fn callback(&self, cx: *mut JSContext, v: HandleValue) {
+ let global = unsafe { global_root_from_context(cx) };
+ let _ = self.handler.Call_(&global.r(), v, ExceptionHandling::Report);
+ }
+ }
+ }
+
+ #[allow(unrooted_must_root)]
+ fn PromiseAttribute(&self) -> Rc<Promise> {
+ Promise::new(self.global().r())
+ }
+
+ fn AcceptPromise(&self, _promise: &Promise) {
+ }
+
+ fn AcceptNullablePromise(&self, _promise: Option<&Promise>) {
+ }
+
fn PassSequenceSequence(&self, _seq: Vec<Vec<i32>>) {}
fn ReturnSequenceSequence(&self) -> Vec<Vec<i32>> { vec![] }
fn PassUnionSequenceSequence(&self, seq: LongOrLongSequenceSequence) {
@@ -693,3 +774,20 @@ impl TestBinding {
pub unsafe fn condition_satisfied(_: *mut JSContext, _: HandleObject) -> bool { true }
pub unsafe fn condition_unsatisfied(_: *mut JSContext, _: HandleObject) -> bool { false }
}
+
+#[derive(JSTraceable, HeapSizeOf)]
+pub struct TestBindingCallback {
+ #[ignore_heap_size_of = "unclear ownership semantics"]
+ promise: TrustedPromise,
+ value: DOMString,
+}
+
+impl TestBindingCallback {
+ #[allow(unrooted_must_root)]
+ pub fn invoke(self) {
+ let p = self.promise.root();
+ let cx = p.global().r().get_cx();
+ let _ac = JSAutoCompartment::new(cx, p.reflector().get_jsobject().get());
+ p.resolve_native(cx, &self.value);
+ }
+}
diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs
index f42eeab4dbf..d1342190f73 100644
--- a/components/script/dom/webglrenderingcontext.rs
+++ b/components/script/dom/webglrenderingcontext.rs
@@ -511,6 +511,19 @@ impl WebGLRenderingContext {
.send(CanvasMsg::WebGL(msg))
.unwrap()
}
+
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14
+ fn validate_feature_enum(&self, cap: u32) -> bool {
+ match cap {
+ constants::BLEND | constants::CULL_FACE | constants::DEPTH_TEST | constants::DITHER |
+ constants::POLYGON_OFFSET_FILL | constants::SAMPLE_ALPHA_TO_COVERAGE | constants::SAMPLE_COVERAGE |
+ constants::SAMPLE_COVERAGE_INVERT | constants::SCISSOR_TEST => true,
+ _ => {
+ self.webgl_error(InvalidEnum);
+ false
+ },
+ }
+ }
}
impl Drop for WebGLRenderingContext {
@@ -1114,27 +1127,19 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn Enable(&self, cap: u32) {
- match cap {
- constants::BLEND | constants::CULL_FACE | constants::DEPTH_TEST | constants::DITHER |
- constants::POLYGON_OFFSET_FILL | constants::SAMPLE_ALPHA_TO_COVERAGE | constants::SAMPLE_COVERAGE |
- constants::SAMPLE_COVERAGE_INVERT | constants::SCISSOR_TEST =>
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Enable(cap)))
- .unwrap(),
- _ => self.webgl_error(InvalidEnum),
+ if self.validate_feature_enum(cap) {
+ self.ipc_renderer
+ .send(CanvasMsg::WebGL(WebGLCommand::Enable(cap)))
+ .unwrap();
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
fn Disable(&self, cap: u32) {
- match cap {
- constants::BLEND | constants::CULL_FACE | constants::DEPTH_TEST | constants::DITHER |
- constants::POLYGON_OFFSET_FILL | constants::SAMPLE_ALPHA_TO_COVERAGE | constants::SAMPLE_COVERAGE |
- constants::SAMPLE_COVERAGE_INVERT | constants::SCISSOR_TEST =>
- self.ipc_renderer
- .send(CanvasMsg::WebGL(WebGLCommand::Disable(cap)))
- .unwrap(),
- _ => self.webgl_error(InvalidEnum),
+ if self.validate_feature_enum(cap) {
+ self.ipc_renderer
+ .send(CanvasMsg::WebGL(WebGLCommand::Disable(cap)))
+ .unwrap()
}
}
@@ -1455,6 +1460,20 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
buffer.map_or(false, |buf| buf.target().is_some() && !buf.is_deleted())
}
+ // TODO: We could write this without IPC, recording the calls to `enable` and `disable`.
+ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3
+ fn IsEnabled(&self, cap: u32) -> bool {
+ if self.validate_feature_enum(cap) {
+ let (sender, receiver) = ipc::channel().unwrap();
+ self.ipc_renderer
+ .send(CanvasMsg::WebGL(WebGLCommand::IsEnabled(cap, sender)))
+ .unwrap();
+ return receiver.recv().unwrap();
+ }
+
+ false
+ }
+
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6
fn IsFramebuffer(&self, frame_buffer: Option<&WebGLFramebuffer>) -> bool {
frame_buffer.map_or(false, |buf| buf.target().is_some() && !buf.is_deleted())
diff --git a/components/script/dom/webidls/HTMLScriptElement.webidl b/components/script/dom/webidls/HTMLScriptElement.webidl
index 17df2e7e08b..1636df7a12b 100644
--- a/components/script/dom/webidls/HTMLScriptElement.webidl
+++ b/components/script/dom/webidls/HTMLScriptElement.webidl
@@ -10,7 +10,7 @@ interface HTMLScriptElement : HTMLElement {
attribute DOMString charset;
// attribute boolean async;
attribute boolean defer;
- // attribute DOMString crossOrigin;
+ attribute DOMString? crossOrigin;
[Pure]
attribute DOMString text;
diff --git a/components/script/dom/webidls/HTMLSelectElement.webidl b/components/script/dom/webidls/HTMLSelectElement.webidl
index ba84d183a72..b513806f965 100644
--- a/components/script/dom/webidls/HTMLSelectElement.webidl
+++ b/components/script/dom/webidls/HTMLSelectElement.webidl
@@ -15,8 +15,8 @@ interface HTMLSelectElement : HTMLElement {
readonly attribute DOMString type;
//readonly attribute HTMLOptionsCollection options;
- // attribute unsigned long length;
- //getter Element? item(unsigned long index);
+ attribute unsigned long length;
+ getter Element? item(unsigned long index);
//HTMLOptionElement? namedItem(DOMString name);
// Note: this function currently only exists for union.html.
void add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null);
diff --git a/components/script/dom/webidls/Headers.webidl b/components/script/dom/webidls/Headers.webidl
index ee3d4b2fe94..5ae08ad2bd7 100644
--- a/components/script/dom/webidls/Headers.webidl
+++ b/components/script/dom/webidls/Headers.webidl
@@ -4,8 +4,7 @@
// https://fetch.spec.whatwg.org/#headers-class
-// TODO support OpenEndedDictionary<ByteString>
-typedef (Headers or sequence<sequence<ByteString>>) HeadersInit;
+typedef (Headers or sequence<sequence<ByteString>> or MozMap<ByteString>) HeadersInit;
[Constructor(optional HeadersInit init),
Exposed=(Window,Worker)]
diff --git a/components/script/dom/webidls/Promise.webidl b/components/script/dom/webidls/Promise.webidl
new file mode 100644
index 00000000000..5897ff422f9
--- /dev/null
+++ b/components/script/dom/webidls/Promise.webidl
@@ -0,0 +1,16 @@
+/* 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/. */
+
+// This interface is entirely internal to Servo, and should not be accessible to
+// web pages.
+
+callback PromiseJobCallback = void();
+
+[TreatNonCallableAsNull]
+callback AnyCallback = any (any value);
+
+[NoInterfaceObject, Exposed=(Window,Worker)]
+// Need to escape "Promise" so it's treated as an identifier.
+interface _Promise {
+};
diff --git a/components/script/dom/webidls/PromiseNativeHandler.webidl b/components/script/dom/webidls/PromiseNativeHandler.webidl
new file mode 100644
index 00000000000..caa1692dfd3
--- /dev/null
+++ b/components/script/dom/webidls/PromiseNativeHandler.webidl
@@ -0,0 +1,13 @@
+/* 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/. */
+
+// This interface is entirely internal to Servo, and should not be accessible to
+// web pages.
+
+// Hack to allow us to have JS owning and properly tracing/CCing/etc a
+// PromiseNativeHandler.
+[NoInterfaceObject,
+ Exposed=(Window,Worker)]
+interface PromiseNativeHandler {
+};
diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl
index e35b914d08e..c3274057a40 100644
--- a/components/script/dom/webidls/TestBinding.webidl
+++ b/components/script/dom/webidls/TestBinding.webidl
@@ -508,9 +508,24 @@ interface TestBinding {
[Func="TestBinding::condition_satisfied"]
const unsigned short funcControlledConstEnabled = 0;
+ [Throws]
+ Promise<any> returnResolvedPromise(any value);
+ [Throws]
+ Promise<any> returnRejectedPromise(any value);
+ readonly attribute Promise<boolean> promiseAttribute;
+ void acceptPromise(Promise<DOMString> string);
+ void acceptNullablePromise(Promise<DOMString>? string);
+ Promise<any> promiseNativeHandler(SimpleCallback? resolve, SimpleCallback? reject);
+ void promiseResolveNative(Promise<any> p, any value);
+ void promiseRejectNative(Promise<any> p, any value);
+ void promiseRejectWithTypeError(Promise<any> p, USVString message);
+ void resolvePromiseDelayed(Promise<any> p, DOMString value, unsigned long long ms);
+
void panic();
};
+callback SimpleCallback = void(any value);
+
partial interface TestBinding {
[Pref="dom.testable_crash.enabled"]
void crashHard();
diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl
index e95ce00e5e5..06727b3a536 100644
--- a/components/script/dom/webidls/WebGLRenderingContext.webidl
+++ b/components/script/dom/webidls/WebGLRenderingContext.webidl
@@ -603,7 +603,7 @@ interface WebGLRenderingContextBase
void hint(GLenum target, GLenum mode);
[WebGLHandlesContextLoss] GLboolean isBuffer(WebGLBuffer? buffer);
- //[WebGLHandlesContextLoss] GLboolean isEnabled(GLenum cap);
+ [WebGLHandlesContextLoss] GLboolean isEnabled(GLenum cap);
[WebGLHandlesContextLoss] GLboolean isFramebuffer(WebGLFramebuffer? framebuffer);
[WebGLHandlesContextLoss] GLboolean isProgram(WebGLProgram? program);
[WebGLHandlesContextLoss] GLboolean isRenderbuffer(WebGLRenderbuffer? renderbuffer);
diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs
index bb9a9c3f4ab..2520f57dbac 100644
--- a/components/script/dom/workerglobalscope.rs
+++ b/components/script/dom/workerglobalscope.rs
@@ -7,9 +7,10 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNo
use dom::bindings::codegen::Bindings::FunctionBinding::Function;
use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception, ErrorInfo};
-use dom::bindings::global::GlobalRef;
+use dom::bindings::global::{GlobalRef, GlobalRoot};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root};
+use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::Reflectable;
use dom::bindings::str::DOMString;
use dom::console::TimerSet;
@@ -29,7 +30,8 @@ use net_traits::{IpcSend, LoadOrigin};
use net_traits::{LoadContext, ResourceThreads, load_whole_resource};
use profile_traits::{mem, time};
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, maybe_take_panic_result};
-use script_thread::RunnableWrapper;
+use script_runtime::{ScriptThreadEventCategory, PromiseJobQueue, EnqueuedPromiseCallback};
+use script_thread::{Runnable, RunnableWrapper};
use script_traits::{MsDuration, TimerEvent, TimerEventId, TimerEventRequest, TimerSource};
use script_traits::ScriptMsg as ConstellationMsg;
use script_traits::WorkerGlobalScopeInit;
@@ -112,6 +114,8 @@ pub struct WorkerGlobalScope {
/// Timers used by the Console API.
console_timers: TimerSet,
+
+ promise_job_queue: PromiseJobQueue,
}
impl WorkerGlobalScope {
@@ -143,6 +147,7 @@ impl WorkerGlobalScope {
constellation_chan: init.constellation_chan,
scheduler_chan: init.scheduler_chan,
console_timers: TimerSet::new(),
+ promise_job_queue: PromiseJobQueue::new(),
}
}
@@ -228,6 +233,25 @@ impl WorkerGlobalScope {
cancelled: self.closing.clone().unwrap(),
}
}
+
+ pub fn enqueue_promise_job(&self, job: EnqueuedPromiseCallback) {
+ self.promise_job_queue.enqueue(job, GlobalRef::Worker(self));
+ }
+
+ pub fn flush_promise_jobs(&self) {
+ self.script_chan().send(CommonScriptMsg::RunnableMsg(
+ ScriptThreadEventCategory::WorkerEvent,
+ box FlushPromiseJobs {
+ global: Trusted::new(self),
+ })).unwrap();
+ }
+
+ fn do_flush_promise_jobs(&self) {
+ self.promise_job_queue.flush_promise_jobs(|id| {
+ assert_eq!(self.pipeline_id(), id);
+ Some(GlobalRoot::Worker(Root::from_ref(self)))
+ });
+ }
}
impl LoadOrigin for WorkerGlobalScope {
@@ -466,3 +490,14 @@ impl WorkerGlobalScope {
.report_an_error(error_info, value);
}
}
+
+struct FlushPromiseJobs {
+ global: Trusted<WorkerGlobalScope>,
+}
+
+impl Runnable for FlushPromiseJobs {
+ fn handler(self: Box<FlushPromiseJobs>) {
+ let global = self.global.root();
+ global.do_flush_promise_jobs();
+ }
+}
diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs
index d034b60aebd..24ee2c28d36 100644
--- a/components/script/dom/xmlhttprequest.rs
+++ b/components/script/dom/xmlhttprequest.rs
@@ -45,8 +45,8 @@ use js::jsapi::{JSContext, JS_ParseJSON};
use js::jsapi::JS_ClearPendingException;
use js::jsval::{JSVal, NullValue, UndefinedValue};
use msg::constellation_msg::{PipelineId, ReferrerPolicy};
-use net_traits::{CoreResourceThread, LoadOrigin};
-use net_traits::{FetchResponseListener, Metadata, NetworkError};
+use net_traits::{CoreResourceThread, FetchMetadata, FilteredMetadata};
+use net_traits::{FetchResponseListener, LoadOrigin, NetworkError};
use net_traits::CoreResourceMsg::Fetch;
use net_traits::request::{CredentialsMode, Destination, RequestInit, RequestMode};
use net_traits::trim_http_whitespace;
@@ -219,35 +219,39 @@ impl XMLHttpRequest {
core_resource_thread: CoreResourceThread,
init: RequestInit) {
impl FetchResponseListener for XHRContext {
- fn process_request_body(&mut self) {
- // todo
- }
- fn process_request_eof(&mut self) {
- // todo
- }
- fn process_response(&mut self, metadata: Result<Metadata, NetworkError>) {
- let xhr = self.xhr.root();
- let rv = xhr.process_headers_available(self.gen_id,
- metadata);
- if rv.is_err() {
- *self.sync_status.borrow_mut() = Some(rv);
- }
- }
- fn process_response_chunk(&mut self, mut chunk: Vec<u8>) {
- self.buf.borrow_mut().append(&mut chunk);
- self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone());
- }
- fn process_response_eof(&mut self, response: Result<(), NetworkError>) {
- let rv = match response {
- Ok(()) => {
- self.xhr.root().process_response_complete(self.gen_id, Ok(()))
- }
- Err(e) => {
- self.xhr.root().process_response_complete(self.gen_id, Err(e))
- }
- };
+ fn process_request_body(&mut self) {
+ // todo
+ }
+
+ fn process_request_eof(&mut self) {
+ // todo
+ }
+
+ fn process_response(&mut self,
+ metadata: Result<FetchMetadata, NetworkError>) {
+ let xhr = self.xhr.root();
+ let rv = xhr.process_headers_available(self.gen_id, metadata);
+ if rv.is_err() {
*self.sync_status.borrow_mut() = Some(rv);
}
+ }
+
+ fn process_response_chunk(&mut self, mut chunk: Vec<u8>) {
+ self.buf.borrow_mut().append(&mut chunk);
+ self.xhr.root().process_data_available(self.gen_id, self.buf.borrow().clone());
+ }
+
+ fn process_response_eof(&mut self, response: Result<(), NetworkError>) {
+ let rv = match response {
+ Ok(()) => {
+ self.xhr.root().process_response_complete(self.gen_id, Ok(()))
+ }
+ Err(e) => {
+ self.xhr.root().process_response_complete(self.gen_id, Err(e))
+ }
+ };
+ *self.sync_status.borrow_mut() = Some(rv);
+ }
}
impl PreInvoke for XHRContext {
@@ -273,9 +277,11 @@ impl LoadOrigin for XMLHttpRequest {
fn referrer_url(&self) -> Option<Url> {
return self.referrer_url.clone();
}
+
fn referrer_policy(&self) -> Option<ReferrerPolicy> {
return self.referrer_policy;
}
+
fn pipeline_id(&self) -> Option<PipelineId> {
let global = self.global();
Some(global.r().pipeline_id())
@@ -596,6 +602,7 @@ impl XMLHttpRequestMethods for XMLHttpRequest {
referrer_url: self.referrer_url.clone(),
referrer_policy: self.referrer_policy.clone(),
pipeline_id: self.pipeline_id(),
+ .. RequestInit::default()
};
if bypass_cross_origin_check {
@@ -862,10 +869,16 @@ impl XMLHttpRequest {
}
fn process_headers_available(&self,
- gen_id: GenerationId, metadata: Result<Metadata, NetworkError>)
+ gen_id: GenerationId, metadata: Result<FetchMetadata, NetworkError>)
-> Result<(), Error> {
let metadata = match metadata {
- Ok(meta) => meta,
+ Ok(meta) => match meta {
+ FetchMetadata::Unfiltered(m) => m,
+ FetchMetadata::Filtered { filtered, .. } => match filtered {
+ FilteredMetadata::Opaque => return Err(Error::Network),
+ FilteredMetadata::Transparent(m) => m
+ }
+ },
Err(_) => {
self.process_partial_response(XHRProgress::Errored(gen_id, Error::Network));
return Err(Error::Network);