diff options
Diffstat (limited to 'components/script')
133 files changed, 3122 insertions, 1471 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 4645d2ce419..ee380e92854 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -23,11 +23,12 @@ tinyfiledialogs = {git = "https://github.com/jdm/tinyfiledialogs"} [dependencies] angle = {git = "https://github.com/servo/angle", branch = "servo"} app_units = "0.3" +audio-video-metadata = "0.1.2" bitflags = "0.7" canvas_traits = {path = "../canvas_traits"} caseless = "0.1.0" cookie = {version = "0.2.5", features = ["serialize-rustc"]} -cssparser = {version = "0.5.7", features = ["heap_size", "serde-serialization"]} +cssparser = {version = "0.7", features = ["heap_size", "serde-serialization"]} devtools_traits = {path = "../devtools_traits"} encoding = "0.2" euclid = "0.10.1" @@ -56,16 +57,15 @@ plugins = {path = "../plugins"} profile_traits = {path = "../profile_traits"} rand = "0.3" range = {path = "../range"} -ref_filter_map = "1.0" ref_slice = "1.0" regex = "0.1.43" rustc-serialize = "0.3" script_layout_interface = {path = "../script_layout_interface"} script_traits = {path = "../script_traits"} -selectors = {version = "0.11", features = ["heap_size"]} +selectors = {version = "0.13", features = ["heap_size"]} serde = "0.8" smallvec = "0.1" -string_cache = {version = "0.2.23", features = ["heap_size", "unstable"]} +string_cache = {version = "0.2.26", features = ["heap_size", "unstable"]} style = {path = "../style"} time = "0.1.12" url = {version = "1.2", features = ["heap_size", "query_encoding"]} diff --git a/components/script/devtools.rs b/components/script/devtools.rs index 024307bc696..d515d1fb99a 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -2,20 +2,21 @@ * 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 devtools_traits::TimelineMarkerType; use devtools_traits::{AutoMargins, CONSOLE_API, CachedConsoleMessage, CachedConsoleMessageTypes}; use devtools_traits::{ComputedNodeLayout, ConsoleAPI, PageError, ScriptToDevtoolsControlMsg}; use devtools_traits::{EvaluateJSReply, Modification, NodeInfo, PAGE_ERROR, TimelineMarker}; +use devtools_traits::TimelineMarkerType; use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods; use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::LocationBinding::LocationMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use dom::bindings::conversions::{FromJSValConvertible, jsstring_to_str}; +use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, jsstring_to_str}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; +use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; use dom::browsingcontext::BrowsingContext; use dom::element::Element; @@ -27,9 +28,10 @@ use js::jsval::UndefinedValue; use msg::constellation_msg::PipelineId; use std::ffi::CStr; use std::str; -use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left}; +use style::properties::longhands::{margin_bottom, margin_left, margin_right, margin_top}; use uuid::Uuid; + #[allow(unsafe_code)] pub fn handle_evaluate_js(global: &GlobalRef, eval: String, reply: IpcSender<EvaluateJSReply>) { // global.get_cx() returns a valid `JSContext` pointer, so this is safe. @@ -45,8 +47,11 @@ pub fn handle_evaluate_js(global: &GlobalRef, eval: String, reply: IpcSender<Eva } else if rval.is_boolean() { EvaluateJSReply::BooleanValue(rval.to_boolean()) } else if rval.is_double() || rval.is_int32() { - EvaluateJSReply::NumberValue(FromJSValConvertible::from_jsval(cx, rval.handle(), ()) - .unwrap()) + EvaluateJSReply::NumberValue( + match FromJSValConvertible::from_jsval(cx, rval.handle(), ()) { + Ok(ConversionResult::Success(v)) => v, + _ => unreachable!(), + }) } else if rval.is_string() { EvaluateJSReply::StringValue(String::from(jsstring_to_str(cx, rval.to_string()))) } else if rval.is_null() { diff --git a/components/script/dom/abstractworker.rs b/components/script/dom/abstractworker.rs index 28c755d4f03..6c5abd0d020 100644 --- a/components/script/dom/abstractworker.rs +++ b/components/script/dom/abstractworker.rs @@ -4,7 +4,6 @@ use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::Reflectable; -use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; use js::jsapi::{JSRuntime, JS_RequestInterruptCallback}; use js::rust::Runtime; @@ -30,27 +29,6 @@ impl<T: Reflectable> SimpleWorkerErrorHandler<T> { } } -pub struct WorkerErrorHandler<T: Reflectable> { - pub addr: Trusted<T>, - pub msg: DOMString, - pub file_name: DOMString, - pub line_num: u32, - pub col_num: u32, -} - -impl<T: Reflectable> WorkerErrorHandler<T> { - pub fn new(addr: Trusted<T>, msg: DOMString, file_name: DOMString, line_num: u32, col_num: u32) - -> WorkerErrorHandler<T> { - WorkerErrorHandler { - addr: addr, - msg: msg, - file_name: file_name, - line_num: line_num, - col_num: col_num, - } - } -} - #[derive(Copy, Clone)] pub struct SharedRt { pub rt: *mut JSRuntime diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs index d5a00e6c1fa..36de48a1f9b 100644 --- a/components/script/dom/attr.rs +++ b/components/script/dom/attr.rs @@ -15,10 +15,10 @@ use dom::element::{AttributeMutation, Element}; use dom::virtualmethods::vtable_for; use dom::window::Window; use std::borrow::ToOwned; -use std::cell::Ref; use std::mem; use string_cache::{Atom, Namespace}; use style::attr::{AttrIdentifier, AttrValue}; +use style::refcell::Ref; // https://dom.spec.whatwg.org/#interface-attr #[dom_struct] diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs index c34598a26be..7e6bb3534fb 100644 --- a/components/script/dom/bindings/callback.rs +++ b/components/script/dom/bindings/callback.rs @@ -7,11 +7,12 @@ use dom::bindings::error::{Error, Fallible, report_pending_exception}; use dom::bindings::global::global_root_from_object; use dom::bindings::reflector::Reflectable; -use js::jsapi::GetGlobalForObjectCrossCompartment; -use js::jsapi::JS_GetProperty; use js::jsapi::{Heap, MutableHandleObject, RootedObject}; use js::jsapi::{IsCallable, JSContext, JSObject, JS_WrapObject}; use js::jsapi::{JSCompartment, JS_EnterCompartment, JS_LeaveCompartment}; +use js::jsapi::GetGlobalForObjectCrossCompartment; +use js::jsapi::JSAutoCompartment; +use js::jsapi::JS_GetProperty; use js::jsval::{JSVal, UndefinedValue}; use js::rust::RootedGuard; use std::default::Default; @@ -189,7 +190,8 @@ impl<'a> Drop for CallSetup<'a> { unsafe { JS_LeaveCompartment(self.cx, self.old_compartment); if self.handling == ExceptionHandling::Report { - report_pending_exception(self.cx, *self.exception_compartment); + let _ac = JSAutoCompartment::new(self.cx, *self.exception_compartment); + report_pending_exception(self.cx, true); } } } diff --git a/components/script/dom/bindings/cell.rs b/components/script/dom/bindings/cell.rs deleted file mode 100644 index d1b40af5b1a..00000000000 --- a/components/script/dom/bindings/cell.rs +++ /dev/null @@ -1,139 +0,0 @@ -/* 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/. */ - -//! A shareable mutable container for the DOM. - -use dom::bindings::trace::JSTraceable; -use js::jsapi::JSTracer; -use std::cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut}; -use style::thread_state; -use style::thread_state::SCRIPT; - -/// A mutable field in the DOM. -/// -/// This extends the API of `core::cell::RefCell` to allow unsafe access in -/// certain situations, with dynamic checking in debug builds. -#[derive(Clone, HeapSizeOf)] -pub struct DOMRefCell<T> { - value: RefCell<T>, -} - -// Functionality specific to Servo's `DOMRefCell` type -// =================================================== - -impl<T> DOMRefCell<T> { - /// Return a reference to the contents. - /// - /// For use in the layout thread only. - #[allow(unsafe_code)] - pub unsafe fn borrow_for_layout(&self) -> &T { - debug_assert!(thread_state::get().is_layout()); - &*self.value.as_ptr() - } - - /// Borrow the contents for the purpose of GC tracing. - /// - /// This succeeds even if the object is mutably borrowed, - /// so you have to be careful in trace code! - #[allow(unsafe_code)] - pub unsafe fn borrow_for_gc_trace(&self) -> &T { - // FIXME: IN_GC isn't reliable enough - doesn't catch minor GCs - // https://github.com/servo/servo/issues/6389 - // debug_assert!(thread_state::get().contains(SCRIPT | IN_GC)); - &*self.value.as_ptr() - } - - /// Borrow the contents for the purpose of script deallocation. - /// - #[allow(unsafe_code)] - pub unsafe fn borrow_for_script_deallocation(&self) -> &mut T { - debug_assert!(thread_state::get().contains(SCRIPT)); - &mut *self.value.as_ptr() - } - - /// Version of the above that we use during restyle while the script thread - /// is blocked. - pub fn borrow_mut_for_layout(&self) -> RefMut<T> { - debug_assert!(thread_state::get().is_layout()); - self.value.borrow_mut() - } -} - -impl<T: JSTraceable> JSTraceable for DOMRefCell<T> { - fn trace(&self, trc: *mut JSTracer) { - unsafe { - (*self).borrow_for_gc_trace().trace(trc) - } - } -} - -// Functionality duplicated with `core::cell::RefCell` -// =================================================== -impl<T> DOMRefCell<T> { - /// Create a new `DOMRefCell` containing `value`. - pub fn new(value: T) -> DOMRefCell<T> { - DOMRefCell { - value: RefCell::new(value), - } - } - - - /// Immutably borrows the wrapped value. - /// - /// The borrow lasts until the returned `Ref` exits scope. Multiple - /// immutable borrows can be taken out at the same time. - /// - /// # Panics - /// - /// Panics if this is called off the script thread. - /// - /// Panics if the value is currently mutably borrowed. - pub fn borrow(&self) -> Ref<T> { - self.try_borrow().expect("DOMRefCell<T> already mutably borrowed") - } - - /// Mutably borrows the wrapped value. - /// - /// The borrow lasts until the returned `RefMut` exits scope. The value - /// cannot be borrowed while this borrow is active. - /// - /// # Panics - /// - /// Panics if this is called off the script thread. - /// - /// Panics if the value is currently borrowed. - pub fn borrow_mut(&self) -> RefMut<T> { - self.try_borrow_mut().expect("DOMRefCell<T> already borrowed") - } - - /// Attempts to immutably borrow the wrapped value. - /// - /// The borrow lasts until the returned `Ref` exits scope. Multiple - /// immutable borrows can be taken out at the same time. - /// - /// Returns `None` if the value is currently mutably borrowed. - /// - /// # Panics - /// - /// Panics if this is called off the script thread. - pub fn try_borrow(&self) -> Result<Ref<T>, BorrowError<T>> { - debug_assert!(thread_state::get().is_script()); - self.value.try_borrow() - } - - /// Mutably borrows the wrapped value. - /// - /// The borrow lasts until the returned `RefMut` exits scope. The value - /// cannot be borrowed while this borrow is active. - /// - /// Returns `None` if the value is currently borrowed. - /// - /// # Panics - /// - /// Panics if this is called off the script thread. - pub fn try_borrow_mut(&self) -> Result<RefMut<T>, BorrowMutError<T>> { - debug_assert!(thread_state::get().is_script()); - self.value.try_borrow_mut() - } -} diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 8a288694431..486758182d2 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -18,17 +18,21 @@ from WebIDL import ( BuiltinTypes, IDLBuiltinType, IDLNullValue, + IDLNullableType, IDLType, IDLInterfaceMember, IDLUndefinedValue, + IDLWrapperType, ) from Configuration import ( + MakeNativeName, MemberIsUnforgeable, getModuleFromObject, getTypesFromCallback, getTypesFromDescriptor, getTypesFromDictionary, + iteratorNativeType ) AUTOGENERATED_WARNING_COMMENT = \ @@ -77,7 +81,7 @@ def toStringBool(arg): def toBindingNamespace(arg): - return re.sub("((_workers)?$)", "Binding\\1", arg) + return re.sub("((_workers)?$)", "Binding\\1", MakeNativeName(arg)) def stripTrailingWhitespace(text): @@ -93,9 +97,6 @@ def innerSequenceType(type): return type.inner.inner if type.nullable() else type.inner -def MakeNativeName(name): - return name[0].upper() + name[1:] - builtinNames = { IDLType.Tags.bool: 'bool', IDLType.Tags.int8: 'i8', @@ -719,7 +720,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, raise TypeError("Can't handle array arguments yet") if type.isSequence(): - innerInfo = getJSToNativeConversionInfo(innerSequenceType(type), descriptorProvider) + innerInfo = getJSToNativeConversionInfo(innerSequenceType(type), + descriptorProvider, + isMember=isMember) declType = CGWrapper(innerInfo.declType, pre="Vec<", post=">") config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs) @@ -727,9 +730,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, declType = CGWrapper(declType, pre="Option<", post=" >") templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n" - " Ok(value) => value,\n" - " Err(()) => { %s },\n" - "}" % (config, exceptionCode)) + " Ok(ConversionResult::Success(value)) => value,\n" + " Ok(ConversionResult::Failure(error)) => {\n" + " throw_type_error(cx, &error);\n" + " %s\n" + " }\n" + " _ => { %s },\n" + "}" % (config, exceptionCode, exceptionCode)) return handleOptional(templateBody, declType, handleDefaultNull("None")) @@ -739,9 +746,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, declType = CGWrapper(declType, pre="Option<", post=" >") templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" - " Ok(value) => value,\n" - " Err(()) => { %s },\n" - "}" % exceptionCode) + " Ok(ConversionResult::Success(value)) => value,\n" + " Ok(ConversionResult::Failure(error)) => {\n" + " throw_type_error(cx, &error);\n" + " %s\n" + " }\n" + " _ => { %s },\n" + "}" % (exceptionCode, exceptionCode)) return handleOptional(templateBody, declType, handleDefaultNull("None")) @@ -810,9 +821,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, conversionCode = ( "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n" - " Ok(strval) => strval,\n" - " Err(_) => { %s },\n" - "}" % (nullBehavior, exceptionCode)) + " Ok(ConversionResult::Success(strval)) => strval,\n" + " Ok(ConversionResult::Failure(error)) => {\n" + " throw_type_error(cx, &error);\n" + " %s\n" + " }\n" + " _ => { %s },\n" + "}" % (nullBehavior, exceptionCode, exceptionCode)) if defaultValue is None: default = None @@ -836,9 +851,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, conversionCode = ( "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" - " Ok(strval) => strval,\n" - " Err(_) => { %s },\n" - "}" % exceptionCode) + " Ok(ConversionResult::Success(strval)) => strval,\n" + " Ok(ConversionResult::Failure(error)) => {\n" + " throw_type_error(cx, &error);\n" + " %s\n" + " }\n" + " _ => { %s },\n" + "}" % (exceptionCode, exceptionCode)) if defaultValue is None: default = None @@ -862,9 +881,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, conversionCode = ( "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" - " Ok(strval) => strval,\n" - " Err(_) => { %s },\n" - "}" % exceptionCode) + " Ok(ConversionResult::Success(strval)) => strval,\n" + " Ok(ConversionResult::Failure(error)) => {\n" + " throw_type_error(cx, &error);\n" + " %s\n" + " }\n" + " _ => { %s },\n" + "}" % (exceptionCode, exceptionCode)) if defaultValue is None: default = None @@ -922,7 +945,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull() callback = type.unroll().callback - declType = CGGeneric('%s::%s' % (getModuleFromObject(callback), callback.identifier.name)) + declType = CGGeneric(callback.identifier.name) finalDeclType = CGTemplatedType("Rc", declType) conversion = CGCallbackTempRoot(declType.define()) @@ -1017,9 +1040,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, CGDictionary.makeDictionaryName(type.inner)) declType = CGGeneric(typeName) template = ("match %s::new(cx, ${val}) {\n" - " Ok(dictionary) => dictionary,\n" - " Err(_) => { %s },\n" - "}" % (typeName, exceptionCode)) + " Ok(ConversionResult::Success(dictionary)) => dictionary,\n" + " Ok(ConversionResult::Failure(error)) => {\n" + " throw_type_error(cx, &error);\n" + " %s\n" + " }\n" + " _ => { %s },\n" + "}" % (typeName, exceptionCode, exceptionCode)) return handleOptional(template, declType, handleDefaultNull("%s::empty(cx)" % typeName)) @@ -1042,9 +1069,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, template = ( "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n" - " Ok(v) => v,\n" - " Err(_) => { %s }\n" - "}" % (conversionBehavior, exceptionCode)) + " Ok(ConversionResult::Success(v)) => v,\n" + " Ok(ConversionResult::Failure(error)) => {\n" + " throw_type_error(cx, &error);\n" + " %s\n" + " }\n" + " _ => { %s }\n" + "}" % (conversionBehavior, exceptionCode, exceptionCode)) if defaultValue is not None: if isinstance(defaultValue, IDLNullValue): @@ -1103,20 +1134,20 @@ def instantiateJSToNativeConversionTemplate(templateBody, replacements, def convertConstIDLValueToJSVal(value): if isinstance(value, IDLNullValue): - return "NullVal" + return "ConstantVal::NullVal" tag = value.type.tag() if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, IDLType.Tags.uint16, IDLType.Tags.int32]: - return "IntVal(%s)" % (value.value) + return "ConstantVal::IntVal(%s)" % (value.value) if tag == IDLType.Tags.uint32: - return "UintVal(%s)" % (value.value) + return "ConstantVal::UintVal(%s)" % (value.value) if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]: - return "DoubleVal(%s)" % (value.value) + return "ConstantVal::DoubleVal(%s)" % (value.value) if tag == IDLType.Tags.bool: - return "BoolVal(true)" if value.value else "BoolVal(false)" + return "ConstantVal::BoolVal(true)" if value.value else "ConstantVal::BoolVal(false)" if tag in [IDLType.Tags.unrestricted_float, IDLType.Tags.float, IDLType.Tags.unrestricted_double, IDLType.Tags.double]: - return "DoubleVal(%s)" % (value.value) + return "ConstantVal::DoubleVal(%s)" % (value.value) raise TypeError("Const value of unhandled type: " + value.type) @@ -1317,7 +1348,10 @@ def getRetvalDeclarationForType(returnType, descriptorProvider): if returnType.isAny(): return CGGeneric("JSVal") if returnType.isObject() or returnType.isSpiderMonkeyInterface(): - return CGGeneric("*mut JSObject") + result = CGGeneric("NonZero<*mut JSObject>") + if returnType.nullable(): + result = CGWrapper(result, pre="Option<", post=">") + return result if returnType.isSequence(): result = getRetvalDeclarationForType(innerSequenceType(returnType), descriptorProvider) result = CGWrapper(result, pre="Vec<", post=">") @@ -1490,6 +1524,46 @@ class MethodDefiner(PropertyDefiner): "length": 0, "condition": "Condition::Satisfied"}) + # Generate the keys/values/entries aliases for value iterables. + maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable + if (not static and not unforgeable and + (maplikeOrSetlikeOrIterable and + maplikeOrSetlikeOrIterable.isIterable() and + maplikeOrSetlikeOrIterable.isValueIterator())): + # Add our keys/values/entries/forEach + self.regular.append({ + "name": "keys", + "methodInfo": False, + "selfHostedName": "ArrayKeys", + "length": 0, + "condition": PropertyDefiner.getControllingCondition(m, + descriptor) + }) + self.regular.append({ + "name": "values", + "methodInfo": False, + "selfHostedName": "ArrayValues", + "length": 0, + "condition": PropertyDefiner.getControllingCondition(m, + descriptor) + }) + self.regular.append({ + "name": "entries", + "methodInfo": False, + "selfHostedName": "ArrayEntries", + "length": 0, + "condition": PropertyDefiner.getControllingCondition(m, + descriptor) + }) + self.regular.append({ + "name": "forEach", + "methodInfo": False, + "selfHostedName": "ArrayForEach", + "length": 0, + "condition": PropertyDefiner.getControllingCondition(m, + descriptor) + }) + isUnforgeableInterface = bool(descriptor.interface.getExtendedAttribute("Unforgeable")) if not static and unforgeable == isUnforgeableInterface: stringifier = descriptor.operations['Stringifier'] @@ -1598,7 +1672,8 @@ class AttrDefiner(PropertyDefiner): "native": accessor}) def setter(attr): - if attr.readonly and not attr.getExtendedAttribute("PutForwards"): + if (attr.readonly and not attr.getExtendedAttribute("PutForwards") + and not attr.getExtendedAttribute("Replaceable")): return "JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }" if self.static: @@ -1710,7 +1785,7 @@ class CGImports(CGWrapper): """ Generates the appropriate import/use statements. """ - def __init__(self, child, descriptors, callbacks, imports, config, ignored_warnings=None): + def __init__(self, child, descriptors, callbacks, dictionaries, enums, imports, config, ignored_warnings=None): """ Adds a set of imports. """ @@ -1724,17 +1799,20 @@ class CGImports(CGWrapper): ] def componentTypes(type): - if type.nullable(): + if type.isType() and type.nullable(): type = type.unroll() if type.isUnion(): return type.flatMemberTypes + if type.isDictionary(): + return [type] + getTypesFromDictionary(type) return [type] def isImportable(type): if not type.isType(): - assert type.isInterface() - return not type.isCallback() - return type.isNonCallbackInterface() and not type.builtin + assert (type.isInterface() or type.isDictionary() or + type.isEnum() or type.isNamespace()) + return True + return not (type.builtin or type.isSequence() or type.isUnion()) def relatedTypesForSignatures(method): types = [] @@ -1746,13 +1824,30 @@ class CGImports(CGWrapper): def getIdentifier(t): if t.isType(): - return t.inner.identifier - assert t.isInterface() + if t.nullable(): + t = t.inner + if t.isCallback(): + return t.callback.identifier + return t.identifier + assert t.isInterface() or t.isDictionary() or t.isEnum() or t.isNamespace() return t.identifier + def removeWrapperAndNullableTypes(types): + normalized = [] + for t in types: + while (t.isType() and t.nullable()) or isinstance(t, IDLWrapperType): + t = t.inner + if isImportable(t): + normalized += [t] + return normalized + types = [] for d in descriptors: - types += [d.interface] + if not d.interface.isCallback(): + types += [d.interface] + + if d.interface.isIteratorInterface(): + types += [d.interface.iterableInterface] members = d.interface.members + d.interface.namedConstructors constructor = d.interface.ctor() @@ -1768,19 +1863,39 @@ class CGImports(CGWrapper): elif m.isAttr(): types += componentTypes(m.type) + # Import the type names used in the callbacks that are being defined. for c in callbacks: types += relatedTypesForSignatures(c) + # Import the type names used in the dictionaries that are being defined. + for d in dictionaries: + types += componentTypes(d) + + # Normalize the types we've collected and remove any ones which can't be imported. + types = removeWrapperAndNullableTypes(types) + descriptorProvider = config.getDescriptorProvider() + extras = [] for t in types: - if isImportable(t): + # Importing these types in the same module that defines them is an error. + if t in dictionaries or t in enums: + continue + if t.isInterface() or t.isNamespace(): descriptor = descriptorProvider.getDescriptor(getIdentifier(t).name) - imports += ['%s' % descriptor.path] + extras += [descriptor.path] + if descriptor.interface.parent: + parentName = getIdentifier(descriptor.interface.parent).name + descriptor = descriptorProvider.getDescriptor(parentName) + extras += [descriptor.path, descriptor.bindingPath] + else: + if t.isEnum(): + extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name + 'Values'] + extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name] statements = [] if len(ignored_warnings) > 0: statements.append('#![allow(%s)]' % ','.join(ignored_warnings)) - statements.extend('use %s;' % i for i in sorted(set(imports))) + statements.extend('use %s;' % i for i in sorted(set(imports + extras))) CGWrapper.__init__(self, child, pre='\n'.join(statements) + '\n\n') @@ -1840,7 +1955,7 @@ def DOMClass(descriptor): # padding. protoList.extend(['PrototypeList::ID::Last'] * (descriptor.config.maxProtoChainLength - len(protoList))) prototypeChainString = ', '.join(protoList) - heapSizeOf = 'heap_size_of_raw_self_and_children::<%s>' % descriptor.interface.identifier.name + heapSizeOf = 'heap_size_of_raw_self_and_children::<%s>' % descriptor.concreteType if descriptor.isGlobal(): globals_ = camel_to_upper_snake(descriptor.name) else: @@ -1883,7 +1998,7 @@ class CGDOMJSClass(CGThing): elif self.descriptor.weakReferenceable: args["slots"] = "2" return """\ -static CLASS_OPS: js::jsapi::ClassOps = js::jsapi::ClassOps { +static CLASS_OPS: js::jsapi::JSClassOps = js::jsapi::JSClassOps { addProperty: None, delProperty: None, getProperty: None, @@ -1899,15 +2014,13 @@ static CLASS_OPS: js::jsapi::ClassOps = js::jsapi::ClassOps { }; static Class: DOMJSClass = DOMJSClass { - base: js::jsapi::Class { + base: js::jsapi::JSClass { name: %(name)s as *const u8 as *const libc::c_char, flags: JSCLASS_IS_DOMJSCLASS | %(flags)s | (((%(slots)s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT) /* JSCLASS_HAS_RESERVED_SLOTS(%(slots)s) */, cOps: &CLASS_OPS, - spec: ptr::null(), - ext: ptr::null(), - oOps: ptr::null(), + reserved: [0 as *mut _; 3], }, dom_class: %(domClass)s };""" % args @@ -1946,6 +2059,17 @@ class CGInterfaceObjectJSClass(CGThing): self.descriptor = descriptor def define(self): + if self.descriptor.interface.isNamespace(): + classString = self.descriptor.interface.getExtendedAttribute("ClassString") + if classString: + classString = classString[0] + else: + classString = "Object" + return """\ +static NAMESPACE_OBJECT_CLASS: NamespaceObjectClass = unsafe { + NamespaceObjectClass::new(%s) +}; +""" % str_to_const_array(classString) if self.descriptor.interface.ctor(): constructorBehavior = "InterfaceConstructorBehavior::call(%s)" % CONSTRUCT_HOOK_NAME else: @@ -1958,12 +2082,9 @@ class CGInterfaceObjectJSClass(CGThing): "depth": self.descriptor.prototypeDepth } return """\ -static INTERFACE_OBJECT_OPS: js::jsapi::ClassOps = - NonCallbackInterfaceObjectClass::ops(%(constructorBehavior)s); - -static InterfaceObjectClass: NonCallbackInterfaceObjectClass = +static INTERFACE_OBJECT_CLASS: NonCallbackInterfaceObjectClass = NonCallbackInterfaceObjectClass::new( - &INTERFACE_OBJECT_OPS, + &%(constructorBehavior)s, %(representation)s, PrototypeList::ID::%(id)s, %(depth)s); @@ -2023,7 +2144,7 @@ class CGCallbackTempRoot(CGGeneric): CGGeneric.__init__(self, "%s::new(${val}.get().to_object())" % name) -def getAllTypes(descriptors, dictionaries, callbacks): +def getAllTypes(descriptors, dictionaries, callbacks, typedefs): """ Generate all the types we're dealing with. For each type, a tuple containing type, descriptor, dictionary is yielded. The @@ -2039,15 +2160,18 @@ def getAllTypes(descriptors, dictionaries, callbacks): for callback in callbacks: for t in getTypesFromCallback(callback): yield (t, None, None) + for typedef in typedefs: + yield (typedef.innerType, None, None) -def UnionTypes(descriptors, dictionaries, callbacks, config): +def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): """ Returns a CGList containing CGUnionStructs for every union. """ imports = [ 'dom::bindings::codegen::PrototypeList', + 'dom::bindings::conversions::ConversionResult', 'dom::bindings::conversions::FromJSValConvertible', 'dom::bindings::conversions::ToJSValConvertible', 'dom::bindings::conversions::ConversionBehavior', @@ -2055,17 +2179,21 @@ def UnionTypes(descriptors, dictionaries, callbacks, config): 'dom::bindings::conversions::root_from_handlevalue', 'dom::bindings::error::throw_not_in_union', 'dom::bindings::js::Root', - 'dom::bindings::str::{ByteString, DOMString, USVString}', + 'dom::bindings::str::ByteString', + 'dom::bindings::str::DOMString', + 'dom::bindings::str::USVString', 'dom::types::*', + 'js::error::throw_type_error', + 'js::jsapi::HandleValue', 'js::jsapi::JSContext', - 'js::jsapi::{HandleValue, MutableHandleValue}', + 'js::jsapi::MutableHandleValue', 'js::jsval::JSVal', ] # Now find all the things we'll need as arguments and return values because # we need to wrap or unwrap them. unionStructs = dict() - for (t, descriptor, dictionary) in getAllTypes(descriptors, dictionaries, callbacks): + for (t, descriptor, dictionary) in getAllTypes(descriptors, dictionaries, callbacks, typedefs): assert not descriptor or not dictionary t = t.unroll() if not t.isUnion(): @@ -2081,7 +2209,14 @@ def UnionTypes(descriptors, dictionaries, callbacks, config): # Sort unionStructs by key, retrieve value unionStructs = (i[1] for i in sorted(unionStructs.items(), key=operator.itemgetter(0))) - return CGImports(CGList(unionStructs, "\n\n"), [], [], imports, config, ignored_warnings=[]) + return CGImports(CGList(unionStructs, "\n\n"), + descriptors=[], + callbacks=[], + dictionaries=[], + enums=[], + imports=imports, + config=config, + ignored_warnings=[]) class Argument(): @@ -2255,9 +2390,9 @@ class CGConstructorEnabled(CGAbstractMethod): def CreateBindingJSObject(descriptor, parent=None): + assert not descriptor.isGlobal() create = "let raw = Box::into_raw(object);\nlet _rt = RootedTraceable::new(&*raw);\n" if descriptor.proxy: - assert not descriptor.isGlobal() create += """ let handler = RegisterBindings::proxy_handlers[PrototypeList::Proxies::%s as usize]; rooted!(in(cx) let private = PrivateValue(raw as *const libc::c_void)); @@ -2268,18 +2403,9 @@ let obj = NewProxyObject(cx, handler, assert!(!obj.is_null()); rooted!(in(cx) let obj = obj);\ """ % (descriptor.name, parent) - elif descriptor.isGlobal(): - create += ("rooted!(in(cx) let obj =\n" - " create_dom_global(\n" - " cx,\n" - " &Class.base as *const js::jsapi::Class as *const JSClass,\n" - " raw as *const libc::c_void,\n" - " Some(%s))\n" - ");\n" - "assert!(!obj.is_null());" % TRACE_HOOK_NAME) else: create += ("rooted!(in(cx) let obj = JS_NewObjectWithGivenProto(\n" - " cx, &Class.base as *const js::jsapi::Class as *const JSClass, proto.handle()));\n" + " cx, &Class.base as *const JSClass, proto.handle()));\n" "assert!(!obj.is_null());\n" "\n" "JS_SetReservedSlot(obj.get(), DOM_OBJECT_SLOT,\n" @@ -2355,21 +2481,17 @@ class CGWrapMethod(CGAbstractMethod): """ def __init__(self, descriptor): assert not descriptor.interface.isCallback() - if not descriptor.isGlobal(): - args = [Argument('*mut JSContext', 'cx'), Argument('GlobalRef', 'scope'), - Argument("Box<%s>" % descriptor.concreteType, 'object')] - else: - args = [Argument('*mut JSContext', 'cx'), - Argument("Box<%s>" % descriptor.concreteType, 'object')] + assert not descriptor.isGlobal() + args = [Argument('*mut JSContext', 'cx'), Argument('GlobalRef', 'scope'), + Argument("Box<%s>" % descriptor.concreteType, 'object')] retval = 'Root<%s>' % descriptor.concreteType CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args, pub=True, unsafe=True) def definition_body(self): unforgeable = CopyUnforgeablePropertiesToInstance(self.descriptor) - if not self.descriptor.isGlobal(): - create = CreateBindingJSObject(self.descriptor, "scope") - return CGGeneric("""\ + create = CreateBindingJSObject(self.descriptor, "scope") + return CGGeneric("""\ let scope = scope.reflector().get_jsobject(); assert!(!scope.get().is_null()); assert!(((*JS_GetClass(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0); @@ -2385,21 +2507,65 @@ assert!(!proto.is_null()); (*raw).init_reflector(obj.get()); Root::from_ref(&*raw)""" % {'copyUnforgeable': unforgeable, 'createObject': create}) - else: - create = CreateBindingJSObject(self.descriptor) - return CGGeneric("""\ -%(createObject)s + + +class CGWrapGlobalMethod(CGAbstractMethod): + """ + Class that generates the FooBinding::Wrap function for global interfaces. + """ + def __init__(self, descriptor, properties): + assert not descriptor.interface.isCallback() + assert descriptor.isGlobal() + args = [Argument('*mut JSContext', 'cx'), + Argument("Box<%s>" % descriptor.concreteType, 'object')] + retval = 'Root<%s>' % descriptor.concreteType + CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args, + pub=True, unsafe=True) + self.properties = properties + + def definition_body(self): + values = { + "unforgeable": CopyUnforgeablePropertiesToInstance(self.descriptor) + } + + pairs = [ + ("define_guarded_properties", self.properties.attrs), + ("define_guarded_methods", self.properties.methods), + ("define_guarded_constants", self.properties.consts) + ] + members = ["%s(cx, obj.handle(), %s);" % (function, array.variableName()) + for (function, array) in pairs if array.length() > 0] + values["members"] = "\n".join(members) + + return CGGeneric("""\ +let raw = Box::into_raw(object); +let _rt = RootedTraceable::new(&*raw); + +rooted!(in(cx) let mut obj = ptr::null_mut()); +create_global_object( + cx, + &Class.base, + raw as *const libc::c_void, + _trace, + obj.handle_mut()); +assert!(!obj.is_null()); + (*raw).init_reflector(obj.get()); let _ac = JSAutoCompartment::new(cx, obj.get()); rooted!(in(cx) let mut proto = ptr::null_mut()); GetProtoObject(cx, obj.handle(), proto.handle_mut()); -JS_SetPrototype(cx, obj.handle(), proto.handle()); +assert!(JS_SplicePrototype(cx, obj.handle(), proto.handle())); +let mut immutable = false; +assert!(JS_SetImmutablePrototype(cx, obj.handle(), &mut immutable)); +assert!(immutable); -%(copyUnforgeable)s +%(members)s + +%(unforgeable)s Root::from_ref(&*raw)\ -""" % {'copyUnforgeable': unforgeable, 'createObject': create}) +""" % values) class CGIDLInterface(CGThing): @@ -2412,7 +2578,7 @@ class CGIDLInterface(CGThing): def define(self): interface = self.descriptor.interface - name = self.descriptor.name + name = self.descriptor.concreteType if (interface.getUserData("hasConcreteDescendant", False) or interface.getUserData("hasProxyDescendant", False)): depth = self.descriptor.prototypeDepth @@ -2503,6 +2669,28 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): def definition_body(self): name = self.descriptor.interface.identifier.name + if self.descriptor.interface.isNamespace(): + if self.descriptor.interface.getExtendedAttribute("ProtoObjectHack"): + proto = "JS_GetObjectPrototype(cx, global)" + else: + proto = "JS_NewPlainObject(cx)" + if self.properties.static_methods.length(): + methods = self.properties.static_methods.variableName() + else: + methods = "&[]" + return CGGeneric("""\ +rooted!(in(cx) let proto = %(proto)s); +assert!(!proto.is_null()); +rooted!(in(cx) let mut namespace = ptr::null_mut()); +create_namespace_object(cx, global, proto.handle(), &NAMESPACE_OBJECT_CLASS, + %(methods)s, %(name)s, namespace.handle_mut()); +assert!(!namespace.is_null()); +assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null()); +(*cache)[PrototypeList::Constructor::%(id)s as usize] = namespace.get(); +<*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize), + ptr::null_mut(), + namespace.get()); +""" % {"id": MakeNativeName(name), "methods": methods, "name": str_to_const_array(name), "proto": proto}) if self.descriptor.interface.isCallback(): assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants() return CGGeneric("""\ @@ -2519,6 +2707,8 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null()); if len(self.descriptor.prototypeChain) == 1: if self.descriptor.interface.getExtendedAttribute("ExceptionClass"): getPrototypeProto = "prototype_proto.set(JS_GetErrorPrototype(cx))" + elif self.descriptor.interface.isIteratorInterface(): + getPrototypeProto = "prototype_proto.set(JS_GetIteratorPrototype(cx))" else: getPrototypeProto = "prototype_proto.set(JS_GetObjectPrototype(cx, global))" else: @@ -2541,6 +2731,18 @@ assert!(!prototype_proto.is_null());""" % getPrototypeProto)] else: properties[arrayName] = "&[]" + if self.descriptor.isGlobal(): + assert not self.haveUnscopables + proto_properties = { + "attrs": "&[]", + "consts": "&[]", + "id": name, + "methods": "&[]", + "unscopables": "&[]", + } + else: + proto_properties = properties + code.append(CGGeneric(""" rooted!(in(cx) let mut prototype = ptr::null_mut()); create_interface_prototype_object(cx, @@ -2557,7 +2759,7 @@ assert!((*cache)[PrototypeList::ID::%(id)s as usize].is_null()); <*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::ID::%(id)s as isize), ptr::null_mut(), prototype.get()); -""" % properties)) +""" % proto_properties)) if self.descriptor.interface.hasInterfaceObject(): properties["name"] = str_to_const_array(name) @@ -2580,7 +2782,7 @@ rooted!(in(cx) let mut interface = ptr::null_mut()); create_noncallback_interface_object(cx, global, interface_proto.handle(), - &InterfaceObjectClass, + &INTERFACE_OBJECT_CLASS, %(static_methods)s, %(static_attrs)s, %(consts)s, @@ -2598,15 +2800,64 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null()); interface.get()); """ % properties)) + aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases] + if aliasedMembers: + def defineAlias(alias): + if alias == "@@iterator": + symbolJSID = "RUST_SYMBOL_TO_JSID(GetWellKnownSymbol(cx, SymbolCode::iterator))" + getSymbolJSID = CGGeneric(fill("rooted!(in(cx) let iteratorId = ${symbolJSID});", + symbolJSID=symbolJSID)) + defineFn = "JS_DefinePropertyById2" + prop = "iteratorId.handle()" + elif alias.startswith("@@"): + raise TypeError("Can't handle any well-known Symbol other than @@iterator") + else: + getSymbolJSID = None + defineFn = "JS_DefineProperty" + prop = '"%s"' % alias + return CGList([ + getSymbolJSID, + # XXX If we ever create non-enumerable properties that can + # be aliased, we should consider making the aliases + # match the enumerability of the property being aliased. + CGGeneric(fill( + """ + assert!(${defineFn}(cx, prototype.handle(), ${prop}, aliasedVal.handle(), + JSPROP_ENUMERATE, None, None)); + """, + defineFn=defineFn, + prop=prop)) + ], "\n") + + def defineAliasesFor(m): + return CGList([ + CGGeneric(fill( + """ + assert!(JS_GetProperty(cx, prototype.handle(), + b\"${prop}\0\" as *const u8 as *const _, + aliasedVal.handle_mut())); + """, + prop=m.identifier.name)) + ] + [defineAlias(alias) for alias in sorted(m.aliases)]) + + defineAliases = CGList([ + CGGeneric(fill(""" + // Set up aliases on the interface prototype object we just created. + + """)), + CGGeneric("rooted!(in(cx) let mut aliasedVal = UndefinedValue());\n\n") + ] + [defineAliasesFor(m) for m in sorted(aliasedMembers)]) + code.append(defineAliases) + constructors = self.descriptor.interface.namedConstructors if constructors: - decl = "let named_constructors: [(NonNullJSNative, &'static [u8], u32); %d]" % len(constructors) + decl = "let named_constructors: [(ConstructorClassHook, &'static [u8], u32); %d]" % len(constructors) specs = [] for constructor in constructors: hook = CONSTRUCT_HOOK_NAME + "_" + constructor.identifier.name name = str_to_const_array(constructor.identifier.name) length = methodLength(constructor) - specs.append(CGGeneric("(%s as NonNullJSNative, %s, %d)" % (hook, name, length))) + specs.append(CGGeneric("(%s as ConstructorClassHook, %s, %d)" % (hook, name, length))) values = CGIndenter(CGList(specs, "\n"), 4) code.append(CGWrapper(values, pre="%s = [\n" % decl, post="\n];")) code.append(CGGeneric("create_named_constructors(cx, global, &named_constructors, prototype.handle());")) @@ -2627,7 +2878,7 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null()); holderClass = "ptr::null()" holderProto = "HandleObject::null()" else: - holderClass = "&Class.base as *const js::jsapi::Class as *const JSClass" + holderClass = "&Class.base as *const JSClass" holderProto = "prototype.handle()" code.append(CGGeneric(""" rooted!(in(cx) let mut unforgeable_holder = ptr::null_mut()); @@ -2654,7 +2905,7 @@ class CGGetPerInterfaceObject(CGAbstractMethod): Argument('MutableHandleObject', 'rval')] CGAbstractMethod.__init__(self, descriptor, name, 'void', args, pub=pub, unsafe=True) - self.id = idPrefix + "::" + self.descriptor.name + self.id = idPrefix + "::" + MakeNativeName(self.descriptor.name) def definition_body(self): return CGGeneric(""" @@ -2679,7 +2930,7 @@ class CGGetProtoObjectMethod(CGGetPerInterfaceObject): """ def __init__(self, descriptor): CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject", - "PrototypeList::ID", pub=descriptor.hasDescendants()) + "PrototypeList::ID", pub=True) def definition_body(self): return CGList([ @@ -2697,7 +2948,7 @@ class CGGetConstructorObjectMethod(CGGetPerInterfaceObject): def __init__(self, descriptor): CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject", "PrototypeList::Constructor", - pub=descriptor.hasDescendants()) + pub=True) def definition_body(self): return CGList([ @@ -2797,7 +3048,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): return CGAbstractMethod.define(self) def definition_body(self): - if self.descriptor.interface.isCallback(): + if self.descriptor.interface.isCallback() or self.descriptor.interface.isNamespace(): function = "GetConstructorObject" else: function = "GetProtoObject" @@ -2828,7 +3079,7 @@ class CGCallGenerator(CGThing): exception from the native code, or None if no error reporting is needed. """ def __init__(self, errorResult, arguments, argsPre, returnType, - extendedAttributes, descriptorProvider, nativeMethodName, + extendedAttributes, descriptor, nativeMethodName, static, object="this"): CGThing.__init__(self) @@ -2836,7 +3087,7 @@ class CGCallGenerator(CGThing): isFallible = errorResult is not None - result = getRetvalDeclarationForType(returnType, descriptorProvider) + result = getRetvalDeclarationForType(returnType, descriptor) if isFallible: result = CGWrapper(result, pre="Result<", post=", Error>") @@ -2857,7 +3108,7 @@ class CGCallGenerator(CGThing): call = CGGeneric(nativeMethodName) if static: - call = CGWrapper(call, pre="%s::" % descriptorProvider.interface.identifier.name) + call = CGWrapper(call, pre="%s::" % MakeNativeName(descriptor.interface.identifier.name)) else: call = CGWrapper(call, pre="%s." % object) call = CGList([call, CGWrapper(args, pre="(", post=")")]) @@ -2936,11 +3187,21 @@ class CGPerSignatureCall(CGThing): if self.isFallible(): errorResult = " false" - cgThings.append(CGCallGenerator( - errorResult, - self.getArguments(), self.argsPre, returnType, - self.extendedAttributes, descriptor, nativeMethodName, - static)) + if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod(): + if idlNode.maplikeOrSetlikeOrIterable.isMaplike() or \ + idlNode.maplikeOrSetlikeOrIterable.isSetlike(): + raise TypeError('Maplike/Setlike methods are not supported yet') + else: + cgThings.append(CGIterableMethodGenerator(descriptor, + idlNode.maplikeOrSetlikeOrIterable, + idlNode.identifier.name)) + else: + cgThings.append(CGCallGenerator( + errorResult, + self.getArguments(), self.argsPre, returnType, + self.extendedAttributes, descriptor, nativeMethodName, + static)) + self.cgRoot = CGList(cgThings, "\n") def getArgs(self): @@ -3272,6 +3533,23 @@ JS_SetProperty(cx, target_obj.handle(), %s as *const u8 as *const libc::c_char, """ % (str_to_const_array(attrName), attrName, str_to_const_array(forwardToAttrName))) +class CGSpecializedReplaceableSetter(CGSpecializedSetter): + """ + A class for generating the code for an IDL replaceable attribute setter. + """ + def __init__(self, descriptor, attr): + CGSpecializedSetter.__init__(self, descriptor, attr) + + def definition_body(self): + assert self.attr.readonly + name = str_to_const_array(self.attr.identifier.name) + # JS_DefineProperty can only deal with ASCII. + assert all(ord(c) < 128 for c in name) + return CGGeneric("""\ +JS_DefineProperty(cx, obj, %s as *const u8 as *const libc::c_char, + args.get(0), JSPROP_ENUMERATE, None, None)""" % name) + + class CGMemberJITInfo(CGThing): """ A class for generating the JITInfo for a property that points to @@ -3384,7 +3662,8 @@ class CGMemberJITInfo(CGThing): isAlwaysInSlot, isLazilyCachedInSlot, slotIndex, [self.member.type], None) - if (not self.member.readonly or self.member.getExtendedAttribute("PutForwards")): + if (not self.member.readonly or self.member.getExtendedAttribute("PutForwards") + or self.member.getExtendedAttribute("Replaceable")): setterinfo = ("%s_setterinfo" % internalMemberName) setter = ("set_%s" % internalMemberName) # Setters are always fallible, since they have to do a typed unwrap. @@ -3826,7 +4105,7 @@ class CGUnionConversionStruct(CGThing): return ( "match %s::TryConvertTo%s(cx, value) {\n" " Err(_) => return Err(()),\n" - " Ok(Some(value)) => return Ok(%s::%s(value)),\n" + " Ok(Some(value)) => return Ok(ConversionResult::Success(%s::%s(value))),\n" " Ok(None) => (),\n" "}\n") % (self.type, name, self.type, name) @@ -3923,7 +4202,9 @@ class CGUnionConversionStruct(CGThing): method = CGWrapper( CGIndenter(CGList(conversions, "\n\n")), pre="unsafe fn from_jsval(cx: *mut JSContext,\n" - " value: HandleValue, _option: ()) -> Result<%s, ()> {\n" % self.type, + " value: HandleValue,\n" + " _option: ())\n" + " -> Result<ConversionResult<%s>, ()> {\n" % self.type, post="\n}") return CGWrapper( CGIndenter(CGList([ @@ -4306,6 +4587,8 @@ class CGProxySpecialOperation(CGPerSignatureCall): signature = operation.signatures()[0] (returnType, arguments) = signature + if operation.isGetter() and not returnType.nullable(): + returnType = IDLNullableType(returnType.location, returnType) # We pass len(arguments) as the final argument so that the # CGPerSignatureCall won't do any argument conversion of its own. @@ -4328,8 +4611,6 @@ class CGProxySpecialOperation(CGPerSignatureCall): self.cgRoot.prepend(instantiateJSToNativeConversionTemplate( template, templateValues, declType, argument.identifier.name)) self.cgRoot.prepend(CGGeneric("rooted!(in(cx) let value = desc.value);")) - elif operation.isGetter(): - self.cgRoot.prepend(CGGeneric("let mut found = false;")) def getArguments(self): def process(arg): @@ -4338,10 +4619,6 @@ class CGProxySpecialOperation(CGPerSignatureCall): argVal += ".r()" return argVal args = [(a, process(a)) for a in self.arguments] - if self.idlNode.isGetter(): - args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean], - self.idlNode), - "&mut found")) return args def wrap_return_value(self): @@ -4349,7 +4626,7 @@ class CGProxySpecialOperation(CGPerSignatureCall): return "" wrap = CGGeneric(wrapForType(**self.templateValues)) - wrap = CGIfWrapper("found", wrap) + wrap = CGIfWrapper("let Some(result) = result", wrap) return "\n" + wrap.define() @@ -4498,10 +4775,17 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod): # Once we start supporting OverrideBuiltins we need to make # ResolveOwnProperty or EnumerateOwnProperties filter out named # properties that shadow prototype properties. - namedGet = ("\n" + - "if RUST_JSID_IS_STRING(id) && !has_property_on_prototype(cx, proxy, id) {\n" + - CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" + - "}\n") + namedGet = """ +if RUST_JSID_IS_STRING(id) { + let mut has_on_proto = false; + if !has_property_on_prototype(cx, proxy, id, &mut has_on_proto) { + return false; + } + if !has_on_proto { + %s + } +} +""" % CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues), 8).define() else: namedGet = "" @@ -4718,7 +5002,7 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod): " let this = UnwrapProxy(proxy);\n" + " let this = &*this;\n" + CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" + - " *bp = found;\n" + + " *bp = result.is_some();\n" + " return true;\n" + "}\n\n") else: @@ -4726,12 +5010,20 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod): namedGetter = self.descriptor.operations['NamedGetter'] if namedGetter: - named = ("if RUST_JSID_IS_STRING(id) && !has_property_on_prototype(cx, proxy, id) {\n" + - CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" + - " *bp = found;\n" - " return true;\n" - "}\n" + - "\n") + named = """\ +if RUST_JSID_IS_STRING(id) { + let mut has_on_proto = false; + if !has_property_on_prototype(cx, proxy, id, &mut has_on_proto) { + return false; + } + if !has_on_proto { + %s + *bp = result.is_some(); + return true; + } +} + +""" % CGIndenter(CGProxyNamedGetter(self.descriptor), 8).define() else: named = "" @@ -4809,7 +5101,7 @@ if !expando.is_null() { %s let mut found = false; -if !get_property_on_prototype(cx, proxy, id, &mut found, vp) { +if !get_property_on_prototype(cx, proxy, receiver, id, &mut found, vp) { return false; } @@ -4945,7 +5237,7 @@ class CGClassFinalizeHook(CGAbstractClassHook): A hook for finalize, used to release our native object. """ def __init__(self, descriptor): - args = [Argument('*mut FreeOp', '_fop'), Argument('*mut JSObject', 'obj')] + args = [Argument('*mut JSFreeOp', '_fop'), Argument('*mut JSObject', 'obj')] CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, 'void', args) @@ -4976,6 +5268,7 @@ class CGInterfaceTrait(CGThing): def members(): for m in descriptor.interface.members: if (m.isMethod() and not m.isStatic() and + not m.isMaplikeOrSetlikeOrIterableMethod() and (not m.isIdentifierLess() or m.isStringifier())): name = CGSpecializedMethod.makeNativeName(descriptor, m) infallible = 'infallible' in descriptor.getExtendedAttributes(m) @@ -5009,7 +5302,9 @@ class CGInterfaceTrait(CGThing): infallible = 'infallible' in descriptor.getExtendedAttributes(operation) if operation.isGetter(): - arguments = method_arguments(descriptor, rettype, arguments, trailing=("found", "&mut bool")) + if not rettype.nullable(): + rettype = IDLNullableType(rettype.location, rettype) + arguments = method_arguments(descriptor, rettype, arguments) # If this interface 'supports named properties', then we # should be able to access 'supported property names' @@ -5036,6 +5331,7 @@ class CGInterfaceTrait(CGThing): post="}") else: self.cgRoot = CGGeneric("") + self.empty = not methods def define(self): return self.cgRoot.define() @@ -5051,18 +5347,261 @@ class CGWeakReferenceableTrait(CGThing): return self.code +def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries=None, enums=None): + if not callbacks: + callbacks = [] + if not dictionaries: + dictionaries = [] + if not enums: + enums = [] + + return CGImports(cgthings, descriptors, callbacks, dictionaries, enums, [ + 'core::nonzero::NonZero', + 'js', + 'js::JSCLASS_GLOBAL_SLOT_COUNT', + 'js::JSCLASS_IS_DOMJSCLASS', + 'js::JSCLASS_IS_GLOBAL', + 'js::JSCLASS_RESERVED_SLOTS_MASK', + 'js::JS_CALLEE', + 'js::error::throw_type_error', + 'js::jsapi::AutoIdVector', + 'js::jsapi::Call', + 'js::jsapi::CallArgs', + 'js::jsapi::FreeOp', + 'js::jsapi::GetPropertyKeys', + 'js::jsapi::GetWellKnownSymbol', + 'js::jsapi::Handle', + 'js::jsapi::HandleId', + 'js::jsapi::HandleObject', + 'js::jsapi::HandleValue', + 'js::jsapi::HandleValueArray', + 'js::jsapi::INTERNED_STRING_TO_JSID', + 'js::jsapi::IsCallable', + 'js::jsapi::JSAutoCompartment', + 'js::jsapi::JSCLASS_RESERVED_SLOTS_SHIFT', + 'js::jsapi::JSClass', + 'js::jsapi::JSContext', + 'js::jsapi::JSFreeOp', + 'js::jsapi::JSFunctionSpec', + 'js::jsapi::JSITER_HIDDEN', + 'js::jsapi::JSITER_OWNONLY', + 'js::jsapi::JSITER_SYMBOLS', + 'js::jsapi::JSJitGetterCallArgs', + 'js::jsapi::JSJitInfo', + 'js::jsapi::JSJitInfo_AliasSet', + 'js::jsapi::JSJitInfo_ArgType', + 'js::jsapi::JSJitInfo_OpType', + 'js::jsapi::JSJitMethodCallArgs', + 'js::jsapi::JSJitSetterCallArgs', + 'js::jsapi::JSNative', + 'js::jsapi::JSNativeWrapper', + 'js::jsapi::JSObject', + 'js::jsapi::JSPROP_ENUMERATE', + 'js::jsapi::JSPROP_PERMANENT', + 'js::jsapi::JSPROP_READONLY', + 'js::jsapi::JSPROP_SHARED', + 'js::jsapi::JSPropertySpec', + 'js::jsapi::JSString', + 'js::jsapi::JSTracer', + 'js::jsapi::JSType', + 'js::jsapi::JSTypedMethodJitInfo', + 'js::jsapi::JSValueType', + 'js::jsapi::JS_AtomizeAndPinString', + 'js::jsapi::JS_CallFunctionValue', + 'js::jsapi::JS_CopyPropertiesFrom', + 'js::jsapi::JS_DefineProperty', + 'js::jsapi::JS_DefinePropertyById2', + 'js::jsapi::JS_ForwardGetPropertyTo', + 'js::jsapi::JS_GetClass', + 'js::jsapi::JS_GetErrorPrototype', + 'js::jsapi::JS_GetFunctionPrototype', + 'js::jsapi::JS_GetGlobalForObject', + 'js::jsapi::JS_GetIteratorPrototype', + 'js::jsapi::JS_GetObjectPrototype', + 'js::jsapi::JS_GetProperty', + 'js::jsapi::JS_GetPropertyById', + 'js::jsapi::JS_GetPropertyDescriptorById', + 'js::jsapi::JS_GetReservedSlot', + 'js::jsapi::JS_HasProperty', + 'js::jsapi::JS_HasPropertyById', + 'js::jsapi::JS_InitializePropertiesFromCompatibleNativeObject', + 'js::jsapi::JS_NewObject', + 'js::jsapi::JS_NewObjectWithGivenProto', + 'js::jsapi::JS_NewObjectWithoutMetadata', + 'js::jsapi::JS_SetImmutablePrototype', + 'js::jsapi::JS_SetProperty', + 'js::jsapi::JS_SetReservedSlot', + 'js::jsapi::JS_SplicePrototype', + 'js::jsapi::MutableHandle', + 'js::jsapi::MutableHandleObject', + 'js::jsapi::MutableHandleValue', + 'js::jsapi::ObjectOpResult', + 'js::jsapi::PropertyDescriptor', + 'js::jsapi::RootedObject', + 'js::jsapi::SymbolCode', + 'js::jsapi::jsid', + 'js::jsval::JSVal', + 'js::jsval::NullValue', + 'js::jsval::ObjectValue', + 'js::jsval::ObjectOrNullValue', + 'js::jsval::PrivateValue', + 'js::jsval::UndefinedValue', + 'js::glue::AppendToAutoIdVector', + 'js::glue::CallJitGetterOp', + 'js::glue::CallJitMethodOp', + 'js::glue::CallJitSetterOp', + 'js::glue::CreateProxyHandler', + 'js::glue::GetProxyPrivate', + 'js::glue::NewProxyObject', + 'js::glue::ProxyTraps', + 'js::glue::RUST_JSID_IS_STRING', + 'js::glue::RUST_SYMBOL_TO_JSID', + 'js::glue::int_to_jsid', + 'js::rust::GCMethods', + 'js::rust::define_methods', + 'js::rust::define_properties', + 'dom', + 'dom::bindings', + 'dom::bindings::codegen::InterfaceObjectMap', + 'dom::bindings::constant::ConstantSpec', + 'dom::bindings::constant::ConstantVal', + 'dom::bindings::global::GlobalRef', + 'dom::bindings::global::global_root_from_object', + 'dom::bindings::global::global_root_from_reflector', + 'dom::bindings::interface::ConstructorClassHook', + 'dom::bindings::interface::InterfaceConstructorBehavior', + 'dom::bindings::interface::NonCallbackInterfaceObjectClass', + 'dom::bindings::interface::create_callback_interface_object', + 'dom::bindings::interface::create_global_object', + 'dom::bindings::interface::create_interface_prototype_object', + 'dom::bindings::interface::create_named_constructors', + 'dom::bindings::interface::create_noncallback_interface_object', + 'dom::bindings::interface::define_guarded_constants', + 'dom::bindings::interface::define_guarded_methods', + 'dom::bindings::interface::define_guarded_properties', + 'dom::bindings::interface::is_exposed_in', + 'dom::bindings::iterable::Iterable', + 'dom::bindings::iterable::IteratorType', + 'dom::bindings::js::JS', + 'dom::bindings::js::OptionalRootedReference', + 'dom::bindings::js::Root', + 'dom::bindings::js::RootedReference', + 'dom::bindings::namespace::NamespaceObjectClass', + 'dom::bindings::namespace::create_namespace_object', + 'dom::bindings::reflector::MutReflectable', + 'dom::bindings::reflector::Reflectable', + 'dom::bindings::utils::DOMClass', + 'dom::bindings::utils::DOMJSClass', + 'dom::bindings::utils::DOM_PROTO_UNFORGEABLE_HOLDER_SLOT', + 'dom::bindings::utils::JSCLASS_DOM_GLOBAL', + 'dom::bindings::utils::ProtoOrIfaceArray', + 'dom::bindings::utils::enumerate_global', + 'dom::bindings::utils::finalize_global', + 'dom::bindings::utils::find_enum_string_index', + 'dom::bindings::utils::generic_getter', + 'dom::bindings::utils::generic_lenient_getter', + 'dom::bindings::utils::generic_lenient_setter', + 'dom::bindings::utils::generic_method', + 'dom::bindings::utils::generic_setter', + 'dom::bindings::utils::get_array_index_from_id', + 'dom::bindings::utils::get_dictionary_property', + 'dom::bindings::utils::get_property_on_prototype', + 'dom::bindings::utils::get_proto_or_iface_array', + 'dom::bindings::utils::has_property_on_prototype', + 'dom::bindings::utils::is_platform_object', + 'dom::bindings::utils::resolve_global', + 'dom::bindings::utils::set_dictionary_property', + 'dom::bindings::utils::trace_global', + 'dom::bindings::trace::JSTraceable', + 'dom::bindings::trace::RootedTraceable', + 'dom::bindings::callback::CallSetup', + 'dom::bindings::callback::CallbackContainer', + 'dom::bindings::callback::CallbackInterface', + 'dom::bindings::callback::CallbackFunction', + 'dom::bindings::callback::ExceptionHandling', + 'dom::bindings::callback::wrap_call_this_object', + 'dom::bindings::conversions::ConversionBehavior', + 'dom::bindings::conversions::ConversionResult', + 'dom::bindings::conversions::DOM_OBJECT_SLOT', + 'dom::bindings::conversions::FromJSValConvertible', + 'dom::bindings::conversions::IDLInterface', + 'dom::bindings::conversions::StringificationBehavior', + 'dom::bindings::conversions::ToJSValConvertible', + 'dom::bindings::conversions::is_array_like', + 'dom::bindings::conversions::jsid_to_str', + 'dom::bindings::conversions::native_from_handlevalue', + 'dom::bindings::conversions::native_from_object', + 'dom::bindings::conversions::private_from_object', + 'dom::bindings::conversions::root_from_handleobject', + 'dom::bindings::conversions::root_from_handlevalue', + 'dom::bindings::conversions::root_from_object', + 'dom::bindings::codegen::PrototypeList', + 'dom::bindings::codegen::RegisterBindings', + 'dom::bindings::codegen::UnionTypes', + 'dom::bindings::error::Error', + 'dom::bindings::error::ErrorResult', + 'dom::bindings::error::Fallible', + 'dom::bindings::error::Error::JSFailed', + 'dom::bindings::error::throw_dom_exception', + 'dom::bindings::guard::Condition', + 'dom::bindings::guard::Guard', + 'dom::bindings::proxyhandler', + 'dom::bindings::proxyhandler::ensure_expando_object', + 'dom::bindings::proxyhandler::fill_property_descriptor', + 'dom::bindings::proxyhandler::get_expando_object', + 'dom::bindings::proxyhandler::get_property_descriptor', + 'dom::bindings::num::Finite', + 'dom::bindings::str::ByteString', + 'dom::bindings::str::DOMString', + 'dom::bindings::str::USVString', + 'dom::bindings::weakref::DOM_WEAK_SLOT', + 'dom::bindings::weakref::WeakBox', + 'dom::bindings::weakref::WeakReferenceable', + 'dom::browsingcontext::BrowsingContext', + 'mem::heap_size_of_raw_self_and_children', + 'libc', + 'util::prefs::PREFS', + 'script_runtime::maybe_take_panic_result', + 'script_runtime::store_panic_result', + 'std::borrow::ToOwned', + 'std::cmp', + 'std::mem', + 'std::num', + 'std::os', + 'std::panic', + 'std::panic::AssertUnwindSafe', + 'std::ptr', + 'std::str', + 'std::rc', + 'std::rc::Rc', + 'std::default::Default', + 'std::ffi::CString', + ], config) + + class CGDescriptor(CGThing): - def __init__(self, descriptor): + def __init__(self, descriptor, config, soleDescriptor): CGThing.__init__(self) assert not descriptor.concrete or not descriptor.interface.isCallback() + reexports = [] + + def reexportedName(name): + if name.startswith(descriptor.name): + return name + if not soleDescriptor: + return '%s as %s%s' % (name, descriptor.name, name) + return name + cgThings = [] - if not descriptor.interface.isCallback(): + if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace(): cgThings.append(CGGetProtoObjectMethod(descriptor)) + reexports.append('GetProtoObject') if (descriptor.interface.hasInterfaceObject() and descriptor.shouldHaveGetConstructorObjectMethod()): cgThings.append(CGGetConstructorObjectMethod(descriptor)) + reexports.append('GetConstructorObject') unscopableNames = [] for m in descriptor.interface.members: @@ -5099,6 +5638,8 @@ class CGDescriptor(CGThing): cgThings.append(CGSpecializedSetter(descriptor, m)) elif m.getExtendedAttribute("PutForwards"): cgThings.append(CGSpecializedForwardingSetter(descriptor, m)) + elif m.getExtendedAttribute("Replaceable"): + cgThings.append(CGSpecializedReplaceableSetter(descriptor, m)) if (not m.isStatic() and not descriptor.interface.isCallback()): cgThings.append(CGMemberJITInfo(descriptor, m)) @@ -5115,7 +5656,7 @@ class CGDescriptor(CGThing): if not descriptor.interface.isCallback(): cgThings.append(CGInterfaceObjectJSClass(descriptor)) - if not descriptor.interface.isCallback(): + if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace(): cgThings.append(CGPrototypeJSClass(descriptor)) # If there are no constant members, don't make a module for constants @@ -5124,14 +5665,18 @@ class CGDescriptor(CGThing): cgThings.append(CGNamespace.build([descriptor.name + "Constants"], CGConstant(constMembers), public=True)) + reexports.append(descriptor.name + 'Constants') - if descriptor.interface.hasInterfaceObject(): + if descriptor.interface.hasInterfaceObject() and descriptor.register: cgThings.append(CGDefineDOMInterfaceMethod(descriptor)) + reexports.append('DefineDOMInterface') cgThings.append(CGConstructorEnabled(descriptor)) if descriptor.proxy: cgThings.append(CGDefineProxyHandler(descriptor)) + properties = PropertyArrays(descriptor) + if descriptor.concrete: if descriptor.proxy: # cgThings.append(CGProxyIsProxy(descriptor)) @@ -5161,10 +5706,14 @@ class CGDescriptor(CGThing): cgThings.append(CGDOMJSClass(descriptor)) pass - cgThings.append(CGWrapMethod(descriptor)) + if descriptor.isGlobal(): + cgThings.append(CGWrapGlobalMethod(descriptor, properties)) + else: + cgThings.append(CGWrapMethod(descriptor)) + reexports.append('Wrap') haveUnscopables = False - if not descriptor.interface.isCallback(): + if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace(): if unscopableNames: haveUnscopables = True cgThings.append( @@ -5174,19 +5723,25 @@ class CGDescriptor(CGThing): CGGeneric("];\n")], "\n")) if descriptor.concrete or descriptor.hasDescendants(): cgThings.append(CGIDLInterface(descriptor)) - cgThings.append(CGInterfaceTrait(descriptor)) + + interfaceTrait = CGInterfaceTrait(descriptor) + cgThings.append(interfaceTrait) + if not interfaceTrait.empty: + reexports.append('%sMethods' % descriptor.name) + if descriptor.weakReferenceable: cgThings.append(CGWeakReferenceableTrait(descriptor)) - properties = PropertyArrays(descriptor) cgThings.append(CGGeneric(str(properties))) cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, haveUnscopables)) - cgThings = CGList(cgThings, "\n") - # self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name), - # cgThings), - # post='\n') - self.cgRoot = cgThings + cgThings = generate_imports(config, CGList(cgThings, '\n'), [descriptor]) + cgThings = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name), + cgThings, public=True), + post='\n') + reexports = ', '.join(map(lambda name: reexportedName(name), reexports)) + self.cgRoot = CGList([CGGeneric('pub use self::%s::{%s};' % (toBindingNamespace(descriptor.name), reexports)), + cgThings], '\n') def define(self): return self.cgRoot.define() @@ -5271,9 +5826,14 @@ class CGDictionary(CGThing): def impl(self): d = self.dictionary if d.parent: - initParent = "parent: try!(%s::%s::new(cx, val)),\n" % ( - self.makeModuleName(d.parent), - self.makeClassName(d.parent)) + initParent = ("parent: match try!(%s::%s::new(cx, val)) {\n" + " ConversionResult::Success(v) => v,\n" + " ConversionResult::Failure(error) => {\n" + " throw_type_error(cx, &error);\n" + " return Err(());\n" + " }\n" + " },\n" % (self.makeModuleName(d.parent), + self.makeClassName(d.parent))) else: initParent = "" @@ -5307,9 +5867,13 @@ class CGDictionary(CGThing): return string.Template( "impl ${selfName} {\n" " pub unsafe fn empty(cx: *mut JSContext) -> ${selfName} {\n" - " ${selfName}::new(cx, HandleValue::null()).unwrap()\n" + " match ${selfName}::new(cx, HandleValue::null()) {\n" + " Ok(ConversionResult::Success(v)) => v,\n" + " _ => unreachable!(),\n" + " }\n" " }\n" - " pub unsafe fn new(cx: *mut JSContext, val: HandleValue) -> Result<${selfName}, ()> {\n" + " pub unsafe fn new(cx: *mut JSContext, val: HandleValue) \n" + " -> Result<ConversionResult<${selfName}>, ()> {\n" " let object = if val.get().is_null_or_undefined() {\n" " ptr::null_mut()\n" " } else if val.get().is_object() {\n" @@ -5319,17 +5883,17 @@ class CGDictionary(CGThing): " return Err(());\n" " };\n" " rooted!(in(cx) let object = object);\n" - " Ok(${selfName} {\n" + " Ok(ConversionResult::Success(${selfName} {\n" "${initParent}" "${initMembers}" - " })\n" + " }))\n" " }\n" "}\n" "\n" "impl FromJSValConvertible for ${selfName} {\n" " type Config = ();\n" " unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ())\n" - " -> Result<${selfName}, ()> {\n" + " -> Result<ConversionResult<${selfName}>, ()> {\n" " ${selfName}::new(cx, value)\n" " }\n" "}\n" @@ -5427,8 +5991,8 @@ class CGRegisterProxyHandlersMethod(CGAbstractMethod): def definition_body(self): return CGList([ - CGGeneric("proxy_handlers[Proxies::%s as usize] = codegen::Bindings::%sBinding::DefineProxyHandler();" - % (desc.name, desc.name)) + CGGeneric("proxy_handlers[Proxies::%s as usize] = Bindings::%s::DefineProxyHandler();" + % (desc.name, '::'.join([desc.name + 'Binding'] * 2))) for desc in self.descriptors ], "\n") @@ -5501,7 +6065,7 @@ class CGBindingRoot(CGThing): for c in mainCallbacks) # Do codegen for all the descriptors - cgthings.extend([CGDescriptor(x) for x in descriptors]) + cgthings.extend([CGDescriptor(x, config, len(descriptors) == 1) for x in descriptors]) # Do codegen for all the callback interfaces. cgthings.extend(CGList([CGCallbackInterface(x), @@ -5512,102 +6076,8 @@ class CGBindingRoot(CGThing): curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") # Add imports - curr = CGImports(curr, descriptors + callbackDescriptors, mainCallbacks, [ - 'js', - 'js::{JS_CALLEE, JSCLASS_GLOBAL_SLOT_COUNT}', - 'js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_MASK}', - 'js::error::throw_type_error', - 'js::jsapi::{JSJitInfo_AliasSet, JSJitInfo_ArgType, AutoIdVector, CallArgs, FreeOp}', - 'js::jsapi::{JSITER_SYMBOLS, JSPROP_ENUMERATE, JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_SHARED}', - 'js::jsapi::{JSCLASS_RESERVED_SLOTS_SHIFT, JSITER_HIDDEN, JSITER_OWNONLY}', - 'js::jsapi::{GetPropertyKeys, Handle}', - 'js::jsapi::{HandleId, HandleObject, HandleValue, HandleValueArray}', - 'js::jsapi::{INTERNED_STRING_TO_JSID, IsCallable, JS_CallFunctionValue}', - 'js::jsapi::{JS_CopyPropertiesFrom, JS_ForwardGetPropertyTo}', - 'js::jsapi::{JS_GetClass, JS_GetErrorPrototype, JS_GetFunctionPrototype}', - 'js::jsapi::{JS_GetGlobalForObject, JS_GetObjectPrototype, JS_GetProperty}', - 'js::jsapi::{JS_GetPropertyById, JS_GetPropertyDescriptorById, JS_GetReservedSlot}', - 'js::jsapi::{JS_HasProperty, JS_HasPropertyById, JS_InitializePropertiesFromCompatibleNativeObject}', - 'js::jsapi::{JS_AtomizeAndPinString, JS_NewObject, JS_NewObjectWithGivenProto}', - 'js::jsapi::{JS_NewObjectWithoutMetadata, JS_SetProperty}', - 'js::jsapi::{JS_SetPrototype, JS_SetReservedSlot, JSAutoCompartment}', - 'js::jsapi::{JSContext, JSClass, JSFreeOp, JSFunctionSpec}', - 'js::jsapi::{JSJitGetterCallArgs, JSJitInfo, JSJitMethodCallArgs, JSJitSetterCallArgs}', - 'js::jsapi::{JSNative, JSObject, JSNativeWrapper, JSPropertySpec}', - 'js::jsapi::{JSString, JSTracer, JSType, JSTypedMethodJitInfo, JSValueType}', - 'js::jsapi::{ObjectOpResult, JSJitInfo_OpType, MutableHandle, MutableHandleObject}', - 'js::jsapi::{MutableHandleValue, PropertyDescriptor, RootedObject}', - 'js::jsapi::{SymbolCode, jsid}', - 'js::jsval::JSVal', - 'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}', - 'js::jsval::{NullValue, UndefinedValue}', - 'js::glue::{CallJitMethodOp, CallJitGetterOp, CallJitSetterOp, CreateProxyHandler}', - 'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}', - 'js::glue::{RUST_JSID_IS_STRING, int_to_jsid}', - 'js::glue::AppendToAutoIdVector', - 'js::rust::{GCMethods, define_methods, define_properties}', - 'dom::bindings', - 'dom::bindings::codegen::InterfaceObjectMap', - 'dom::bindings::global::{GlobalRef, global_root_from_object, global_root_from_reflector}', - 'dom::bindings::interface::{InterfaceConstructorBehavior, NonCallbackInterfaceObjectClass}', - 'dom::bindings::interface::{create_callback_interface_object, create_interface_prototype_object}', - 'dom::bindings::interface::{create_named_constructors, create_noncallback_interface_object}', - 'dom::bindings::interface::{define_guarded_methods, define_guarded_properties}', - 'dom::bindings::interface::{ConstantSpec, NonNullJSNative}', - 'dom::bindings::interface::ConstantVal::{IntVal, UintVal}', - 'dom::bindings::interface::is_exposed_in', - 'dom::bindings::js::{JS, Root, RootedReference}', - 'dom::bindings::js::{OptionalRootedReference}', - 'dom::bindings::reflector::{Reflectable}', - 'dom::bindings::utils::{DOMClass, DOMJSClass}', - 'dom::bindings::utils::{DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, JSCLASS_DOM_GLOBAL}', - 'dom::bindings::utils::{ProtoOrIfaceArray, create_dom_global}', - 'dom::bindings::utils::{enumerate_global, finalize_global, find_enum_string_index}', - 'dom::bindings::utils::{generic_getter, generic_lenient_getter, generic_lenient_setter}', - 'dom::bindings::utils::{generic_method, generic_setter, get_array_index_from_id}', - 'dom::bindings::utils::{get_dictionary_property, get_property_on_prototype}', - 'dom::bindings::utils::{get_proto_or_iface_array, has_property_on_prototype}', - 'dom::bindings::utils::{is_platform_object, resolve_global, set_dictionary_property, trace_global}', - 'dom::bindings::trace::{JSTraceable, RootedTraceable}', - 'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}', - 'dom::bindings::callback::{CallSetup,ExceptionHandling}', - 'dom::bindings::callback::wrap_call_this_object', - 'dom::bindings::conversions::{ConversionBehavior, DOM_OBJECT_SLOT}', - 'dom::bindings::conversions::{IDLInterface, is_array_like}', - 'dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}', - 'dom::bindings::conversions::{ToJSValConvertible, jsid_to_str, native_from_handlevalue}', - 'dom::bindings::conversions::{native_from_object, private_from_object, root_from_object}', - 'dom::bindings::conversions::{root_from_handleobject, root_from_handlevalue}', - 'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}', - 'dom::bindings::codegen::Bindings::*', - 'dom::bindings::error::{Fallible, Error, ErrorResult}', - 'dom::bindings::error::Error::JSFailed', - 'dom::bindings::error::throw_dom_exception', - 'dom::bindings::guard::{Condition, Guard}', - 'dom::bindings::proxyhandler', - 'dom::bindings::proxyhandler::{ensure_expando_object, fill_property_descriptor}', - 'dom::bindings::proxyhandler::{get_expando_object, get_property_descriptor}', - 'dom::bindings::num::Finite', - 'dom::bindings::str::{ByteString, DOMString, USVString}', - 'dom::bindings::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable}', - 'dom::browsingcontext::BrowsingContext', - 'mem::heap_size_of_raw_self_and_children', - 'libc', - 'util::prefs::PREFS', - 'script_runtime::{store_panic_result, maybe_take_panic_result}', - 'std::borrow::ToOwned', - 'std::cmp', - 'std::mem', - 'std::num', - 'std::os', - 'std::panic::{self, AssertUnwindSafe}', - 'std::ptr', - 'std::str', - 'std::rc', - 'std::rc::Rc', - 'std::default::Default', - 'std::ffi::CString', - ], config) + curr = generate_imports(config, curr, callbackDescriptors, mainCallbacks, + dictionaries, enums) # Add the auto-generated comment. curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) @@ -6071,7 +6541,8 @@ class CallbackMethod(CallbackMember): def getCall(self): replacements = { "thisObj": self.getThisObj(), - "getCallable": self.getCallableDecl() + "getCallable": self.getCallableDecl(), + "callGuard": self.getCallGuard(), } if self.argCount > 0: replacements["argv"] = "argv.as_ptr()" @@ -6082,7 +6553,7 @@ class CallbackMethod(CallbackMember): return string.Template( "${getCallable}" "rooted!(in(cx) let rootedThis = ${thisObj});\n" - "let ok = JS_CallFunctionValue(\n" + "let ok = ${callGuard}JS_CallFunctionValue(\n" " cx, rootedThis.handle(), callable.handle(),\n" " &HandleValueArray {\n" " length_: ${argc} as ::libc::size_t,\n" @@ -6098,6 +6569,7 @@ class CallbackMethod(CallbackMember): class CallCallback(CallbackMethod): def __init__(self, callback, descriptorProvider): + self.callback = callback CallbackMethod.__init__(self, callback.signatures()[0], "Call", descriptorProvider, needThisHandling=True) @@ -6107,6 +6579,11 @@ class CallCallback(CallbackMethod): def getCallableDecl(self): return "rooted!(in(cx) let callable = ObjectValue(&*self.parent.callback()));\n" + def getCallGuard(self): + if self.callback._treatNonObjectAsNull: + return "!IsCallable(self.parent.callback()) || " + return "" + class CallbackOperationBase(CallbackMethod): """ @@ -6142,6 +6619,9 @@ class CallbackOperationBase(CallbackMethod): CGGeneric('ObjectValue(&*self.parent.callback())'), CGGeneric(getCallableFromProp))).define() + ');\n') + def getCallGuard(self): + return "" + class CallbackOperation(CallbackOperationBase): """ @@ -6209,6 +6689,53 @@ class CallbackSetter(CallbackMember): return None +class CGIterableMethodGenerator(CGGeneric): + """ + Creates methods for iterable interfaces. Unwrapping/wrapping + will be taken care of by the usual method generation machinery in + CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of + using CGCallGenerator. + """ + def __init__(self, descriptor, iterable, methodName): + if methodName == "forEach": + CGGeneric.__init__(self, fill( + """ + if !IsCallable(arg0) { + throw_type_error(cx, "Argument 1 of ${ifaceName}.forEach is not callable."); + return false; + } + rooted!(in(cx) let arg0 = ObjectValue(&*arg0)); + rooted!(in(cx) let mut call_arg1 = UndefinedValue()); + rooted!(in(cx) let mut call_arg2 = UndefinedValue()); + let mut call_args = vec![UndefinedValue(), UndefinedValue(), ObjectValue(&**_obj)]; + rooted!(in(cx) let mut ignoredReturnVal = UndefinedValue()); + for i in 0..(*this).get_iterable_length() { + (*this).get_value_at_index(i).to_jsval(cx, call_arg1.handle_mut()); + (*this).get_key_at_index(i).to_jsval(cx, call_arg2.handle_mut()); + call_args[0] = call_arg1.handle().get(); + call_args[1] = call_arg2.handle().get(); + let call_args = HandleValueArray { length_: 3, elements_: call_args.as_ptr() }; + if !Call(cx, arg1, arg0.handle(), &call_args, + ignoredReturnVal.handle_mut()) { + return false; + } + } + + let result = (); + """, + ifaceName=descriptor.interface.identifier.name)) + return + CGGeneric.__init__(self, fill( + """ + let result = ${iterClass}::new(&*this, + IteratorType::${itrMethod}, + super::${ifaceName}IteratorBinding::Wrap); + """, + iterClass=iteratorNativeType(descriptor, True), + ifaceName=descriptor.interface.identifier.name, + itrMethod=methodName.title())) + + def camel_to_upper_snake(s): return "_".join(m.group(0).upper() for m in re.finditer("[A-Z][a-z]*", s)) @@ -6245,12 +6772,12 @@ class GlobalGenRoots(): pairs = [] for d in config.getDescriptors(hasInterfaceObject=True): binding = toBindingNamespace(d.name) - pairs.append((d.name, binding)) + pairs.append((d.name, binding, binding)) for ctor in d.interface.namedConstructors: - pairs.append((ctor.identifier.name, binding)) + pairs.append((ctor.identifier.name, binding, binding)) pairs.sort(key=operator.itemgetter(0)) mappings = [ - CGGeneric('b"%s" => codegen::Bindings::%s::DefineDOMInterface as unsafe fn(_, _),' % pair) + CGGeneric('b"%s" => codegen::Bindings::%s::%s::DefineDOMInterface as unsafe fn(_, _),' % pair) for pair in pairs ] mapType = "phf::Map<&'static [u8], unsafe fn(*mut JSContext, HandleObject)>" @@ -6267,10 +6794,12 @@ class GlobalGenRoots(): @staticmethod def PrototypeList(config): # Prototype ID enum. - interfaces = config.getDescriptors(isCallback=False) + interfaces = config.getDescriptors(isCallback=False, isNamespace=False) protos = [d.name for d in interfaces] - constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True) - if d.shouldHaveGetConstructorObjectMethod()] + constructors = sorted([MakeNativeName(d.name) + for d in config.getDescriptors(hasInterfaceObject=True) + if d.shouldHaveGetConstructorObjectMethod()]) + proxies = [d.name for d in config.getDescriptors(proxy=True)] return CGList([ @@ -6299,25 +6828,34 @@ class GlobalGenRoots(): CGRegisterProxyHandlers(config), ], "\n") - return CGImports(code, [], [], [ - 'dom::bindings::codegen', + return CGImports(code, descriptors=[], callbacks=[], dictionaries=[], enums=[], imports=[ + 'dom::bindings::codegen::Bindings', 'dom::bindings::codegen::PrototypeList::Proxies', 'libc', - ], config, ignored_warnings=[]) + ], config=config, ignored_warnings=[]) @staticmethod def InterfaceTypes(config): - descriptors = [d.name for d in config.getDescriptors(register=True, isCallback=False)] - curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), name)) for name in descriptors]) + descriptors = sorted([MakeNativeName(d.name) + for d in config.getDescriptors(register=True, + isCallback=False, + isIteratorInterface=False)]) + curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), + MakeNativeName(name))) + for name in descriptors]) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) return curr @staticmethod def Bindings(config): - descriptors = (set(d.name + "Binding" for d in config.getDescriptors(register=True)) | - set(getModuleFromObject(d) for d in config.callbacks) | - set(getModuleFromObject(d) for d in config.getDictionaries())) + def leafModule(d): + return getModuleFromObject(d).split('::')[-1] + + descriptors = config.getDescriptors(register=True, isIteratorInterface=False) + descriptors = (set(toBindingNamespace(d.name) for d in descriptors) | + set(leafModule(d) for d in config.callbacks) | + set(leafModule(d) for d in config.getDictionaries())) curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)]) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) return curr @@ -6417,6 +6955,7 @@ impl %(base)s { curr = UnionTypes(config.getDescriptors(), config.getDictionaries(), config.getCallbacks(), + config.typedefs, config) # Add the auto-generated comment. diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py index 4074736a462..6e71bd4bd00 100644 --- a/components/script/dom/bindings/codegen/Configuration.py +++ b/components/script/dom/bindings/codegen/Configuration.py @@ -4,7 +4,7 @@ import os -from WebIDL import IDLExternalInterface, IDLInterface, WebIDLError +from WebIDL import IDLExternalInterface, IDLWrapperType, WebIDLError class Configuration: @@ -30,10 +30,9 @@ class Configuration: raise WebIDLError("Servo does not support external interfaces.", [thing.location]) - # Some toplevel things are sadly types, and those have an - # isInterface that doesn't mean the same thing as IDLObject's - # isInterface()... - if not isinstance(thing, IDLInterface): + assert not thing.isType() + + if not thing.isInterface() and not thing.isNamespace(): continue iface = thing @@ -83,12 +82,16 @@ class Configuration: getter = lambda x: x.interface.hasInterfaceObject() elif key == 'isCallback': getter = lambda x: x.interface.isCallback() + elif key == 'isNamespace': + getter = lambda x: x.interface.isNamespace() elif key == 'isJSImplemented': getter = lambda x: x.interface.isJSImplemented() elif key == 'isGlobal': getter = lambda x: x.isGlobal() elif key == 'isExposedConditionally': getter = lambda x: x.interface.isExposedConditionally() + elif key == 'isIteratorInterface': + getter = lambda x: x.interface.isIteratorInterface() else: getter = lambda x: getattr(x, key) curr = filter(lambda x: getter(x) == val, curr) @@ -177,13 +180,26 @@ class Descriptor(DescriptorProvider): # Read the desc, and fill in the relevant defaults. ifaceName = self.interface.identifier.name - typeName = desc.get('nativeType', ifaceName) + nativeTypeDefault = ifaceName + + # For generated iterator interfaces for other iterable interfaces, we + # just use IterableIterator as the native type, templated on the + # nativeType of the iterable interface. That way we can have a + # templated implementation for all the duplicated iterator + # functionality. + if self.interface.isIteratorInterface(): + itrName = self.interface.iterableInterface.identifier.name + itrDesc = self.getDescriptor(itrName) + nativeTypeDefault = iteratorNativeType(itrDesc) + + typeName = desc.get('nativeType', nativeTypeDefault) # Callback types do not use JS smart pointers, so we should not use the # built-in rooting mechanisms for them. if self.interface.isCallback(): self.needsRooting = False - ty = "%sBinding::%s" % (ifaceName, ifaceName) + ty = 'dom::bindings::codegen::Bindings::%sBinding::%s' % (ifaceName, ifaceName) + pathDefault = ty self.returnType = "Rc<%s>" % ty self.argumentType = "???" self.nativeType = ty @@ -192,10 +208,15 @@ class Descriptor(DescriptorProvider): self.returnType = "Root<%s>" % typeName self.argumentType = "&%s" % typeName self.nativeType = "*const %s" % typeName + if self.interface.isIteratorInterface(): + pathDefault = 'dom::bindings::iterable::IterableIterator' + else: + pathDefault = 'dom::types::%s' % MakeNativeName(typeName) self.concreteType = typeName self.register = desc.get('register', True) - self.path = desc.get('path', 'dom::types::%s' % typeName) + self.path = desc.get('path', pathDefault) + self.bindingPath = 'dom::bindings::codegen::Bindings::%s' % ('::'.join([ifaceName + 'Binding'] * 2)) self.outerObjectHook = desc.get('outerObjectHook', 'None') self.proxy = False self.weakReferenceable = desc.get('weakReferenceable', False) @@ -203,6 +224,7 @@ class Descriptor(DescriptorProvider): # If we're concrete, we need to crawl our ancestor interfaces and mark # them as having a concrete descendant. self.concrete = (not self.interface.isCallback() and + not self.interface.isNamespace() and not self.interface.getExtendedAttribute("Abstract")) self.hasUnforgeableMembers = (self.concrete and any(MemberIsUnforgeable(m, self) for m in @@ -361,7 +383,7 @@ class Descriptor(DescriptorProvider): def shouldHaveGetConstructorObjectMethod(self): assert self.interface.hasInterfaceObject() - return self.interface.isCallback() or self.hasDescendants() + return self.interface.isCallback() or self.interface.isNamespace() or self.hasDescendants() def isExposedConditionally(self): return self.interface.isExposedConditionally() @@ -376,8 +398,15 @@ class Descriptor(DescriptorProvider): # Some utility methods + + +def MakeNativeName(name): + return name[0].upper() + name[1:] + + def getModuleFromObject(object): - return os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding' + return ('dom::bindings::codegen::Bindings::' + + os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding') def getTypesFromDescriptor(descriptor): @@ -404,6 +433,8 @@ def getTypesFromDictionary(dictionary): """ Get all member types for this dictionary """ + if isinstance(dictionary, IDLWrapperType): + dictionary = dictionary.inner types = [] curDict = dictionary while curDict: @@ -421,3 +452,10 @@ def getTypesFromCallback(callback): types = [sig[0]] # Return type types.extend(arg.type for arg in sig[1]) # Arguments return types + + +def iteratorNativeType(descriptor, infer=False): + assert descriptor.interface.isIterable() + iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable + assert iterableDecl.isPairIterator() + return "IterableIterator%s" % ("" if infer else '<%s>' % descriptor.interface.identifier.name) diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py index 54d510781a1..878c221f01c 100644 --- a/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -1261,10 +1261,7 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): member.getExtendedAttribute("ChromeOnly") or member.getExtendedAttribute("Pref") or member.getExtendedAttribute("Func") or - member.getExtendedAttribute("SecureContext") or - member.getExtendedAttribute("AvailableIn") or - member.getExtendedAttribute("CheckAnyPermissions") or - member.getExtendedAttribute("CheckAllPermissions")): + member.getExtendedAttribute("SecureContext")): raise WebIDLError("[Alias] must not be used on a " "conditionally exposed operation", [member.location]) @@ -1290,17 +1287,11 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): alias, [member.location, m.location]) - for attribute in ["CheckAnyPermissions", "CheckAllPermissions"]: - if (self.getExtendedAttribute(attribute) and - self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])): - raise WebIDLError("[%s] used on an interface that is " - "not %s-only" % - (attribute, self.parentScope.primaryGlobalName), - [self.location]) - # Conditional exposure makes no sense for interfaces with no # interface object, unless they're navigator properties. - if (self.isExposedConditionally() and + # And SecureContext makes sense for interfaces with no interface object, + # since it is also propagated to interface members. + if (self.isExposedConditionally(exclusions=["SecureContext"]) and not self.hasInterfaceObject() and not self.isNavigatorProperty()): raise WebIDLError("Interface with no interface object is " @@ -1538,8 +1529,8 @@ class IDLInterfaceOrNamespace(IDLObjectWithScope, IDLExposureMixins): "SecureContext", "CheckAnyPermissions", "CheckAllPermissions" ] - def isExposedConditionally(self): - return any(self.getExtendedAttribute(a) for a in self.conditionExtendedAttributes) + def isExposedConditionally(self, exclusions=[]): + return any(((not a in exclusions) and self.getExtendedAttribute(a)) for a in self.conditionExtendedAttributes) class IDLInterface(IDLInterfaceOrNamespace): def __init__(self, location, parentScope, name, parent, members, @@ -1715,10 +1706,7 @@ class IDLInterface(IDLInterfaceOrNamespace): identifier == "JSImplementation" or identifier == "HeaderFile" or identifier == "NavigatorProperty" or - identifier == "AvailableIn" or identifier == "Func" or - identifier == "CheckAnyPermissions" or - identifier == "CheckAllPermissions" or identifier == "Deprecated"): # Known extended attributes that take a string value if not attr.hasValue(): @@ -2170,7 +2158,7 @@ class IDLUnresolvedType(IDLType): return typedefType.complete(scope) elif obj.isCallback() and not obj.isInterface(): assert self.name.name == obj.identifier.name - return IDLCallbackType(self.location, obj) + return IDLCallbackType(obj.location, obj) if self._promiseInnerType and not self._promiseInnerType.isComplete(): self._promiseInnerType = self._promiseInnerType.complete(scope) @@ -2503,10 +2491,18 @@ class IDLUnionType(IDLType): return type.name for (i, type) in enumerate(self.memberTypes): - if not type.isComplete(): + # Exclude typedefs because if given "typedef (B or C) test", + # we want AOrTest, not AOrBOrC + if not type.isComplete() and not isinstance(type, IDLTypedefType): self.memberTypes[i] = type.complete(scope) self.name = "Or".join(typeName(type) for type in self.memberTypes) + + # We do this again to complete the typedef types + for (i, type) in enumerate(self.memberTypes): + if not type.isComplete(): + self.memberTypes[i] = type.complete(scope) + self.flatMemberTypes = list(self.memberTypes) i = 0 while i < len(self.flatMemberTypes): @@ -3544,14 +3540,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): IDLExposureMixins.finish(self, scope) def validate(self): - for attribute in ["CheckAnyPermissions", "CheckAllPermissions"]: - if (self.getExtendedAttribute(attribute) and - self.exposureSet != set([self._globalScope.primaryGlobalName])): - raise WebIDLError("[%s] used on an interface member that is " - "not %s-only" % - (attribute, self.parentScope.primaryGlobalName), - [self.location]) - if self.isAttr() or self.isMethod(): if self.affects == "Everything" and self.dependsOn != "Everything": raise WebIDLError("Interface member is flagged as affecting " @@ -3968,10 +3956,7 @@ class IDLConst(IDLInterfaceMember): elif (identifier == "Pref" or identifier == "ChromeOnly" or identifier == "Func" or - identifier == "SecureContext" or - identifier == "AvailableIn" or - identifier == "CheckAnyPermissions" or - identifier == "CheckAllPermissions"): + identifier == "SecureContext"): # Known attributes that we don't need to do anything with here pass else: @@ -4311,11 +4296,8 @@ class IDLAttribute(IDLInterfaceMember): identifier == "Func" or identifier == "SecureContext" or identifier == "Frozen" or - identifier == "AvailableIn" or identifier == "NewObject" or identifier == "UnsafeInPrerendering" or - identifier == "CheckAnyPermissions" or - identifier == "CheckAllPermissions" or identifier == "BinaryName"): # Known attributes that we don't need to do anything with here pass @@ -5037,9 +5019,6 @@ class IDLMethod(IDLInterfaceMember, IDLScope): identifier == "Deprecated" or identifier == "Func" or identifier == "SecureContext" or - identifier == "AvailableIn" or - identifier == "CheckAnyPermissions" or - identifier == "CheckAllPermissions" or identifier == "BinaryName" or identifier == "StaticClassOverride"): # Known attributes that we don't need to do anything with here @@ -6534,7 +6513,7 @@ class Parser(Tokenizer): type = IDLTypedefType(self.getLocation(p, 1), obj.innerType, obj.identifier.name) elif obj.isCallback() and not obj.isInterface(): - type = IDLCallbackType(self.getLocation(p, 1), obj) + type = IDLCallbackType(obj.location, obj) else: type = IDLWrapperType(self.getLocation(p, 1), p[1]) p[0] = self.handleModifiers(type, p[2]) diff --git a/components/script/dom/bindings/codegen/parser/abstract.patch b/components/script/dom/bindings/codegen/parser/abstract.patch new file mode 100644 index 00000000000..a8e2ddcf759 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/abstract.patch @@ -0,0 +1,12 @@ +--- WebIDL.py ++++ WebIDL.py +@@ -1416,7 +1416,8 @@ + identifier == "LegacyEventInit" or + identifier == "ProbablyShortLivingObject" or + identifier == "LegacyUnenumerableNamedProperties" or +- identifier == "NonOrdinaryGetPrototypeOf"): ++ identifier == "NonOrdinaryGetPrototypeOf" or ++ identifier == "Abstract"): + # Known extended attributes that do not take values + if not attr.noArguments(): + raise WebIDLError("[%s] must take no arguments" % identifier, diff --git a/components/script/dom/bindings/codegen/parser/bytestring.patch b/components/script/dom/bindings/codegen/parser/bytestring.patch new file mode 100644 index 00000000000..823f14cf996 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/bytestring.patch @@ -0,0 +1,29 @@ +--- WebIDL.py ++++ WebIDL.py +@@ -3391,6 +3391,11 @@ class IDLValue(IDLObject): + # extra normalization step. + assert self.type.isDOMString() + return self ++ elif self.type.isString() and type.isByteString(): ++ # Allow ByteStrings to use default value just like ++ # DOMString. No coercion is required here. ++ assert self.type.isDOMString() ++ return self + raise WebIDLError("Cannot coerce type %s to type %s." % + (self.type, type), [location]) + +@@ -5759,6 +5764,14 @@ class Parser(Tokenizer): + booleanType = BuiltinTypes[IDLBuiltinType.Types.boolean] + p[0] = IDLValue(location, booleanType, p[1]) + ++ def p_ConstValueByteString(self, p): ++ """ ++ ConstValue : BYTESTRING ++ """ ++ location = self.getLocation(p, 1) ++ bytestringType = BuiltinTypes[IDLBuiltinType.Types.bytestring] ++ p[0] = IDLValue(location, bytestringType, p[1]) ++ + def p_ConstValueInteger(self, p): + """ + ConstValue : INTEGER diff --git a/components/script/dom/bindings/codegen/parser/callback-location.patch b/components/script/dom/bindings/codegen/parser/callback-location.patch new file mode 100644 index 00000000000..fac5d035801 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/callback-location.patch @@ -0,0 +1,22 @@ +diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py +index da32340..81c52b7 100644 +--- WebIDL.py ++++ WebIDL.py +@@ -2170,7 +2170,7 @@ class IDLUnresolvedType(IDLType): + return typedefType.complete(scope) + elif obj.isCallback() and not obj.isInterface(): + assert self.name.name == obj.identifier.name +- return IDLCallbackType(self.location, obj) ++ return IDLCallbackType(obj.location, obj) + + if self._promiseInnerType and not self._promiseInnerType.isComplete(): + self._promiseInnerType = self._promiseInnerType.complete(scope) +@@ -6521,7 +6521,7 @@ class Parser(Tokenizer): + type = IDLTypedefType(self.getLocation(p, 1), obj.innerType, + obj.identifier.name) + elif obj.isCallback() and not obj.isInterface(): +- type = IDLCallbackType(self.getLocation(p, 1), obj) ++ type = IDLCallbackType(obj.location, obj) + else: + type = IDLWrapperType(self.getLocation(p, 1), p[1]) + p[0] = self.handleModifiers(type, p[2]) diff --git a/components/script/dom/bindings/codegen/parser/pref-main-thread.patch b/components/script/dom/bindings/codegen/parser/pref-main-thread.patch index 4e4f8945f60..7be2dcbfc5e 100644 --- a/components/script/dom/bindings/codegen/parser/pref-main-thread.patch +++ b/components/script/dom/bindings/codegen/parser/pref-main-thread.patch @@ -2,7 +2,7 @@ +++ WebIDL.py @@ -1239,12 +1239,6 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins): alias, - [member.location, m.location]) + [member.location, m.location]) - if (self.getExtendedAttribute("Pref") and - self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])): @@ -10,9 +10,9 @@ - self.parentScope.primaryGlobalName, - [self.location]) - - for attribute in ["CheckAnyPermissions", "CheckAllPermissions"]: - if (self.getExtendedAttribute(attribute) and - self._exposureGlobalNames != set([self.parentScope.primaryGlobalName])): + # Conditional exposure makes no sense for interfaces with no + # interface object, unless they're navigator properties. + # And SecureContext makes sense for interfaces with no interface object, @@ -3459,12 +3453,6 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins): IDLExposureMixins.finish(self, scope) @@ -23,6 +23,6 @@ - "%s-only" % self._globalScope.primaryGlobalName, - [self.location]) - - for attribute in ["CheckAnyPermissions", "CheckAllPermissions"]: - if (self.getExtendedAttribute(attribute) and - self.exposureSet != set([self._globalScope.primaryGlobalName])): + if self.isAttr() or self.isMethod(): + if self.affects == "Everything" and self.dependsOn != "Everything": + raise WebIDLError("Interface member is flagged as affecting " diff --git a/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py b/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py index d907d08449f..084f19fa7f5 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_securecontext_extended_attribute.py @@ -316,3 +316,17 @@ def WebIDLTest(parser, harness): harness.ok(results[0].members[3].getExtendedAttribute("SecureContext") is None, "Methods copied from non-[SecureContext] interface should not be [SecureContext]") + # Test SecureContext and NoInterfaceObject + parser = parser.reset() + parser.parse(""" + [NoInterfaceObject, SecureContext] + interface TestSecureContextNoInterfaceObject { + void testSecureMethod(byte foo); + }; + """) + results = parser.finish() + harness.check(len(results[0].members), 1, "TestSecureContextNoInterfaceObject should have only one member") + harness.ok(results[0].getExtendedAttribute("SecureContext"), + "Interface should have [SecureContext] extended attribute") + harness.ok(results[0].members[0].getExtendedAttribute("SecureContext"), + "Interface member should have [SecureContext] extended attribute") diff --git a/components/script/dom/bindings/codegen/parser/update.sh b/components/script/dom/bindings/codegen/parser/update.sh index 76a99d9cecb..ef1da728b13 100755 --- a/components/script/dom/bindings/codegen/parser/update.sh +++ b/components/script/dom/bindings/codegen/parser/update.sh @@ -2,6 +2,8 @@ wget https://hg.mozilla.org/mozilla-central/raw-file/tip/dom/bindings/parser/Web patch < abstract.patch patch < debug.patch patch < pref-main-thread.patch +patch < callback-location.patch +patch < bytestring.patch wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz rm -r tests diff --git a/components/script/dom/bindings/constant.rs b/components/script/dom/bindings/constant.rs new file mode 100644 index 00000000000..7d453a1fd09 --- /dev/null +++ b/components/script/dom/bindings/constant.rs @@ -0,0 +1,65 @@ +/* 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/. */ + +//! WebIDL constants. + +use js::jsapi::{HandleObject, JSContext, JSPROP_ENUMERATE, JSPROP_PERMANENT}; +use js::jsapi::{JSPROP_READONLY, JS_DefineProperty}; +use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UInt32Value}; +use libc; + +/// Representation of an IDL constant. +#[derive(Clone)] +pub struct ConstantSpec { + /// name of the constant. + pub name: &'static [u8], + /// value of the constant. + pub value: ConstantVal, +} + +/// Representation of an IDL constant value. +#[derive(Clone)] +pub enum ConstantVal { + /// `long` constant. + IntVal(i32), + /// `unsigned long` constant. + UintVal(u32), + /// `double` constant. + DoubleVal(f64), + /// `boolean` constant. + BoolVal(bool), + /// `null` constant. + NullVal, +} + +impl ConstantSpec { + /// Returns a `JSVal` that represents the value of this `ConstantSpec`. + pub fn get_value(&self) -> JSVal { + match self.value { + ConstantVal::NullVal => NullValue(), + ConstantVal::IntVal(i) => Int32Value(i), + ConstantVal::UintVal(u) => UInt32Value(u), + ConstantVal::DoubleVal(d) => DoubleValue(d), + ConstantVal::BoolVal(b) => BooleanValue(b), + } + } +} + +/// Defines constants on `obj`. +/// Fails on JSAPI failure. +pub unsafe fn define_constants( + cx: *mut JSContext, + obj: HandleObject, + constants: &[ConstantSpec]) { + for spec in constants { + rooted!(in(cx) let value = spec.get_value()); + assert!(JS_DefineProperty(cx, + obj, + spec.name.as_ptr() as *const libc::c_char, + value.handle(), + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT, + None, + None)); + } +} diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index 01282ea3b1f..113a9d279d5 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -39,7 +39,8 @@ use dom::bindings::reflector::{Reflectable, Reflector}; use dom::bindings::str::{ByteString, DOMString, USVString}; use dom::bindings::utils::DOMClass; use js; -pub use js::conversions::{FromJSValConvertible, ToJSValConvertible, ConversionBehavior}; +pub use js::conversions::{FromJSValConvertible, ToJSValConvertible, ConversionResult}; +pub use js::conversions::ConversionBehavior; use js::conversions::latin1_to_string; use js::error::throw_type_error; use js::glue::{GetProxyPrivate, IsWrapper}; @@ -81,10 +82,17 @@ impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, option: ()) - -> Result<Finite<T>, ()> { - let result = try!(FromJSValConvertible::from_jsval(cx, value, option)); + -> Result<ConversionResult<Finite<T>>, ()> { + let result = match FromJSValConvertible::from_jsval(cx, value, option) { + Ok(ConversionResult::Success(v)) => v, + Ok(ConversionResult::Failure(error)) => { + throw_type_error(cx, &error); + return Err(()); + } + _ => return Err(()), + }; match Finite::new(result) { - Some(v) => Ok(v), + Some(v) => Ok(ConversionResult::Success(v)), None => { throw_type_error(cx, "this argument is not a finite floating-point value"); Err(()) @@ -96,15 +104,14 @@ impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite impl <T: Reflectable + IDLInterface> FromJSValConvertible for Root<T> { type Config = (); - unsafe fn from_jsval(cx: *mut JSContext, + unsafe fn from_jsval(_cx: *mut JSContext, value: HandleValue, _config: Self::Config) - -> Result<Root<T>, ()> { - let result = root_from_handlevalue(value); - if let Err(()) = result { - throw_type_error(cx, "value is not an object"); - } - result + -> Result<ConversionResult<Root<T>>, ()> { + Ok(match root_from_handlevalue(value) { + Ok(result) => ConversionResult::Success(result), + Err(()) => ConversionResult::Failure("value is not an object".into()), + }) } } @@ -146,17 +153,17 @@ impl FromJSValConvertible for DOMString { unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, null_behavior: StringificationBehavior) - -> Result<DOMString, ()> { + -> Result<ConversionResult<DOMString>, ()> { if null_behavior == StringificationBehavior::Empty && value.get().is_null() { - Ok(DOMString::new()) + Ok(ConversionResult::Success(DOMString::new())) } else { let jsstr = ToString(cx, value); if jsstr.is_null() { debug!("ToString failed"); Err(()) } else { - Ok(jsstring_to_str(cx, jsstr)) + Ok(ConversionResult::Success(jsstring_to_str(cx, jsstr))) } } } @@ -203,7 +210,8 @@ pub unsafe fn jsstring_to_str(cx: *mut JSContext, s: *mut JSString) -> DOMString // http://heycam.github.io/webidl/#es-USVString impl FromJSValConvertible for USVString { type Config = (); - unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) -> Result<USVString, ()> { + unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) + -> Result<ConversionResult<USVString>, ()> { let jsstr = ToString(cx, value); if jsstr.is_null() { debug!("ToString failed"); @@ -212,13 +220,14 @@ impl FromJSValConvertible for USVString { let latin1 = JS_StringHasLatin1Chars(jsstr); if latin1 { // FIXME(ajeffrey): Convert directly from DOMString to USVString - return Ok(USVString(String::from(jsstring_to_str(cx, jsstr)))); + return Ok(ConversionResult::Success( + USVString(String::from(jsstring_to_str(cx, jsstr))))); } let mut length = 0; let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr, &mut length); assert!(!chars.is_null()); let char_vec = slice::from_raw_parts(chars as *const u16, length as usize); - Ok(USVString(String::from_utf16_lossy(char_vec))) + Ok(ConversionResult::Success(USVString(String::from_utf16_lossy(char_vec)))) } } @@ -241,7 +250,7 @@ impl FromJSValConvertible for ByteString { unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ()) - -> Result<ByteString, ()> { + -> Result<ConversionResult<ByteString>, ()> { let string = ToString(cx, value); if string.is_null() { debug!("ToString failed"); @@ -255,7 +264,7 @@ impl FromJSValConvertible for ByteString { assert!(!chars.is_null()); let char_slice = slice::from_raw_parts(chars as *mut u8, length as usize); - return Ok(ByteString::new(char_slice.to_vec())); + return Ok(ConversionResult::Success(ByteString::new(char_slice.to_vec()))); } let mut length = 0; @@ -266,7 +275,8 @@ impl FromJSValConvertible for ByteString { throw_type_error(cx, "Invalid ByteString"); Err(()) } else { - Ok(ByteString::new(char_vec.iter().map(|&c| c as u8).collect())) + Ok(ConversionResult::Success( + ByteString::new(char_vec.iter().map(|&c| c as u8).collect()))) } } } diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index c4bda97e521..c9cbeb09f21 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -6,16 +6,14 @@ use dom::bindings::codegen::Bindings::DOMExceptionBinding::DOMExceptionMethods; use dom::bindings::codegen::PrototypeList::proto_id_to_name; +use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, ToJSValConvertible}; use dom::bindings::conversions::root_from_object; -use dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible}; -use dom::bindings::global::GlobalRef; +use dom::bindings::global::{GlobalRef, global_root_from_context}; use dom::bindings::str::USVString; use dom::domexception::{DOMErrorName, DOMException}; use js::error::{throw_range_error, throw_type_error}; use js::jsapi::HandleObject; -use js::jsapi::JSAutoCompartment; use js::jsapi::JSContext; -use js::jsapi::JSObject; use js::jsapi::JS_ClearPendingException; use js::jsapi::JS_ErrorFromException; use js::jsapi::JS_GetPendingException; @@ -134,11 +132,16 @@ pub unsafe fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef, result: JS_SetPendingException(cx, thrown.handle()); } -struct ErrorInfo { - filename: String, - message: String, - lineno: c_uint, - column: c_uint, +/// A struct encapsulating information about a runtime script error. +pub struct ErrorInfo { + /// The error message. + pub message: String, + /// The file name. + pub filename: String, + /// The line number. + pub lineno: c_uint, + /// The column number. + pub column: c_uint, } impl ErrorInfo { @@ -194,9 +197,11 @@ impl ErrorInfo { } /// Report a pending exception, thereby clearing it. -pub unsafe fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) { +/// +/// The `dispatch_event` argument is temporary and non-standard; passing false +/// prevents dispatching the `error` event. +pub unsafe fn report_pending_exception(cx: *mut JSContext, dispatch_event: bool) { if JS_IsExceptionPending(cx) { - let _ac = JSAutoCompartment::new(cx, obj); rooted!(in(cx) let mut value = UndefinedValue()); if !JS_GetPendingException(cx, value.handle_mut()) { JS_ClearPendingException(cx); @@ -205,22 +210,30 @@ pub unsafe fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) { } JS_ClearPendingException(cx); - if !value.is_object() { - match USVString::from_jsval(cx, value.handle(), ()) { - Ok(USVString(string)) => error!("Uncaught exception: {}", string), - Err(_) => error!("Uncaught exception: failed to stringify primitive"), + let error_info = if value.is_object() { + rooted!(in(cx) let object = value.to_object()); + let error_info = ErrorInfo::from_native_error(cx, object.handle()) + .or_else(|| ErrorInfo::from_dom_exception(object.handle())); + match error_info { + Some(error_info) => error_info, + None => { + error!("Uncaught exception: failed to extract information"); + return; + } } - return; - } - - rooted!(in(cx) let object = value.to_object()); - let error_info = ErrorInfo::from_native_error(cx, object.handle()) - .or_else(|| ErrorInfo::from_dom_exception(object.handle())); - let error_info = match error_info { - Some(error_info) => error_info, - None => { - error!("Uncaught exception: failed to extract information"); - return; + } else { + match USVString::from_jsval(cx, value.handle(), ()) { + Ok(ConversionResult::Success(USVString(string))) => { + ErrorInfo { + message: format!("uncaught exception: {}", string), + filename: String::new(), + lineno: 0, + column: 0, + } + }, + _ => { + panic!("Uncaught exception: failed to stringify primitive"); + }, } }; @@ -229,6 +242,11 @@ pub unsafe fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) { error_info.lineno, error_info.column, error_info.message); + + if dispatch_event { + let global = global_root_from_context(cx); + global.r().report_an_error(error_info, value.handle()); + } } } diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index bfc3212a9c4..a5c9d1a3d79 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -10,19 +10,22 @@ use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::conversions::root_from_object; +use dom::bindings::error::ErrorInfo; use dom::bindings::js::Root; use dom::bindings::reflector::{Reflectable, Reflector}; +use dom::console::TimerSet; use dom::window::{self, ScriptHelpers}; use dom::workerglobalscope::WorkerGlobalScope; use ipc_channel::ipc::IpcSender; +use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; use js::jsapi::{CurrentGlobalOrNull, GetGlobalForObjectCrossCompartment}; use js::jsapi::{JSContext, JSObject, JS_GetClass, MutableHandleValue}; -use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; +use js::jsapi::HandleValue; use msg::constellation_msg::PipelineId; -use net_traits::{ResourceThreads, CoreResourceThread, IpcSend}; +use net_traits::{CoreResourceThread, IpcSend, ResourceThreads}; use profile_traits::{mem, time}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort}; -use script_thread::{MainThreadScriptChan, ScriptThread, RunnableWrapper}; +use script_thread::{MainThreadScriptChan, RunnableWrapper, ScriptThread}; use script_traits::{MsDuration, ScriptMsg as ConstellationMsg, TimerEventRequest}; use task_source::dom_manipulation::DOMManipulationTaskSource; use task_source::file_reading::FileReadingTaskSource; @@ -269,11 +272,11 @@ impl<'a> GlobalRef<'a> { } } - /// Returns the receiver's reflector. - pub fn reflector(&self) -> &Reflector { + /// Returns the global's timers for the Console API. + pub fn console_timers(&self) -> &TimerSet { match *self { - GlobalRef::Window(ref window) => window.reflector(), - GlobalRef::Worker(ref worker) => worker.reflector(), + GlobalRef::Window(ref window) => window.console_timers(), + GlobalRef::Worker(ref worker) => worker.console_timers(), } } @@ -285,6 +288,23 @@ impl<'a> GlobalRef<'a> { GlobalRef::Worker(ref worker) => worker.get_runnable_wrapper(), } } + + /// https://html.spec.whatwg.org/multipage/#report-the-error + pub fn report_an_error(&self, error_info: ErrorInfo, value: HandleValue) { + match *self { + GlobalRef::Window(ref window) => window.report_an_error(error_info, value), + GlobalRef::Worker(ref worker) => worker.report_an_error(error_info, value), + } + } +} + +impl<'a> Reflectable for GlobalRef<'a> { + fn reflector(&self) -> &Reflector { + match *self { + GlobalRef::Window(ref window) => window.reflector(), + GlobalRef::Worker(ref worker) => worker.reflector(), + } + } } impl GlobalRoot { diff --git a/components/script/dom/bindings/inheritance.rs b/components/script/dom/bindings/inheritance.rs index c9ccdbd0325..f4f1274ac6e 100644 --- a/components/script/dom/bindings/inheritance.rs +++ b/components/script/dom/bindings/inheritance.rs @@ -6,8 +6,8 @@ pub use dom::bindings::codegen::InheritTypes::*; -use dom::bindings::conversions::get_dom_class; use dom::bindings::conversions::{DerivedFrom, IDLInterface}; +use dom::bindings::conversions::get_dom_class; use dom::bindings::reflector::Reflectable; use std::mem; diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs index 3b2b5dbde36..cae5da97887 100644 --- a/components/script/dom/bindings/interface.rs +++ b/components/script/dom/bindings/interface.rs @@ -6,116 +6,30 @@ use dom::bindings::codegen::InterfaceObjectMap::Globals; use dom::bindings::codegen::PrototypeList; -use dom::bindings::conversions::get_dom_class; +use dom::bindings::constant::{ConstantSpec, define_constants}; +use dom::bindings::conversions::{DOM_OBJECT_SLOT, get_dom_class}; use dom::bindings::guard::Guard; -use dom::bindings::utils::get_proto_or_iface_array; +use dom::bindings::utils::{DOM_PROTOTYPE_SLOT, ProtoOrIfaceArray, get_proto_or_iface_array}; use js::error::throw_type_error; use js::glue::{RUST_SYMBOL_TO_JSID, UncheckedUnwrapObject}; -use js::jsapi::{Class, ClassOps, GetGlobalForObjectCrossCompartment}; -use js::jsapi::{GetWellKnownSymbol, HandleObject, HandleValue, JSClass, JSContext}; -use js::jsapi::{JSFunctionSpec, JSNative, JSFUN_CONSTRUCTOR, JSPROP_ENUMERATE}; -use js::jsapi::{JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_RESOLVING, JSPropertySpec}; -use js::jsapi::{JSString, JS_AtomizeAndPinString, JS_DefineProperty, JS_DefineProperty1}; -use js::jsapi::{JS_DefineProperty2, JS_DefineProperty4, JS_DefinePropertyById3}; -use js::jsapi::{JS_GetClass, JS_GetFunctionObject, JS_GetPrototype, JS_LinkConstructorAndPrototype}; -use js::jsapi::{JS_NewFunction, JS_NewObject, JS_NewObjectWithUniqueType}; -use js::jsapi::{JS_NewPlainObject, JS_NewStringCopyN, MutableHandleObject}; -use js::jsapi::{MutableHandleValue, ObjectOps}; -use js::jsapi::{SymbolCode, TrueHandleValue, Value}; -use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UInt32Value}; +use js::jsapi::{Class, ClassOps, CompartmentOptions, GetGlobalForObjectCrossCompartment}; +use js::jsapi::{GetWellKnownSymbol, HandleObject, HandleValue, JSAutoCompartment}; +use js::jsapi::{JSClass, JSContext, JSFUN_CONSTRUCTOR, JSFunctionSpec, JSObject}; +use js::jsapi::{JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_RESOLVING}; +use js::jsapi::{JSPropertySpec, JSString, JSTracer, JSVersion, JS_AtomizeAndPinString}; +use js::jsapi::{JS_DefineProperty, JS_DefineProperty1, JS_DefineProperty2}; +use js::jsapi::{JS_DefineProperty4, JS_DefinePropertyById3, JS_FireOnNewGlobalObject}; +use js::jsapi::{JS_GetClass, JS_GetFunctionObject, JS_GetPrototype}; +use js::jsapi::{JS_LinkConstructorAndPrototype, JS_NewFunction, JS_NewGlobalObject}; +use js::jsapi::{JS_NewObject, JS_NewObjectWithUniqueType, JS_NewPlainObject}; +use js::jsapi::{JS_NewStringCopyN, JS_SetReservedSlot, MutableHandleObject}; +use js::jsapi::{MutableHandleValue, ObjectOps, OnNewGlobalHookOption, SymbolCode}; +use js::jsapi::{TrueHandleValue, Value}; +use js::jsval::{JSVal, PrivateValue}; use js::rust::{define_methods, define_properties}; use libc; use std::ptr; -/// Representation of an IDL constant value. -#[derive(Clone)] -pub enum ConstantVal { - /// `long` constant. - IntVal(i32), - /// `unsigned long` constant. - UintVal(u32), - /// `double` constant. - DoubleVal(f64), - /// `boolean` constant. - BoolVal(bool), - /// `null` constant. - NullVal, -} - -/// Representation of an IDL constant. -#[derive(Clone)] -pub struct ConstantSpec { - /// name of the constant. - pub name: &'static [u8], - /// value of the constant. - pub value: ConstantVal, -} - -impl ConstantSpec { - /// Returns a `JSVal` that represents the value of this `ConstantSpec`. - pub fn get_value(&self) -> JSVal { - match self.value { - ConstantVal::NullVal => NullValue(), - ConstantVal::IntVal(i) => Int32Value(i), - ConstantVal::UintVal(u) => UInt32Value(u), - ConstantVal::DoubleVal(d) => DoubleValue(d), - ConstantVal::BoolVal(b) => BooleanValue(b), - } - } -} - -/// A JSNative that cannot be null. -pub type NonNullJSNative = - unsafe extern "C" fn (arg1: *mut JSContext, arg2: libc::c_uint, arg3: *mut JSVal) -> bool; - -/// Defines constants on `obj`. -/// Fails on JSAPI failure. -fn define_constants( - cx: *mut JSContext, - obj: HandleObject, - constants: &[ConstantSpec]) { - for spec in constants { - rooted!(in(cx) let value = spec.get_value()); - unsafe { - assert!(JS_DefineProperty(cx, - obj, - spec.name.as_ptr() as *const libc::c_char, - value.handle(), - JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT, - None, - None)); - } - } -} - -unsafe extern "C" fn fun_to_string_hook(cx: *mut JSContext, - obj: HandleObject, - _indent: u32) - -> *mut JSString { - let js_class = JS_GetClass(obj.get()); - assert!(!js_class.is_null()); - let repr = (*(js_class as *const NonCallbackInterfaceObjectClass)).representation; - assert!(!repr.is_empty()); - let ret = JS_NewStringCopyN(cx, repr.as_ptr() as *const libc::c_char, repr.len()); - assert!(!ret.is_null()); - ret -} - -const OBJECT_OPS: ObjectOps = ObjectOps { - lookupProperty: None, - defineProperty: None, - hasProperty: None, - getProperty: None, - setProperty: None, - getOwnPropertyDescriptor: None, - deleteProperty: None, - watch: None, - unwatch: None, - getElements: None, - enumerate: None, - funToString: Some(fun_to_string_hook), -}; - /// The class of a non-callback interface object. #[derive(Copy, Clone)] pub struct NonCallbackInterfaceObjectClass { @@ -132,27 +46,8 @@ pub struct NonCallbackInterfaceObjectClass { unsafe impl Sync for NonCallbackInterfaceObjectClass {} impl NonCallbackInterfaceObjectClass { - /// Create `ClassOps` for a `NonCallbackInterfaceObjectClass`. - pub const fn ops(constructor_behavior: InterfaceConstructorBehavior) - -> ClassOps { - ClassOps { - addProperty: None, - delProperty: None, - getProperty: None, - setProperty: None, - enumerate: None, - resolve: None, - mayResolve: None, - finalize: None, - call: constructor_behavior.call, - construct: constructor_behavior.construct, - hasInstance: Some(has_instance_hook), - trace: None, - } - } - /// Create a new `NonCallbackInterfaceObjectClass` structure. - pub const fn new(ops: &'static ClassOps, + pub const fn new(constructor_behavior: &'static InterfaceConstructorBehavior, string_rep: &'static [u8], proto_id: PrototypeList::ID, proto_depth: u16) @@ -161,7 +56,7 @@ impl NonCallbackInterfaceObjectClass { class: Class { name: b"Function\0" as *const _ as *const libc::c_char, flags: 0, - cOps: ops, + cOps: &constructor_behavior.0, spec: ptr::null(), ext: ptr::null(), oOps: &OBJECT_OPS, @@ -185,29 +80,84 @@ pub type ConstructorClassHook = unsafe extern "C" fn(cx: *mut JSContext, argc: u32, vp: *mut Value) -> bool; /// The constructor behavior of a non-callback interface object. -pub struct InterfaceConstructorBehavior { - call: JSNative, - construct: JSNative, -} +pub struct InterfaceConstructorBehavior(ClassOps); impl InterfaceConstructorBehavior { /// An interface constructor that unconditionally throws a type error. - pub const fn throw() -> InterfaceConstructorBehavior { - InterfaceConstructorBehavior { + pub const fn throw() -> Self { + InterfaceConstructorBehavior(ClassOps { + addProperty: None, + delProperty: None, + getProperty: None, + setProperty: None, + enumerate: None, + resolve: None, + mayResolve: None, + finalize: None, call: Some(invalid_constructor), construct: Some(invalid_constructor), - } + hasInstance: Some(has_instance_hook), + trace: None, + }) } /// An interface constructor that calls a native Rust function. - pub const fn call(hook: ConstructorClassHook) -> InterfaceConstructorBehavior { - InterfaceConstructorBehavior { + pub const fn call(hook: ConstructorClassHook) -> Self { + InterfaceConstructorBehavior(ClassOps { + addProperty: None, + delProperty: None, + getProperty: None, + setProperty: None, + enumerate: None, + resolve: None, + mayResolve: None, + finalize: None, call: Some(non_new_constructor), construct: Some(hook), - } + hasInstance: Some(has_instance_hook), + trace: None, + }) } } +/// A trace hook. +pub type TraceHook = + unsafe extern "C" fn(trc: *mut JSTracer, obj: *mut JSObject); + +/// Create a global object with the given class. +pub unsafe fn create_global_object( + cx: *mut JSContext, + class: &'static JSClass, + private: *const libc::c_void, + trace: TraceHook, + rval: MutableHandleObject) { + assert!(rval.is_null()); + + let mut options = CompartmentOptions::default(); + options.behaviors_.version_ = JSVersion::JSVERSION_ECMA_5; + options.creationOptions_.traceGlobal_ = Some(trace); + options.creationOptions_.sharedMemoryAndAtomics_ = true; + + rval.set(JS_NewGlobalObject(cx, + class, + ptr::null_mut(), + OnNewGlobalHookOption::DontFireOnNewGlobalHook, + &options)); + assert!(!rval.is_null()); + + // Initialize the reserved slots before doing anything that can GC, to + // avoid getting trace hooks called on a partially initialized object. + JS_SetReservedSlot(rval.get(), DOM_OBJECT_SLOT, PrivateValue(private)); + let proto_array: Box<ProtoOrIfaceArray> = + box [0 as *mut JSObject; PrototypeList::PROTO_OR_IFACE_LENGTH]; + JS_SetReservedSlot(rval.get(), + DOM_PROTOTYPE_SLOT, + PrivateValue(Box::into_raw(proto_array) as *const libc::c_void)); + + let _ac = JSAutoCompartment::new(cx, rval.get()); + JS_FireOnNewGlobalObject(cx, rval.handle()); +} + /// Create and define the interface object of a callback interface. pub unsafe fn create_callback_interface_object( cx: *mut JSContext, @@ -218,11 +168,7 @@ pub unsafe fn create_callback_interface_object( assert!(!constants.is_empty()); rval.set(JS_NewObject(cx, ptr::null())); assert!(!rval.ptr.is_null()); - for guard in constants { - if let Some(specs) = guard.expose(cx, rval.handle()) { - define_constants(cx, rval.handle(), specs); - } - } + define_guarded_constants(cx, rval.handle(), constants); define_name(cx, rval.handle(), name); define_on_global_object(cx, global, name, rval.handle()); } @@ -283,7 +229,7 @@ pub unsafe fn create_noncallback_interface_object( pub unsafe fn create_named_constructors( cx: *mut JSContext, global: HandleObject, - named_constructors: &[(NonNullJSNative, &[u8], u32)], + named_constructors: &[(ConstructorClassHook, &[u8], u32)], interface_prototype_object: HandleObject) { rooted!(in(cx) let mut constructor = ptr::null_mut()); @@ -311,6 +257,110 @@ pub unsafe fn create_named_constructors( } } +/// Create a new object with a unique type. +pub unsafe fn create_object( + cx: *mut JSContext, + proto: HandleObject, + class: &'static JSClass, + methods: &[Guard<&'static [JSFunctionSpec]>], + properties: &[Guard<&'static [JSPropertySpec]>], + constants: &[Guard<&[ConstantSpec]>], + rval: MutableHandleObject) { + rval.set(JS_NewObjectWithUniqueType(cx, class, proto)); + assert!(!rval.ptr.is_null()); + define_guarded_methods(cx, rval.handle(), methods); + define_guarded_properties(cx, rval.handle(), properties); + define_guarded_constants(cx, rval.handle(), constants); +} + +/// Conditionally define constants on an object. +pub unsafe fn define_guarded_constants( + cx: *mut JSContext, + obj: HandleObject, + constants: &[Guard<&[ConstantSpec]>]) { + for guard in constants { + if let Some(specs) = guard.expose(cx, obj) { + define_constants(cx, obj, specs); + } + } +} + +/// Conditionally define methods on an object. +pub unsafe fn define_guarded_methods( + cx: *mut JSContext, + obj: HandleObject, + methods: &[Guard<&'static [JSFunctionSpec]>]) { + for guard in methods { + if let Some(specs) = guard.expose(cx, obj) { + define_methods(cx, obj, specs).unwrap(); + } + } +} + +/// Conditionally define properties on an object. +pub unsafe fn define_guarded_properties( + cx: *mut JSContext, + obj: HandleObject, + properties: &[Guard<&'static [JSPropertySpec]>]) { + for guard in properties { + if let Some(specs) = guard.expose(cx, obj) { + define_properties(cx, obj, specs).unwrap(); + } + } +} + +/// Returns whether an interface with exposure set given by `globals` should +/// be exposed in the global object `obj`. +pub unsafe fn is_exposed_in(object: HandleObject, globals: Globals) -> bool { + let unwrapped = UncheckedUnwrapObject(object.get(), /* stopAtWindowProxy = */ 0); + let dom_class = get_dom_class(unwrapped).unwrap(); + globals.contains(dom_class.global) +} + +/// Define a property with a given name on the global object. Should be called +/// through the resolve hook. +pub unsafe fn define_on_global_object( + cx: *mut JSContext, + global: HandleObject, + name: &[u8], + obj: HandleObject) { + assert!(*name.last().unwrap() == b'\0'); + assert!(JS_DefineProperty1(cx, + global, + name.as_ptr() as *const libc::c_char, + obj, + JSPROP_RESOLVING, + None, None)); +} + +const OBJECT_OPS: ObjectOps = ObjectOps { + lookupProperty: None, + defineProperty: None, + hasProperty: None, + getProperty: None, + setProperty: None, + getOwnPropertyDescriptor: None, + deleteProperty: None, + watch: None, + unwatch: None, + getElements: None, + enumerate: None, + funToString: Some(fun_to_string_hook), +}; + +unsafe extern "C" fn fun_to_string_hook(cx: *mut JSContext, + obj: HandleObject, + _indent: u32) + -> *mut JSString { + let js_class = JS_GetClass(obj.get()); + assert!(!js_class.is_null()); + let repr = (*(js_class as *const NonCallbackInterfaceObjectClass)).representation; + assert!(!repr.is_empty()); + let ret = JS_NewStringCopyN(cx, repr.as_ptr() as *const libc::c_char, repr.len()); + assert!(!ret.is_null()); + ret +} + /// Hook for instanceof on interface objects. unsafe extern "C" fn has_instance_hook(cx: *mut JSContext, obj: HandleObject, @@ -370,25 +420,6 @@ unsafe fn has_instance( Err(()) } -unsafe fn create_object( - cx: *mut JSContext, - proto: HandleObject, - class: &'static JSClass, - methods: &[Guard<&'static [JSFunctionSpec]>], - properties: &[Guard<&'static [JSPropertySpec]>], - constants: &[Guard<&[ConstantSpec]>], - rval: MutableHandleObject) { - rval.set(JS_NewObjectWithUniqueType(cx, class, proto)); - assert!(!rval.ptr.is_null()); - define_guarded_methods(cx, rval.handle(), methods); - define_guarded_properties(cx, rval.handle(), properties); - for guard in constants { - if let Some(specs) = guard.expose(cx, rval.handle()) { - define_constants(cx, rval.handle(), specs); - } - } -} - unsafe fn create_unscopable_object( cx: *mut JSContext, names: &[&[u8]], @@ -405,30 +436,6 @@ unsafe fn create_unscopable_object( } } -/// Conditionally define methods on an object. -pub unsafe fn define_guarded_methods( - cx: *mut JSContext, - obj: HandleObject, - methods: &[Guard<&'static [JSFunctionSpec]>]) { - for guard in methods { - if let Some(specs) = guard.expose(cx, obj) { - define_methods(cx, obj, specs).unwrap(); - } - } -} - -/// Conditionally define properties on an object. -pub unsafe fn define_guarded_properties( - cx: *mut JSContext, - obj: HandleObject, - properties: &[Guard<&'static [JSPropertySpec]>]) { - for guard in properties { - if let Some(specs) = guard.expose(cx, obj) { - define_properties(cx, obj, specs).unwrap(); - } - } -} - unsafe fn define_name(cx: *mut JSContext, obj: HandleObject, name: &[u8]) { assert!(*name.last().unwrap() == b'\0'); rooted!(in(cx) let name = JS_AtomizeAndPinString(cx, name.as_ptr() as *const libc::c_char)); @@ -450,20 +457,6 @@ unsafe fn define_length(cx: *mut JSContext, obj: HandleObject, length: u32) { None, None)); } -unsafe fn define_on_global_object( - cx: *mut JSContext, - global: HandleObject, - name: &[u8], - obj: HandleObject) { - assert!(*name.last().unwrap() == b'\0'); - assert!(JS_DefineProperty1(cx, - global, - name.as_ptr() as *const libc::c_char, - obj, - JSPROP_RESOLVING, - None, None)); -} - unsafe extern "C" fn invalid_constructor( cx: *mut JSContext, _argc: libc::c_uint, @@ -481,11 +474,3 @@ unsafe extern "C" fn non_new_constructor( throw_type_error(cx, "This constructor needs to be called with `new`."); false } - -/// Returns whether an interface with exposure set given by `globals` should -/// be exposed in the global object `obj`. -pub unsafe fn is_exposed_in(object: HandleObject, globals: Globals) -> bool { - let unwrapped = UncheckedUnwrapObject(object.get(), /* stopAtWindowProxy = */ 0); - let dom_class = get_dom_class(unwrapped).unwrap(); - globals.contains(dom_class.global) -} diff --git a/components/script/dom/bindings/iterable.rs b/components/script/dom/bindings/iterable.rs new file mode 100644 index 00000000000..84381b52655 --- /dev/null +++ b/components/script/dom/bindings/iterable.rs @@ -0,0 +1,168 @@ +/* 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/. */ + +#![allow(unsafe_code)] + +//! Implementation of `iterable<...>` and `iterable<..., ...>` WebIDL declarations. + +use core::nonzero::NonZero; +use dom::bindings::codegen::Bindings::IterableIteratorBinding::IterableKeyAndValueResult; +use dom::bindings::codegen::Bindings::IterableIteratorBinding::IterableKeyOrValueResult; +use dom::bindings::error::Fallible; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::{JS, Root}; +use dom::bindings::reflector::{Reflector, Reflectable, MutReflectable, reflect_dom_object}; +use dom::bindings::trace::JSTraceable; +use js::conversions::ToJSValConvertible; +use js::jsapi::{JSContext, JSObject, MutableHandleValue, MutableHandleObject, HandleValue}; +use js::jsval::UndefinedValue; +use std::cell::Cell; +use std::ptr; + +/// The values that an iterator will iterate over. +#[derive(JSTraceable, HeapSizeOf)] +pub enum IteratorType { + /// The keys of the iterable object. + Keys, + /// The values of the iterable object. + Values, + /// The keys and values of the iterable object combined. + Entries, +} + +/// A DOM object that can be iterated over using a pair value iterator. +pub trait Iterable { + /// The type of the key of the iterator pair. + type Key: ToJSValConvertible; + /// The type of the value of the iterator pair. + type Value: ToJSValConvertible; + /// Return the number of entries that can be iterated over. + fn get_iterable_length(&self) -> u32; + /// Return the value at the provided index. + fn get_value_at_index(&self, index: u32) -> Self::Value; + /// Return the key at the provided index. + fn get_key_at_index(&self, index: u32) -> Self::Key; +} + +/// An iterator over the iterable entries of a given DOM interface. +//FIXME: #12811 prevents dom_struct with type parameters +//#[dom_struct] +#[must_root] +#[privatize] +#[derive(JSTraceable)] +#[derive(HeapSizeOf)] +pub struct IterableIterator<T: Reflectable + JSTraceable + Iterable> { + reflector: Reflector, + iterable: JS<T>, + type_: IteratorType, + index: Cell<u32>, +} + +impl<T: Reflectable + JSTraceable + Iterable> Reflectable for IterableIterator<T> { + fn reflector<'a>(&'a self) -> &'a Reflector { + &self.reflector + } +} + +impl<T: Reflectable + JSTraceable + Iterable> MutReflectable for IterableIterator<T> { + fn init_reflector(&mut self, obj: *mut JSObject) { + self.reflector.set_jsobject(obj); + } +} + +impl<T: Reflectable + JSTraceable + Iterable> ToJSValConvertible for IterableIterator<T> { + #[allow(unsafe_code)] + unsafe fn to_jsval(&self, + cx: *mut JSContext, + rval: MutableHandleValue) { + let object = Reflectable::reflector(self).get_jsobject(); + object.to_jsval(cx, rval) + } +} + +impl<T: Reflectable + JSTraceable + Iterable> IterableIterator<T> { + /// Create a new iterator instance for the provided iterable DOM interface. + pub fn new(iterable: &T, + type_: IteratorType, + wrap: fn(*mut JSContext, GlobalRef, Box<IterableIterator<T>>) + -> Root<Self>) -> Root<Self> { + let iterator = box IterableIterator { + reflector: Reflector::new(), + type_: type_, + iterable: JS::from_ref(iterable), + index: Cell::new(0), + }; + let global = iterable.global(); + reflect_dom_object(iterator, global.r(), wrap) + } + + /// Return the next value from the iterable object. + #[allow(non_snake_case)] + pub fn Next(&self, cx: *mut JSContext) -> Fallible<NonZero<*mut JSObject>> { + let index = self.index.get(); + rooted!(in(cx) let mut value = UndefinedValue()); + rooted!(in(cx) let mut rval = ptr::null_mut()); + let result = if index >= self.iterable.get_iterable_length() { + dict_return(cx, rval.handle_mut(), true, value.handle()) + } else { + match self.type_ { + IteratorType::Keys => { + unsafe { + self.iterable.get_key_at_index(index).to_jsval(cx, value.handle_mut()); + } + dict_return(cx, rval.handle_mut(), false, value.handle()) + } + IteratorType::Values => { + unsafe { + self.iterable.get_value_at_index(index).to_jsval(cx, value.handle_mut()); + } + dict_return(cx, rval.handle_mut(), false, value.handle()) + } + IteratorType::Entries => { + rooted!(in(cx) let mut key = UndefinedValue()); + unsafe { + self.iterable.get_key_at_index(index).to_jsval(cx, key.handle_mut()); + self.iterable.get_value_at_index(index).to_jsval(cx, value.handle_mut()); + } + key_and_value_return(cx, rval.handle_mut(), key.handle(), value.handle()) + } + } + }; + self.index.set(index + 1); + result.map(|_| { + assert!(!rval.is_null()); + unsafe { NonZero::new(rval.get()) } + }) + } +} + +fn dict_return(cx: *mut JSContext, + result: MutableHandleObject, + done: bool, + value: HandleValue) -> Fallible<()> { + let mut dict = unsafe { IterableKeyOrValueResult::empty(cx) }; + dict.done = done; + dict.value = value.get(); + rooted!(in(cx) let mut dict_value = UndefinedValue()); + unsafe { + dict.to_jsval(cx, dict_value.handle_mut()); + } + result.set(dict_value.to_object()); + Ok(()) +} + +fn key_and_value_return(cx: *mut JSContext, + result: MutableHandleObject, + key: HandleValue, + value: HandleValue) -> Fallible<()> { + let mut dict = unsafe { IterableKeyAndValueResult::empty(cx) }; + dict.done = false; + dict.value = Some(vec![key.get(), value.get()]); + rooted!(in(cx) let mut dict_value = UndefinedValue()); + unsafe { + dict.to_jsval(cx, dict_value.handle_mut()); + } + result.set(dict_value.to_object()); + Ok(()) +} diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs index 6a0dfd83dea..a7ecc975129 100644 --- a/components/script/dom/bindings/js.rs +++ b/components/script/dom/bindings/js.rs @@ -126,7 +126,10 @@ impl<T: Castable> LayoutJS<T> { T: DerivedFrom<U> { debug_assert!(thread_state::get().is_layout()); - unsafe { mem::transmute_copy(self) } + let ptr: *const T = *self.ptr; + LayoutJS { + ptr: unsafe { NonZero::new(ptr as *const U) }, + } } /// Cast a DOM object downwards to one of the interfaces it might implement. @@ -136,7 +139,10 @@ impl<T: Castable> LayoutJS<T> { debug_assert!(thread_state::get().is_layout()); unsafe { if (*self.unsafe_get()).is::<U>() { - Some(mem::transmute_copy(self)) + let ptr: *const T = *self.ptr; + Some(LayoutJS { + ptr: NonZero::new(ptr as *const U), + }) } else { None } diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index 19797a473d0..16607c9045f 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -128,15 +128,19 @@ //! return `Err()` from the method with the appropriate [error value] //! (error/enum.Error.html). +pub use style::domrefcell as cell; + pub mod callback; -pub mod cell; +pub mod constant; pub mod conversions; pub mod error; pub mod global; pub mod guard; pub mod inheritance; pub mod interface; +pub mod iterable; pub mod js; +pub mod namespace; pub mod num; pub mod proxyhandler; pub mod refcounted; diff --git a/components/script/dom/bindings/namespace.rs b/components/script/dom/bindings/namespace.rs new file mode 100644 index 00000000000..38055dea180 --- /dev/null +++ b/components/script/dom/bindings/namespace.rs @@ -0,0 +1,42 @@ +/* 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/. */ + +//! Machinery to initialise namespace objects. + +use dom::bindings::guard::Guard; +use dom::bindings::interface::{create_object, define_on_global_object}; +use js::jsapi::{HandleObject, JSClass, JSContext, JSFunctionSpec, MutableHandleObject}; +use libc; +use std::ptr; + +/// The class of a namespace object. +#[derive(Copy, Clone)] +pub struct NamespaceObjectClass(JSClass); + +unsafe impl Sync for NamespaceObjectClass {} + +impl NamespaceObjectClass { + /// Create a new `NamespaceObjectClass` structure. + pub const unsafe fn new(name: &'static [u8]) -> Self { + NamespaceObjectClass(JSClass { + name: name as *const _ as *const libc::c_char, + flags: 0, + cOps: ptr::null_mut(), + reserved: [ptr::null_mut(); 3], + }) + } +} + +/// Create a new namespace object. +pub unsafe fn create_namespace_object( + cx: *mut JSContext, + global: HandleObject, + proto: HandleObject, + class: &'static NamespaceObjectClass, + methods: &[Guard<&'static [JSFunctionSpec]>], + name: &[u8], + rval: MutableHandleObject) { + create_object(cx, proto, &class.0, methods, &[], &[], rval); + define_on_global_object(cx, global, name, rval.handle()); +} diff --git a/components/script/dom/bindings/proxyhandler.rs b/components/script/dom/bindings/proxyhandler.rs index c863cc31994..cacac064376 100644 --- a/components/script/dom/bindings/proxyhandler.rs +++ b/components/script/dom/bindings/proxyhandler.rs @@ -8,23 +8,54 @@ use dom::bindings::conversions::is_dom_proxy; use dom::bindings::utils::delete_property_by_id; +use js::glue::{GetProxyHandler, GetProxyHandlerFamily, SetProxyExtra}; use js::glue::GetProxyExtra; use js::glue::InvokeGetOwnPropertyDescriptor; -use js::glue::{GetProxyHandler, SetProxyExtra}; +use js::jsapi::{DOMProxyShadowsResult, JSContext, JSObject, JSPROP_GETTER, PropertyDescriptor}; +use js::jsapi::{Handle, HandleId, HandleObject, MutableHandle, ObjectOpResult}; +use js::jsapi::{JSErrNum, JS_AlreadyHasOwnPropertyById, JS_StrictPropertyStub}; +use js::jsapi::{JS_DefinePropertyById, JS_NewObjectWithGivenProto, SetDOMProxyInformation}; use js::jsapi::GetObjectProto; use js::jsapi::GetStaticPrototype; use js::jsapi::JS_GetPropertyDescriptorById; use js::jsapi::MutableHandleObject; -use js::jsapi::{Handle, HandleId, HandleObject, MutableHandle, ObjectOpResult}; -use js::jsapi::{JSContext, JSObject, JSPROP_GETTER, PropertyDescriptor}; -use js::jsapi::{JSErrNum, JS_StrictPropertyStub}; -use js::jsapi::{JS_DefinePropertyById, JS_NewObjectWithGivenProto}; use js::jsval::ObjectValue; use libc; use std::{mem, ptr}; + static JSPROXYSLOT_EXPANDO: u32 = 0; +/// Determine if this id shadows any existing properties for this proxy. +pub unsafe extern "C" fn shadow_check_callback(cx: *mut JSContext, + object: HandleObject, + id: HandleId) + -> DOMProxyShadowsResult { + // TODO: support OverrideBuiltins when #12978 is fixed. + + rooted!(in(cx) let expando = get_expando_object(object)); + if !expando.get().is_null() { + let mut has_own = false; + if !JS_AlreadyHasOwnPropertyById(cx, expando.handle(), id, &mut has_own) { + return DOMProxyShadowsResult::ShadowCheckFailed; + } + + if has_own { + return DOMProxyShadowsResult::ShadowsViaDirectExpando; + } + } + + // Our expando, if any, didn't shadow, so we're not shadowing at all. + DOMProxyShadowsResult::DoesntShadow +} + +/// Initialize the infrastructure for DOM proxy objects. +pub unsafe fn init() { + SetDOMProxyInformation(GetProxyHandlerFamily(), + JSPROXYSLOT_EXPANDO, + Some(shadow_check_callback)); +} + /// Invoke the [[GetOwnProperty]] trap (`getOwnPropertyDescriptor`) on `proxy`, /// with argument `id` and return the result, if it is not `undefined`. /// Otherwise, walk along the prototype chain to find a property with that diff --git a/components/script/dom/bindings/refcounted.rs b/components/script/dom/bindings/refcounted.rs index 25239a3dc3b..ea4c47b1148 100644 --- a/components/script/dom/bindings/refcounted.rs +++ b/components/script/dom/bindings/refcounted.rs @@ -6,21 +6,21 @@ //! between threads (or intra-thread for asynchronous events). Akin to Gecko's //! nsMainThreadPtrHandle, this uses thread-safe reference counting and ensures //! that the actual SpiderMonkey GC integration occurs on the script thread via -//! message passing. Ownership of a `Trusted<T>` object means the DOM object of +//! weak refcounts. Ownership of a `Trusted<T>` object means the DOM object of //! type T to which it points remains alive. Any other behaviour is undefined. //! To guarantee the lifetime of a DOM object when performing asynchronous operations, //! obtain a `Trusted<T>` from that object and pass it along with each operation. //! A usable pointer to the original DOM object can be obtained on the script thread //! from a `Trusted<T>` via the `to_temporary` method. //! -//! The implementation of Trusted<T> is as follows: -//! A hashtable resides in the script thread, keyed on the pointer to the Rust DOM object. -//! The values in this hashtable are atomic reference counts. When a Trusted<T> object is -//! created or cloned, this count is increased. When a Trusted<T> is dropped, the count -//! decreases. If the count hits zero, a message is dispatched to the script thread to remove -//! the entry from the hashmap if the count is still zero. The JS reflector for the DOM object -//! is rooted when a hashmap entry is first created, and unrooted when the hashmap entry -//! is removed. +//! The implementation of `Trusted<T>` is as follows: +//! The `Trusted<T>` object contains an atomic reference counted pointer to the Rust DOM object. +//! A hashtable resides in the script thread, keyed on the pointer. +//! The values in this hashtable are weak reference counts. When a `Trusted<T>` object is +//! created or cloned, the reference count is increased. When a `Trusted<T>` is dropped, the count +//! decreases. If the count hits zero, the weak reference is emptied, and is removed from +//! its hash table during the next GC. During GC, the entries of the hash table are counted +//! as JS roots. use core::nonzero::NonZero; use dom::bindings::js::Root; @@ -28,13 +28,13 @@ use dom::bindings::reflector::{Reflectable, Reflector}; use dom::bindings::trace::trace_reflector; use js::jsapi::JSTracer; use libc; -use script_runtime::{CommonScriptMsg, ScriptChan}; 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::sync::{Arc, Mutex}; +use std::sync::{Arc, Weak}; #[allow(missing_docs)] // FIXME @@ -52,6 +52,12 @@ pub use self::dummy::LIVE_REFERENCES; pub struct TrustedReference(*const libc::c_void); unsafe impl Send for TrustedReference {} +impl TrustedReference { + fn new<T: Reflectable>(ptr: *const T) -> TrustedReference { + TrustedReference(ptr as *const libc::c_void) + } +} + /// 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 @@ -60,9 +66,7 @@ unsafe impl Send for TrustedReference {} pub struct Trusted<T: Reflectable> { /// A pointer to the Rust DOM object of type T, but void to allow /// sending `Trusted<T>` between threads, regardless of T's sendability. - ptr: *const libc::c_void, - refcount: Arc<Mutex<usize>>, - script_chan: Box<ScriptChan + Send>, + refcount: Arc<TrustedReference>, owner_thread: *const libc::c_void, phantom: PhantomData<T>, } @@ -74,15 +78,12 @@ impl<T: Reflectable> Trusted<T> { /// be prevented from being GCed for the duration of the resulting `Trusted<T>` object's /// lifetime. pub fn new(ptr: &T) -> Trusted<T> { - let script_chan = ptr.global().r().script_chan(); LIVE_REFERENCES.with(|ref r| { let r = r.borrow(); let live_references = r.as_ref().unwrap(); let refcount = live_references.addref(&*ptr as *const T); Trusted { - ptr: &*ptr as *const T as *const libc::c_void, refcount: refcount, - script_chan: script_chan.clone(), owner_thread: (&*live_references) as *const _ as *const libc::c_void, phantom: PhantomData, } @@ -99,48 +100,26 @@ impl<T: Reflectable> Trusted<T> { self.owner_thread == (&*live_references) as *const _ as *const libc::c_void })); unsafe { - Root::new(NonZero::new(self.ptr as *const T)) + Root::new(NonZero::new(self.refcount.0 as *const T)) } } } impl<T: Reflectable> Clone for Trusted<T> { fn clone(&self) -> Trusted<T> { - { - let mut refcount = self.refcount.lock().unwrap(); - *refcount += 1; - } - Trusted { - ptr: self.ptr, refcount: self.refcount.clone(), - script_chan: self.script_chan.clone(), owner_thread: self.owner_thread, phantom: PhantomData, } } } -impl<T: Reflectable> Drop for Trusted<T> { - fn drop(&mut self) { - let mut refcount = self.refcount.lock().unwrap(); - assert!(*refcount > 0); - *refcount -= 1; - if *refcount == 0 { - // It's possible this send will fail if the script thread - // has already exited. There's not much we can do at this - // point though. - let msg = CommonScriptMsg::RefcountCleanup(TrustedReference(self.ptr)); - let _ = self.script_chan.send(msg); - } - } -} - /// The set of live, pinned DOM objects that are currently prevented /// from being garbage collected due to outstanding references. pub struct LiveDOMReferences { // keyed on pointer to Rust DOM object - table: RefCell<HashMap<*const libc::c_void, Arc<Mutex<usize>>>>, + table: RefCell<HashMap<*const libc::c_void, Weak<TrustedReference>>>, } impl LiveDOMReferences { @@ -153,59 +132,55 @@ impl LiveDOMReferences { }); } - fn addref<T: Reflectable>(&self, ptr: *const T) -> Arc<Mutex<usize>> { + fn addref<T: Reflectable>(&self, ptr: *const T) -> Arc<TrustedReference> { let mut table = self.table.borrow_mut(); + let capacity = table.capacity(); + let len = table.len(); + if (0 < capacity) && (capacity <= len) { + info!("growing refcounted references by {}", len); + remove_nulls(&mut table); + table.reserve(len); + } match table.entry(ptr as *const libc::c_void) { - Occupied(mut entry) => { - let refcount = entry.get_mut(); - *refcount.lock().unwrap() += 1; - refcount.clone() - } + Occupied(mut entry) => match entry.get().upgrade() { + Some(refcount) => refcount, + None => { + let refcount = Arc::new(TrustedReference::new(ptr)); + entry.insert(Arc::downgrade(&refcount)); + refcount + }, + }, Vacant(entry) => { - let refcount = Arc::new(Mutex::new(1)); - entry.insert(refcount.clone()); + let refcount = Arc::new(TrustedReference::new(ptr)); + entry.insert(Arc::downgrade(&refcount)); refcount } } } +} - /// Unpin the given DOM object if its refcount is 0. - pub fn cleanup(raw_reflectable: TrustedReference) { - let TrustedReference(raw_reflectable) = raw_reflectable; - LIVE_REFERENCES.with(|ref r| { - let r = r.borrow(); - let live_references = r.as_ref().unwrap(); - let mut table = live_references.table.borrow_mut(); - match table.entry(raw_reflectable) { - Occupied(entry) => { - if *entry.get().lock().unwrap() != 0 { - // there could have been a new reference taken since - // this message was dispatched. - return; - } - - let _ = entry.remove(); - } - Vacant(_) => { - // there could be a cleanup message dispatched, then a new - // pinned reference obtained and released before the message - // is processed, at which point there would be no matching - // hashtable entry. - info!("attempt to cleanup an unrecognized reflector"); - } - } - }) +/// Remove null entries from the live references table +fn remove_nulls<K: Eq + Hash + Clone, V> (table: &mut HashMap<K, Weak<V>>) { + let to_remove: Vec<K> = + table.iter() + .filter(|&(_, value)| Weak::upgrade(value).is_none()) + .map(|(key, _)| key.clone()) + .collect(); + info!("removing {} refcounted references", to_remove.len()); + for key in to_remove { + table.remove(&key); } } /// A JSTraceDataOp for tracing reflectors held in LIVE_REFERENCES pub unsafe extern "C" fn trace_refcounted_objects(tracer: *mut JSTracer, _data: *mut os::raw::c_void) { - debug!("tracing live refcounted references"); + info!("tracing live refcounted references"); LIVE_REFERENCES.with(|ref r| { let r = r.borrow(); let live_references = r.as_ref().unwrap(); - let table = live_references.table.borrow(); + 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); diff --git a/components/script/dom/bindings/reflector.rs b/components/script/dom/bindings/reflector.rs index 04466ea6926..d5280712a03 100644 --- a/components/script/dom/bindings/reflector.rs +++ b/components/script/dom/bindings/reflector.rs @@ -73,11 +73,15 @@ impl Reflector { pub trait Reflectable { /// Returns the receiver's reflector. fn reflector(&self) -> &Reflector; - /// Initializes the Reflector - fn init_reflector(&mut self, obj: *mut JSObject); /// Returns the global object of the realm that the Reflectable was created in. fn global(&self) -> GlobalRoot where Self: Sized { global_root_from_reflector(self) } } + +/// A trait to initialize the `Reflector` for a DOM object. +pub trait MutReflectable: Reflectable { + /// Initializes the Reflector + fn init_reflector(&mut self, obj: *mut JSObject); +} diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs index c73a08d182b..2e28a1b314e 100644 --- a/components/script/dom/bindings/str.rs +++ b/components/script/dom/bindings/str.rs @@ -5,7 +5,7 @@ //! The `ByteString` struct. use std::ascii::AsciiExt; -use std::borrow::{ToOwned, Cow}; +use std::borrow::{Borrow, Cow, ToOwned}; use std::fmt; use std::hash::{Hash, Hasher}; use std::ops; @@ -180,6 +180,13 @@ impl DOMString { } } +impl Borrow<str> for DOMString { + #[inline] + fn borrow(&self) -> &str { + &self.0 + } +} + impl Default for DOMString { fn default() -> Self { DOMString(String::new()) diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index d6969f38d6c..fae24096bac 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -50,20 +50,22 @@ use html5ever::tree_builder::QuirksMode; use hyper::header::Headers; use hyper::method::Method; use hyper::mime::Mime; +use hyper::status::StatusCode; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use js::glue::{CallObjectTracer, CallUnbarrieredObjectTracer, CallValueTracer}; -use js::jsapi::{GCTraceKindToAscii, Heap, TraceKind, JSObject, JSTracer}; +use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind}; use js::jsval::JSVal; use js::rust::Runtime; use libc; -use msg::constellation_msg::{FrameType, PipelineId, SubpageId, WindowSizeType, ReferrerPolicy}; +use msg::constellation_msg::{FrameType, PipelineId, ReferrerPolicy, SubpageId, WindowSizeType}; +use net_traits::{Metadata, NetworkError, ResourceThreads}; use net_traits::filemanager_thread::RelativePos; use net_traits::image::base::{Image, ImageMetadata}; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread}; use net_traits::request::Request; +use net_traits::response::{Response, ResponseBody}; use net_traits::response::HttpsState; use net_traits::storage_thread::StorageType; -use net_traits::{Metadata, NetworkError, ResourceThreads}; use offscreen_gl_context::GLLimits; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; @@ -85,12 +87,13 @@ use std::rc::Rc; use std::sync::Arc; use std::sync::atomic::{AtomicBool, AtomicUsize}; use std::sync::mpsc::{Receiver, Sender}; -use std::time::SystemTime; +use std::time::{SystemTime, Instant}; use string_cache::{Atom, Namespace, QualName}; use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto}; +use style::domrefcell::DOMRefCell; use style::element_state::*; use style::properties::PropertyDeclarationBlock; -use style::selector_impl::{PseudoElement, ElementSnapshot}; +use style::selector_impl::{ElementSnapshot, PseudoElement}; use style::values::specified::Length; use time::Duration; use url::Origin as UrlOrigin; @@ -172,6 +175,13 @@ impl<T: JSTraceable> JSTraceable for UnsafeCell<T> { } } +impl<T: JSTraceable> JSTraceable for DOMRefCell<T> { + fn trace(&self, trc: *mut JSTracer) { + unsafe { + (*self).borrow_for_gc_trace().trace(trc) + } + } +} impl JSTraceable for Heap<*mut JSObject> { fn trace(&self, trc: *mut JSTracer) { @@ -331,8 +341,12 @@ no_jsmanaged_fields!(SharedRt); no_jsmanaged_fields!(TouchpadPressurePhase); no_jsmanaged_fields!(USVString); no_jsmanaged_fields!(ReferrerPolicy); +no_jsmanaged_fields!(Response); +no_jsmanaged_fields!(ResponseBody); no_jsmanaged_fields!(ResourceThreads); +no_jsmanaged_fields!(StatusCode); no_jsmanaged_fields!(SystemTime); +no_jsmanaged_fields!(Instant); no_jsmanaged_fields!(RelativePos); no_jsmanaged_fields!(OpaqueStyleAndLayoutData); no_jsmanaged_fields!(PathBuf); diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 6b2856e7a9e..32ffe4109ae 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -7,8 +7,7 @@ use dom::bindings::codegen::InterfaceObjectMap; use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList::{MAX_PROTO_CHAIN_LENGTH, PROTO_OR_IFACE_LENGTH}; -use dom::bindings::conversions::{DOM_OBJECT_SLOT, is_dom_class}; -use dom::bindings::conversions::{jsstring_to_str, private_from_proto_check}; +use dom::bindings::conversions::{is_dom_class, jsstring_to_str, private_from_proto_check}; use dom::bindings::error::throw_invalid_this; use dom::bindings::inheritance::TopTypeId; use dom::bindings::str::DOMString; @@ -21,20 +20,18 @@ use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper}; use js::glue::{GetCrossCompartmentWrapper, WrapperNew}; use js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT, RUST_JSID_IS_STRING}; use js::glue::{RUST_JSID_TO_INT, RUST_JSID_TO_STRING, UnwrapObject}; -use js::jsapi::{CallArgs, CompartmentOptions, DOMCallbacks, GetGlobalForObjectCrossCompartment}; -use js::jsapi::{HandleId, HandleObject, HandleValue, Heap, JSAutoCompartment, JSClass, JSContext}; -use js::jsapi::{JSJitInfo, JSObject, JSTraceOp, JSTracer, JSVersion, JSWrapObjectCallbacks}; -use js::jsapi::{JS_DeletePropertyById, JS_EnumerateStandardClasses, JS_FireOnNewGlobalObject}; +use js::jsapi::{CallArgs, DOMCallbacks, GetGlobalForObjectCrossCompartment}; +use js::jsapi::{HandleId, HandleObject, HandleValue, Heap, JSAutoCompartment, JSContext}; +use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks}; +use js::jsapi::{JS_DeletePropertyById, JS_EnumerateStandardClasses}; use js::jsapi::{JS_ForwardGetPropertyTo, JS_GetClass, JS_GetLatin1StringCharsAndLength}; use js::jsapi::{JS_GetProperty, JS_GetPrototype, JS_GetReservedSlot, JS_HasProperty}; -use js::jsapi::{JS_HasPropertyById, JS_IsExceptionPending, JS_IsGlobalObject, JS_NewGlobalObject}; +use js::jsapi::{JS_HasPropertyById, JS_IsExceptionPending, JS_IsGlobalObject}; use js::jsapi::{JS_ResolveStandardClass, JS_SetProperty, ToWindowProxyIfWindow}; -use js::jsapi::{JS_SetReservedSlot, JS_StringHasLatin1Chars, MutableHandleValue}; -use js::jsapi::{ObjectOpResult, OnNewGlobalHookOption}; -use js::jsval::{JSVal, ObjectValue, PrivateValue, UndefinedValue}; +use js::jsapi::{JS_StringHasLatin1Chars, MutableHandleValue, ObjectOpResult}; +use js::jsval::{JSVal, UndefinedValue}; use js::rust::{GCMethods, ToString}; use libc; -use std::default::Default; use std::ffi::CString; use std::os::raw::c_void; use std::ptr; @@ -103,7 +100,7 @@ unsafe impl Sync for DOMClass {} #[derive(Copy)] pub struct DOMJSClass { /// The actual JSClass. - pub base: js::jsapi::Class, + pub base: js::jsapi::JSClass, /// Associated data for DOM object reflectors. pub dom_class: DOMClass, } @@ -130,32 +127,29 @@ pub type ProtoOrIfaceArray = [*mut JSObject; PROTO_OR_IFACE_LENGTH]; /// set to true and `*vp` to the value, otherwise `*found` is set to false. /// /// Returns false on JSAPI failure. -pub fn get_property_on_prototype(cx: *mut JSContext, - proxy: HandleObject, - id: HandleId, - found: *mut bool, - vp: MutableHandleValue) - -> bool { - unsafe { - // let proto = GetObjectProto(proxy); - rooted!(in(cx) let mut proto = ptr::null_mut()); - if !JS_GetPrototype(cx, proxy, proto.handle_mut()) || proto.is_null() { - *found = false; - return true; - } - let mut has_property = false; - if !JS_HasPropertyById(cx, proto.handle(), id, &mut has_property) { - return false; - } - *found = has_property; - let no_output = vp.ptr.is_null(); - if !has_property || no_output { - return true; - } - - rooted!(in(cx) let receiver = ObjectValue(&*proxy.get())); - JS_ForwardGetPropertyTo(cx, proto.handle(), id, receiver.handle(), vp) +pub unsafe fn get_property_on_prototype(cx: *mut JSContext, + proxy: HandleObject, + receiver: HandleValue, + id: HandleId, + found: *mut bool, + vp: MutableHandleValue) + -> bool { + rooted!(in(cx) let mut proto = ptr::null_mut()); + if !JS_GetPrototype(cx, proxy, proto.handle_mut()) || proto.is_null() { + *found = false; + return true; + } + let mut has_property = false; + if !JS_HasPropertyById(cx, proto.handle(), id, &mut has_property) { + return false; } + *found = has_property; + let no_output = vp.ptr.is_null(); + if !has_property || no_output { + return true; + } + + JS_ForwardGetPropertyTo(cx, proto.handle(), id, receiver, vp) } /// Get an array index from the given `jsid`. Returns `None` if the given @@ -288,49 +282,17 @@ pub fn set_dictionary_property(cx: *mut JSContext, } /// Returns whether `proxy` has a property `id` on its prototype. -pub fn has_property_on_prototype(cx: *mut JSContext, proxy: HandleObject, id: HandleId) -> bool { - // MOZ_ASSERT(js::IsProxy(proxy) && js::GetProxyHandler(proxy) == handler); - let mut found = false; - !get_property_on_prototype(cx, proxy, id, &mut found, unsafe { - MutableHandleValue::from_marked_location(ptr::null_mut()) - }) || found -} - -/// Create a DOM global object with the given class. -pub fn create_dom_global(cx: *mut JSContext, - class: *const JSClass, - private: *const libc::c_void, - trace: JSTraceOp) - -> *mut JSObject { - unsafe { - let mut options = CompartmentOptions::default(); - options.behaviors_.version_ = JSVersion::JSVERSION_ECMA_5; - options.creationOptions_.traceGlobal_ = trace; - options.creationOptions_.sharedMemoryAndAtomics_ = true; - - rooted!(in(cx) let obj = - JS_NewGlobalObject(cx, - class, - ptr::null_mut(), - OnNewGlobalHookOption::DontFireOnNewGlobalHook, - &options)); - if obj.is_null() { - return ptr::null_mut(); - } - - // Initialize the reserved slots before doing amything that can GC, to - // avoid getting trace hooks called on a partially initialized object. - JS_SetReservedSlot(obj.get(), DOM_OBJECT_SLOT, PrivateValue(private)); - let proto_array: Box<ProtoOrIfaceArray> = - box [0 as *mut JSObject; PROTO_OR_IFACE_LENGTH]; - JS_SetReservedSlot(obj.get(), - DOM_PROTOTYPE_SLOT, - PrivateValue(Box::into_raw(proto_array) as *const libc::c_void)); - - let _ac = JSAutoCompartment::new(cx, obj.get()); - JS_FireOnNewGlobalObject(cx, obj.handle()); - obj.get() +pub unsafe fn has_property_on_prototype(cx: *mut JSContext, + proxy: HandleObject, + id: HandleId, + found: &mut bool) + -> bool { + rooted!(in(cx) let mut proto = ptr::null_mut()); + if !JS_GetPrototype(cx, proxy, proto.handle_mut()) { + return false; } + assert!(!proto.is_null()); + JS_HasPropertyById(cx, proto.handle(), id, found) } /// Drop the resources held by reserved slots of a global object diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index 0e513081e10..78990f40564 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -14,9 +14,9 @@ use dom::bindings::str::DOMString; use encoding::all::UTF_8; use encoding::types::{EncoderTrap, Encoding}; use ipc_channel::ipc; -use net_traits::blob_url_store::{BlobBuf, get_blob_origin}; -use net_traits::filemanager_thread::{FileManagerThreadMsg, RelativePos, ReadFileProgress}; use net_traits::{CoreResourceMsg, IpcSend}; +use net_traits::blob_url_store::{BlobBuf, get_blob_origin}; +use net_traits::filemanager_thread::{FileManagerThreadMsg, ReadFileProgress, RelativePos}; use std::cell::Cell; use std::mem; use std::ops::Index; diff --git a/components/script/dom/bluetooth.rs b/components/script/dom/bluetooth.rs index c86fa0150a3..6540f6c971f 100644 --- a/components/script/dom/bluetooth.rs +++ b/components/script/dom/bluetooth.rs @@ -5,8 +5,8 @@ use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted}; use core::clone::Clone; use dom::bindings::codegen::Bindings::BluetoothBinding; +use dom::bindings::codegen::Bindings::BluetoothBinding::{BluetoothMethods, BluetoothScanFilter}; use dom::bindings::codegen::Bindings::BluetoothBinding::RequestDeviceOptions; -use dom::bindings::codegen::Bindings::BluetoothBinding::{BluetoothScanFilter, BluetoothMethods}; use dom::bindings::error::Error::{self, Security, Type}; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; diff --git a/components/script/dom/bluetoothremotegattcharacteristic.rs b/components/script/dom/bluetoothremotegattcharacteristic.rs index 0c27097625c..c88463920e6 100644 --- a/components/script/dom/bluetoothremotegattcharacteristic.rs +++ b/components/script/dom/bluetoothremotegattcharacteristic.rs @@ -12,8 +12,8 @@ use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding:: BluetoothRemoteGATTCharacteristicMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods; +use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::Error::{self, InvalidModification, Network, NotSupported, Security}; -use dom::bindings::error::{Fallible, ErrorResult}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, MutHeap, Root}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; diff --git a/components/script/dom/bluetoothremotegattdescriptor.rs b/components/script/dom/bluetoothremotegattdescriptor.rs index 0ba52181b48..a726de42974 100644 --- a/components/script/dom/bluetoothremotegattdescriptor.rs +++ b/components/script/dom/bluetoothremotegattdescriptor.rs @@ -11,8 +11,8 @@ use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTDescriptorBinding::BluetoothRemoteGATTDescriptorMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods; +use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::Error::{self, InvalidModification, Network, Security}; -use dom::bindings::error::{Fallible, ErrorResult}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, MutHeap, Root}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; diff --git a/components/script/dom/bluetoothremotegattserver.rs b/components/script/dom/bluetoothremotegattserver.rs index f4b1e532f10..615094623e9 100644 --- a/components/script/dom/bluetoothremotegattserver.rs +++ b/components/script/dom/bluetoothremotegattserver.rs @@ -6,8 +6,8 @@ use bluetooth_blacklist::{Blacklist, uuid_is_blacklisted}; use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; +use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::error::Error::{self, Security}; -use dom::bindings::error::{Fallible, ErrorResult}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, MutHeap, Root}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; diff --git a/components/script/dom/browsingcontext.rs b/components/script/dom/browsingcontext.rs index 878d376ca66..919b574c27a 100644 --- a/components/script/dom/browsingcontext.rs +++ b/components/script/dom/browsingcontext.rs @@ -7,7 +7,7 @@ use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject}; use dom::bindings::js::{JS, Root, RootedReference}; use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor}; -use dom::bindings::reflector::{Reflectable, Reflector}; +use dom::bindings::reflector::{Reflectable, MutReflectable, Reflector}; use dom::bindings::str::DOMString; use dom::bindings::trace::JSTraceable; use dom::bindings::utils::WindowProxyHandler; @@ -18,11 +18,13 @@ use dom::window::Window; use js::JSCLASS_IS_GLOBAL; use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy}; use js::glue::{GetProxyPrivate, SetProxyExtra, GetProxyExtra}; -use js::jsapi::{Handle, HandleId, HandleObject, HandleValue, JSAutoCompartment}; -use js::jsapi::{JSContext, JSPROP_READONLY, JSErrNum, JSObject, PropertyDescriptor, JS_DefinePropertyById}; -use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo, JS_GetClass, JSTracer, FreeOp}; -use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById, MutableHandle}; -use js::jsapi::{MutableHandleObject, MutableHandleValue, ObjectOpResult}; +use js::jsapi::{Handle, HandleId, HandleObject, HandleValue}; +use js::jsapi::{JSAutoCompartment, JSContext, JSErrNum, JSFreeOp, JSObject}; +use js::jsapi::{JSPROP_READONLY, JSTracer, JS_DefinePropertyById}; +use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo, JS_GetClass}; +use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById}; +use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue}; +use js::jsapi::{ObjectOpResult, PropertyDescriptor}; use js::jsval::{UndefinedValue, PrivateValue}; use msg::constellation_msg::{PipelineId, SubpageId}; use std::cell::Cell; @@ -409,7 +411,7 @@ static PROXY_HANDLER: ProxyTraps = ProxyTraps { }; #[allow(unsafe_code)] -unsafe extern fn finalize(_fop: *mut FreeOp, obj: *mut JSObject) { +unsafe extern fn finalize(_fop: *mut JSFreeOp, obj: *mut JSObject) { let this = GetProxyExtra(obj, 0).to_private() as *mut BrowsingContext; assert!(!this.is_null()); let _ = Box::from_raw(this); diff --git a/components/script/dom/canvasgradient.rs b/components/script/dom/canvasgradient.rs index f47589b60af..a805b485796 100644 --- a/components/script/dom/canvasgradient.rs +++ b/components/script/dom/canvasgradient.rs @@ -3,8 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use canvas_traits::{CanvasGradientStop, FillOrStrokeStyle, LinearGradientStyle, RadialGradientStyle}; -use cssparser::Color as CSSColor; use cssparser::{Parser, RGBA}; +use cssparser::Color as CSSColor; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::CanvasGradientBinding; use dom::bindings::codegen::Bindings::CanvasGradientBinding::CanvasGradientMethods; diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index 09d4a8f04e1..13610c53487 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -5,9 +5,9 @@ use canvas_traits::{Canvas2dMsg, CanvasCommonMsg, CanvasMsg}; use canvas_traits::{CompositionOrBlending, FillOrStrokeStyle, FillRule}; use canvas_traits::{LineCapStyle, LineJoinStyle, LinearGradientStyle}; -use canvas_traits::{RadialGradientStyle, RepetitionStyle, byte_swap}; -use cssparser::Color as CSSColor; +use canvas_traits::{RadialGradientStyle, RepetitionStyle, byte_swap, byte_swap_and_premultiply}; use cssparser::{Parser, RGBA}; +use cssparser::Color as CSSColor; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::CSSStyleDeclarationBinding::CSSStyleDeclarationMethods; use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding; @@ -19,7 +19,7 @@ use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D; use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern; -use dom::bindings::error::{Error, Fallible, ErrorResult}; +use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, LayoutJS, Root}; @@ -42,11 +42,12 @@ use net_traits::image::base::PixelFormat; use net_traits::image_cache_thread::ImageResponse; use num_traits::ToPrimitive; use script_traits::ScriptMsg as ConstellationMsg; +use std::{cmp, fmt}; use std::cell::Cell; use std::str::FromStr; -use std::{cmp, fmt}; use unpremultiplytable::UNPREMULTIPLY_TABLE; use url::Url; +use util::opts; #[must_root] #[derive(JSTraceable, Clone, HeapSizeOf)] @@ -299,7 +300,14 @@ impl CanvasRenderingContext2D { Some((mut data, size)) => { // Pixels come from cache in BGRA order and drawImage expects RGBA so we // have to swap the color values - byte_swap(&mut data); + if opts::get().use_webrender { + // Webrender doesn't pre-multiply alpha when decoding + // images, but canvas expects the images to be + // pre-multiplied alpha. + byte_swap_and_premultiply(&mut data); + } else { + byte_swap(&mut data); + } let size = Size2D::new(size.width as f64, size.height as f64); (data, size) }, @@ -1092,7 +1100,7 @@ impl CanvasRenderingContext2DMethods for CanvasRenderingContext2D { dirtyY: Finite<f64>, dirtyWidth: Finite<f64>, dirtyHeight: Finite<f64>) { - let data = imagedata.get_data_array(&self.global().r()); + let data = imagedata.get_data_array(); let offset = Point2D::new(*dx, *dy); let image_data_size = Size2D::new(imagedata.Width() as f64, imagedata.Height() as f64); diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs index 01f11e3ac50..979e9a70434 100644 --- a/components/script/dom/characterdata.rs +++ b/components/script/dom/characterdata.rs @@ -19,7 +19,7 @@ use dom::element::Element; use dom::node::{Node, NodeDamage}; use dom::processinginstruction::ProcessingInstruction; use dom::text::Text; -use std::cell::Ref; +use style::refcell::Ref; use util::opts; // https://dom.spec.whatwg.org/#characterdata diff --git a/components/script/dom/client.rs b/components/script/dom/client.rs index 1039ff1a04a..d800d6f55e0 100644 --- a/components/script/dom/client.rs +++ b/components/script/dom/client.rs @@ -2,8 +2,8 @@ * 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::ClientBinding::FrameType; use dom::bindings::codegen::Bindings::ClientBinding::{ClientMethods, Wrap}; +use dom::bindings::codegen::Bindings::ClientBinding::FrameType; use dom::bindings::global::GlobalRef; use dom::bindings::js::JS; use dom::bindings::js::Root; diff --git a/components/script/dom/console.rs b/components/script/dom/console.rs index 9382bf936a8..37d549befd9 100644 --- a/components/script/dom/console.rs +++ b/components/script/dom/console.rs @@ -4,39 +4,17 @@ use devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg}; use dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::ConsoleBinding; -use dom::bindings::codegen::Bindings::ConsoleBinding::ConsoleMethods; use dom::bindings::global::GlobalRef; -use dom::bindings::js::Root; -use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use std::collections::HashMap; +use std::collections::hash_map::Entry; use time::{Timespec, get_time}; // https://developer.mozilla.org/en-US/docs/Web/API/Console -#[dom_struct] -pub struct Console { - reflector_: Reflector, - timers: DOMRefCell<HashMap<DOMString, u64>>, -} +pub struct Console(()); impl Console { - fn new_inherited() -> Console { - Console { - reflector_: Reflector::new(), - timers: DOMRefCell::new(HashMap::new()), - } - } - - pub fn new(global: GlobalRef) -> Root<Console> { - reflect_dom_object(box Console::new_inherited(), - global, - ConsoleBinding::Wrap) - } - - fn send_to_devtools(&self, level: LogLevel, message: DOMString) { - let global = self.global(); - let global = global.r(); + fn send_to_devtools(global: GlobalRef, level: LogLevel, message: DOMString) { if let Some(chan) = global.devtools_chan() { let console_message = prepare_message(level, message); let devtools_message = ScriptToDevtoolsControlMsg::ConsoleAPI( @@ -48,83 +26,73 @@ impl Console { } } -impl ConsoleMethods for Console { +impl Console { // https://developer.mozilla.org/en-US/docs/Web/API/Console/log - fn Log(&self, messages: Vec<DOMString>) { + pub fn Log(global: GlobalRef, messages: Vec<DOMString>) { for message in messages { println!("{}", message); - self.send_to_devtools(LogLevel::Log, message); + Self::send_to_devtools(global, LogLevel::Log, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console - fn Debug(&self, messages: Vec<DOMString>) { + pub fn Debug(global: GlobalRef, messages: Vec<DOMString>) { for message in messages { println!("{}", message); - self.send_to_devtools(LogLevel::Debug, message); + Self::send_to_devtools(global, LogLevel::Debug, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/info - fn Info(&self, messages: Vec<DOMString>) { + pub fn Info(global: GlobalRef, messages: Vec<DOMString>) { for message in messages { println!("{}", message); - self.send_to_devtools(LogLevel::Info, message); + Self::send_to_devtools(global, LogLevel::Info, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/warn - fn Warn(&self, messages: Vec<DOMString>) { + pub fn Warn(global: GlobalRef, messages: Vec<DOMString>) { for message in messages { println!("{}", message); - self.send_to_devtools(LogLevel::Warn, message); + Self::send_to_devtools(global, LogLevel::Warn, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/error - fn Error(&self, messages: Vec<DOMString>) { + pub fn Error(global: GlobalRef, messages: Vec<DOMString>) { for message in messages { println!("{}", message); - self.send_to_devtools(LogLevel::Error, message); + Self::send_to_devtools(global, LogLevel::Error, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/assert - fn Assert(&self, condition: bool, message: Option<DOMString>) { + pub fn Assert(global: GlobalRef, condition: bool, message: Option<DOMString>) { if !condition { let message = message.unwrap_or_else(|| DOMString::from("no message")); println!("Assertion failed: {}", message); - self.send_to_devtools(LogLevel::Error, message); + Self::send_to_devtools(global, LogLevel::Error, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/time - fn Time(&self, label: DOMString) { - let mut timers = self.timers.borrow_mut(); - if timers.contains_key(&label) { - // Timer already started - return; - } - if timers.len() >= 10000 { - // Too many timers on page - return; + pub fn Time(global: GlobalRef, label: DOMString) { + if let Ok(()) = global.console_timers().time(label.clone()) { + let message = DOMString::from(format!("{}: timer started", label)); + println!("{}", message); + Self::send_to_devtools(global, LogLevel::Log, message); } - - timers.insert(label.clone(), timestamp_in_ms(get_time())); - let message = DOMString::from(format!("{}: timer started", label)); - println!("{}", message); - self.send_to_devtools(LogLevel::Log, message); } // https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd - fn TimeEnd(&self, label: DOMString) { - let mut timers = self.timers.borrow_mut(); - if let Some(start) = timers.remove(&label) { + pub fn TimeEnd(global: GlobalRef, label: DOMString) { + if let Ok(delta) = global.console_timers().time_end(&label) { let message = DOMString::from( - format!("{}: {}ms", label, timestamp_in_ms(get_time()) - start) + format!("{}: {}ms", label, delta) ); println!("{}", message); - self.send_to_devtools(LogLevel::Log, message); + Self::send_to_devtools(global, LogLevel::Log, message); }; } } @@ -143,3 +111,32 @@ fn prepare_message(logLevel: LogLevel, message: DOMString) -> ConsoleMessage { columnNumber: 1, } } + +#[derive(HeapSizeOf, JSTraceable)] +pub struct TimerSet(DOMRefCell<HashMap<DOMString, u64>>); + +impl TimerSet { + pub fn new() -> Self { + TimerSet(DOMRefCell::new(Default::default())) + } + + fn time(&self, label: DOMString) -> Result<(), ()> { + let mut timers = self.0.borrow_mut(); + if timers.len() >= 10000 { + return Err(()); + } + match timers.entry(label) { + Entry::Vacant(entry) => { + entry.insert(timestamp_in_ms(get_time())); + Ok(()) + }, + Entry::Occupied(_) => Err(()), + } + } + + fn time_end(&self, label: &str) -> Result<u64, ()> { + self.0.borrow_mut().remove(label).ok_or(()).map(|start| { + timestamp_in_ms(get_time()) - start + }) + } +} diff --git a/components/script/dom/crypto.rs b/components/script/dom/crypto.rs index 6e0351d37ee..165a3834227 100644 --- a/components/script/dom/crypto.rs +++ b/components/script/dom/crypto.rs @@ -2,6 +2,7 @@ * 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 core::nonzero::NonZero; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::CryptoBinding; use dom::bindings::codegen::Bindings::CryptoBinding::CryptoMethods; @@ -43,7 +44,8 @@ impl CryptoMethods for Crypto { fn GetRandomValues(&self, _cx: *mut JSContext, input: *mut JSObject) - -> Fallible<*mut JSObject> { + -> Fallible<NonZero<*mut JSObject>> { + assert!(!input.is_null()); let mut data = match unsafe { array_buffer_view_data::<u8>(input) } { Some(data) => data, None => { @@ -62,7 +64,7 @@ impl CryptoMethods for Crypto { self.rng.borrow_mut().fill_bytes(&mut data); - Ok(input) + Ok(unsafe { NonZero::new(input) }) } } diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index 076a2283b67..c048446d813 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -14,12 +14,13 @@ use dom::element::Element; use dom::node::{Node, NodeDamage, window_from_node}; use dom::window::Window; use std::ascii::AsciiExt; -use std::cell::Ref; use std::slice; +use std::sync::Arc; use string_cache::Atom; use style::parser::ParserContextExtraData; use style::properties::{PropertyDeclaration, Shorthand, Importance}; use style::properties::{is_supported_property, parse_one_declaration, parse_style_attribute}; +use style::refcell::Ref; use style::selector_impl::PseudoElement; // http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface @@ -100,18 +101,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-item fn Item(&self, index: u32) -> DOMString { - let index = index as usize; - let elem = self.owner.upcast::<Element>(); - let style_attribute = elem.style_attribute().borrow(); - style_attribute.as_ref().and_then(|declarations| { - declarations.declarations.get(index) - }).map(|&(ref declaration, importance)| { - let mut css = declaration.to_css_string(); - if importance.important() { - css += " !important"; - } - DOMString::from(css) - }).unwrap_or_else(DOMString::new) + self.IndexedGetter(index).unwrap_or_default() } // https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue @@ -333,10 +323,19 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { } // https://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface - fn IndexedGetter(&self, index: u32, found: &mut bool) -> DOMString { - let rval = self.Item(index); - *found = index < self.Length(); - rval + fn IndexedGetter(&self, index: u32) -> Option<DOMString> { + let index = index as usize; + let elem = self.owner.upcast::<Element>(); + let style_attribute = elem.style_attribute().borrow(); + style_attribute.as_ref().and_then(|declarations| { + declarations.declarations.get(index) + }).map(|&(ref declaration, importance)| { + let mut css = declaration.to_css_string(); + if importance.important() { + css += " !important"; + } + DOMString::from(css) + }) } // https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext @@ -367,7 +366,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration { *element.style_attribute().borrow_mut() = if decl_block.declarations.is_empty() { None // Step 2 } else { - Some(decl_block) + Some(Arc::new(decl_block)) }; element.sync_property_with_attrs_style(); let node = element.upcast::<Node>(); diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index 68899bd36b8..4f0bad72949 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -4,22 +4,24 @@ use devtools; use devtools_traits::DevtoolScriptControlMsg; -use dom::abstractworker::{WorkerScriptMsg, SharedRt , SimpleWorkerErrorHandler}; +use dom::abstractworker::{SharedRt, SimpleWorkerErrorHandler, WorkerScriptMsg}; use dom::abstractworkerglobalscope::{SendableWorkerScriptChan, WorkerThreadWorkerChan}; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding; use dom::bindings::codegen::Bindings::DedicatedWorkerGlobalScopeBinding::DedicatedWorkerGlobalScopeMethods; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; -use dom::bindings::error::ErrorResult; +use dom::bindings::error::{ErrorInfo, ErrorResult}; use dom::bindings::global::{GlobalRef, global_root_from_context}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{Root, RootCollection}; -use dom::bindings::refcounted::LiveDOMReferences; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; +use dom::errorevent::ErrorEvent; +use dom::event::{Event, EventBubbles, EventCancelable}; +use dom::eventtarget::EventTarget; use dom::messageevent::MessageEvent; -use dom::worker::{TrustedWorkerAddress, WorkerMessageHandler}; +use dom::worker::{TrustedWorkerAddress, WorkerErrorHandler, WorkerMessageHandler}; use dom::workerglobalscope::WorkerGlobalScope; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; @@ -28,15 +30,16 @@ use js::jsapi::{JSAutoCompartment, JSContext}; use js::jsval::UndefinedValue; use js::rust::Runtime; use msg::constellation_msg::PipelineId; -use net_traits::{LoadContext, load_whole_resource, IpcSend}; +use net_traits::{IpcSend, LoadContext, load_whole_resource}; use rand::random; -use script_runtime::ScriptThreadEventCategory::WorkerEvent; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, StackRootTLS, get_reports, new_rt_and_cx}; -use script_traits::{TimerEvent, TimerSource, WorkerScriptLoadOrigin, WorkerGlobalScopeInit}; +use script_runtime::ScriptThreadEventCategory::WorkerEvent; +use script_traits::{TimerEvent, TimerSource, WorkerGlobalScopeInit, WorkerScriptLoadOrigin}; +use std::cell::Cell; use std::mem::replace; +use std::sync::{Arc, Mutex}; use std::sync::atomic::AtomicBool; use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel}; -use std::sync::{Arc, Mutex}; use style::thread_state; use url::Url; use util::thread::spawn_named; @@ -89,6 +92,8 @@ pub struct DedicatedWorkerGlobalScope { #[ignore_heap_size_of = "Can't measure trait objects"] /// Sender to the parent thread. parent_sender: Box<ScriptChan + Send>, + /// https://html.spec.whatwg.org/multipage/#in-error-reporting-mode + in_error_reporting_mode: Cell<bool> } impl DedicatedWorkerGlobalScope { @@ -117,6 +122,7 @@ impl DedicatedWorkerGlobalScope { timer_event_port: timer_event_port, parent_sender: parent_sender, worker: DOMRefCell::new(None), + in_error_reporting_mode: Cell::new(false), } } @@ -298,9 +304,6 @@ impl DedicatedWorkerGlobalScope { WorkerScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) => { runnable.handler() }, - WorkerScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => { - LiveDOMReferences::cleanup(addr); - }, WorkerScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => { let scope = self.upcast::<WorkerGlobalScope>(); let cx = scope.get_cx(); @@ -343,6 +346,42 @@ impl DedicatedWorkerGlobalScope { } } } + + /// https://html.spec.whatwg.org/multipage/#report-the-error + pub fn report_an_error(&self, error_info: ErrorInfo, value: HandleValue) { + // Step 1. + if self.in_error_reporting_mode.get() { + return; + } + + // Step 2. + self.in_error_reporting_mode.set(true); + + // Steps 3-12. + // FIXME(#13195): muted errors. + let event = ErrorEvent::new(GlobalRef::Worker(self.upcast()), + atom!("error"), + EventBubbles::DoesNotBubble, + EventCancelable::Cancelable, + error_info.message.as_str().into(), + error_info.filename.as_str().into(), + error_info.lineno, + error_info.column, + value); + + // Step 13. + let handled = !event.upcast::<Event>().fire(self.upcast::<EventTarget>()); + if !handled { + let worker = self.worker.borrow().as_ref().unwrap().clone(); + // TODO: Should use the DOM manipulation task source. + self.parent_sender + .send(CommonScriptMsg::RunnableMsg(WorkerEvent, + box WorkerErrorHandler::new(worker, error_info))) + .unwrap(); + } + + self.in_error_reporting_mode.set(false); + } } #[allow(unsafe_code)] diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index cbd4b2ac253..fe55c035ff7 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -2,6 +2,7 @@ * 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 core::nonzero::NonZero; use document_loader::{DocumentLoader, LoadType}; use dom::activation::{ActivationSource, synthetic_click_activation}; use dom::attr::Attr; @@ -22,14 +23,14 @@ use dom::bindings::codegen::UnionTypes::NodeOrString; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; -use dom::bindings::js::RootedReference; use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root}; +use dom::bindings::js::RootedReference; use dom::bindings::num::Finite; use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::{DOMString, USVString}; +use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type}; use dom::bindings::xmlname::XMLName::InvalidXMLName; -use dom::bindings::xmlname::{validate_and_extract, namespace_from_domstring, xml_name_type}; use dom::browsingcontext::BrowsingContext; use dom::closeevent::CloseEvent; use dom::comment::Comment; @@ -89,39 +90,40 @@ use encoding::all::UTF_8; use euclid::point::Point2D; use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks, QuirksMode}; use ipc_channel::ipc::{self, IpcSender}; -use js::jsapi::JS_GetRuntime; use js::jsapi::{JSContext, JSObject, JSRuntime}; +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, SubpageId}; +use net_traits::{AsyncResponseTarget, IpcSend, PendingAsyncLoad}; use net_traits::CookieSource::NonHTTP; use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl}; use net_traits::response::HttpsState; -use net_traits::{AsyncResponseTarget, PendingAsyncLoad, IpcSend}; use num_traits::ToPrimitive; use origin::Origin; -use parse::{ParserRoot, ParserRef, MutNullableParserField}; +use parse::{MutNullableParserField, ParserRef, ParserRoot}; use script_layout_interface::message::{Msg, ReflowQueryType}; use script_thread::{MainThreadScriptMsg, Runnable}; -use script_traits::UntrustedNodeAddress; use script_traits::{AnimationState, MouseButton, MouseEventType, MozBrowserEvent}; use script_traits::{ScriptMsg as ConstellationMsg, TouchpadPressurePhase}; use script_traits::{TouchEventType, TouchId}; +use script_traits::UntrustedNodeAddress; use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::boxed::FnBox; -use std::cell::{Cell, Ref, RefMut}; +use std::cell::Cell; use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::default::Default; use std::iter::once; use std::mem; -use std::ptr; use std::rc::Rc; use std::sync::Arc; +use std::time::{Duration, Instant}; use string_cache::{Atom, QualName}; use style::attr::AttrValue; use style::context::ReflowGoal; +use style::refcell::{Ref, RefMut}; use style::selector_impl::ElementSnapshot; use style::str::{split_html_space_chars, str_join}; use style::stylesheets::Stylesheet; @@ -221,6 +223,9 @@ pub struct Document { /// For each element that has had a state or attribute change since the last restyle, /// track the original condition of the element. modified_elements: DOMRefCell<HashMap<JS<Element>, ElementSnapshot>>, + /// This flag will be true if layout suppressed a reflow attempt that was + /// needed in order for the page to be painted. + needs_paint: Cell<bool>, /// http://w3c.github.io/touch-events/#dfn-active-touch-point active_touch_points: DOMRefCell<Vec<JS<Touch>>>, /// Navigation Timing properties: @@ -243,6 +248,9 @@ pub struct Document { referrer: Option<String>, /// https://html.spec.whatwg.org/multipage/#target-element target_element: MutNullableHeap<JS<Element>>, + /// https://w3c.github.io/uievents/#event-type-dblclick + #[ignore_heap_size_of = "Defined in std"] + last_click_info: DOMRefCell<Option<(Instant, Point2D<f32>)>>, } #[derive(JSTraceable, HeapSizeOf)] @@ -376,6 +384,10 @@ impl Document { } } + pub fn needs_paint(&self) -> bool { + self.needs_paint.get() + } + pub fn needs_reflow(&self) -> bool { // FIXME: This should check the dirty bit on the document, // not the document element. Needs some layout changes to make @@ -384,7 +396,8 @@ impl Document { Some(root) => { root.upcast::<Node>().is_dirty() || root.upcast::<Node>().has_dirty_descendants() || - !self.modified_elements.borrow().is_empty() + !self.modified_elements.borrow().is_empty() || + self.needs_paint() } None => false, } @@ -765,12 +778,64 @@ impl Document { if let MouseEventType::Click = mouse_event_type { self.commit_focus_transaction(FocusType::Element); + self.maybe_fire_dblclick(client_point, node); } + self.window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::MouseEvent); } + fn maybe_fire_dblclick(&self, click_pos: Point2D<f32>, target: &Node) { + // https://w3c.github.io/uievents/#event-type-dblclick + let now = Instant::now(); + + let opt = self.last_click_info.borrow_mut().take(); + + if let Some((last_time, last_pos)) = opt { + let DBL_CLICK_TIMEOUT = Duration::from_millis(PREFS.get("dom.document.dblclick_timeout").as_u64() + .unwrap_or(300)); + let DBL_CLICK_DIST_THRESHOLD = PREFS.get("dom.document.dblclick_dist").as_u64().unwrap_or(1); + + // Calculate distance between this click and the previous click. + let line = click_pos - last_pos; + let dist = (line.dot(line) as f64).sqrt(); + + if now.duration_since(last_time) < DBL_CLICK_TIMEOUT && + dist < DBL_CLICK_DIST_THRESHOLD as f64 { + // A double click has occurred if this click is within a certain time and dist. of previous click. + let clickCount = 2; + let client_x = click_pos.x as i32; + let client_y = click_pos.y as i32; + + let event = MouseEvent::new(&self.window, + DOMString::from("dblclick"), + EventBubbles::Bubbles, + EventCancelable::Cancelable, + Some(&self.window), + clickCount, + client_x, + client_y, + client_x, + client_y, + false, + false, + false, + false, + 0i16, + None); + event.upcast::<Event>().fire(target.upcast()); + + // When a double click occurs, self.last_click_info is left as None so that a + // third sequential click will not cause another double click. + return; + } + } + + // Update last_click_info with the time and position of the click. + *self.last_click_info.borrow_mut() = Some((now, click_pos)); + } + pub fn handle_touchpad_pressure_event(&self, js_runtime: *mut JSRuntime, client_point: Point2D<f32>, @@ -1602,6 +1667,8 @@ pub enum DocumentSource { pub trait LayoutDocumentHelpers { unsafe fn is_html_document_for_layout(&self) -> bool; unsafe fn drain_modified_elements(&self) -> Vec<(LayoutJS<Element>, ElementSnapshot)>; + unsafe fn needs_paint_from_layout(&self); + unsafe fn will_paint(&self); } #[allow(unsafe_code)] @@ -1618,6 +1685,16 @@ impl LayoutDocumentHelpers for LayoutJS<Document> { let result = elements.drain().map(|(k, v)| (k.to_layout(), v)).collect(); result } + + #[inline] + unsafe fn needs_paint_from_layout(&self) { + (*self.unsafe_get()).needs_paint.set(true) + } + + #[inline] + unsafe fn will_paint(&self) { + (*self.unsafe_get()).needs_paint.set(false) + } } /// https://url.spec.whatwg.org/#network-scheme @@ -1723,6 +1800,7 @@ impl Document { base_element: Default::default(), appropriate_template_contents_owner_document: Default::default(), modified_elements: DOMRefCell::new(HashMap::new()), + needs_paint: Cell::new(false), active_touch_points: DOMRefCell::new(Vec::new()), dom_loading: Cell::new(Default::default()), dom_interactive: Cell::new(Default::default()), @@ -1737,6 +1815,7 @@ impl Document { referrer: referrer, referrer_policy: Cell::new(referrer_policy), target_element: MutNullableHeap::new(None), + last_click_info: DOMRefCell::new(None), } } @@ -2689,8 +2768,9 @@ impl DocumentMethods for Document { self.set_body_attribute(&atom!("text"), value) } + #[allow(unsafe_code)] // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:dom-document-nameditem-filter - fn NamedGetter(&self, _cx: *mut JSContext, name: DOMString, found: &mut bool) -> *mut JSObject { + fn NamedGetter(&self, _cx: *mut JSContext, name: DOMString) -> Option<NonZero<*mut JSObject>> { #[derive(JSTraceable, HeapSizeOf)] struct NamedElementFilter { name: Atom, @@ -2756,23 +2836,24 @@ impl DocumentMethods for Document { .peekable(); if let Some(first) = elements.next() { if elements.peek().is_none() { - *found = true; // TODO: Step 2. // Step 3. - return first.reflector().get_jsobject().get(); + return unsafe { + Some(NonZero::new(first.reflector().get_jsobject().get())) + }; } } else { - *found = false; - return ptr::null_mut(); + return None; } } // Step 4. - *found = true; let filter = NamedElementFilter { name: name, }; let collection = HTMLCollection::create(self.window(), root, box filter); - collection.reflector().get_jsobject().get() + unsafe { + Some(NonZero::new(collection.reflector().get_jsobject().get())) + } } // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names diff --git a/components/script/dom/domimplementation.rs b/components/script/dom/domimplementation.rs index 824cc0b88cf..dd55630204c 100644 --- a/components/script/dom/domimplementation.rs +++ b/components/script/dom/domimplementation.rs @@ -13,9 +13,9 @@ use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; -use dom::bindings::xmlname::{validate_qualified_name, namespace_from_domstring}; -use dom::document::DocumentSource; +use dom::bindings::xmlname::{namespace_from_domstring, validate_qualified_name}; use dom::document::{Document, IsHTMLDocument}; +use dom::document::DocumentSource; use dom::documenttype::DocumentType; use dom::htmlbodyelement::HTMLBodyElement; use dom::htmlheadelement::HTMLHeadElement; diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs index 3964c84abcb..a52f0b8b82f 100644 --- a/components/script/dom/domparser.rs +++ b/components/script/dom/domparser.rs @@ -16,8 +16,8 @@ use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; -use dom::document::DocumentSource; use dom::document::{Document, IsHTMLDocument}; +use dom::document::DocumentSource; use dom::window::Window; use parse::html::{ParseContext, parse_html}; use parse::xml::{self, parse_xml}; diff --git a/components/script/dom/domrectlist.rs b/components/script/dom/domrectlist.rs index adb246158f4..86774a49ff3 100644 --- a/components/script/dom/domrectlist.rs +++ b/components/script/dom/domrectlist.rs @@ -52,8 +52,7 @@ impl DOMRectListMethods for DOMRectList { } // check-tidy: no specs after this line - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<DOMRect>> { - *found = index < self.rects.len() as u32; + fn IndexedGetter(&self, index: u32) -> Option<Root<DOMRect>> { self.Item(index) } } diff --git a/components/script/dom/domstringmap.rs b/components/script/dom/domstringmap.rs index c5e534d242e..11479d6b5df 100644 --- a/components/script/dom/domstringmap.rs +++ b/components/script/dom/domstringmap.rs @@ -47,10 +47,8 @@ impl DOMStringMapMethods for DOMStringMap { } // https://html.spec.whatwg.org/multipage/#dom-domstringmap-nameditem - fn NamedGetter(&self, name: DOMString, found: &mut bool) -> DOMString { - let attr = self.element.get_custom_attr(name); - *found = attr.is_some(); - attr.unwrap_or_default() + fn NamedGetter(&self, name: DOMString) -> Option<DOMString> { + self.element.get_custom_attr(name) } // https://html.spec.whatwg.org/multipage/#the-domstringmap-interface:supported-property-names diff --git a/components/script/dom/domtokenlist.rs b/components/script/dom/domtokenlist.rs index 364b6366282..c1c09de94c2 100644 --- a/components/script/dom/domtokenlist.rs +++ b/components/script/dom/domtokenlist.rs @@ -171,9 +171,7 @@ impl DOMTokenListMethods for DOMTokenList { } // check-tidy: no specs after this line - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<DOMString> { - let item = self.Item(index); - *found = item.is_some(); - item + fn IndexedGetter(&self, index: u32) -> Option<DOMString> { + self.Item(index) } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 2d57ddab4d6..f85f608545f 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -17,8 +17,8 @@ use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; -use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions}; +use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::UnionTypes::NodeOrString; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; @@ -26,8 +26,8 @@ use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, Nod use dom::bindings::js::{JS, LayoutJS, MutNullableHeap}; use dom::bindings::js::{Root, RootedReference}; use dom::bindings::str::DOMString; -use dom::bindings::xmlname::XMLName::InvalidXMLName; use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type}; +use dom::bindings::xmlname::XMLName::InvalidXMLName; use dom::characterdata::CharacterData; use dom::create::create_element; use dom::document::{Document, LayoutDocumentHelpers}; @@ -70,13 +70,12 @@ use html5ever::serialize::SerializeOpts; use html5ever::serialize::TraversalScope; use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks}; -use ref_filter_map::ref_filter_map; -use selectors::matching::{ElementFlags, matches}; -use selectors::matching::{HAS_SLOW_SELECTOR, HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS}; +use selectors::matching::{ElementFlags, MatchingReason, matches}; +use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS}; use selectors::parser::{AttrSelector, NamespaceConstraint, parse_author_origin_selector_list_from_str}; use std::ascii::AsciiExt; use std::borrow::Cow; -use std::cell::{Cell, Ref}; +use std::cell::Cell; use std::convert::TryFrom; use std::default::Default; use std::fmt; @@ -87,11 +86,12 @@ use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::element_state::*; use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes}; use style::parser::ParserContextExtraData; -use style::properties::longhands::{self, background_image, border_spacing, font_family, overflow_x, font_size}; use style::properties::{DeclaredValue, Importance}; use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute}; +use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size, overflow_x}; +use style::refcell::Ref; use style::selector_impl::{NonTSPseudoClass, ServoSelectorImpl}; -use style::selector_matching::DeclarationBlock; +use style::selector_matching::ApplicableDeclarationBlock; use style::sink::Push; use style::values::CSSFloat; use style::values::specified::{self, CSSColor, CSSRGBA, LengthOrPercentage}; @@ -109,7 +109,7 @@ pub struct Element { prefix: Option<DOMString>, attrs: DOMRefCell<Vec<JS<Attr>>>, id_attribute: DOMRefCell<Option<Atom>>, - style_attribute: DOMRefCell<Option<PropertyDeclarationBlock>>, + style_attribute: DOMRefCell<Option<Arc<PropertyDeclarationBlock>>>, attr_list: MutNullableHeap<JS<NamedNodeMap>>, class_list: MutNullableHeap<JS<DOMTokenList>>, state: Cell<ElementState>, @@ -291,13 +291,13 @@ pub trait LayoutElementHelpers { #[allow(unsafe_code)] unsafe fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, &mut V) - where V: Push<DeclarationBlock>; + where V: Push<ApplicableDeclarationBlock>; #[allow(unsafe_code)] unsafe fn get_colspan(self) -> u32; #[allow(unsafe_code)] unsafe fn html_element_in_html_document_for_layout(&self) -> bool; fn id_attribute(&self) -> *const Option<Atom>; - fn style_attribute(&self) -> *const Option<PropertyDeclarationBlock>; + fn style_attribute(&self) -> *const Option<Arc<PropertyDeclarationBlock>>; fn local_name(&self) -> &Atom; fn namespace(&self) -> &Namespace; fn get_checked_state_for_layout(&self) -> bool; @@ -324,12 +324,15 @@ impl LayoutElementHelpers for LayoutJS<Element> { #[allow(unsafe_code)] unsafe fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V) - where V: Push<DeclarationBlock> + where V: Push<ApplicableDeclarationBlock> { #[inline] - fn from_declaration(rule: PropertyDeclaration) -> DeclarationBlock { - DeclarationBlock::from_declarations( - Arc::new(vec![(rule, Importance::Normal)]), + fn from_declaration(rule: PropertyDeclaration) -> ApplicableDeclarationBlock { + ApplicableDeclarationBlock::from_declarations( + Arc::new(PropertyDeclarationBlock { + declarations: vec![(rule, Importance::Normal)], + important_count: 0, + }), Importance::Normal) } @@ -615,7 +618,7 @@ impl LayoutElementHelpers for LayoutJS<Element> { } #[allow(unsafe_code)] - fn style_attribute(&self) -> *const Option<PropertyDeclarationBlock> { + fn style_attribute(&self) -> *const Option<Arc<PropertyDeclarationBlock>> { unsafe { (*self.unsafe_get()).style_attribute.borrow_for_layout() } @@ -704,7 +707,7 @@ impl Element { self.attrs.borrow() } - pub fn style_attribute(&self) -> &DOMRefCell<Option<PropertyDeclarationBlock>> { + pub fn style_attribute(&self) -> &DOMRefCell<Option<Arc<PropertyDeclarationBlock>>> { &self.style_attribute } @@ -774,7 +777,8 @@ impl Element { matching }); if let Some(index) = index { - Arc::make_mut(&mut declarations.declarations).remove(index); + let declarations = Arc::make_mut(declarations); + declarations.declarations.remove(index); if importance.unwrap().important() { declarations.important_count -= 1; } @@ -796,7 +800,8 @@ impl Element { { // Usually, the reference count will be 1 here. But transitions could make it greater // than that. - let existing_declarations = Arc::make_mut(&mut declaration_block.declarations); + let declaration_block = Arc::make_mut(declaration_block); + let existing_declarations = &mut declaration_block.declarations; 'outer: for incoming_declaration in declarations { for existing_declaration in &mut *existing_declarations { @@ -829,10 +834,10 @@ impl Element { 0 }; - *inline_declarations = Some(PropertyDeclarationBlock { - declarations: Arc::new(declarations.into_iter().map(|d| (d, importance)).collect()), + *inline_declarations = Some(Arc::new(PropertyDeclarationBlock { + declarations: declarations.into_iter().map(|d| (d, importance)).collect(), important_count: important_count, - }); + })); } update(self, declarations, importance); @@ -847,7 +852,8 @@ impl Element { if let &mut Some(ref mut block) = &mut *inline_declarations { // Usually, the reference counts of `from` and `to` will be 1 here. But transitions // could make them greater than that. - let declarations = Arc::make_mut(&mut block.declarations); + let block = Arc::make_mut(block); + let declarations = &mut block.declarations; for &mut (ref declaration, ref mut importance) in declarations { if properties.iter().any(|p| declaration.name() == **p) { match (*importance, new_importance) { @@ -871,7 +877,7 @@ impl Element { pub fn get_inline_style_declaration(&self, property: &Atom) -> Option<Ref<(PropertyDeclaration, Importance)>> { - ref_filter_map(self.style_attribute.borrow(), |inline_declarations| { + Ref::filter_map(self.style_attribute.borrow(), |inline_declarations| { inline_declarations.as_ref().and_then(|declarations| { declarations.declarations .iter() @@ -2000,7 +2006,7 @@ impl ElementMethods for Element { match parse_author_origin_selector_list_from_str(&selectors) { Err(()) => Err(Error::Syntax), Ok(ref selectors) => { - Ok(matches(selectors, &Root::from_ref(self), None)) + Ok(matches(selectors, &Root::from_ref(self), None, MatchingReason::Other)) } } } @@ -2018,7 +2024,7 @@ impl ElementMethods for Element { let root = self.upcast::<Node>(); for element in root.inclusive_ancestors() { if let Some(element) = Root::downcast::<Element>(element) { - if matches(selectors, &element, None) { + if matches(selectors, &element, None, MatchingReason::Other) { return Ok(Some(element)); } } @@ -2102,8 +2108,11 @@ impl VirtualMethods for Element { *self.style_attribute.borrow_mut() = mutation.new_value(attr).map(|value| { let win = window_from_node(self); - parse_style_attribute(&value, &doc.base_url(), win.css_error_reporter(), - ParserContextExtraData::default()) + Arc::new(parse_style_attribute( + &value, + &doc.base_url(), + win.css_error_reporter(), + ParserContextExtraData::default())) }); if node.is_in_doc() { node.dirty(NodeDamage::NodeStyleDamaged); diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs index da7664e0e19..07330a776ae 100644 --- a/components/script/dom/event.rs +++ b/components/script/dom/event.rs @@ -154,11 +154,6 @@ impl Event { } #[inline] - pub fn clear_current_target(&self) { - self.current_target.set(None); - } - - #[inline] pub fn set_current_target(&self, val: &EventTarget) { self.current_target.set(Some(val)); } @@ -199,8 +194,22 @@ impl Event { } #[inline] - pub fn set_dispatching(&self, val: bool) { - self.dispatching.set(val) + // https://dom.spec.whatwg.org/#concept-event-dispatch Step 1. + pub fn mark_as_dispatching(&self) { + assert!(!self.dispatching.get()); + self.dispatching.set(true); + } + + #[inline] + // https://dom.spec.whatwg.org/#concept-event-dispatch Steps 10-12. + pub fn clear_dispatching_flags(&self) { + assert!(self.dispatching.get()); + + self.dispatching.set(false); + self.stop_propagation.set(false); + self.stop_immediate.set(false); + self.set_phase(EventPhase::None); + self.current_target.set(None); } #[inline] diff --git a/components/script/dom/eventdispatcher.rs b/components/script/dom/eventdispatcher.rs index 7509df2ef13..01973e7c916 100644 --- a/components/script/dom/eventdispatcher.rs +++ b/components/script/dom/eventdispatcher.rs @@ -123,7 +123,7 @@ pub fn dispatch_event(target: &EventTarget, } // Step 1. Postponed here for the reason stated above. - event.set_dispatching(true); + event.mark_as_dispatching(); // Step 3. The "invoke" algorithm is only used on `target` separately, // so we don't put it in the path. @@ -158,14 +158,8 @@ pub fn dispatch_event(target: &EventTarget, None => {} } - // Step 10. - event.set_dispatching(false); - - // Step 11. - event.set_phase(EventPhase::None); - - // Step 12. - event.clear_current_target(); + // Step 10-12. + event.clear_dispatching_flags(); // Step 13. !event.DefaultPrevented() diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs index 0175023deb1..c941eb8e20e 100644 --- a/components/script/dom/eventtarget.rs +++ b/components/script/dom/eventtarget.rs @@ -426,7 +426,9 @@ impl EventTarget { if !rv || handler.get().is_null() { // Step 1.8.2 unsafe { - report_pending_exception(cx, self.reflector().get_jsobject().get()); + let _ac = JSAutoCompartment::new(cx, self.reflector().get_jsobject().get()); + // FIXME(#13152): dispatch error event. + report_pending_exception(cx, false); } // Step 1.8.1 / 1.8.3 return None; diff --git a/components/script/dom/filelist.rs b/components/script/dom/filelist.rs index 4f8e976f5f7..8adbe1ed467 100644 --- a/components/script/dom/filelist.rs +++ b/components/script/dom/filelist.rs @@ -55,9 +55,7 @@ impl FileListMethods for FileList { } // check-tidy: no specs after this line - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<File>> { - let item = self.Item(index); - *found = item.is_some(); - item + fn IndexedGetter(&self, index: u32) -> Option<Root<File>> { + self.Item(index) } } diff --git a/components/script/dom/headers.rs b/components/script/dom/headers.rs index 4ed9a5aebec..b977110955c 100644 --- a/components/script/dom/headers.rs +++ b/components/script/dom/headers.rs @@ -5,9 +5,11 @@ 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::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; +use dom::bindings::iterable::Iterable; use dom::bindings::js::Root; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::{ByteString, is_token}; @@ -45,7 +47,7 @@ impl Headers { } pub fn new(global: GlobalRef) -> Root<Headers> { - reflect_dom_object(box Headers::new_inherited(), global, HeadersBinding::Wrap) + reflect_dom_object(box Headers::new_inherited(), global, HeadersWrap) } // https://fetch.spec.whatwg.org/#dom-headers @@ -205,7 +207,13 @@ impl Headers { headers_for_request } - pub fn set_guard(&self, new_guard: Guard) { + pub fn for_response(global: GlobalRef) -> Root<Headers> { + let headers_for_response = Headers::new(global); + headers_for_response.guard.set(Guard::Response); + headers_for_response + } + + pub fn set_guard(&self, new_guard: Guard) { self.guard.set(new_guard) } @@ -221,6 +229,41 @@ impl Headers { pub fn extract_mime_type(&self) -> Vec<u8> { self.header_list.borrow().get_raw("content-type").map_or(vec![], |v| v[0].clone()) } + + pub fn sort_header_list(&self) -> Vec<(String, String)> { + let borrowed_header_list = self.header_list.borrow(); + let headers_iter = borrowed_header_list.iter(); + let mut header_vec = vec![]; + for header in headers_iter { + let name = header.name().to_string(); + let value = header.value_string(); + let name_value = (name, value); + header_vec.push(name_value); + } + header_vec.sort(); + header_vec + } +} + +impl Iterable for Headers { + type Key = ByteString; + type Value = ByteString; + + fn get_iterable_length(&self) -> u32 { + self.header_list.borrow().iter().count() as u32 + } + + fn get_value_at_index(&self, n: u32) -> ByteString { + let sorted_header_vec = self.sort_header_list(); + let value = sorted_header_vec[n as usize].1.clone(); + ByteString::new(value.into_bytes().to_vec()) + } + + fn get_key_at_index(&self, n: u32) -> ByteString { + let sorted_header_vec = self.sort_header_list(); + let key = sorted_header_vec[n as usize].0.clone(); + ByteString::new(key.into_bytes().to_vec()) + } } fn is_cors_safelisted_request_content_type(value: &[u8]) -> bool { @@ -307,7 +350,7 @@ pub fn is_forbidden_header_name(name: &str) -> bool { // [3] https://tools.ietf.org/html/rfc7230#section-3.2.6 // [4] https://www.rfc-editor.org/errata_search.php?rfc=7230 fn validate_name_and_value(name: ByteString, value: ByteString) - -> Result<(String, Vec<u8>), Error> { + -> Fallible<(String, Vec<u8>)> { let valid_name = try!(validate_name(name)); if !is_field_content(&value) { return Err(Error::Type("Value is not valid".to_string())); @@ -315,7 +358,7 @@ fn validate_name_and_value(name: ByteString, value: ByteString) Ok((valid_name, value.into())) } -fn validate_name(name: ByteString) -> Result<String, Error> { +fn validate_name(name: ByteString) -> Fallible<String> { if !is_field_name(&name) { return Err(Error::Type("Name is not valid".to_string())); } @@ -405,7 +448,7 @@ fn is_field_vchar(x: u8) -> bool { } // https://tools.ietf.org/html/rfc5234#appendix-B.1 -fn is_vchar(x: u8) -> bool { +pub fn is_vchar(x: u8) -> bool { match x { 0x21...0x7E => true, _ => false, @@ -413,7 +456,7 @@ fn is_vchar(x: u8) -> bool { } // http://tools.ietf.org/html/rfc7230#section-3.2.6 -fn is_obs_text(x: u8) -> bool { +pub fn is_obs_text(x: u8) -> bool { match x { 0x80...0xFF => true, _ => false, diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs index 6c15cf92150..af95ec404b9 100644 --- a/components/script/dom/htmlbuttonelement.rs +++ b/components/script/dom/htmlbuttonelement.rs @@ -15,9 +15,9 @@ use dom::event::Event; use dom::eventtarget::EventTarget; use dom::htmlelement::HTMLElement; use dom::htmlfieldsetelement::HTMLFieldSetElement; -use dom::htmlformelement::HTMLFormElement; use dom::htmlformelement::{FormControl, FormDatum, FormDatumValue}; use dom::htmlformelement::{FormSubmitter, ResetFrom, SubmittedFrom}; +use dom::htmlformelement::HTMLFormElement; use dom::node::{Node, UnbindContext, document_from_node, window_from_node}; use dom::nodelist::NodeList; use dom::validation::Validatable; diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index 38969860722..73b51962fe4 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -10,6 +10,7 @@ use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding; use dom::bindings::codegen::Bindings::HTMLCanvasElementBinding::HTMLCanvasElementMethods; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLContextAttributes; use dom::bindings::codegen::UnionTypes::CanvasRenderingContext2DOrWebGLRenderingContext; +use dom::bindings::conversions::ConversionResult; use dom::bindings::error::{Error, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; @@ -27,6 +28,7 @@ use euclid::size::Size2D; use image::ColorType; use image::png::PNGEncoder; use ipc_channel::ipc::{self, IpcSender}; +use js::error::throw_type_error; use js::jsapi::{HandleValue, JSContext}; use offscreen_gl_context::GLContextAttributes; use rustc_serialize::base64::{STANDARD, ToBase64}; @@ -159,11 +161,17 @@ impl HTMLCanvasElement { let size = self.get_size(); let attrs = if let Some(webgl_attributes) = attrs { - if let Ok(ref attrs) = unsafe { WebGLContextAttributes::new(cx, webgl_attributes) } { - From::from(attrs) - } else { - debug!("Unexpected error on conversion of WebGLContextAttributes"); - return None; + match unsafe { + WebGLContextAttributes::new(cx, webgl_attributes) } { + Ok(ConversionResult::Success(ref attrs)) => From::from(attrs), + Ok(ConversionResult::Failure(ref error)) => { + unsafe { throw_type_error(cx, &error); } + return None; + } + _ => { + debug!("Unexpected error on conversion of WebGLContextAttributes"); + return None; + } } } else { GLContextAttributes::default() @@ -265,11 +273,10 @@ impl HTMLCanvasElementMethods for HTMLCanvasElement { // Step 3. let raw_data = match *self.context.borrow() { Some(CanvasContext::Context2d(ref context)) => { - let window = window_from_node(self); let image_data = try!(context.GetImageData(Finite::wrap(0f64), Finite::wrap(0f64), Finite::wrap(self.Width() as f64), Finite::wrap(self.Height() as f64))); - image_data.get_data_array(&GlobalRef::Window(window.r())) + image_data.get_data_array() } None => { // Each pixel is fully-transparent black. diff --git a/components/script/dom/htmlcollection.rs b/components/script/dom/htmlcollection.rs index 95134556bc0..2f226590719 100644 --- a/components/script/dom/htmlcollection.rs +++ b/components/script/dom/htmlcollection.rs @@ -317,17 +317,13 @@ impl HTMLCollectionMethods for HTMLCollection { } // https://dom.spec.whatwg.org/#dom-htmlcollection-item - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Element>> { - let maybe_elem = self.Item(index); - *found = maybe_elem.is_some(); - maybe_elem + fn IndexedGetter(&self, index: u32) -> Option<Root<Element>> { + self.Item(index) } // check-tidy: no specs after this line - fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<Root<Element>> { - let maybe_elem = self.NamedItem(name); - *found = maybe_elem.is_some(); - maybe_elem + fn NamedGetter(&self, name: DOMString) -> Option<Root<Element>> { + self.NamedItem(name) } // https://dom.spec.whatwg.org/#interface-htmlcollection diff --git a/components/script/dom/htmldataelement.rs b/components/script/dom/htmldataelement.rs index f412fd57570..c4bd0bfd173 100644 --- a/components/script/dom/htmldataelement.rs +++ b/components/script/dom/htmldataelement.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::Bindings::HTMLDataElementBinding; +use dom::bindings::codegen::Bindings::HTMLDataElementBinding::HTMLDataElementMethods; use dom::bindings::js::Root; use dom::bindings::str::DOMString; use dom::document::Document; @@ -33,3 +34,11 @@ impl HTMLDataElement { HTMLDataElementBinding::Wrap) } } + +impl HTMLDataElementMethods for HTMLDataElement { + // https://html.spec.whatwg.org/multipage/#dom-data-value + make_getter!(Value, "value"); + + // https://html.spec.whatwg.org/multipage/#dom-data-value + make_setter!(SetValue, "value"); +} diff --git a/components/script/dom/htmldialogelement.rs b/components/script/dom/htmldialogelement.rs index 621eefcd8a4..1afd7f0d76f 100644 --- a/components/script/dom/htmldialogelement.rs +++ b/components/script/dom/htmldialogelement.rs @@ -5,11 +5,14 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::HTMLDialogElementBinding; use dom::bindings::codegen::Bindings::HTMLDialogElementBinding::HTMLDialogElementMethods; +use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::bindings::str::DOMString; use dom::document::Document; +use dom::element::Element; +use dom::eventtarget::EventTarget; use dom::htmlelement::HTMLElement; -use dom::node::Node; +use dom::node::{Node, window_from_node}; use string_cache::Atom; #[dom_struct] @@ -56,4 +59,26 @@ impl HTMLDialogElementMethods for HTMLDialogElement { fn SetReturnValue(&self, return_value: DOMString) { *self.return_value.borrow_mut() = return_value; } + + // https://html.spec.whatwg.org/multipage/#dom-dialog-close + fn Close(&self, return_value: Option<DOMString>) { + let element = self.upcast::<Element>(); + let target = self.upcast::<EventTarget>(); + let win = window_from_node(self); + + // Step 1 & 2 + if element.remove_attribute(&ns!(), &atom!("open")).is_none() { + return; + } + + // Step 3 + if let Some(new_value) = return_value { + *self.return_value.borrow_mut() = new_value; + } + + // TODO: Step 4 implement pending dialog stack removal + + // Step 5 + win.dom_manipulation_task_source().queue_simple_event(target, atom!("close"), win.r()); + } } diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index e9d967514d7..4ec42ced6d6 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -11,8 +11,8 @@ use dom::bindings::codegen::Bindings::HTMLElementBinding; use dom::bindings::codegen::Bindings::HTMLElementBinding::HTMLElementMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::error::{Error, ErrorResult}; -use dom::bindings::inheritance::Castable; use dom::bindings::inheritance::{ElementTypeId, HTMLElementTypeId, NodeTypeId}; +use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root, RootedReference}; use dom::bindings::str::DOMString; use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration}; diff --git a/components/script/dom/htmlformcontrolscollection.rs b/components/script/dom/htmlformcontrolscollection.rs index 9229b854b26..e52a541225f 100644 --- a/components/script/dom/htmlformcontrolscollection.rs +++ b/components/script/dom/htmlformcontrolscollection.rs @@ -77,10 +77,8 @@ impl HTMLFormControlsCollectionMethods for HTMLFormControlsCollection { } // https://html.spec.whatwg.org/multipage/#dom-htmlformcontrolscollection-nameditem - fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<RadioNodeListOrElement> { - let maybe_elem = self.NamedItem(name); - *found = maybe_elem.is_some(); - maybe_elem + fn NamedGetter(&self, name: DOMString) -> Option<RadioNodeListOrElement> { + self.NamedItem(name) } // https://html.spec.whatwg.org/multipage/#the-htmlformcontrolscollection-interface:supported-property-names @@ -93,7 +91,7 @@ impl HTMLFormControlsCollectionMethods for HTMLFormControlsCollection { // https://github.com/servo/servo/issues/5875 // // https://dom.spec.whatwg.org/#dom-htmlcollection-item - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Element>> { - self.collection.IndexedGetter(index, found) + fn IndexedGetter(&self, index: u32) -> Option<Root<Element>> { + self.collection.IndexedGetter(index) } } diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index db9e3cb8d41..fa16618b564 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -230,9 +230,9 @@ impl HTMLFormElementMethods for HTMLFormElement { } // https://html.spec.whatwg.org/multipage/#dom-form-item - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Element>> { + fn IndexedGetter(&self, index: u32) -> Option<Root<Element>> { let elements = self.Elements(); - elements.IndexedGetter(index, found) + elements.IndexedGetter(index) } } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 73d750cc8dd..59578b37b59 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -2,7 +2,7 @@ * 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 document_loader::{LoadType, LoadBlocker}; +use document_loader::{LoadBlocker, LoadType}; use dom::attr::Attr; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementErrorEventDetail; @@ -20,7 +20,7 @@ use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; -use dom::bindings::js::{JS, MutNullableHeap, Root, LayoutJS}; +use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root}; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; use dom::browsingcontext::BrowsingContext; @@ -31,18 +31,18 @@ use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers}; use dom::event::Event; use dom::eventtarget::EventTarget; use dom::htmlelement::HTMLElement; -use dom::node::{Node, NodeDamage, UnbindContext, window_from_node, document_from_node}; +use dom::node::{Node, NodeDamage, UnbindContext, document_from_node, window_from_node}; use dom::urlhelper::UrlHelper; use dom::virtualmethods::VirtualMethods; use dom::window::{ReflowReason, Window}; use ipc_channel::ipc; use js::jsapi::{JSAutoCompartment, JSContext, MutableHandleValue}; -use js::jsval::{UndefinedValue, NullValue}; -use msg::constellation_msg::{FrameType, LoadData, TraversalDirection, PipelineId, SubpageId}; +use js::jsval::{NullValue, UndefinedValue}; +use msg::constellation_msg::{FrameType, LoadData, PipelineId, SubpageId, TraversalDirection}; use net_traits::response::HttpsState; use script_layout_interface::message::ReflowQueryType; -use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; use script_traits::{IFrameLoadInfo, MozBrowserEvent, ScriptMsg as ConstellationMsg}; +use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; use std::cell::Cell; use string_cache::Atom; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; @@ -355,7 +355,7 @@ unsafe fn build_mozbrowser_event_detail(event: MozBrowserEvent, type_: Some(DOMString::from(error_type.name())), description: Some(DOMString::from(description)), report: Some(DOMString::from(report)), - version: Some(DOMString::from_string(servo_version().into())), + version: Some(DOMString::from_string(servo_version())), }.to_jsval(cx, rval); }, MozBrowserEvent::SecurityChange(https_state) => { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index a98fcbcfb4e..978d93f6138 100644 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -13,17 +13,17 @@ use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementM use dom::bindings::codegen::Bindings::KeyboardEventBinding::KeyboardEventMethods; use dom::bindings::error::{Error, ErrorResult}; use dom::bindings::inheritance::Castable; -use dom::bindings::js::{JS, LayoutJS, Root, RootedReference, MutNullableHeap}; +use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root, RootedReference}; use dom::bindings::str::DOMString; use dom::document::Document; -use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers, LayoutElementHelpers}; +use dom::element::{AttributeMutation, Element, LayoutElementHelpers, RawLayoutElementHelpers}; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; use dom::file::File; use dom::filelist::FileList; use dom::htmlelement::HTMLElement; use dom::htmlfieldsetelement::HTMLFieldSetElement; -use dom::htmlformelement::{FormDatumValue, FormControl, FormDatum, FormSubmitter, HTMLFormElement}; +use dom::htmlformelement::{FormControl, FormDatum, FormDatumValue, FormSubmitter, HTMLFormElement}; use dom::htmlformelement::{ResetFrom, SubmittedFrom}; use dom::keyboardevent::KeyboardEvent; use dom::node::{Node, NodeDamage, UnbindContext}; @@ -34,9 +34,9 @@ use dom::virtualmethods::VirtualMethods; use ipc_channel::ipc::{self, IpcSender}; use mime_guess; use msg::constellation_msg::Key; +use net_traits::{CoreResourceMsg, IpcSend}; use net_traits::blob_url_store::get_blob_origin; use net_traits::filemanager_thread::{FileManagerThreadMsg, FilterPattern}; -use net_traits::{IpcSend, CoreResourceMsg}; use script_traits::ScriptMsg as ConstellationMsg; use std::borrow::ToOwned; use std::cell::Cell; @@ -45,9 +45,9 @@ use string_cache::Atom; use style::attr::AttrValue; use style::element_state::*; use style::str::split_commas; +use textinput::{SelectionDirection, TextInput}; use textinput::KeyReaction::{DispatchInput, Nothing, RedrawSelection, TriggerDefaultAction}; use textinput::Lines::Single; -use textinput::{TextInput, SelectionDirection}; const DEFAULT_SUBMIT_VALUE: &'static str = "Submit"; const DEFAULT_RESET_VALUE: &'static str = "Reset"; diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 9264bcb0490..07cd47cbcec 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -22,7 +22,6 @@ use dom::virtualmethods::VirtualMethods; use encoding::EncodingRef; use encoding::all::UTF_8; use hyper::header::ContentType; -use hyper::http::RawStatus; use hyper::mime::{Mime, TopLevel, SubLevel}; use hyper_serde::Serde; use ipc_channel::ipc; @@ -335,7 +334,7 @@ impl AsyncResponseListener for StylesheetContext { document.invalidate_stylesheets(); // FIXME: Revisit once consensus is reached at: https://github.com/whatwg/html/issues/1142 - successful = metadata.status.map_or(false, |Serde(RawStatus(code, _))| code == 200); + successful = metadata.status.map_or(false, |(code, _)| code == 200); } if elem.parser_inserted.get() { diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index d26ac6a044e..6be1fbb3d96 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -2,6 +2,7 @@ * 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 audio_video_metadata; use document_loader::LoadType; use dom::attr::Attr; use dom::bindings::cell::DOMRefCell; @@ -24,7 +25,6 @@ use dom::htmlsourceelement::HTMLSourceElement; use dom::mediaerror::MediaError; use dom::node::{window_from_node, document_from_node, Node, UnbindContext}; use dom::virtualmethods::VirtualMethods; -use hyper_serde::Serde; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkError}; @@ -66,7 +66,7 @@ impl AsyncResponseListener for HTMLMediaElementContext { .as_ref() .and_then(|m| m.status .as_ref() - .map(|&Serde(ref s)| s.0 < 200 || s.0 >= 300)) + .map(|&(s, _)| s < 200 || s >= 300)) .unwrap_or(false); if is_failure { // Ensure that the element doesn't receive any further notifications @@ -160,12 +160,24 @@ impl HTMLMediaElementContext { } fn check_metadata(&mut self, elem: &HTMLMediaElement) { - // Step 6. - // - // TODO: Properly implement once we have figured out the build and - // licensing ffmpeg issues. - elem.change_ready_state(HAVE_METADATA); - self.have_metadata = true; + match audio_video_metadata::get_format_from_slice(&self.data) { + Ok(audio_video_metadata::Metadata::Video(meta)) => { + let dur = meta.audio.duration.unwrap_or(::std::time::Duration::new(0, 0)); + *elem.video.borrow_mut() = Some(VideoMedia { + format: format!("{:?}", meta.format), + duration: Duration::seconds(dur.as_secs() as i64) + + Duration::nanoseconds(dur.subsec_nanos() as i64), + width: meta.dimensions.width, + height: meta.dimensions.height, + video: meta.video.unwrap_or("".to_owned()), + audio: meta.audio.audio, + }); + // Step 6 + elem.change_ready_state(HAVE_METADATA); + self.have_metadata = true; + } + _ => {} + } } } diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index b8c2cd423fa..e6f86f41d86 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -79,7 +79,7 @@ impl HTMLMetaElement { if !content.is_empty() { if let Some(translated_rule) = ViewportRule::from_meta(&**content) { *self.stylesheet.borrow_mut() = Some(Arc::new(Stylesheet { - rules: vec![CSSRule::Viewport(translated_rule)], + rules: vec![CSSRule::Viewport(Arc::new(translated_rule))], origin: Origin::Author, media: None, // Viewport constraints are always recomputed on resize; they don't need to diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs index 737bc67bd4c..9183a78de0c 100644 --- a/components/script/dom/htmloptionelement.rs +++ b/components/script/dom/htmloptionelement.rs @@ -6,6 +6,7 @@ use dom::attr::Attr; use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; use dom::bindings::codegen::Bindings::HTMLOptionElementBinding; use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods; +use dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementBinding::HTMLSelectElementMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; @@ -14,6 +15,8 @@ use dom::characterdata::CharacterData; use dom::document::Document; use dom::element::{AttributeMutation, Element}; use dom::htmlelement::HTMLElement; +use dom::htmlformelement::HTMLFormElement; +use dom::htmloptgroupelement::HTMLOptGroupElement; use dom::htmlscriptelement::HTMLScriptElement; use dom::htmlselectelement::HTMLSelectElement; use dom::node::{Node, UnbindContext}; @@ -110,6 +113,19 @@ impl HTMLOptionElementMethods for HTMLOptionElement { self.upcast::<Node>().SetTextContent(Some(value)) } + // https://html.spec.whatwg.org/multipage/#dom-option-form + fn GetForm(&self) -> Option<Root<HTMLFormElement>> { + let parent = self.upcast::<Node>().GetParentNode().and_then(|p| + if p.is::<HTMLOptGroupElement>() { + p.upcast::<Node>().GetParentNode() + } else { + Some(p) + } + ); + + parent.and_then(|p| p.downcast::<HTMLSelectElement>().and_then(|s| s.GetForm())) + } + // https://html.spec.whatwg.org/multipage/#attr-option-value fn Value(&self) -> DOMString { let element = self.upcast::<Element>(); diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 6a6fe7d50b4..6484de1e029 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -11,8 +11,8 @@ use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElemen use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; -use dom::bindings::js::RootedReference; use dom::bindings::js::{JS, Root}; +use dom::bindings::js::RootedReference; use dom::bindings::refcounted::Trusted; use dom::bindings::str::DOMString; use dom::document::Document; @@ -26,8 +26,6 @@ use dom::window::ScriptHelpers; use encoding::label::encoding_from_whatwg_label; use encoding::types::{DecoderTrap, EncodingRef}; use html5ever::tree_builder::NextParserState; -use hyper::http::RawStatus; -use hyper_serde::Serde; use ipc_channel::ipc; use ipc_channel::router::ROUTER; use js::jsval::UndefinedValue; @@ -159,7 +157,7 @@ impl AsyncResponseListener for ScriptContext { let status_code = self.metadata.as_ref().and_then(|m| { match m.status { - Some(Serde(RawStatus(c, _))) => Some(c), + Some((c, _)) => Some(c), _ => None, } }).unwrap_or(0); diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index a89df91f99b..5df0778fa2b 100644 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -12,8 +12,8 @@ use dom::bindings::inheritance::Castable; use dom::bindings::js::{LayoutJS, Root}; use dom::bindings::str::DOMString; use dom::document::Document; -use dom::element::RawLayoutElementHelpers; use dom::element::{AttributeMutation, Element}; +use dom::element::RawLayoutElementHelpers; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::htmlelement::HTMLElement; use dom::htmlfieldsetelement::HTMLFieldSetElement; @@ -31,7 +31,7 @@ use std::ops::Range; use string_cache::Atom; use style::attr::AttrValue; use style::element_state::*; -use textinput::{KeyReaction, Lines, TextInput, SelectionDirection}; +use textinput::{KeyReaction, Lines, SelectionDirection, TextInput}; #[dom_struct] pub struct HTMLTextAreaElement { diff --git a/components/script/dom/imagedata.rs b/components/script/dom/imagedata.rs index abcb9b5640b..0959a52eb32 100644 --- a/components/script/dom/imagedata.rs +++ b/components/script/dom/imagedata.rs @@ -2,6 +2,7 @@ * 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 core::nonzero::NonZero; use dom::bindings::codegen::Bindings::ImageDataBinding; use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods; use dom::bindings::global::GlobalRef; @@ -37,6 +38,7 @@ impl ImageData { unsafe { let cx = global.get_cx(); let js_object: *mut JSObject = JS_NewUint8ClampedArray(cx, width * height * 4); + assert!(!js_object.is_null()); if let Some(vec) = data { let mut is_shared = false; @@ -52,12 +54,13 @@ impl ImageData { } #[allow(unsafe_code)] - pub fn get_data_array(&self, global: &GlobalRef) -> Vec<u8> { + pub fn get_data_array(&self) -> Vec<u8> { unsafe { - let cx = global.get_cx(); let mut is_shared = false; + assert!(!self.data.get().is_null()); let data: *const uint8_t = - JS_GetUint8ClampedArrayData(self.Data(cx), &mut is_shared, ptr::null()) as *const uint8_t; + JS_GetUint8ClampedArrayData(self.data.get(), &mut is_shared, ptr::null()) as *const uint8_t; + assert!(!data.is_null()); assert!(!is_shared); let len = self.Width() * self.Height() * 4; slice::from_raw_parts(data, len as usize).to_vec() @@ -80,8 +83,10 @@ impl ImageDataMethods for ImageData { self.height } + #[allow(unsafe_code)] // https://html.spec.whatwg.org/multipage/#dom-imagedata-data - fn Data(&self, _: *mut JSContext) -> *mut JSObject { - self.data.get() + fn Data(&self, _: *mut JSContext) -> NonZero<*mut JSObject> { + assert!(!self.data.get().is_null()); + unsafe { NonZero::new(self.data.get()) } } } diff --git a/components/script/dom/mimetypearray.rs b/components/script/dom/mimetypearray.rs index 96fc48c86d0..de820f6d06a 100644 --- a/components/script/dom/mimetypearray.rs +++ b/components/script/dom/mimetypearray.rs @@ -46,12 +46,12 @@ impl MimeTypeArrayMethods for MimeTypeArray { } // https://html.spec.whatwg.org/multipage/#dom-mimetypearray-item - fn IndexedGetter(&self, _index: u32, _found: &mut bool) -> Option<Root<MimeType>> { + fn IndexedGetter(&self, _index: u32) -> Option<Root<MimeType>> { None } // check-tidy: no specs after this line - fn NamedGetter(&self, _name: DOMString, _found: &mut bool) -> Option<Root<MimeType>> { + fn NamedGetter(&self, _name: DOMString) -> Option<Root<MimeType>> { None } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 432246d814f..f38d1e8949c 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -372,6 +372,7 @@ pub mod progressevent; pub mod radionodelist; pub mod range; pub mod request; +pub mod response; pub mod screen; pub mod serviceworker; pub mod serviceworkercontainer; @@ -384,6 +385,8 @@ pub mod storageevent; pub mod stylesheet; pub mod stylesheetlist; pub mod testbinding; +pub mod testbindingiterable; +pub mod testbindingpairiterable; pub mod testbindingproxy; pub mod text; pub mod textdecoder; diff --git a/components/script/dom/namednodemap.rs b/components/script/dom/namednodemap.rs index 10b6b8982ae..9edc1b1e93b 100644 --- a/components/script/dom/namednodemap.rs +++ b/components/script/dom/namednodemap.rs @@ -85,17 +85,13 @@ impl NamedNodeMapMethods for NamedNodeMap { } // https://dom.spec.whatwg.org/#dom-namednodemap-item - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Attr>> { - let item = self.Item(index); - *found = item.is_some(); - item + fn IndexedGetter(&self, index: u32) -> Option<Root<Attr>> { + self.Item(index) } // check-tidy: no specs after this line - fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<Root<Attr>> { - let item = self.GetNamedItem(name); - *found = item.is_some(); - item + fn NamedGetter(&self, name: DOMString) -> Option<Root<Attr>> { + self.GetNamedItem(name) } // https://heycam.github.io/webidl/#dfn-supported-property-names diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs index 75bd2c13c23..61936c59e5f 100644 --- a/components/script/dom/navigator.rs +++ b/components/script/dom/navigator.rs @@ -107,4 +107,10 @@ impl NavigatorMethods for Navigator { fn ServiceWorker(&self) -> Root<ServiceWorkerContainer> { self.serviceWorker.or_init(|| ServiceWorkerContainer::new(self.global().r())) } + + // https://html.spec.whatwg.org/multipage/#dom-navigator-cookieenabled + fn CookieEnabled(&self) -> bool { + true + } + } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 49aa8242b34..5f1bee3a730 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -23,9 +23,9 @@ use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::{Castable, CharacterDataTypeId, ElementTypeId}; use dom::bindings::inheritance::{EventTargetTypeId, HTMLElementTypeId, NodeTypeId}; +use dom::bindings::js::{JS, LayoutJS, MutNullableHeap}; use dom::bindings::js::Root; use dom::bindings::js::RootedReference; -use dom::bindings::js::{JS, LayoutJS, MutNullableHeap}; use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::{DOMString, USVString}; use dom::bindings::xmlname::namespace_from_domstring; @@ -59,11 +59,11 @@ use libc::{self, c_void, uintptr_t}; use msg::constellation_msg::PipelineId; use parse::html::parse_html_fragment; use ref_slice::ref_slice; -use script_layout_interface::message::Msg; use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData}; -use script_layout_interface::{LayoutNodeType, LayoutElementType, TrustedNodeAddress}; +use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddress}; +use script_layout_interface::message::Msg; use script_traits::UntrustedNodeAddress; -use selectors::matching::matches; +use selectors::matching::{MatchingReason, matches}; use selectors::parser::Selector; use selectors::parser::parse_author_origin_selector_list_from_str; use std::borrow::ToOwned; @@ -319,7 +319,7 @@ impl<'a> Iterator for QuerySelectorIterator { // (instead of passing `None`)? Probably. self.iterator.by_ref().filter_map(|node| { if let Some(element) = Root::downcast(node) { - if matches(selectors, &element, None) { + if matches(selectors, &element, None, MatchingReason::Other) { return Some(Root::upcast(element)); } } @@ -711,7 +711,7 @@ impl Node { // Step 3. Ok(ref selectors) => { Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| { - matches(selectors, element, None) + matches(selectors, element, None, MatchingReason::Other) })) } } diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs index 2503378187e..8f8a5515592 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -75,10 +75,8 @@ impl NodeListMethods for NodeList { } // https://dom.spec.whatwg.org/#dom-nodelist-item - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Node>> { - let item = self.Item(index); - *found = item.is_some(); - item + fn IndexedGetter(&self, index: u32) -> Option<Root<Node>> { + self.Item(index) } } diff --git a/components/script/dom/plugin.rs b/components/script/dom/plugin.rs index dc4ca4fe42d..222e9a2840a 100644 --- a/components/script/dom/plugin.rs +++ b/components/script/dom/plugin.rs @@ -45,12 +45,12 @@ impl PluginMethods for Plugin { } // https://html.spec.whatwg.org/multipage/#dom-plugin-item - fn IndexedGetter(&self, _index: u32, _found: &mut bool) -> Option<Root<MimeType>> { + fn IndexedGetter(&self, _index: u32) -> Option<Root<MimeType>> { unreachable!() } // check-tidy: no specs after this line - fn NamedGetter(&self, _name: DOMString, _found: &mut bool) -> Option<Root<MimeType>> { + fn NamedGetter(&self, _name: DOMString) -> Option<Root<MimeType>> { unreachable!() } diff --git a/components/script/dom/pluginarray.rs b/components/script/dom/pluginarray.rs index aabba4928a4..aa6b779280d 100644 --- a/components/script/dom/pluginarray.rs +++ b/components/script/dom/pluginarray.rs @@ -50,12 +50,12 @@ impl PluginArrayMethods for PluginArray { } // https://html.spec.whatwg.org/multipage/#dom-pluginarray-item - fn IndexedGetter(&self, _index: u32, _found: &mut bool) -> Option<Root<Plugin>> { + fn IndexedGetter(&self, _index: u32) -> Option<Root<Plugin>> { None } // check-tidy: no specs after this line - fn NamedGetter(&self, _name: DOMString, _found: &mut bool) -> Option<Root<Plugin>> { + fn NamedGetter(&self, _name: DOMString) -> Option<Root<Plugin>> { None } diff --git a/components/script/dom/radionodelist.rs b/components/script/dom/radionodelist.rs index d88fc69eacd..9bbdae00c85 100644 --- a/components/script/dom/radionodelist.rs +++ b/components/script/dom/radionodelist.rs @@ -105,7 +105,7 @@ impl RadioNodeListMethods for RadioNodeList { // https://github.com/servo/servo/issues/5875 // // https://dom.spec.whatwg.org/#dom-nodelist-item - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Node>> { - self.node_list.IndexedGetter(index, found) + fn IndexedGetter(&self, index: u32) -> Option<Root<Node>> { + self.node_list.IndexedGetter(index) } } diff --git a/components/script/dom/range.rs b/components/script/dom/range.rs index b8cb72c4dc4..bab6bdcc3a2 100644 --- a/components/script/dom/range.rs +++ b/components/script/dom/range.rs @@ -6,14 +6,14 @@ use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods use dom::bindings::codegen::Bindings::NodeBinding::NodeConstants; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; -use dom::bindings::codegen::Bindings::RangeBinding::RangeMethods; use dom::bindings::codegen::Bindings::RangeBinding::{self, RangeConstants}; +use dom::bindings::codegen::Bindings::RangeBinding::RangeMethods; use dom::bindings::codegen::Bindings::TextBinding::TextMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; -use dom::bindings::inheritance::Castable; use dom::bindings::inheritance::{CharacterDataTypeId, NodeTypeId}; +use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutHeap, Root, RootedReference}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; diff --git a/components/script/dom/request.rs b/components/script/dom/request.rs index c396c0d36e1..a32799cd61c 100644 --- a/components/script/dom/request.rs +++ b/components/script/dom/request.rs @@ -19,10 +19,11 @@ use dom::bindings::error::{Error, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; -use dom::bindings::str::{ByteString, USVString, DOMString}; -use dom::headers::{Headers, Guard}; +use dom::bindings::str::{ByteString, DOMString, USVString}; +use dom::headers::{Guard, Headers}; use hyper; -use msg::constellation_msg::{ReferrerPolicy as MsgReferrerPolicy}; +use msg::constellation_msg::ReferrerPolicy as MsgReferrerPolicy; +use net_traits::request::{Origin, Window}; use net_traits::request::CacheMode as NetTraitsRequestCache; use net_traits::request::CredentialsMode as NetTraitsRequestCredentials; use net_traits::request::Destination as NetTraitsRequestDestination; @@ -31,7 +32,6 @@ use net_traits::request::Referer as NetTraitsRequestReferer; use net_traits::request::Request as NetTraitsRequest; use net_traits::request::RequestMode as NetTraitsRequestMode; use net_traits::request::Type as NetTraitsRequestType; -use net_traits::request::{Origin, Window}; use std::cell::Cell; use url::Url; diff --git a/components/script/dom/response.rs b/components/script/dom/response.rs new file mode 100644 index 00000000000..acfc181283a --- /dev/null +++ b/components/script/dom/response.rs @@ -0,0 +1,290 @@ +/* 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 core::cell::Cell; +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::HeadersBinding::HeadersMethods; +use dom::bindings::codegen::Bindings::ResponseBinding; +use dom::bindings::codegen::Bindings::ResponseBinding::{ResponseMethods, ResponseType as DOMResponseType}; +use dom::bindings::error::{Error, Fallible}; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::{JS, MutNullableHeap, Root}; +use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; +use dom::bindings::str::{ByteString, USVString}; +use dom::headers::{Headers, Guard}; +use dom::headers::{is_vchar, is_obs_text}; +use hyper::status::StatusCode; +use net_traits::response::{ResponseBody as NetTraitsResponseBody}; +use std::str::FromStr; +use url::Position; +use url::Url; + +#[dom_struct] +pub struct Response { + reflector_: Reflector, + headers_reflector: MutNullableHeap<JS<Headers>>, + mime_type: DOMRefCell<Vec<u8>>, + body_used: Cell<bool>, + /// `None` can be considered a StatusCode of `0`. + #[ignore_heap_size_of = "Defined in hyper"] + status: DOMRefCell<Option<StatusCode>>, + raw_status: DOMRefCell<Option<(u16, Vec<u8>)>>, + response_type: DOMRefCell<DOMResponseType>, + url: DOMRefCell<Option<Url>>, + url_list: DOMRefCell<Vec<Url>>, + // For now use the existing NetTraitsResponseBody enum, until body + // is implemented. + body: DOMRefCell<NetTraitsResponseBody>, +} + +impl Response { + pub fn new_inherited() -> Response { + Response { + reflector_: Reflector::new(), + headers_reflector: Default::default(), + mime_type: DOMRefCell::new("".to_string().into_bytes()), + body_used: Cell::new(false), + status: DOMRefCell::new(Some(StatusCode::Ok)), + raw_status: DOMRefCell::new(Some((200, b"OK".to_vec()))), + response_type: DOMRefCell::new(DOMResponseType::Default), + url: DOMRefCell::new(None), + url_list: DOMRefCell::new(vec![]), + body: DOMRefCell::new(NetTraitsResponseBody::Empty), + } + } + + // https://fetch.spec.whatwg.org/#dom-response + pub fn new(global: GlobalRef) -> Root<Response> { + reflect_dom_object(box Response::new_inherited(), global, ResponseBinding::Wrap) + } + + pub fn Constructor(global: GlobalRef, _body: Option<USVString>, init: &ResponseBinding::ResponseInit) + -> Fallible<Root<Response>> { + // Step 1 + if init.status < 200 || init.status > 599 { + return Err(Error::Range( + format!("init's status member should be in the range 200 to 599, inclusive, but is {}" + , init.status))); + } + + // Step 2 + if !is_valid_status_text(&init.statusText) { + return Err(Error::Type("init's statusText member does not match the reason-phrase token production" + .to_string())); + } + + // Step 3 + let r = Response::new(global); + + // Step 4 + *r.status.borrow_mut() = Some(StatusCode::from_u16(init.status)); + + // Step 5 + *r.raw_status.borrow_mut() = Some((init.status, init.statusText.clone().into())); + + // Step 6 + if let Some(ref headers_member) = init.headers { + // Step 6.1 + // TODO: Figure out how/if we should make r's response's + // header list and r's Headers object the same thing. For + // now just working with r's Headers object. Also, the + // header list should already be empty so this step may be + // unnecessary. + r.Headers().empty_header_list(); + + // Step 6.2 + try!(r.Headers().fill(Some(headers_member.clone()))); + } + + // Step 7 + if let Some(_) = _body { + // Step 7.1 + if is_null_body_status(init.status) { + return Err(Error::Type( + "Body is non-null but init's status member is a null body status".to_string())); + }; + + // Step 7.2 + let content_type: Option<ByteString> = None; + + // Step 7.3 + // TODO: Extract body and implement step 7.3. + + // Step 7.4 + if let Some(content_type_contents) = content_type { + if !r.Headers().Has(ByteString::new(b"Content-Type".to_vec())).unwrap() { + try!(r.Headers().Append(ByteString::new(b"Content-Type".to_vec()), content_type_contents)); + } + }; + } + + // Step 8 + *r.mime_type.borrow_mut() = r.Headers().extract_mime_type(); + + // Step 9 + // TODO: `entry settings object` is not implemented in Servo yet. + + // Step 10 + // TODO: Write this step once Promises are merged in + + // Step 11 + Ok(r) + } + + // https://fetch.spec.whatwg.org/#dom-response-error + pub fn Error(global: GlobalRef) -> Root<Response> { + let r = Response::new(global); + *r.response_type.borrow_mut() = DOMResponseType::Error; + r.Headers().set_guard(Guard::Immutable); + *r.raw_status.borrow_mut() = Some((0, b"".to_vec())); + r + } + + // https://fetch.spec.whatwg.org/#dom-response-redirect + pub fn Redirect(global: GlobalRef, url: USVString, status: u16) -> Fallible<Root<Response>> { + // Step 1 + // TODO: `entry settings object` is not implemented in Servo yet. + let base_url = global.get_url(); + let parsed_url = base_url.join(&url.0); + + // Step 2 + let url = match parsed_url { + Ok(url) => url, + Err(_) => return Err(Error::Type("Url could not be parsed".to_string())), + }; + + // Step 3 + if !is_redirect_status(status) { + return Err(Error::Range("status is not a redirect status".to_string())); + } + + // Step 4 + // see Step 4 continued + let r = Response::new(global); + + // Step 5 + *r.status.borrow_mut() = Some(StatusCode::from_u16(status)); + *r.raw_status.borrow_mut() = Some((status, b"".to_vec())); + + // Step 6 + let url_bytestring = ByteString::from_str(url.as_str()).unwrap_or(ByteString::new(b"".to_vec())); + try!(r.Headers().Set(ByteString::new(b"Location".to_vec()), url_bytestring)); + + // Step 4 continued + // Headers Guard is set to Immutable here to prevent error in Step 6 + r.Headers().set_guard(Guard::Immutable); + + // Step 7 + Ok(r) + } +} + +// https://fetch.spec.whatwg.org/#redirect-status +fn is_redirect_status(status: u16) -> bool { + status == 301 || status == 302 || status == 303 || status == 307 || status == 308 +} + +// https://tools.ietf.org/html/rfc7230#section-3.1.2 +fn is_valid_status_text(status_text: &ByteString) -> bool { + // reason-phrase = *( HTAB / SP / VCHAR / obs-text ) + for byte in status_text.iter() { + if !(*byte == b'\t' || *byte == b' ' || is_vchar(*byte) || is_obs_text(*byte)) { + return false; + } + } + true +} + +// https://fetch.spec.whatwg.org/#null-body-status +fn is_null_body_status(status: u16) -> bool { + status == 101 || status == 204 || status == 205 || status == 304 +} + +impl ResponseMethods for Response { + // https://fetch.spec.whatwg.org/#dom-response-type + fn Type(&self) -> DOMResponseType { + *self.response_type.borrow()//into() + } + + // https://fetch.spec.whatwg.org/#dom-response-url + fn Url(&self) -> USVString { + USVString(String::from((*self.url.borrow()).as_ref().map(|u| serialize_without_fragment(u)).unwrap_or(""))) + } + + // https://fetch.spec.whatwg.org/#dom-response-redirected + fn Redirected(&self) -> bool { + let url_list_len = self.url_list.borrow().len(); + url_list_len > 1 + } + + // https://fetch.spec.whatwg.org/#dom-response-status + fn Status(&self) -> u16 { + match *self.raw_status.borrow() { + Some((s, _)) => s, + None => 0, + } + } + + // https://fetch.spec.whatwg.org/#dom-response-ok + fn Ok(&self) -> bool { + match *self.status.borrow() { + Some(s) => { + let status_num = s.to_u16(); + return status_num >= 200 && status_num <= 299; + } + None => false, + } + } + + // https://fetch.spec.whatwg.org/#dom-response-statustext + fn StatusText(&self) -> ByteString { + match *self.raw_status.borrow() { + Some((_, ref st)) => ByteString::new(st.clone()), + None => ByteString::new(b"OK".to_vec()), + } + } + + // https://fetch.spec.whatwg.org/#dom-response-headers + fn Headers(&self) -> Root<Headers> { + self.headers_reflector.or_init(|| Headers::for_response(self.global().r())) + } + + // https://fetch.spec.whatwg.org/#dom-response-clone + fn Clone(&self) -> Fallible<Root<Response>> { + // Step 1 + // TODO: This step relies on body and stream, which are still unimplemented. + + // Step 2 + let new_response = Response::new(self.global().r()); + new_response.Headers().set_guard(self.Headers().get_guard()); + + // https://fetch.spec.whatwg.org/#concept-response-clone + // Instead of storing a net_traits::Response internally, we + // only store the relevant fields, and only clone them here + *new_response.response_type.borrow_mut() = self.response_type.borrow().clone(); + *new_response.status.borrow_mut() = self.status.borrow().clone(); + *new_response.raw_status.borrow_mut() = self.raw_status.borrow().clone(); + *new_response.url.borrow_mut() = self.url.borrow().clone(); + *new_response.url_list.borrow_mut() = self.url_list.borrow().clone(); + + if *self.body.borrow() != NetTraitsResponseBody::Empty { + *new_response.body.borrow_mut() = self.body.borrow().clone(); + } + + // Step 3 + // TODO: This step relies on promises, which are still unimplemented. + + // Step 4 + Ok(new_response) + } + + // https://fetch.spec.whatwg.org/#dom-body-bodyused + fn BodyUsed(&self) -> bool { + self.body_used.get() + } +} + +fn serialize_without_fragment(url: &Url) -> &str { + &url[..Position::AfterQuery] +} diff --git a/components/script/dom/serviceworker.rs b/components/script/dom/serviceworker.rs index 19af9b09ba4..2b79be43dc8 100644 --- a/components/script/dom/serviceworker.rs +++ b/components/script/dom/serviceworker.rs @@ -2,8 +2,7 @@ * 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::abstractworker::WorkerScriptMsg; -use dom::abstractworker::{SimpleWorkerErrorHandler, WorkerErrorHandler}; +use dom::abstractworker::{SimpleWorkerErrorHandler, WorkerScriptMsg}; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::ServiceWorkerBinding::{ServiceWorkerMethods, ServiceWorkerState, Wrap}; @@ -11,12 +10,9 @@ use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::bindings::refcounted::Trusted; -use dom::bindings::reflector::{Reflectable, reflect_dom_object}; -use dom::bindings::str::{DOMString, USVString}; -use dom::errorevent::ErrorEvent; -use dom::event::{Event, EventBubbles, EventCancelable}; +use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::str::USVString; use dom::eventtarget::EventTarget; -use js::jsval::UndefinedValue; use script_thread::Runnable; use std::cell::Cell; use std::sync::mpsc::Sender; @@ -66,18 +62,6 @@ impl ServiceWorker { Url::parse(&self.script_url.borrow().clone()).unwrap() } - pub fn handle_error_message(address: TrustedServiceWorkerAddress, message: DOMString, - filename: DOMString, lineno: u32, colno: u32) { - let worker = address.root(); - - let global = worker.r().global(); - rooted!(in(global.r().get_cx()) let error = UndefinedValue()); - let errorevent = ErrorEvent::new(global.r(), atom!("error"), - EventBubbles::Bubbles, EventCancelable::Cancelable, - message, filename, lineno, colno, error.handle()); - errorevent.upcast::<Event>().fire(worker.upcast()); - } - pub fn install_serviceworker(global: GlobalRef, script_url: Url, skip_waiting: bool) -> Root<ServiceWorker> { @@ -112,11 +96,3 @@ impl Runnable for SimpleWorkerErrorHandler<ServiceWorker> { ServiceWorker::dispatch_simple_error(this.addr); } } - -impl Runnable for WorkerErrorHandler<ServiceWorker> { - #[allow(unrooted_must_root)] - fn handler(self: Box<WorkerErrorHandler<ServiceWorker>>) { - let this = *self; - ServiceWorker::handle_error_message(this.addr, this.msg, this.file_name, this.line_num, this.col_num); - } -} diff --git a/components/script/dom/serviceworkercontainer.rs b/components/script/dom/serviceworkercontainer.rs index 4487c064fe1..06080b7731c 100644 --- a/components/script/dom/serviceworkercontainer.rs +++ b/components/script/dom/serviceworkercontainer.rs @@ -2,8 +2,8 @@ * 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::ServiceWorkerContainerBinding::RegistrationOptions; use dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::{ServiceWorkerContainerMethods, Wrap}; +use dom::bindings::codegen::Bindings::ServiceWorkerContainerBinding::RegistrationOptions; use dom::bindings::error::{Error, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index 275fffdb99a..3d26cbdb67f 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -12,7 +12,6 @@ use dom::bindings::codegen::Bindings::ServiceWorkerGlobalScopeBinding::ServiceWo use dom::bindings::global::{GlobalRef, global_root_from_context}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{Root, RootCollection}; -use dom::bindings::refcounted::LiveDOMReferences; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; use dom::eventtarget::EventTarget; @@ -238,9 +237,6 @@ impl ServiceWorkerGlobalScope { CommonWorker(WorkerScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable))) => { runnable.handler() }, - CommonWorker(WorkerScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr))) => { - LiveDOMReferences::cleanup(addr); - }, CommonWorker(WorkerScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan))) => { let scope = self.upcast::<WorkerGlobalScope>(); let cx = scope.get_cx(); diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs index d4d2cbc911a..548dad24dae 100644 --- a/components/script/dom/servohtmlparser.rs +++ b/components/script/dom/servohtmlparser.rs @@ -33,9 +33,9 @@ use js::jsapi::JSTracer; use msg::constellation_msg::{PipelineId, SubpageId}; use net_traits::{AsyncResponseListener, Metadata, NetworkError}; use network_listener::PreInvoke; -use parse::{TrustedParser, ParserRef, Parser}; +use parse::{Parser, ParserRef, TrustedParser}; +use profile_traits::time::{TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType, profile}; use profile_traits::time::ProfilerCategory; -use profile_traits::time::{profile, TimerMetadata, TimerMetadataReflowType, TimerMetadataFrameType}; use script_thread::ScriptThread; use std::cell::Cell; use std::default::Default; diff --git a/components/script/dom/storage.rs b/components/script/dom/storage.rs index d6d1e1968f8..7827cdb6b15 100644 --- a/components/script/dom/storage.rs +++ b/components/script/dom/storage.rs @@ -136,10 +136,8 @@ impl StorageMethods for Storage { } // check-tidy: no specs after this line - fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<DOMString> { - let item = self.GetItem(name); - *found = item.is_some(); - item + fn NamedGetter(&self, name: DOMString) -> Option<DOMString> { + self.GetItem(name) } fn NamedSetter(&self, name: DOMString, value: DOMString) -> ErrorResult { diff --git a/components/script/dom/stylesheetlist.rs b/components/script/dom/stylesheetlist.rs index 1b7eb643e50..721ac06525c 100644 --- a/components/script/dom/stylesheetlist.rs +++ b/components/script/dom/stylesheetlist.rs @@ -46,9 +46,7 @@ impl StyleSheetListMethods for StyleSheetList { } // check-tidy: no specs after this line - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<StyleSheet>>{ - let item = self.Item(index); - *found = item.is_some(); - item + fn IndexedGetter(&self, index: u32) -> Option<Root<StyleSheet>> { + self.Item(index) } } diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs index 9143c8b0188..b9e34ebe0a7 100644 --- a/components/script/dom/testbinding.rs +++ b/components/script/dom/testbinding.rs @@ -4,6 +4,7 @@ // check-tidy: no specs after this line +use core::nonzero::NonZero; use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener; use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::TestBindingBinding; @@ -11,8 +12,9 @@ use dom::bindings::codegen::Bindings::TestBindingBinding::{TestBindingMethods, T use dom::bindings::codegen::Bindings::TestBindingBinding::{TestDictionaryDefaults, TestEnum}; use dom::bindings::codegen::UnionTypes::{BlobOrBoolean, BlobOrBlobSequence, LongOrLongSequenceSequence}; use dom::bindings::codegen::UnionTypes::{BlobOrString, BlobOrUnsignedLong, EventOrString}; -use dom::bindings::codegen::UnionTypes::{ByteStringOrLong, ByteStringSequenceOrLongOrString, ByteStringSequenceOrLong}; -use dom::bindings::codegen::UnionTypes::{EventOrUSVString, HTMLElementOrLong}; +use dom::bindings::codegen::UnionTypes::{ByteStringOrLong, ByteStringSequenceOrLongOrString}; +use dom::bindings::codegen::UnionTypes::{ByteStringSequenceOrLong, DocumentOrTestTypedef}; +use dom::bindings::codegen::UnionTypes::{EventOrUSVString, HTMLElementOrLong, LongSequenceOrTestTypedef}; use dom::bindings::codegen::UnionTypes::{HTMLElementOrUnsignedLongOrStringOrBoolean, LongSequenceOrBoolean}; use dom::bindings::codegen::UnionTypes::{StringOrLongSequence, StringOrStringSequence, StringSequenceOrUnsignedLong}; use dom::bindings::codegen::UnionTypes::{StringOrUnsignedLong, StringOrBoolean, UnsignedLongOrBoolean}; @@ -26,6 +28,7 @@ use dom::bindings::weakref::MutableWeakRef; use dom::blob::{Blob, BlobImpl}; use dom::url::URL; use js::jsapi::{HandleObject, HandleValue, JSContext, JSObject}; +use js::jsapi::{JS_NewPlainObject, JS_NewUint8ClampedArray}; use js::jsval::{JSVal, NullValue}; use std::borrow::ToOwned; use std::ptr; @@ -137,10 +140,24 @@ impl TestBindingMethods for TestBinding { ByteStringOrLong::ByteString(ByteString::new(vec!())) } fn SetUnion9Attribute(&self, _: ByteStringOrLong) {} - fn ArrayAttribute(&self, _: *mut JSContext) -> *mut JSObject { NullValue().to_object_or_null() } + #[allow(unsafe_code)] + fn ArrayAttribute(&self, cx: *mut JSContext) -> NonZero<*mut JSObject> { + unsafe { + rooted!(in(cx) let array = JS_NewUint8ClampedArray(cx, 16)); + assert!(!array.is_null()); + NonZero::new(array.get()) + } + } fn AnyAttribute(&self, _: *mut JSContext) -> JSVal { NullValue() } fn SetAnyAttribute(&self, _: *mut JSContext, _: HandleValue) {} - fn ObjectAttribute(&self, _: *mut JSContext) -> *mut JSObject { panic!() } + #[allow(unsafe_code)] + fn ObjectAttribute(&self, cx: *mut JSContext) -> NonZero<*mut JSObject> { + unsafe { + rooted!(in(cx) let obj = JS_NewPlainObject(cx)); + assert!(!obj.is_null()); + NonZero::new(obj.get()) + } + } fn SetObjectAttribute(&self, _: *mut JSContext, _: *mut JSObject) {} fn GetBooleanAttributeNullable(&self) -> Option<bool> { Some(false) } @@ -193,7 +210,7 @@ impl TestBindingMethods for TestBinding { fn SetInterfaceAttributeWeak(&self, url: Option<&URL>) { self.url.set(url); } - fn GetObjectAttributeNullable(&self, _: *mut JSContext) -> *mut JSObject { ptr::null_mut() } + fn GetObjectAttributeNullable(&self, _: *mut JSContext) -> Option<NonZero<*mut JSObject>> { None } fn SetObjectAttributeNullable(&self, _: *mut JSContext, _: *mut JSObject) {} fn GetUnionAttributeNullable(&self) -> Option<HTMLElementOrLong> { Some(HTMLElementOrLong::Long(0)) @@ -242,7 +259,9 @@ impl TestBindingMethods for TestBinding { Blob::new(self.global().r(), BlobImpl::new_from_bytes(vec![]), "".to_owned()) } fn ReceiveAny(&self, _: *mut JSContext) -> JSVal { NullValue() } - fn ReceiveObject(&self, _: *mut JSContext) -> *mut JSObject { panic!() } + fn ReceiveObject(&self, cx: *mut JSContext) -> NonZero<*mut JSObject> { + self.ObjectAttribute(cx) + } fn ReceiveUnion(&self) -> HTMLElementOrLong { HTMLElementOrLong::Long(0) } fn ReceiveUnion2(&self) -> EventOrString { EventOrString::String(DOMString::new()) } fn ReceiveUnion3(&self) -> StringOrLongSequence { StringOrLongSequence::LongSequence(vec![]) } @@ -283,7 +302,9 @@ impl TestBindingMethods for TestBinding { fn ReceiveNullableInterface(&self) -> Option<Root<Blob>> { Some(Blob::new(self.global().r(), BlobImpl::new_from_bytes(vec![]), "".to_owned())) } - fn ReceiveNullableObject(&self, _: *mut JSContext) -> *mut JSObject { ptr::null_mut() } + fn ReceiveNullableObject(&self, cx: *mut JSContext) -> Option<NonZero<*mut JSObject>> { + self.GetObjectAttributeNullable(cx) + } fn ReceiveNullableUnion(&self) -> Option<HTMLElementOrLong> { Some(HTMLElementOrLong::Long(0)) } @@ -401,6 +422,8 @@ impl TestBindingMethods for TestBinding { fn PassUnion6(&self, _: UnsignedLongOrBoolean) {} fn PassUnion7(&self, _: StringSequenceOrUnsignedLong) {} fn PassUnion8(&self, _: ByteStringSequenceOrLong) {} + fn PassUnionWithTypedef(&self, _: DocumentOrTestTypedef) {} + fn PassUnionWithTypedef2(&self, _: LongSequenceOrTestTypedef) {} fn PassAny(&self, _: *mut JSContext, _: HandleValue) {} fn PassObject(&self, _: *mut JSContext, _: *mut JSObject) {} fn PassCallbackFunction(&self, _: Rc<Function>) {} diff --git a/components/script/dom/testbindingiterable.rs b/components/script/dom/testbindingiterable.rs new file mode 100644 index 00000000000..2ad0df6dbb6 --- /dev/null +++ b/components/script/dom/testbindingiterable.rs @@ -0,0 +1,41 @@ +/* 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/. */ + +// check-tidy: no specs after this line + +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::TestBindingIterableBinding::{self, TestBindingIterableMethods}; +use dom::bindings::error::Fallible; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::Root; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; + +#[dom_struct] +pub struct TestBindingIterable { + reflector: Reflector, + vals: DOMRefCell<Vec<DOMString>>, +} + +impl TestBindingIterable { + fn new(global: GlobalRef) -> Root<TestBindingIterable> { + reflect_dom_object(box TestBindingIterable { + reflector: Reflector::new(), + vals: DOMRefCell::new(vec![]), + }, global, TestBindingIterableBinding::Wrap) + } + + pub fn Constructor(global: GlobalRef) -> Fallible<Root<TestBindingIterable>> { + Ok(TestBindingIterable::new(global)) + } +} + +impl TestBindingIterableMethods for TestBindingIterable { + fn Add(&self, v: DOMString) { self.vals.borrow_mut().push(v); } + fn Length(&self) -> u32 { self.vals.borrow().len() as u32 } + fn GetItem(&self, n: u32) -> DOMString { self.IndexedGetter(n).unwrap_or_default() } + fn IndexedGetter(&self, n: u32) -> Option<DOMString> { + self.vals.borrow().get(n as usize).cloned() + } +} diff --git a/components/script/dom/testbindingpairiterable.rs b/components/script/dom/testbindingpairiterable.rs new file mode 100644 index 00000000000..9bceedd4980 --- /dev/null +++ b/components/script/dom/testbindingpairiterable.rs @@ -0,0 +1,54 @@ +/* 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/. */ + +// check-tidy: no specs after this line + +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::TestBindingPairIterableBinding; +use dom::bindings::codegen::Bindings::TestBindingPairIterableBinding::TestBindingPairIterableMethods; +use dom::bindings::error::Fallible; +use dom::bindings::global::GlobalRef; +use dom::bindings::iterable::Iterable; +use dom::bindings::js::Root; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; + +#[dom_struct] +pub struct TestBindingPairIterable { + reflector: Reflector, + map: DOMRefCell<Vec<(DOMString, u32)>>, +} + +impl Iterable for TestBindingPairIterable { + type Key = DOMString; + type Value = u32; + fn get_iterable_length(&self) -> u32 { + self.map.borrow().len() as u32 + } + fn get_value_at_index(&self, index: u32) -> u32 { + self.map.borrow().iter().nth(index as usize).map(|a| &a.1).unwrap().clone() + } + fn get_key_at_index(&self, index: u32) -> DOMString { + self.map.borrow().iter().nth(index as usize).map(|a| &a.0).unwrap().clone() + } +} + +impl TestBindingPairIterable { + fn new(global: GlobalRef) -> Root<TestBindingPairIterable> { + reflect_dom_object(box TestBindingPairIterable { + reflector: Reflector::new(), + map: DOMRefCell::new(vec![]), + }, global, TestBindingPairIterableBinding::TestBindingPairIterableWrap) + } + + pub fn Constructor(global: GlobalRef) -> Fallible<Root<TestBindingPairIterable>> { + Ok(TestBindingPairIterable::new(global)) + } +} + +impl TestBindingPairIterableMethods for TestBindingPairIterable { + fn Add(&self, key: DOMString, value: u32) { + self.map.borrow_mut().push((key, value)); + } +} diff --git a/components/script/dom/testbindingproxy.rs b/components/script/dom/testbindingproxy.rs index 3308639305c..45e66bc5919 100644 --- a/components/script/dom/testbindingproxy.rs +++ b/components/script/dom/testbindingproxy.rs @@ -23,10 +23,10 @@ impl TestBindingProxyMethods for TestBindingProxy { fn SetItem(&self, _: u32, _: DOMString) -> () {} fn RemoveItem(&self, _: DOMString) -> () {} fn Stringifier(&self) -> DOMString { DOMString::new() } - fn IndexedGetter(&self, _: u32, _: &mut bool) -> DOMString { DOMString::new() } + fn IndexedGetter(&self, _: u32) -> Option<DOMString> { None } fn NamedDeleter(&self, _: DOMString) -> () {} fn IndexedSetter(&self, _: u32, _: DOMString) -> () {} fn NamedSetter(&self, _: DOMString, _: DOMString) -> () {} - fn NamedGetter(&self, _: DOMString, _: &mut bool) -> DOMString { DOMString::new() } + fn NamedGetter(&self, _: DOMString) -> Option<DOMString> { None } } diff --git a/components/script/dom/textencoder.rs b/components/script/dom/textencoder.rs index 84100e723ba..674243dded8 100644 --- a/components/script/dom/textencoder.rs +++ b/components/script/dom/textencoder.rs @@ -2,6 +2,7 @@ * 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 core::nonzero::NonZero; use dom::bindings::codegen::Bindings::TextEncoderBinding; use dom::bindings::codegen::Bindings::TextEncoderBinding::TextEncoderMethods; use dom::bindings::error::{Error, Fallible}; @@ -70,16 +71,17 @@ impl TextEncoderMethods for TextEncoder { #[allow(unsafe_code)] // https://encoding.spec.whatwg.org/#dom-textencoder-encode - fn Encode(&self, cx: *mut JSContext, input: USVString) -> *mut JSObject { + fn Encode(&self, cx: *mut JSContext, input: USVString) -> NonZero<*mut JSObject> { unsafe { let encoded = self.encoder.encode(&input.0, EncoderTrap::Strict).unwrap(); let length = encoded.len() as u32; - let js_object: *mut JSObject = JS_NewUint8Array(cx, length); + rooted!(in(cx) let js_object = JS_NewUint8Array(cx, length)); + assert!(!js_object.is_null()); let mut is_shared = false; - let js_object_data: *mut uint8_t = JS_GetUint8ArrayData(js_object, &mut is_shared, ptr::null()); + let js_object_data: *mut uint8_t = JS_GetUint8ArrayData(js_object.get(), &mut is_shared, ptr::null()); assert!(!is_shared); ptr::copy_nonoverlapping(encoded.as_ptr(), js_object_data, length as usize); - js_object + NonZero::new(js_object.get()) } } } diff --git a/components/script/dom/touchlist.rs b/components/script/dom/touchlist.rs index ae5313e855e..14bb8a68766 100644 --- a/components/script/dom/touchlist.rs +++ b/components/script/dom/touchlist.rs @@ -42,9 +42,7 @@ impl TouchListMethods for TouchList { } /// https://w3c.github.io/touch-events/#widl-TouchList-item-getter-Touch-unsigned-long-index - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Root<Touch>> { - let item = self.Item(index); - *found = item.is_some(); - item + fn IndexedGetter(&self, index: u32) -> Option<Root<Touch>> { + self.Item(index) } } diff --git a/components/script/dom/treewalker.rs b/components/script/dom/treewalker.rs index 8103421a155..c4097fc761a 100644 --- a/components/script/dom/treewalker.rs +++ b/components/script/dom/treewalker.rs @@ -10,8 +10,8 @@ use dom::bindings::codegen::Bindings::TreeWalkerBinding; use dom::bindings::codegen::Bindings::TreeWalkerBinding::TreeWalkerMethods; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; -use dom::bindings::js::Root; use dom::bindings::js::{JS, MutHeap}; +use dom::bindings::js::Root; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::document::Document; use dom::node::Node; diff --git a/components/script/dom/uievent.rs b/components/script/dom/uievent.rs index 16461315ca1..a0f35868662 100644 --- a/components/script/dom/uievent.rs +++ b/components/script/dom/uievent.rs @@ -8,8 +8,8 @@ use dom::bindings::codegen::Bindings::UIEventBinding::UIEventMethods; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; -use dom::bindings::js::Root; use dom::bindings::js::{JS, MutNullableHeap, RootedReference}; +use dom::bindings::js::Root; use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; use dom::event::{Event, EventBubbles, EventCancelable}; diff --git a/components/script/dom/url.rs b/components/script/dom/url.rs index e42b3b7f1cd..bc686d23cc4 100644 --- a/components/script/dom/url.rs +++ b/components/script/dom/url.rs @@ -14,13 +14,13 @@ use dom::blob::Blob; use dom::urlhelper::UrlHelper; use dom::urlsearchparams::URLSearchParams; use ipc_channel::ipc; +use net_traits::{CoreResourceMsg, IpcSend}; use net_traits::blob_url_store::{get_blob_origin, parse_blob_url}; use net_traits::filemanager_thread::FileManagerThreadMsg; -use net_traits::{IpcSend, CoreResourceMsg}; use std::borrow::ToOwned; use std::default::Default; -use url::quirks::domain_to_unicode; use url::{Host, Url}; +use url::quirks::domain_to_unicode; use uuid::Uuid; // https://url.spec.whatwg.org/#url diff --git a/components/script/dom/webglprogram.rs b/components/script/dom/webglprogram.rs index 543a551ee17..9ac3f4e1ad4 100644 --- a/components/script/dom/webglprogram.rs +++ b/components/script/dom/webglprogram.rs @@ -87,6 +87,10 @@ impl WebGLProgram { } } + pub fn is_deleted(&self) -> bool { + self.is_deleted.get() + } + /// glLinkProgram pub fn link(&self) { self.linked.set(false); diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index 4db5654d738..3fc63349634 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -3,12 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use canvas_traits::{CanvasCommonMsg, CanvasMsg, byte_swap}; +use core::nonzero::NonZero; +use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes}; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods; -use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::{self, WebGLContextAttributes}; use dom::bindings::codegen::UnionTypes::ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement; use dom::bindings::conversions::{ToJSValConvertible, array_buffer_view_data, array_buffer_view_data_checked}; -use dom::bindings::conversions::{array_buffer_view_to_vec_checked, array_buffer_view_to_vec}; +use dom::bindings::conversions::{array_buffer_view_to_vec, array_buffer_view_to_vec_checked}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root}; @@ -21,7 +22,7 @@ use dom::node::{Node, NodeDamage, window_from_node}; use dom::webgl_validations::WebGLValidator; use dom::webgl_validations::tex_image_2d::{CommonTexImage2DValidator, CommonTexImage2DValidatorResult}; use dom::webgl_validations::tex_image_2d::{TexImage2DValidator, TexImage2DValidatorResult}; -use dom::webgl_validations::types::{TexFormat, TexImageTarget, TexDataType}; +use dom::webgl_validations::types::{TexDataType, TexFormat, TexImageTarget}; use dom::webglactiveinfo::WebGLActiveInfo; use dom::webglbuffer::WebGLBuffer; use dom::webglcontextevent::WebGLContextEvent; @@ -33,15 +34,15 @@ use dom::webgltexture::{TexParameterValue, WebGLTexture}; use dom::webgluniformlocation::WebGLUniformLocation; use euclid::size::Size2D; use ipc_channel::ipc::{self, IpcSender}; -use js::jsapi::{JSContext, JS_GetArrayBufferViewType, JSObject, Type}; +use js::jsapi::{JSContext, JSObject, JS_GetArrayBufferViewType, Type}; use js::jsval::{BooleanValue, DoubleValue, Int32Value, JSVal, NullValue, UndefinedValue}; use net_traits::image::base::PixelFormat; use net_traits::image_cache_thread::ImageResponse; use offscreen_gl_context::{GLContextAttributes, GLLimits}; use script_traits::ScriptMsg as ConstellationMsg; use std::cell::Cell; -use webrender_traits::WebGLError::*; use webrender_traits::{WebGLCommand, WebGLError, WebGLFramebufferBindingRequest, WebGLParameter}; +use webrender_traits::WebGLError::*; type ImagePixelResult = Result<(Vec<u8>, Size2D<i32>), ()>; pub const MAX_UNIFORM_AND_ATTRIBUTE_LEN: usize = 256; @@ -61,6 +62,38 @@ macro_rules! handle_potential_webgl_error { }; } +// From the GLES 2.0.25 spec, page 85: +// +// "If a texture that is currently bound to one of the targets +// TEXTURE_2D, or TEXTURE_CUBE_MAP is deleted, it is as though +// BindTexture had been executed with the same target and texture +// zero." +// +// and similar text occurs for other object types. +macro_rules! handle_object_deletion { + ($binding:expr, $object:ident) => { + if let Some(bound_object) = $binding.get() { + if bound_object.id() == $object.id() { + $binding.set(None); + } + } + }; +} + +macro_rules! object_binding_to_js_or_null { + ($cx: expr, $binding:expr) => { + { + rooted!(in($cx) let mut rval = NullValue()); + if let Some(bound_object) = $binding.get() { + unsafe { + bound_object.to_jsval($cx, rval.handle_mut()); + } + } + rval.get() + } + }; +} + /// Set of bitflags for texture unpacking (texImage2d, etc...) bitflags! { #[derive(HeapSizeOf, JSTraceable)] @@ -83,6 +116,7 @@ pub struct WebGLRenderingContext { last_error: Cell<Option<WebGLError>>, texture_unpacking_settings: Cell<TextureUnpacking>, bound_framebuffer: MutNullableHeap<JS<WebGLFramebuffer>>, + bound_renderbuffer: MutNullableHeap<JS<WebGLRenderbuffer>>, bound_texture_2d: MutNullableHeap<JS<WebGLTexture>>, bound_texture_cube_map: MutNullableHeap<JS<WebGLTexture>>, bound_buffer_array: MutNullableHeap<JS<WebGLBuffer>>, @@ -117,6 +151,7 @@ impl WebGLRenderingContext { bound_texture_cube_map: MutNullableHeap::new(None), bound_buffer_array: MutNullableHeap::new(None), bound_buffer_element_array: MutNullableHeap::new(None), + bound_renderbuffer: MutNullableHeap::new(None), current_program: MutNullableHeap::new(None), current_vertex_attrib_0: Cell::new((0f32, 0f32, 0f32, 1f32)), } @@ -271,8 +306,7 @@ impl WebGLRenderingContext { // complexity is worth it. let (pixels, size) = match source { ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::ImageData(image_data) => { - let global = self.global(); - (image_data.get_data_array(&global.r()), image_data.get_size()) + (image_data.get_data_array(), image_data.get_size()) }, ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement::HTMLImageElement(image) => { let img_url = match image.get_url() { @@ -513,19 +547,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 fn GetParameter(&self, cx: *mut JSContext, parameter: u32) -> JSVal { - // Handle the GL_FRAMEBUFFER_BINDING without going all the way + // Handle the GL_*_BINDING without going all the way // to the GL, since we would just need to map back from GL's - // returned ID to the WebGLFramebuffer we're tracking. + // returned ID to the WebGL* object we're tracking. match parameter { - constants::FRAMEBUFFER_BINDING => { - rooted!(in(cx) let mut rval = NullValue()); - if let Some(bound_fb) = self.bound_framebuffer.get() { - unsafe { - bound_fb.to_jsval(cx, rval.handle_mut()); - } - } - return rval.get() - } + constants::ARRAY_BUFFER_BINDING => + return object_binding_to_js_or_null!(cx, &self.bound_buffer_array), + constants::ELEMENT_ARRAY_BUFFER_BINDING => + return object_binding_to_js_or_null!(cx, &self.bound_buffer_element_array), + constants::FRAMEBUFFER_BINDING => + return object_binding_to_js_or_null!(cx, &self.bound_framebuffer), + constants::RENDERBUFFER_BINDING => + return object_binding_to_js_or_null!(cx, &self.bound_renderbuffer), + constants::TEXTURE_BINDING_2D => + return object_binding_to_js_or_null!(cx, &self.bound_texture_2d), + constants::TEXTURE_BINDING_CUBE_MAP => + return object_binding_to_js_or_null!(cx, &self.bound_texture_cube_map), _ => {} } @@ -595,8 +632,9 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.14 - fn GetExtension(&self, _cx: *mut JSContext, _name: DOMString) -> *mut JSObject { - 0 as *mut JSObject + fn GetExtension(&self, _cx: *mut JSContext, _name: DOMString) + -> Option<NonZero<*mut JSObject>> { + None } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 @@ -705,13 +743,19 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return self.webgl_error(InvalidEnum); } - if let Some(renderbuffer) = renderbuffer { - renderbuffer.bind(target) - } else { - // Unbind the currently bound renderbuffer - self.ipc_renderer - .send(CanvasMsg::WebGL(WebGLCommand::BindRenderbuffer(target, None))) - .unwrap() + match renderbuffer { + // Implementations differ on what to do in the deleted + // case: Chromium currently unbinds, and Gecko silently + // returns. The conformance tests don't cover this case. + Some(renderbuffer) if !renderbuffer.is_deleted() => { + renderbuffer.bind(target) + } + _ => { + // Unbind the currently bound renderbuffer + self.ipc_renderer + .send(CanvasMsg::WebGL(WebGLCommand::BindRenderbuffer(target, None))) + .unwrap() + } } } @@ -1095,6 +1139,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) { if let Some(buffer) = buffer { + handle_object_deletion!(self.bound_buffer_array, buffer); + handle_object_deletion!(self.bound_buffer_element_array, buffer); buffer.delete() } } @@ -1102,11 +1148,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 fn DeleteFramebuffer(&self, framebuffer: Option<&WebGLFramebuffer>) { if let Some(framebuffer) = framebuffer { - if let Some(bound_fb) = self.bound_framebuffer.get() { - if bound_fb.id() == framebuffer.id() { - self.bound_framebuffer.set(None); - } - } + handle_object_deletion!(self.bound_framebuffer, framebuffer); framebuffer.delete() } } @@ -1114,6 +1156,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 fn DeleteRenderbuffer(&self, renderbuffer: Option<&WebGLRenderbuffer>) { if let Some(renderbuffer) = renderbuffer { + handle_object_deletion!(self.bound_renderbuffer, renderbuffer); renderbuffer.delete() } } @@ -1121,6 +1164,8 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn DeleteTexture(&self, texture: Option<&WebGLTexture>) { if let Some(texture) = texture { + handle_object_deletion!(self.bound_texture_2d, texture); + handle_object_deletion!(self.bound_texture_cube_map, texture); texture.delete() } } @@ -1128,6 +1173,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 fn DeleteProgram(&self, program: Option<&WebGLProgram>) { if let Some(program) = program { + handle_object_deletion!(self.current_program, program); program.delete() } } @@ -1165,10 +1211,12 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 fn DrawElements(&self, mode: u32, count: i32, type_: u32, offset: i64) { + // From the GLES 2.0.25 spec, page 21: + // + // "type must be one of UNSIGNED_BYTE or UNSIGNED_SHORT" let type_size = match type_ { - constants::BYTE | constants::UNSIGNED_BYTE => 1, - constants::SHORT | constants::UNSIGNED_SHORT => 2, - constants::INT | constants::UNSIGNED_INT | constants::FLOAT => 4, + constants::UNSIGNED_BYTE => 1, + constants::UNSIGNED_SHORT => 2, _ => return self.webgl_error(InvalidEnum), }; @@ -1356,6 +1404,11 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { frame_buffer.map_or(false, |buf| buf.target().is_some() && !buf.is_deleted()) } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 + fn IsProgram(&self, program: Option<&WebGLProgram>) -> bool { + program.map_or(false, |p| !p.is_deleted()) + } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 fn IsRenderbuffer(&self, render_buffer: Option<&WebGLRenderbuffer>) -> bool { render_buffer.map_or(false, |buf| buf.ever_bound() && !buf.is_deleted()) @@ -1479,6 +1532,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4 fn Scissor(&self, x: i32, y: i32, width: i32, height: i32) { + if width < 0 || height < 0 { + return self.webgl_error(InvalidValue) + } + self.ipc_renderer .send(CanvasMsg::WebGL(WebGLCommand::Scissor(x, y, width, height))) .unwrap() @@ -1891,6 +1948,10 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4 fn Viewport(&self, x: i32, y: i32, width: i32, height: i32) { + if width < 0 || height < 0 { + return self.webgl_error(InvalidValue) + } + self.ipc_renderer .send(CanvasMsg::WebGL(WebGLCommand::Viewport(x, y, width, height))) .unwrap() diff --git a/components/script/dom/webidls/Console.webidl b/components/script/dom/webidls/Console.webidl index 09273db78b8..90f9bb9f58e 100644 --- a/components/script/dom/webidls/Console.webidl +++ b/components/script/dom/webidls/Console.webidl @@ -9,8 +9,10 @@ * © Copyright 2014 Mozilla Foundation. */ -[Exposed=(Window,Worker)] -interface Console { +[ClassString="Console", + Exposed=(Window,Worker), + ProtoObjectHack] +namespace console { // These should be DOMString message, DOMString message2, ... void log(DOMString... messages); void debug(DOMString... messages); diff --git a/components/script/dom/webidls/HTMLDataElement.webidl b/components/script/dom/webidls/HTMLDataElement.webidl index be932250678..658f5274491 100644 --- a/components/script/dom/webidls/HTMLDataElement.webidl +++ b/components/script/dom/webidls/HTMLDataElement.webidl @@ -4,5 +4,5 @@ // https://html.spec.whatwg.org/multipage/#htmldataelement interface HTMLDataElement : HTMLElement { - // attribute DOMString value; + attribute DOMString value; }; diff --git a/components/script/dom/webidls/HTMLDialogElement.webidl b/components/script/dom/webidls/HTMLDialogElement.webidl index 78a14e1e2a0..0ac76a0465e 100644 --- a/components/script/dom/webidls/HTMLDialogElement.webidl +++ b/components/script/dom/webidls/HTMLDialogElement.webidl @@ -8,5 +8,5 @@ interface HTMLDialogElement : HTMLElement { attribute DOMString returnValue; //void show(optional (MouseEvent or Element) anchor); //void showModal(optional (MouseEvent or Element) anchor); - //void close(optional DOMString returnValue); + void close(optional DOMString returnValue); }; diff --git a/components/script/dom/webidls/HTMLOptionElement.webidl b/components/script/dom/webidls/HTMLOptionElement.webidl index a5c7c3295da..d4bc5bcdc83 100644 --- a/components/script/dom/webidls/HTMLOptionElement.webidl +++ b/components/script/dom/webidls/HTMLOptionElement.webidl @@ -9,7 +9,7 @@ [Exposed=(Window,Worker)] interface HTMLOptionElement : HTMLElement { attribute boolean disabled; - //readonly attribute HTMLFormElement? form; + readonly attribute HTMLFormElement? form; attribute DOMString label; attribute boolean defaultSelected; attribute boolean selected; diff --git a/components/script/dom/webidls/Headers.webidl b/components/script/dom/webidls/Headers.webidl index 6696bf64731..ee3d4b2fe94 100644 --- a/components/script/dom/webidls/Headers.webidl +++ b/components/script/dom/webidls/Headers.webidl @@ -20,5 +20,5 @@ interface Headers { boolean has(ByteString name); [Throws] void set(ByteString name, ByteString value); - // iterable<ByteString, ByteString>; // TODO see issue #12628 + iterable<ByteString, ByteString>; }; diff --git a/components/script/dom/webidls/IterableIterator.webidl b/components/script/dom/webidls/IterableIterator.webidl new file mode 100644 index 00000000000..d975aa5645d --- /dev/null +++ b/components/script/dom/webidls/IterableIterator.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. + +dictionary IterableKeyOrValueResult { + any value; + boolean done = false; +}; + +dictionary IterableKeyAndValueResult { + sequence<any> value; + boolean done = false; +}; diff --git a/components/script/dom/webidls/Navigator.webidl b/components/script/dom/webidls/Navigator.webidl index 493c4ba34e4..ba24348b7f8 100644 --- a/components/script/dom/webidls/Navigator.webidl +++ b/components/script/dom/webidls/Navigator.webidl @@ -14,6 +14,7 @@ Navigator implements NavigatorLanguage; //Navigator implements NavigatorContentUtils; //Navigator implements NavigatorStorageUtils; Navigator implements NavigatorPlugins; +Navigator implements NavigatorCookies; // https://html.spec.whatwg.org/multipage/#navigatorid [NoInterfaceObject, Exposed=(Window,Worker)] @@ -52,3 +53,9 @@ interface NavigatorPlugins { [SameObject] readonly attribute MimeTypeArray mimeTypes; boolean javaEnabled(); }; + +// https://html.spec.whatwg.org/multipage/#navigatorcookies +[NoInterfaceObject, Exposed=(Window,Worker)] +interface NavigatorCookies { + readonly attribute boolean cookieEnabled; +}; diff --git a/components/script/dom/webidls/NodeList.webidl b/components/script/dom/webidls/NodeList.webidl index e61eae6d6b1..780abc66746 100644 --- a/components/script/dom/webidls/NodeList.webidl +++ b/components/script/dom/webidls/NodeList.webidl @@ -12,5 +12,5 @@ interface NodeList { getter Node? item(unsigned long index); [Pure] readonly attribute unsigned long length; - // iterable<Node>; + iterable<Node?>; }; diff --git a/components/script/dom/webidls/Response.webidl b/components/script/dom/webidls/Response.webidl new file mode 100644 index 00000000000..2052f5c6371 --- /dev/null +++ b/components/script/dom/webidls/Response.webidl @@ -0,0 +1,37 @@ +/* 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/. */ + +// https://fetch.spec.whatwg.org/#response-class + +// TODO: pass 'optional ResponseBodyInit? body = null' to constructor in place of USVString + [Constructor(optional USVString? body = null, optional ResponseInit init), + Exposed=(Window,Worker)] +interface Response { + [NewObject] static Response error(); + [NewObject, Throws] static Response redirect(USVString url, optional unsigned short status = 302); + + readonly attribute ResponseType type; + + readonly attribute USVString url; + readonly attribute boolean redirected; + readonly attribute unsigned short status; + readonly attribute boolean ok; + readonly attribute ByteString statusText; + [SameObject] readonly attribute Headers headers; + // readonly attribute ReadableStream? body; + // [SameObject] readonly attribute Promise<Headers> trailer; + + [NewObject, Throws] Response clone(); +}; +Response implements Body; + +dictionary ResponseInit { + unsigned short status = 200; + ByteString statusText = "OK"; + HeadersInit headers; +}; + +enum ResponseType { "basic", "cors", "default", "error", "opaque", "opaqueredirect" }; + +// typedef (BodyInit or ReadableStream) ResponseBodyInit; diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl index 392aee5963b..d98b9428031 100644 --- a/components/script/dom/webidls/TestBinding.webidl +++ b/components/script/dom/webidls/TestBinding.webidl @@ -6,6 +6,7 @@ // web pages. enum TestEnum { "", "foo", "bar" }; +typedef (DOMString or URL or Blob) TestTypedef; dictionary TestDictionary { required boolean requiredValue; @@ -241,6 +242,8 @@ interface TestBinding { void passUnion6((unsigned long or boolean) bool); void passUnion7((sequence<DOMString> or unsigned long) arg); void passUnion8((sequence<ByteString> or long) arg); + void passUnionWithTypedef((Document or TestTypedef) arg); + void passUnionWithTypedef2((sequence<long> or TestTypedef) arg); void passAny(any arg); void passObject(object arg); void passCallbackFunction(Function fun); diff --git a/components/script/dom/webidls/TestBindingIterable.webidl b/components/script/dom/webidls/TestBindingIterable.webidl new file mode 100644 index 00000000000..c9e61074eed --- /dev/null +++ b/components/script/dom/webidls/TestBindingIterable.webidl @@ -0,0 +1,14 @@ +/* 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. + +[Pref="dom.testbinding.enabled", Exposed=(Window,Worker), Constructor] +interface TestBindingIterable { + void add(DOMString arg); + readonly attribute unsigned long length; + getter DOMString getItem(unsigned long index); + iterable<DOMString>; +}; diff --git a/components/script/dom/webidls/TestBindingPairIterable.webidl b/components/script/dom/webidls/TestBindingPairIterable.webidl new file mode 100644 index 00000000000..a7bc66c1be3 --- /dev/null +++ b/components/script/dom/webidls/TestBindingPairIterable.webidl @@ -0,0 +1,12 @@ +/* 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. + +[Pref="dom.testbinding.enabled", Exposed=(Window,Worker), Constructor] +interface TestBindingPairIterable { + void add(DOMString key, unsigned long value); + iterable<DOMString, unsigned long>; +}; diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl index eea550a5bc0..e95ce00e5e5 100644 --- a/components/script/dom/webidls/WebGLRenderingContext.webidl +++ b/components/script/dom/webidls/WebGLRenderingContext.webidl @@ -605,7 +605,7 @@ interface WebGLRenderingContextBase [WebGLHandlesContextLoss] GLboolean isBuffer(WebGLBuffer? buffer); //[WebGLHandlesContextLoss] GLboolean isEnabled(GLenum cap); [WebGLHandlesContextLoss] GLboolean isFramebuffer(WebGLFramebuffer? framebuffer); - //[WebGLHandlesContextLoss] GLboolean isProgram(WebGLProgram? program); + [WebGLHandlesContextLoss] GLboolean isProgram(WebGLProgram? program); [WebGLHandlesContextLoss] GLboolean isRenderbuffer(WebGLRenderbuffer? renderbuffer); [WebGLHandlesContextLoss] GLboolean isShader(WebGLShader? shader); [WebGLHandlesContextLoss] GLboolean isTexture(WebGLTexture? texture); diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index 7057a4541a6..dbe73ca4831 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -89,7 +89,7 @@ Window implements WindowBase64; // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-window.performance-attribute partial interface Window { - /*[Replaceable]*/ readonly attribute Performance performance; + [Replaceable] readonly attribute Performance performance; }; // https://html.spec.whatwg.org/multipage/#Window-partial @@ -161,7 +161,6 @@ partial interface Window { // Proprietary extensions. partial interface Window { - readonly attribute Console console; void debug(DOMString arg); void gc(); void trap(); diff --git a/components/script/dom/webidls/WorkerGlobalScope.webidl b/components/script/dom/webidls/WorkerGlobalScope.webidl index ec65bcb09a6..186e5cd7fee 100644 --- a/components/script/dom/webidls/WorkerGlobalScope.webidl +++ b/components/script/dom/webidls/WorkerGlobalScope.webidl @@ -9,7 +9,7 @@ interface WorkerGlobalScope : EventTarget { readonly attribute WorkerLocation location; //void close(); - // attribute OnErrorEventHandler onerror; + attribute OnErrorEventHandler onerror; // attribute EventHandler onlanguagechange; // attribute EventHandler onoffline; // attribute EventHandler ononline; @@ -24,10 +24,3 @@ partial interface WorkerGlobalScope { // not obsolete }; WorkerGlobalScope implements WindowTimers; WorkerGlobalScope implements WindowBase64; - -// Proprietary -[Exposed=Worker] -partial interface WorkerGlobalScope { - [Replaceable] - readonly attribute Console console; -}; diff --git a/components/script/dom/websocket.rs b/components/script/dom/websocket.rs index 3b3652040e9..df9c2e5ed34 100644 --- a/components/script/dom/websocket.rs +++ b/components/script/dom/websocket.rs @@ -9,7 +9,7 @@ use dom::bindings::codegen::Bindings::WebSocketBinding; use dom::bindings::codegen::Bindings::WebSocketBinding::{BinaryType, WebSocketMethods}; use dom::bindings::codegen::UnionTypes::StringOrStringSequence; use dom::bindings::conversions::ToJSValConvertible; -use dom::bindings::error::{Error, Fallible, ErrorResult}; +use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; @@ -23,18 +23,18 @@ use dom::eventtarget::EventTarget; use dom::messageevent::MessageEvent; use dom::urlhelper::UrlHelper; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; -use js::jsapi::JSAutoCompartment; use js::jsapi::{JS_GetArrayBufferData, JS_NewArrayBuffer}; +use js::jsapi::JSAutoCompartment; use js::jsval::UndefinedValue; use libc::{uint32_t, uint8_t}; +use net_traits::{WebSocketCommunicate, WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent}; use net_traits::CookieSource::HTTP; -use net_traits::CoreResourceMsg::{WebsocketConnect, SetCookiesForUrl}; +use net_traits::CoreResourceMsg::{SetCookiesForUrl, WebsocketConnect}; use net_traits::MessageData; use net_traits::hosts::replace_hosts; use net_traits::unwrap_websocket_protocol; -use net_traits::{WebSocketCommunicate, WebSocketConnectData, WebSocketDomAction, WebSocketNetworkEvent}; -use script_runtime::ScriptThreadEventCategory::WebSocketEvent; use script_runtime::{CommonScriptMsg, ScriptChan}; +use script_runtime::ScriptThreadEventCategory::WebSocketEvent; use script_thread::Runnable; use std::ascii::AsciiExt; use std::borrow::ToOwned; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index e728e38a451..e8006123360 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -12,9 +12,9 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::OnBeforeUnloadEventHa use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull; use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; -use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions}; use dom::bindings::codegen::Bindings::WindowBinding::{self, FrameRequestCallback, WindowMethods}; -use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception}; +use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions}; +use dom::bindings::error::{Error, ErrorInfo, ErrorResult, Fallible, report_pending_exception}; use dom::bindings::global::{GlobalRef, global_root_from_object}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root}; @@ -25,12 +25,13 @@ use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; use dom::bindings::utils::{GlobalStaticData, WindowProxyHandler}; use dom::browsingcontext::BrowsingContext; -use dom::console::Console; +use dom::console::TimerSet; use dom::crypto::Crypto; use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration}; use dom::document::Document; use dom::element::Element; -use dom::event::Event; +use dom::errorevent::ErrorEvent; +use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; use dom::history::History; use dom::htmliframeelement::build_mozbrowser_custom_event; @@ -45,7 +46,7 @@ use euclid::{Point2D, Rect, Size2D}; use gfx_traits::LayerId; use ipc_channel::ipc::{self, IpcSender}; use js::jsapi::{Evaluate2, HandleObject, HandleValue, JSAutoCompartment, JSContext}; -use js::jsapi::{JS_GetRuntime, JS_GC, MutableHandleValue, SetWindowProxy}; +use js::jsapi::{JS_GC, JS_GetRuntime, MutableHandleValue, SetWindowProxy}; use js::jsval::UndefinedValue; use js::rust::CompileOptionsWrapper; use js::rust::Runtime; @@ -67,13 +68,13 @@ use script_layout_interface::message::{Msg, Reflow, ReflowQueryType, ScriptReflo use script_layout_interface::reporter::CSSErrorReporter; use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC}; use script_layout_interface::rpc::{MarginStyleResponse, ResolvedStyleResponse}; -use script_runtime::{ScriptChan, ScriptPort, CommonScriptMsg, ScriptThreadEventCategory, maybe_take_panic_result}; +use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThreadEventCategory, maybe_take_panic_result}; +use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, Runnable, RunnableWrapper}; use script_thread::SendableMainThreadScriptChan; -use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, RunnableWrapper, Runnable}; -use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::{ConstellationControlMsg, MozBrowserEvent, UntrustedNodeAddress}; use script_traits::{DocumentState, MsDuration, TimerEvent, TimerEventId}; use script_traits::{ScriptMsg as ConstellationMsg, TimerEventRequest, TimerSource, WindowSizeData}; +use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use std::ascii::AsciiExt; use std::borrow::ToOwned; use std::cell::Cell; @@ -83,10 +84,10 @@ use std::ffi::CString; use std::io::{Write, stderr, stdout}; use std::panic; use std::rc::Rc; +use std::sync::{Arc, Mutex}; use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::mpsc::TryRecvError::{Disconnected, Empty}; use std::sync::mpsc::{Sender, channel}; -use std::sync::{Arc, Mutex}; +use std::sync::mpsc::TryRecvError::{Disconnected, Empty}; use string_cache::Atom; use style::context::ReflowGoal; use style::error_reporting::ParseErrorReporter; @@ -156,7 +157,6 @@ pub struct Window { history_traversal_task_source: HistoryTraversalTaskSource, #[ignore_heap_size_of = "task sources are hard"] file_reading_task_source: FileReadingTaskSource, - console: MutNullableHeap<JS<Console>>, crypto: MutNullableHeap<JS<Crypto>>, navigator: MutNullableHeap<JS<Navigator>>, #[ignore_heap_size_of = "channels are hard"] @@ -273,6 +273,12 @@ pub struct Window { /// A list of scroll offsets for each scrollable element. scroll_offsets: DOMRefCell<HashMap<UntrustedNodeAddress, Point2D<f32>>>, + + /// https://html.spec.whatwg.org/multipage/#in-error-reporting-mode + in_error_reporting_mode: Cell<bool>, + + /// Timers used by the Console API. + console_timers: TimerSet, } impl Window { @@ -504,11 +510,6 @@ impl WindowMethods for Window { self.local_storage.or_init(|| Storage::new(&GlobalRef::Window(self), StorageType::Local)) } - // https://developer.mozilla.org/en-US/docs/Web/API/Console - fn Console(&self) -> Root<Console> { - self.console.or_init(|| Console::new(GlobalRef::Window(self))) - } - // https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-GlobalCrypto fn Crypto(&self) -> Root<Crypto> { self.crypto.or_init(|| Crypto::new(GlobalRef::Window(self))) @@ -946,13 +947,13 @@ impl<'a, T: Reflectable> ScriptHelpers for &'a T { let filename = CString::new(filename).unwrap(); let _ac = JSAutoCompartment::new(cx, globalhandle.get()); - let options = CompileOptionsWrapper::new(cx, filename.as_ptr(), 0); + let options = CompileOptionsWrapper::new(cx, filename.as_ptr(), 1); unsafe { if !Evaluate2(cx, options.ptr, code.as_ptr(), code.len() as libc::size_t, rval) { debug!("error evaluating JS string"); - report_pending_exception(cx, globalhandle.get()); + report_pending_exception(cx, true); } } @@ -1200,9 +1201,11 @@ impl Window { if !for_display || self.Document().needs_reflow() { issued_reflow = self.force_reflow(goal, query_type, reason); - // If window_size is `None`, we don't reflow, so the document stays dirty. - // Otherwise, we shouldn't need a reflow immediately after a reflow. + // If window_size is `None`, we don't reflow, so the document stays + // dirty. Otherwise, we shouldn't need a reflow immediately after a + // reflow, except if we're waiting for a deferred paint. assert!(!self.Document().needs_reflow() || + (!for_display && self.Document().needs_paint()) || self.window_size.get().is_none() || self.suppress_reflow.get()); } else { @@ -1695,7 +1698,6 @@ impl Window { history_traversal_task_source: history_task_source, file_reading_task_source: file_task_source, image_cache_chan: image_cache_chan, - console: Default::default(), crypto: Default::default(), navigator: Default::default(), image_cache_thread: image_cache_thread, @@ -1740,13 +1742,49 @@ impl Window { ignore_further_async_events: Arc::new(AtomicBool::new(false)), error_reporter: error_reporter, scroll_offsets: DOMRefCell::new(HashMap::new()), + in_error_reporting_mode: Cell::new(false), + console_timers: TimerSet::new(), }; WindowBinding::Wrap(runtime.cx(), win) } + + pub fn console_timers(&self) -> &TimerSet { + &self.console_timers + } + pub fn live_devtools_updates(&self) -> bool { return self.devtools_wants_updates.get(); } + + /// https://html.spec.whatwg.org/multipage/#report-the-error + pub fn report_an_error(&self, error_info: ErrorInfo, value: HandleValue) { + // Step 1. + if self.in_error_reporting_mode.get() { + return; + } + + // Step 2. + self.in_error_reporting_mode.set(true); + + // Steps 3-12. + // FIXME(#13195): muted errors. + let event = ErrorEvent::new(GlobalRef::Window(self), + atom!("error"), + EventBubbles::DoesNotBubble, + EventCancelable::Cancelable, + error_info.message.into(), + error_info.filename.into(), + error_info.lineno, + error_info.column, + value); + + // Step 13. + event.upcast::<Event>().fire(self.upcast::<EventTarget>()); + + // Step 14. + self.in_error_reporting_mode.set(false); + } } fn should_move_clip_rect(clip_rect: Rect<Au>, new_viewport: Rect<f32>) -> bool { diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index c1ad7cf5a96..e40ebee24f0 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -2,13 +2,13 @@ * 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 devtools_traits::{ScriptToDevtoolsControlMsg, DevtoolsPageInfo}; +use devtools_traits::{DevtoolsPageInfo, ScriptToDevtoolsControlMsg}; +use dom::abstractworker::{SharedRt, SimpleWorkerErrorHandler}; use dom::abstractworker::WorkerScriptMsg; -use dom::abstractworker::{SimpleWorkerErrorHandler, SharedRt, WorkerErrorHandler}; use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::WorkerBinding; use dom::bindings::codegen::Bindings::WorkerBinding::WorkerMethods; -use dom::bindings::error::{Error, ErrorResult, Fallible}; +use dom::bindings::error::{Error, ErrorResult, Fallible, ErrorInfo}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; @@ -23,14 +23,14 @@ use dom::eventtarget::EventTarget; use dom::messageevent::MessageEvent; use dom::workerglobalscope::prepare_workerscope_init; use ipc_channel::ipc; -use js::jsapi::{HandleValue, JSContext, JSAutoCompartment}; +use js::jsapi::{HandleValue, JSAutoCompartment, JSContext, NullHandleValue}; use js::jsval::UndefinedValue; use script_thread::Runnable; 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 std::sync::{Arc, Mutex}; pub type TrustedWorkerAddress = Trusted<Worker>; @@ -140,20 +140,24 @@ impl Worker { worker.upcast().fire_simple_event("error"); } - pub fn handle_error_message(address: TrustedWorkerAddress, message: DOMString, - filename: DOMString, lineno: u32, colno: u32) { - let worker = address.root(); - - if worker.is_terminated() { + fn dispatch_error(&self, error_info: ErrorInfo) { + let global = self.global(); + let event = ErrorEvent::new(global.r(), + atom!("error"), + EventBubbles::DoesNotBubble, + EventCancelable::Cancelable, + error_info.message.as_str().into(), + error_info.filename.as_str().into(), + error_info.lineno, + error_info.column, + NullHandleValue); + + let handled = !event.upcast::<Event>().fire(self.upcast::<EventTarget>()); + if handled { return; } - let global = worker.r().global(); - rooted!(in(global.r().get_cx()) let error = UndefinedValue()); - let errorevent = ErrorEvent::new(global.r(), atom!("error"), - EventBubbles::Bubbles, EventCancelable::Cancelable, - message, filename, lineno, colno, error.handle()); - errorevent.upcast::<Event>().fire(worker.upcast()); + global.r().report_an_error(error_info, NullHandleValue); } } @@ -221,10 +225,23 @@ impl Runnable for SimpleWorkerErrorHandler<Worker> { } } -impl Runnable for WorkerErrorHandler<Worker> { - #[allow(unrooted_must_root)] - fn handler(self: Box<WorkerErrorHandler<Worker>>) { +pub struct WorkerErrorHandler { + address: Trusted<Worker>, + error_info: ErrorInfo, +} + +impl WorkerErrorHandler { + pub fn new(address: Trusted<Worker>, error_info: ErrorInfo) -> WorkerErrorHandler { + WorkerErrorHandler { + address: address, + error_info: error_info, + } + } +} + +impl Runnable for WorkerErrorHandler { + fn handler(self: Box<Self>) { let this = *self; - Worker::handle_error_message(this.addr, this.msg, this.file_name, this.line_num, this.col_num); + this.address.root().dispatch_error(this.error_info); } } diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 16388d54d73..1c2c46fb52d 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -3,15 +3,16 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; +use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull; use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods; -use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception}; +use dom::bindings::error::{Error, ErrorResult, Fallible, report_pending_exception, ErrorInfo}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; -use dom::console::Console; +use dom::console::TimerSet; use dom::crypto::Crypto; use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use dom::eventtarget::EventTarget; @@ -20,18 +21,18 @@ use dom::window::{base64_atob, base64_btoa}; use dom::workerlocation::WorkerLocation; use dom::workernavigator::WorkerNavigator; use ipc_channel::ipc::IpcSender; -use js::jsapi::{HandleValue, JSContext, JSRuntime}; +use js::jsapi::{HandleValue, JSAutoCompartment, JSContext, JSRuntime}; use js::jsval::UndefinedValue; use js::rust::Runtime; use msg::constellation_msg::{PipelineId, ReferrerPolicy}; +use net_traits::{IpcSend, LoadOrigin}; use net_traits::{LoadContext, ResourceThreads, load_whole_resource}; -use net_traits::{LoadOrigin, IpcSend}; use profile_traits::{mem, time}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, maybe_take_panic_result}; use script_thread::RunnableWrapper; +use script_traits::{MsDuration, TimerEvent, TimerEventId, TimerEventRequest, TimerSource}; use script_traits::ScriptMsg as ConstellationMsg; use script_traits::WorkerGlobalScopeInit; -use script_traits::{MsDuration, TimerEvent, TimerEventId, TimerEventRequest, TimerSource}; use std::cell::Cell; use std::default::Default; use std::panic; @@ -79,7 +80,6 @@ pub struct WorkerGlobalScope { resource_threads: ResourceThreads, location: MutNullableHeap<JS<WorkerLocation>>, navigator: MutNullableHeap<JS<WorkerNavigator>>, - console: MutNullableHeap<JS<Console>>, crypto: MutNullableHeap<JS<Crypto>>, timers: OneshotTimers, @@ -109,6 +109,9 @@ pub struct WorkerGlobalScope { #[ignore_heap_size_of = "Defined in std"] scheduler_chan: IpcSender<TimerEventRequest>, + + /// Timers used by the Console API. + console_timers: TimerSet, } impl WorkerGlobalScope { @@ -129,7 +132,6 @@ impl WorkerGlobalScope { resource_threads: init.resource_threads, location: Default::default(), navigator: Default::default(), - console: Default::default(), crypto: Default::default(), timers: OneshotTimers::new(timer_event_chan, init.scheduler_chan.clone()), mem_profiler_chan: init.mem_profiler_chan, @@ -140,9 +142,14 @@ impl WorkerGlobalScope { devtools_wants_updates: Cell::new(false), constellation_chan: init.constellation_chan, scheduler_chan: init.scheduler_chan, + console_timers: TimerSet::new(), } } + pub fn console_timers(&self) -> &TimerSet { + &self.console_timers + } + pub fn mem_profiler_chan(&self) -> &mem::ProfilerChan { &self.mem_profiler_chan } @@ -248,6 +255,9 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { }) } + // https://html.spec.whatwg.org/multipage/#handler-workerglobalscope-onerror + error_event_handler!(error, GetOnerror, SetOnerror); + // https://html.spec.whatwg.org/multipage/#dom-workerglobalscope-importscripts fn ImportScripts(&self, url_strings: Vec<DOMString>) -> ErrorResult { let mut urls = Vec::with_capacity(url_strings.len()); @@ -295,11 +305,6 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { self.navigator.or_init(|| WorkerNavigator::new(self)) } - // https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/console - fn Console(&self) -> Root<Console> { - self.console.or_init(|| Console::new(GlobalRef::Worker(self))) - } - // https://html.spec.whatwg.org/multipage/#dfn-Crypto fn Crypto(&self) -> Root<Crypto> { self.crypto.or_init(|| Crypto::new(GlobalRef::Worker(self))) @@ -382,8 +387,9 @@ impl WorkerGlobalScope { // https://github.com/servo/servo/issues/6422 println!("evaluate_script failed"); unsafe { - report_pending_exception( - self.runtime.cx(), self.reflector().get_jsobject().get()); + let _ac = JSAutoCompartment::new(self.runtime.cx(), + self.reflector().get_jsobject().get()); + report_pending_exception(self.runtime.cx(), true); } } } @@ -450,4 +456,11 @@ impl WorkerGlobalScope { closing.store(true, Ordering::SeqCst); } } + + /// https://html.spec.whatwg.org/multipage/#report-the-error + pub fn report_an_error(&self, error_info: ErrorInfo, value: HandleValue) { + self.downcast::<DedicatedWorkerGlobalScope>() + .expect("Should implement report_an_error for this worker") + .report_an_error(error_info, value); + } } diff --git a/components/script/dom/xmldocument.rs b/components/script/dom/xmldocument.rs index 5f6b6e1919c..1a00f67b80c 100644 --- a/components/script/dom/xmldocument.rs +++ b/components/script/dom/xmldocument.rs @@ -2,6 +2,7 @@ * 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 core::nonzero::NonZero; use document_loader::DocumentLoader; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::XMLDocumentBinding::{self, XMLDocumentMethods}; @@ -87,7 +88,7 @@ impl XMLDocumentMethods for XMLDocument { } // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:dom-document-nameditem-filter - fn NamedGetter(&self, _cx: *mut JSContext, name: DOMString, found: &mut bool) -> *mut JSObject { - self.upcast::<Document>().NamedGetter(_cx, name, found) + fn NamedGetter(&self, _cx: *mut JSContext, name: DOMString) -> Option<NonZero<*mut JSObject>> { + self.upcast::<Document>().NamedGetter(_cx, name) } } diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 382e4c69faa..a97dc7f802b 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -21,8 +21,8 @@ use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::{ByteString, DOMString, USVString, is_token}; use dom::blob::{Blob, BlobImpl}; -use dom::document::DocumentSource; use dom::document::{Document, IsHTMLDocument}; +use dom::document::DocumentSource; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; use dom::headers::is_forbidden_header_name; @@ -34,23 +34,22 @@ use encoding::all::UTF_8; use encoding::label::encoding_from_whatwg_label; use encoding::types::{DecoderTrap, EncoderTrap, Encoding, EncodingRef}; use euclid::length::Length; -use hyper::header::Headers; use hyper::header::{ContentLength, ContentType}; -use hyper::http::RawStatus; +use hyper::header::Headers; use hyper::method::Method; -use hyper::mime::{self, Mime, Attr as MimeAttr, Value as MimeValue}; +use hyper::mime::{self, Attr as MimeAttr, Mime, Value as MimeValue}; use hyper_serde::Serde; use ipc_channel::ipc; use ipc_channel::router::ROUTER; -use js::jsapi::JS_ClearPendingException; 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::CoreResourceMsg::Fetch; use net_traits::request::{CredentialsMode, Destination, RequestInit, RequestMode}; use net_traits::trim_http_whitespace; -use net_traits::{CoreResourceThread, LoadOrigin}; -use net_traits::{FetchResponseListener, Metadata, NetworkError}; use network_listener::{NetworkListener, PreInvoke}; use parse::html::{ParseContext, parse_html}; use parse::xml::{self, parse_xml}; @@ -64,7 +63,7 @@ use std::sync::{Arc, Mutex}; use string_cache::Atom; use time; use timers::{OneshotTimerCallback, OneshotTimerHandle}; -use url::{Url, Position}; +use url::{Position, Url}; use util::prefs::PREFS; #[derive(JSTraceable, PartialEq, Copy, Clone, HeapSizeOf)] @@ -91,7 +90,7 @@ struct XHRContext { #[derive(Clone)] pub enum XHRProgress { /// Notify that headers have been received - HeadersReceived(GenerationId, Option<Headers>, Option<RawStatus>), + HeadersReceived(GenerationId, Option<Headers>, Option<(u16, Vec<u8>)>), /// Partial progress (after receiving headers), containing portion of the response Loading(GenerationId, ByteString), /// Loading is done @@ -879,7 +878,7 @@ impl XMLHttpRequest { self.process_partial_response(XHRProgress::HeadersReceived( gen_id, metadata.headers.map(Serde::into_inner), - metadata.status.map(Serde::into_inner))); + metadata.status)); Ok(()) } @@ -943,9 +942,9 @@ impl XMLHttpRequest { // Part of step 13, send() (processing response) // XXXManishearth handle errors, if any (substep 1) // Substep 2 - status.map(|RawStatus(code, reason)| { + status.map(|(code, reason)| { self.status.set(code); - *self.status_text.borrow_mut() = ByteString::new(reason.into_owned().into_bytes()); + *self.status_text.borrow_mut() = ByteString::new(reason); }); headers.as_ref().map(|h| *self.response_headers.borrow_mut() = h.clone()); @@ -1236,8 +1235,8 @@ impl XMLHttpRequest { fn filter_response_headers(&self) -> Headers { // https://fetch.spec.whatwg.org/#concept-response-header-list use hyper::error::Result; - use hyper::header::SetCookie; use hyper::header::{Header, HeaderFormat}; + use hyper::header::SetCookie; use std::fmt; // a dummy header so we can use headers.remove::<SetCookie2>() diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index b4e54fe5bd9..5411deb91a8 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -36,34 +36,34 @@ use dom::bindings::js::LayoutJS; use dom::characterdata::LayoutCharacterDataHelpers; use dom::document::{Document, LayoutDocumentHelpers}; use dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers}; -use dom::node::{CAN_BE_FRAGMENTED, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY, DIRTY_ON_VIEWPORT_SIZE_CHANGE}; -use dom::node::{Node, LayoutNodeHelpers}; +use dom::node::{CAN_BE_FRAGMENTED, DIRTY_ON_VIEWPORT_SIZE_CHANGE, HAS_CHANGED, HAS_DIRTY_DESCENDANTS, IS_DIRTY}; +use dom::node::{LayoutNodeHelpers, Node}; use dom::text::Text; use gfx_traits::ByteIndex; use msg::constellation_msg::PipelineId; use range::Range; -use script_layout_interface::restyle_damage::RestyleDamage; -use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, LayoutNode, PseudoElementType}; -use script_layout_interface::wrapper_traits::{ThreadSafeLayoutNode, ThreadSafeLayoutElement}; use script_layout_interface::{HTMLCanvasData, LayoutNodeType, TrustedNodeAddress}; use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData}; +use script_layout_interface::restyle_damage::RestyleDamage; +use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, LayoutNode, PseudoElementType}; +use script_layout_interface::wrapper_traits::{ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use selectors::matching::ElementFlags; use selectors::parser::{AttrSelector, NamespaceConstraint}; use std::fmt; use std::marker::PhantomData; -use std::mem::{transmute, transmute_copy}; +use std::mem::transmute; use std::sync::Arc; use string_cache::{Atom, Namespace}; use style::attr::AttrValue; use style::computed_values::display; use style::context::SharedStyleContext; use style::data::PrivateStyleData; -use style::dom::{PresentationalHintsSynthetizer, OpaqueNode, TDocument, TElement, TNode, UnsafeNode}; +use style::dom::{OpaqueNode, PresentationalHintsSynthetizer, TDocument, TElement, TNode, UnsafeNode}; use style::element_state::*; use style::properties::{ComputedValues, PropertyDeclarationBlock}; use style::refcell::{Ref, RefCell, RefMut}; use style::selector_impl::{ElementSnapshot, NonTSPseudoClass, PseudoElement, ServoSelectorImpl}; -use style::selector_matching::DeclarationBlock; +use style::selector_matching::ApplicableDeclarationBlock; use style::sink::Push; use style::str::is_whitespace; use url::Url; @@ -115,11 +115,11 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { type ConcreteElement = ServoLayoutElement<'ln>; type ConcreteDocument = ServoLayoutDocument<'ln>; type ConcreteRestyleDamage = RestyleDamage; + type ConcreteChildrenIterator = ServoChildrenIterator<'ln>; fn to_unsafe(&self) -> UnsafeNode { unsafe { - let ptr: usize = transmute_copy(self); - (ptr, 0) + (self.node.unsafe_get() as usize, 0) } } @@ -147,6 +147,12 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { self.dump_style_indent(0); } + fn children(self) -> ServoChildrenIterator<'ln> { + ServoChildrenIterator { + current: self.first_child(), + } + } + fn opaque(&self) -> OpaqueNode { unsafe { self.get_jsmanaged().opaque() } } @@ -163,10 +169,6 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { self.opaque().0 } - fn children_count(&self) -> u32 { - unsafe { self.node.children_count() } - } - fn as_element(&self) -> Option<ServoLayoutElement<'ln>> { as_element(self.node) } @@ -280,6 +282,19 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { } } +pub struct ServoChildrenIterator<'a> { + current: Option<ServoLayoutNode<'a>>, +} + +impl<'a> Iterator for ServoChildrenIterator<'a> { + type Item = ServoLayoutNode<'a>; + fn next(&mut self) -> Option<ServoLayoutNode<'a>> { + let node = self.current; + self.current = node.and_then(|node| node.next_sibling()); + node + } +} + impl<'ln> LayoutNode for ServoLayoutNode<'ln> { type ConcreteThreadSafeLayoutNode = ServoThreadSafeLayoutNode<'ln>; @@ -389,6 +404,14 @@ impl<'ld> TDocument for ServoLayoutDocument<'ld> { let elements = unsafe { self.document.drain_modified_elements() }; elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect() } + + fn needs_paint_from_layout(&self) { + unsafe { self.document.needs_paint_from_layout(); } + } + + fn will_paint(&self) { + unsafe { self.document.will_paint(); } + } } impl<'ld> ServoLayoutDocument<'ld> { @@ -419,7 +442,7 @@ impl<'le> fmt::Debug for ServoLayoutElement<'le> { impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> { fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V) - where V: Push<DeclarationBlock> + where V: Push<ApplicableDeclarationBlock> { unsafe { self.element.synthesize_presentational_hints_for_legacy_attributes(hints); @@ -435,9 +458,9 @@ impl<'le> TElement for ServoLayoutElement<'le> { ServoLayoutNode::from_layout_js(self.element.upcast()) } - fn style_attribute(&self) -> &Option<PropertyDeclarationBlock> { + fn style_attribute(&self) -> Option<&Arc<PropertyDeclarationBlock>> { unsafe { - &*self.element.style_attribute() + (*self.element.style_attribute()).as_ref() } } @@ -1071,5 +1094,5 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { impl<'le> PresentationalHintsSynthetizer for ServoThreadSafeLayoutElement<'le> { fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, _hints: &mut V) - where V: Push<DeclarationBlock> {} + where V: Push<ApplicableDeclarationBlock> {} } diff --git a/components/script/lib.rs b/components/script/lib.rs index 0f28a2b065c..840e078d93f 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -17,7 +17,6 @@ #![feature(slice_patterns)] #![feature(stmt_expr_attributes)] #![feature(question_mark)] -#![feature(try_borrow)] #![feature(try_from)] #![deny(unsafe_code)] @@ -31,6 +30,7 @@ extern crate angle; extern crate app_units; +extern crate audio_video_metadata; #[allow(unused_extern_crates)] #[macro_use] extern crate bitflags; @@ -69,7 +69,6 @@ extern crate phf; extern crate profile_traits; extern crate rand; extern crate range; -extern crate ref_filter_map; extern crate ref_slice; extern crate regex; extern crate rustc_serialize; @@ -114,10 +113,10 @@ mod unpremultiplytable; mod webdriver_handlers; use dom::bindings::codegen::RegisterBindings; -use js::jsapi::{Handle, JSContext, JSObject, SetDOMProxyInformation}; +use dom::bindings::proxyhandler; +use js::jsapi::{Handle, JSContext, JSObject}; use script_traits::SWManagerSenders; use serviceworker_manager::ServiceWorkerManager; -use std::ptr; use util::opts; #[cfg(target_os = "linux")] @@ -164,7 +163,7 @@ fn perform_platform_specific_initialization() {} #[allow(unsafe_code)] pub fn init(sw_senders: SWManagerSenders) { unsafe { - SetDOMProxyInformation(ptr::null(), 0, Some(script_thread::shadow_check_callback)); + proxyhandler::init(); } // Spawn the service worker manager passing the constellation sender diff --git a/components/script/network_listener.rs b/components/script/network_listener.rs index 1dd0d8f0a34..4ff65bf8073 100644 --- a/components/script/network_listener.rs +++ b/components/script/network_listener.rs @@ -4,8 +4,8 @@ use net_traits::{Action, AsyncResponseListener, FetchResponseListener}; use net_traits::{FetchResponseMsg, ResponseAction}; -use script_runtime::ScriptThreadEventCategory::NetworkEvent; use script_runtime::{CommonScriptMsg, ScriptChan}; +use script_runtime::ScriptThreadEventCategory::NetworkEvent; use script_thread::{Runnable, RunnableWrapper}; use std::sync::{Arc, Mutex}; diff --git a/components/script/origin.rs b/components/script/origin.rs index 9b0ba7738e2..21204980e26 100644 --- a/components/script/origin.rs +++ b/components/script/origin.rs @@ -3,8 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use std::sync::Arc; +use url::{Host, Url}; use url::Origin as UrlOrigin; -use url::{Url, Host}; /// A representation of an [origin](https://html.spec.whatwg.org/multipage/#origin-2). #[derive(HeapSizeOf, JSTraceable)] diff --git a/components/script/parse/html.rs b/components/script/parse/html.rs index 659772b851b..39684bd1b3e 100644 --- a/components/script/parse/html.rs +++ b/components/script/parse/html.rs @@ -13,23 +13,23 @@ use dom::bindings::js::{JS, RootedReference}; use dom::bindings::str::DOMString; use dom::characterdata::CharacterData; use dom::comment::Comment; -use dom::document::Document; use dom::document::{DocumentSource, IsHTMLDocument}; +use dom::document::Document; use dom::documenttype::DocumentType; use dom::element::{Element, ElementCreator}; use dom::htmlformelement::HTMLFormElement; use dom::htmlscriptelement::HTMLScriptElement; use dom::htmltemplateelement::HTMLTemplateElement; -use dom::node::Node; use dom::node::{document_from_node, window_from_node}; +use dom::node::Node; use dom::processinginstruction::ProcessingInstruction; use dom::servohtmlparser; use dom::servohtmlparser::{FragmentContext, ServoHTMLParser}; use dom::text::Text; use html5ever::Attribute; +use html5ever::serialize::{AttrRef, Serializable, Serializer}; use html5ever::serialize::TraversalScope; use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; -use html5ever::serialize::{AttrRef, Serializable, Serializer}; use html5ever::tendril::StrTendril; use html5ever::tree_builder::{NextParserState, NodeOrText, QuirksMode, TreeSink}; use msg::constellation_msg::PipelineId; diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index b16ca804f5c..124bf8f2b84 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -6,7 +6,7 @@ //! script thread, the dom, and the worker threads. use dom::bindings::js::{RootCollection, RootCollectionPtr, trace_roots}; -use dom::bindings::refcounted::{LiveDOMReferences, TrustedReference, trace_refcounted_objects}; +use dom::bindings::refcounted::{LiveDOMReferences, trace_refcounted_objects}; use dom::bindings::trace::trace_traceables; use dom::bindings::utils::DOM_CALLBACKS; use js::glue::CollectServoSizes; @@ -35,8 +35,6 @@ pub enum CommonScriptMsg { /// Requests that the script thread measure its memory usage. The results are sent back via the /// supplied channel. CollectReports(ReportsChan), - /// A DOM object's last pinned reference was removed (dispatched to all threads). - RefcountCleanup(TrustedReference), /// Generic message that encapsulates event handling. RunnableMsg(ScriptThreadEventCategory, Box<Runnable + Send>), } diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 4834218a89b..d69124b5d07 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -18,20 +18,20 @@ //! loop. use devtools; -use devtools_traits::CSSError; use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo}; use devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId}; +use devtools_traits::CSSError; use document_loader::DocumentLoader; 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::WindowBinding::WindowMethods; -use dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}; +use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, StringificationBehavior}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root, RootCollection}; use dom::bindings::js::{RootCollectionPtr, RootedReference}; -use dom::bindings::refcounted::{LiveDOMReferences, Trusted}; +use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; use dom::bindings::trace::JSTraceable; @@ -52,25 +52,24 @@ use euclid::Rect; use euclid::point::Point2D; use gfx_traits::LayerId; use hyper::header::{ContentType, Headers, HttpDate, LastModified}; -use hyper::header::{ReferrerPolicy as ReferrerPolicyHeader}; +use hyper::header::ReferrerPolicy as ReferrerPolicyHeader; use hyper::method::Method; use hyper::mime::{Mime, SubLevel, TopLevel}; use hyper_serde::Serde; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use js::glue::GetWindowProxyClass; -use js::jsapi::{DOMProxyShadowsResult, HandleId, HandleObject}; use js::jsapi::{JSAutoCompartment, JSContext, JS_SetWrapObjectCallbacks}; use js::jsapi::{JSTracer, SetWindowProxyClass}; use js::jsval::UndefinedValue; use js::rust::Runtime; use mem::heap_size_of_self_and_children; use msg::constellation_msg::{FrameType, LoadData, PipelineId, PipelineNamespace}; -use msg::constellation_msg::{SubpageId, WindowSizeType, ReferrerPolicy}; -use net_traits::bluetooth_thread::BluetoothMethodMsg; -use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread}; +use msg::constellation_msg::{ReferrerPolicy, SubpageId, WindowSizeType}; use net_traits::{AsyncResponseTarget, CoreResourceMsg, LoadConsumer, LoadContext, Metadata, ResourceThreads}; use net_traits::{IpcSend, LoadData as NetLoadData}; +use net_traits::bluetooth_thread::BluetoothMethodMsg; +use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread}; use network_listener::NetworkListener; use parse::ParserRoot; use parse::html::{ParseContext, parse_html}; @@ -79,15 +78,15 @@ use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan}; use profile_traits::time::{self, ProfilerCategory, profile}; use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryType}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory}; -use script_runtime::{ScriptPort, StackRootTLS, new_rt_and_cx, get_reports}; -use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent}; -use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent}; -use script_traits::webdriver_msg::WebDriverScriptCommand; +use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx}; use script_traits::{CompositorEvent, ConstellationControlMsg, EventResult}; use script_traits::{InitialScriptState, MouseButton, MouseEventType, MozBrowserEvent}; use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg}; use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSource}; use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData}; +use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent}; +use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent}; +use script_traits::webdriver_msg::WebDriverScriptCommand; use std::borrow::ToOwned; use std::cell::Cell; use std::collections::{HashMap, HashSet}; @@ -95,19 +94,19 @@ use std::option::Option; use std::ptr; use std::rc::Rc; use std::result::Result; -use std::sync::atomic::{Ordering, AtomicBool}; -use std::sync::mpsc::{Receiver, Select, Sender, channel}; use std::sync::{Arc, Mutex}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::mpsc::{Receiver, Select, Sender, channel}; use style::context::ReflowGoal; use style::thread_state; use task_source::TaskSource; -use task_source::dom_manipulation::{DOMManipulationTaskSource, DOMManipulationTask}; +use task_source::dom_manipulation::{DOMManipulationTask, DOMManipulationTaskSource}; use task_source::file_reading::FileReadingTaskSource; use task_source::history_traversal::HistoryTraversalTaskSource; use task_source::networking::NetworkingTaskSource; -use task_source::user_interaction::{UserInteractionTaskSource, UserInteractionTask}; +use task_source::user_interaction::{UserInteractionTask, UserInteractionTaskSource}; use time::Tm; -use url::{Url, Position}; +use url::{Position, Url}; use util::opts; use util::thread; use webdriver_handlers; @@ -484,12 +483,6 @@ impl ScriptThreadFactory for ScriptThread { } } -pub unsafe extern "C" fn shadow_check_callback(_cx: *mut JSContext, - _object: HandleObject, _id: HandleId) -> DOMProxyShadowsResult { - // XXX implement me - DOMProxyShadowsResult::ShadowCheckFailed -} - impl ScriptThread { pub fn page_headers_available(id: &PipelineId, subpage: Option<&SubpageId>, metadata: Option<Metadata>) -> Option<ParserRoot> { @@ -955,8 +948,6 @@ impl ScriptThread { runnable.handler() } } - MainThreadScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => - LiveDOMReferences::cleanup(addr), MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => self.collect_reports(reports_chan), MainThreadScriptMsg::DOMManipulation(task) => @@ -1789,7 +1780,10 @@ impl ScriptThread { let strval = DOMString::from_jsval(self.get_cx(), jsval.handle(), StringificationBehavior::Empty); - strval.unwrap_or(DOMString::new()) + match strval { + Ok(ConversionResult::Success(s)) => s, + _ => DOMString::new(), + } } } else { DOMString::new() @@ -2205,7 +2199,7 @@ fn shut_down_layout(context_tree: &BrowsingContext) { let chan = window.layout_chan().clone(); if chan.send(message::Msg::PrepareToExit(response_chan)).is_ok() { channels.push(chan); - response_port.recv().unwrap(); + let _ = response_port.recv(); } } diff --git a/components/script/serviceworker_manager.rs b/components/script/serviceworker_manager.rs index 1c24c6fef3f..09f5288929a 100644 --- a/components/script/serviceworker_manager.rs +++ b/components/script/serviceworker_manager.rs @@ -72,7 +72,7 @@ impl ServiceWorkerManager { } } - if let Some(ref scope_url) = scope_url { + if let Some(scope_url) = scope_url { if self.active_workers.contains_key(&scope_url) { // do not run the same worker if already active. warn!("Service worker for {:?} already active", scope_url); @@ -100,7 +100,7 @@ impl ServiceWorkerManager { self.own_sender.clone(), scope_url.clone()); // We store the activated worker - self.active_workers.insert(scope_url.clone(), scope_things.clone()); + self.active_workers.insert(scope_url, scope_things.clone()); return Some(sender); } else { warn!("Unable to activate service worker"); diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 2d55345d324..b43262080b0 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -12,7 +12,7 @@ use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElemen use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}; +use dom::bindings::conversions::{ConversionResult, FromJSValConvertible, StringificationBehavior}; use dom::bindings::inheritance::Castable; use dom::bindings::js::Root; use dom::bindings::str::DOMString; @@ -29,14 +29,14 @@ use euclid::rect::Rect; use euclid::size::Size2D; use hyper_serde::Serde; use ipc_channel::ipc::{self, IpcSender}; -use js::jsapi::{JSContext, HandleValue}; +use js::jsapi::{HandleValue, JSContext}; use js::jsval::UndefinedValue; use msg::constellation_msg::PipelineId; use net_traits::CookieSource::{HTTP, NonHTTP}; use net_traits::CoreResourceMsg::{GetCookiesDataForUrl, SetCookiesForUrlWithData}; use net_traits::IpcSend; -use script_traits::webdriver_msg::WebDriverCookieError; use script_traits::webdriver_msg::{WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverJSValue}; +use script_traits::webdriver_msg::WebDriverCookieError; use url::Url; fn find_node_by_unique_id(context: &BrowsingContext, @@ -59,10 +59,17 @@ pub unsafe fn jsval_to_webdriver(cx: *mut JSContext, val: HandleValue) -> WebDri } else if val.get().is_boolean() { Ok(WebDriverJSValue::Boolean(val.get().to_boolean())) } else if val.get().is_double() || val.get().is_int32() { - Ok(WebDriverJSValue::Number(FromJSValConvertible::from_jsval(cx, val, ()).unwrap())) + Ok(WebDriverJSValue::Number(match FromJSValConvertible::from_jsval(cx, val, ()).unwrap() { + ConversionResult::Success(c) => c, + _ => unreachable!(), + })) } else if val.get().is_string() { //FIXME: use jsstring_to_str when jsval grows to_jsstring - let string: DOMString = FromJSValConvertible::from_jsval(cx, val, StringificationBehavior::Default).unwrap(); + let string: DOMString = match FromJSValConvertible::from_jsval(cx, val, StringificationBehavior::Default) + .unwrap() { + ConversionResult::Success(c) => c, + _ => unreachable!(), + }; Ok(WebDriverJSValue::String(String::from(string))) } else if val.get().is_null() { Ok(WebDriverJSValue::Null) |