diff options
Diffstat (limited to 'components/script')
93 files changed, 1470 insertions, 846 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 57fd5626195..293c2aefe02 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -61,6 +61,7 @@ plugins = {path = "../plugins"} profile_traits = {path = "../profile_traits"} rand = "0.3" range = {path = "../range"} +ref_filter_map = "1.0.1" ref_slice = "1.0" regex = "0.1.43" rustc-serialize = "0.3" diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs index 2aae65af920..8b9587c9151 100644 --- a/components/script/dom/attr.rs +++ b/components/script/dom/attr.rs @@ -81,8 +81,8 @@ impl Attr { } #[inline] - pub fn prefix(&self) -> &Option<Prefix> { - &self.identifier.prefix + pub fn prefix(&self) -> Option<&Prefix> { + self.identifier.prefix.as_ref() } } @@ -153,7 +153,7 @@ impl AttrMethods for Attr { // https://dom.spec.whatwg.org/#dom-attr-prefix fn GetPrefix(&self) -> Option<DOMString> { // FIXME(ajeffrey): convert directly from LocalName to DOMString - self.prefix().as_ref().map(|p| DOMString::from(&**p)) + self.prefix().map(|p| DOMString::from(&**p)) } // https://dom.spec.whatwg.org/#dom-attr-ownerelement diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index b67ab0bd716..28d0d9af459 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -2377,8 +2377,8 @@ class CGAbstractMethod(CGThing): if self.catchPanic: body = CGWrapper(CGIndenter(body), - pre="return wrap_panic(|| {\n", - post=("""}, %s);""" % ("()" if self.returnType == "void" else "false"))) + pre="return wrap_panic(panic::AssertUnwindSafe(|| {\n", + post=("""}), %s);""" % ("()" if self.returnType == "void" else "false"))) return CGWrapper(CGIndenter(body), pre=self.definition_prologue(), @@ -2537,7 +2537,7 @@ class CGWrapMethod(CGAbstractMethod): return CGGeneric("""\ let scope = scope.reflector().get_jsobject(); assert!(!scope.get().is_null()); -assert!(((*JS_GetClass(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0); +assert!(((*get_object_class(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0); rooted!(in(cx) let mut proto = ptr::null_mut()); let _ac = JSAutoCompartment::new(cx, scope.get()); @@ -2954,7 +2954,7 @@ class CGGetPerInterfaceObject(CGAbstractMethod): def definition_body(self): return CGGeneric(""" -assert!(((*JS_GetClass(global.get())).flags & JSCLASS_DOM_GLOBAL) != 0); +assert!(((*get_object_class(global.get())).flags & JSCLASS_DOM_GLOBAL) != 0); /* Check to see whether the interface objects are already installed */ let proto_or_iface_array = get_proto_or_iface_array(global.get()); @@ -3168,7 +3168,7 @@ class CGCallGenerator(CGThing): if isFallible: if static: - glob = "&global" + glob = "global.upcast::<GlobalScope>()" else: glob = "&this.global()" @@ -3378,12 +3378,13 @@ class CGAbstractStaticBindingMethod(CGAbstractMethod): Argument('*mut JSVal', 'vp'), ] CGAbstractMethod.__init__(self, descriptor, name, "bool", args, extern=True) + self.exposureSet = descriptor.interface.exposureSet def definition_body(self): - preamble = CGGeneric("""\ -let global = GlobalScope::from_object(JS_CALLEE(cx, vp).to_object()); -""") - return CGList([preamble, self.generate_code()]) + preamble = "let global = GlobalScope::from_object(JS_CALLEE(cx, vp).to_object());\n" + if len(self.exposureSet) == 1: + preamble += "let global = Root::downcast::<dom::types::%s>(global).unwrap();\n" % list(self.exposureSet)[0] + return CGList([CGGeneric(preamble), self.generate_code()]) def generate_code(self): raise NotImplementedError # Override me! @@ -5245,12 +5246,14 @@ class CGClassConstructHook(CGAbstractExternMethod): assert constructor CGAbstractExternMethod.__init__(self, descriptor, name, 'bool', args) self.constructor = constructor + self.exposureSet = descriptor.interface.exposureSet def definition_body(self): - preamble = CGGeneric("""\ -let global = GlobalScope::from_object(JS_CALLEE(cx, vp).to_object()); -let args = CallArgs::from_vp(vp, argc); -""") + preamble = """let global = GlobalScope::from_object(JS_CALLEE(cx, vp).to_object());\n""" + if len(self.exposureSet) == 1: + preamble += "let global = Root::downcast::<dom::types::%s>(global).unwrap();\n" % list(self.exposureSet)[0] + preamble += """let args = CallArgs::from_vp(vp, argc);\n""" + preamble = CGGeneric(preamble) name = self.constructor.identifier.name nativeName = MakeNativeName(self.descriptor.binaryNameFor(name)) callGenerator = CGMethodCall(["&global"], nativeName, True, @@ -5448,7 +5451,6 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries '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', @@ -5496,9 +5498,12 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'js::glue::RUST_JSID_IS_STRING', 'js::glue::RUST_SYMBOL_TO_JSID', 'js::glue::int_to_jsid', + 'js::panic::maybe_resume_unwind', + 'js::panic::wrap_panic', 'js::rust::GCMethods', 'js::rust::define_methods', 'js::rust::define_properties', + 'js::rust::get_object_class', 'dom', 'dom::bindings', 'dom::bindings::codegen::InterfaceObjectMap', @@ -5547,7 +5552,6 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'dom::bindings::utils::resolve_global', 'dom::bindings::utils::set_dictionary_property', 'dom::bindings::utils::trace_global', - 'dom::bindings::utils::wrap_panic', 'dom::bindings::trace::JSTraceable', 'dom::bindings::trace::RootedTraceable', 'dom::bindings::callback::CallSetup', @@ -5581,6 +5585,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'dom::bindings::error::throw_dom_exception', 'dom::bindings::guard::Condition', 'dom::bindings::guard::Guard', + 'dom::bindings::inheritance::Castable', 'dom::bindings::proxyhandler', 'dom::bindings::proxyhandler::ensure_expando_object', 'dom::bindings::proxyhandler::fill_property_descriptor', @@ -5599,7 +5604,6 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'mem::heap_size_of_raw_self_and_children', 'libc', 'util::prefs::PREFS', - 'script_runtime::maybe_take_panic_result', 'std::borrow::ToOwned', 'std::cmp', 'std::mem', @@ -6596,9 +6600,7 @@ class CallbackMethod(CallbackMember): " length_: ${argc} as ::libc::size_t,\n" " elements_: ${argv}\n" " }, rval.handle_mut());\n" - "if let Some(error) = maybe_take_panic_result() {\n" - " panic::resume_unwind(error);\n" - "}\n" + "maybe_resume_unwind();\n" "if !ok {\n" " return Err(JSFailed);\n" "}\n").substitute(replacements) diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index 9143c9cb75b..ba67ad11a7e 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -46,14 +46,14 @@ use js::error::throw_type_error; use js::glue::{GetProxyPrivate, IsWrapper}; use js::glue::{RUST_JSID_IS_INT, RUST_JSID_TO_INT}; use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_STRING, UnwrapObject}; -use js::jsapi::{HandleId, HandleObject, HandleValue, JSClass, JSContext}; -use js::jsapi::{JSObject, JSString, JS_GetArrayBufferViewType, JS_GetClass}; +use js::jsapi::{HandleId, HandleObject, HandleValue, JSContext}; +use js::jsapi::{JSObject, JSString, JS_GetArrayBufferViewType}; use js::jsapi::{JS_GetLatin1StringCharsAndLength, JS_GetObjectAsArrayBuffer, JS_GetObjectAsArrayBufferView}; -use js::jsapi::{JS_GetReservedSlot, JS_GetTwoByteStringCharsAndLength, ToWindowProxyIfWindow}; +use js::jsapi::{JS_GetReservedSlot, JS_GetTwoByteStringCharsAndLength}; use js::jsapi::{JS_IsArrayObject, JS_NewStringCopyN, JS_StringHasLatin1Chars}; -use js::jsapi::{JS_WrapValue, MutableHandleValue, Type, IsObjectInContextCompartment}; +use js::jsapi::{MutableHandleValue, Type}; use js::jsval::{ObjectValue, StringValue}; -use js::rust::ToString; +use js::rust::{ToString, get_object_class, is_dom_class, is_dom_object, maybe_wrap_value}; use libc; use num_traits::Float; use std::{char, mem, ptr, slice}; @@ -308,23 +308,8 @@ impl ToJSValConvertible for Reflector { unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { let obj = self.get_jsobject().get(); assert!(!obj.is_null()); - let same_compartment = IsObjectInContextCompartment(obj, cx); - if same_compartment { - rval.set(ObjectValue(ToWindowProxyIfWindow(obj))); - } else { - rval.set(ObjectValue(obj)); - - if !JS_WrapValue(cx, rval) { - panic!("JS_WrapValue failed."); - } - } - } -} - -/// Returns whether the given `clasp` is one for a DOM object. -pub fn is_dom_class(clasp: *const JSClass) -> bool { - unsafe { - ((*clasp).flags & js::JSCLASS_IS_DOMJSCLASS) != 0 + rval.set(ObjectValue(obj)); + maybe_wrap_value(cx, rval); } } @@ -332,7 +317,7 @@ pub fn is_dom_class(clasp: *const JSClass) -> bool { pub fn is_dom_proxy(obj: *mut JSObject) -> bool { use js::glue::IsProxyHandlerFamily; unsafe { - let clasp = JS_GetClass(obj); + let clasp = get_object_class(obj); ((*clasp).flags & js::JSCLASS_IS_PROXY) != 0 && IsProxyHandlerFamily(obj) != 0 } } @@ -345,8 +330,7 @@ pub const DOM_OBJECT_SLOT: u32 = 0; /// Get the private pointer of a DOM object from a given reflector. pub unsafe fn private_from_object(obj: *mut JSObject) -> *const libc::c_void { - let clasp = JS_GetClass(obj); - let value = if is_dom_class(clasp) { + let value = if is_dom_object(obj) { JS_GetReservedSlot(obj, DOM_OBJECT_SLOT) } else { debug_assert!(is_dom_proxy(obj)); @@ -364,7 +348,7 @@ pub unsafe fn get_dom_class(obj: *mut JSObject) -> Result<&'static DOMClass, ()> use dom::bindings::utils::DOMJSClass; use js::glue::GetProxyHandlerExtra; - let clasp = JS_GetClass(obj); + let clasp = get_object_class(obj); if is_dom_class(&*clasp) { trace!("plain old dom object"); let domjsclass: *const DOMJSClass = clasp as *const DOMJSClass; diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index 3b179e85d1d..d38a55c31be 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -214,15 +214,16 @@ pub unsafe fn report_pending_exception(cx: *mut JSContext, dispatch_event: bool) JS_ClearPendingException(cx); 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; - } - } + ErrorInfo::from_native_error(cx, object.handle()) + .or_else(|| ErrorInfo::from_dom_exception(object.handle())) + .unwrap_or_else(|| { + ErrorInfo { + message: format!("uncaught exception: unknown (can't convert to string)"), + filename: String::new(), + lineno: 0, + column: 0, + } + }) } else { match USVString::from_jsval(cx, value.handle(), ()) { Ok(ConversionResult::Success(USVString(string))) => { diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs index cae5da97887..517adbde425 100644 --- a/components/script/dom/bindings/interface.rs +++ b/components/script/dom/bindings/interface.rs @@ -19,14 +19,14 @@ 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_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 js::rust::{define_methods, define_properties, get_object_class}; use libc; use std::ptr; @@ -352,7 +352,7 @@ unsafe extern "C" fn fun_to_string_hook(cx: *mut JSContext, obj: HandleObject, _indent: u32) -> *mut JSString { - let js_class = JS_GetClass(obj.get()); + let js_class = get_object_class(obj.get()); assert!(!js_class.is_null()); let repr = (*(js_class as *const NonCallbackInterfaceObjectClass)).representation; assert!(!repr.is_empty()); @@ -388,7 +388,7 @@ unsafe fn has_instance( } rooted!(in(cx) let mut value = value.to_object()); - let js_class = JS_GetClass(interface_object.get()); + let js_class = get_object_class(interface_object.get()); let object_class = &*(js_class as *const NonCallbackInterfaceObjectClass); if let Ok(dom_class) = get_dom_class(UncheckedUnwrapObject(value.get(), diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 4a7d4d698fb..de1ac8fcbfd 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -7,7 +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::{is_dom_class, jsstring_to_str, private_from_proto_check}; +use dom::bindings::conversions::{jsstring_to_str, private_from_proto_check}; use dom::bindings::error::throw_invalid_this; use dom::bindings::inheritance::TopTypeId; use dom::bindings::str::DOMString; @@ -24,19 +24,16 @@ 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_ForwardGetPropertyTo, JS_GetLatin1StringCharsAndLength}; use js::jsapi::{JS_GetProperty, JS_GetPrototype, JS_GetReservedSlot, JS_HasProperty}; use js::jsapi::{JS_HasPropertyById, JS_IsExceptionPending, JS_IsGlobalObject}; use js::jsapi::{JS_ResolveStandardClass, JS_SetProperty, ToWindowProxyIfWindow}; use js::jsapi::{JS_StringHasLatin1Chars, MutableHandleValue, ObjectOpResult}; use js::jsval::{JSVal, UndefinedValue}; -use js::rust::{GCMethods, ToString}; +use js::rust::{GCMethods, ToString, get_object_class, is_dom_class}; use libc; -use script_runtime::store_panic_result; use std::ffi::CString; use std::os::raw::c_void; -use std::panic; -use std::panic::AssertUnwindSafe; use std::ptr; use std::slice; @@ -118,7 +115,7 @@ unsafe impl Sync for DOMJSClass {} /// Fails if `global` is not a DOM global object. pub fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray { unsafe { - assert!(((*JS_GetClass(global)).flags & JSCLASS_DOM_GLOBAL) != 0); + assert!(((*get_object_class(global)).flags & JSCLASS_DOM_GLOBAL) != 0); JS_GetReservedSlot(global, DOM_PROTOTYPE_SLOT).to_private() as *mut ProtoOrIfaceArray } } @@ -201,7 +198,7 @@ pub unsafe fn find_enum_string_index(cx: *mut JSContext, pub fn is_platform_object(obj: *mut JSObject) -> bool { unsafe { // Fast-path the common case - let mut clasp = JS_GetClass(obj); + let mut clasp = get_object_class(obj); if is_dom_class(&*clasp) { return true; } @@ -211,7 +208,7 @@ pub fn is_platform_object(obj: *mut JSObject) -> bool { if unwrapped_obj.is_null() { return false; } - clasp = js::jsapi::JS_GetClass(obj); + clasp = get_object_class(obj); } // TODO also check if JS_IsArrayBufferObject is_dom_class(&*clasp) @@ -516,15 +513,3 @@ unsafe extern "C" fn instance_class_has_proto_at_depth(clasp: *const js::jsapi:: pub const DOM_CALLBACKS: DOMCallbacks = DOMCallbacks { instanceClassMatchesProto: Some(instance_class_has_proto_at_depth), }; - -/// Generic wrapper for JS engine callbacks panic-catching -pub fn wrap_panic<T: FnMut() -> R, R>(function: T, generic_return_type: R) -> R { - let result = panic::catch_unwind(AssertUnwindSafe(function)); - match result { - Ok(result) => result, - Err(error) => { - store_panic_result(error); - generic_return_type - } - } -} diff --git a/components/script/dom/bluetooth.rs b/components/script/dom/bluetooth.rs index afee568d735..aa2c4c4639a 100644 --- a/components/script/dom/bluetooth.rs +++ b/components/script/dom/bluetooth.rs @@ -11,12 +11,13 @@ use core::clone::Clone; use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::BluetoothBinding::{self, BluetoothDataFilterInit, BluetoothLEScanFilterInit}; use dom::bindings::codegen::Bindings::BluetoothBinding::{BluetoothMethods, RequestDeviceOptions}; +use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::UnionTypes::StringOrUnsignedLong; use dom::bindings::error::Error::{self, NotFound, Security, Type}; use dom::bindings::error::Fallible; use dom::bindings::js::{JS, MutHeap, Root}; use dom::bindings::refcounted::{Trusted, TrustedPromise}; -use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; +use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::bluetoothadvertisingdata::BluetoothAdvertisingData; use dom::bluetoothdevice::BluetoothDevice; @@ -24,6 +25,7 @@ use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic; use dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor; use dom::bluetoothremotegattservice::BluetoothRemoteGATTService; use dom::bluetoothuuid::{BluetoothServiceUUID, BluetoothUUID}; +use dom::eventtarget::EventTarget; use dom::globalscope::GlobalScope; use dom::promise::Promise; use ipc_channel::ipc::{self, IpcSender}; @@ -76,6 +78,8 @@ impl<Listener: AsyncBluetoothListener + Reflectable> BluetoothResponseListener f let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get()); match response { Ok(response) => self.receiver.root().handle_response(response, promise_cx, &promise), + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice + // Step 3 - 4. Err(error) => promise.reject_error(promise_cx, Error::from(error)), } } @@ -84,7 +88,7 @@ impl<Listener: AsyncBluetoothListener + Reflectable> BluetoothResponseListener f // https://webbluetoothcg.github.io/web-bluetooth/#bluetooth #[dom_struct] pub struct Bluetooth { - reflector_: Reflector, + eventtarget: EventTarget, device_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothDevice>>>>, service_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTService>>>>, characteristic_instance_map: DOMRefCell<HashMap<String, MutHeap<JS<BluetoothRemoteGATTCharacteristic>>>>, @@ -94,7 +98,7 @@ pub struct Bluetooth { impl Bluetooth { pub fn new_inherited() -> Bluetooth { Bluetooth { - reflector_: Reflector::new(), + eventtarget: EventTarget::new_inherited(), device_instance_map: DOMRefCell::new(HashMap::new()), service_instance_map: DOMRefCell::new(HashMap::new()), characteristic_instance_map: DOMRefCell::new(HashMap::new()), @@ -132,18 +136,60 @@ impl Bluetooth { optional_services: &Option<Vec<BluetoothServiceUUID>>) { // TODO: Step 1: Triggered by user activation. - // Step 2. - let option = match convert_request_device_options(filters, optional_services) { - Ok(o) => o, - Err(e) => { - p.reject_error(p.global().get_cx(), e); + // Step 2.2: There are no requiredServiceUUIDS, we scan for all devices. + let mut uuid_filters = vec!(); + + if let &Some(ref filters) = filters { + // Step 2.1. + if filters.is_empty() { + p.reject_error(p.global().get_cx(), Type(FILTER_EMPTY_ERROR.to_owned())); return; } - }; - // TODO: Step 3-5: Implement the permission API. + // Step 2.3: There are no requiredServiceUUIDS, we scan for all devices. + + // Step 2.4. + for filter in filters { + // Step 2.4.1. + match canonicalize_filter(&filter) { + // Step 2.4.2. + Ok(f) => uuid_filters.push(f), + Err(e) => { + p.reject_error(p.global().get_cx(), e); + return; + }, + } + // Step 2.4.3: There are no requiredServiceUUIDS, we scan for all devices. + } + } + + let mut optional_services_uuids = vec!(); + if let &Some(ref opt_services) = optional_services { + for opt_service in opt_services { + // Step 2.5 - 2.6. + let uuid = match BluetoothUUID::service(opt_service.clone()) { + Ok(u) => u.to_string(), + Err(e) => { + p.reject_error(p.global().get_cx(), e); + return; + }, + }; + + // Step 2.7. + // Note: What we are doing here, is adding the not blocklisted UUIDs to the result vector, + // instead of removing them from an already filled vector. + if !uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { + optional_services_uuids.push(uuid); + } + } + } + + let option = RequestDeviceoptions::new(BluetoothScanfilterSequence::new(uuid_filters), + ServiceUUIDSequence::new(optional_services_uuids)); + + // TODO: Step 3 - 5: Implement the permission API. - // Note: Steps 6-8 are implemented in + // Note: Steps 6 - 8 are implemented in // components/net/bluetooth_thread.rs in request_device function. let sender = response_async(p, self); self.get_bluetooth_thread().send(BluetoothRequest::RequestDevice(option, sender)).unwrap(); @@ -170,50 +216,9 @@ pub fn response_async<T: AsyncBluetoothListener + Reflectable + 'static>( action_sender } -// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices -fn convert_request_device_options(filters: &Option<Vec<BluetoothLEScanFilterInit>>, - optional_services: &Option<Vec<BluetoothServiceUUID>>) - -> Fallible<RequestDeviceoptions> { - // Step 2.2: There is no requiredServiceUUIDS, we scan for all devices. - let mut uuid_filters = vec!(); - - if let &Some(ref filters) = filters { - // Step 2.1. - if filters.is_empty() { - return Err(Type(FILTER_EMPTY_ERROR.to_owned())); - } - - // Step 2.3: There is no requiredServiceUUIDS, we scan for all devices. - - // Step 2.4. - for filter in filters { - // Step 2.4.8. - uuid_filters.push(try!(canonicalize_filter(&filter))); - } - } - - let mut optional_services_uuids = vec!(); - if let &Some(ref opt_services) = optional_services { - for opt_service in opt_services { - // Step 2.5 - 2.6. - let uuid = try!(BluetoothUUID::service(opt_service.clone())).to_string(); - - // Step 2.7. - // Note: What we are doing here is adding the not blocklisted UUIDs to the result vector, - // insted of removing them from an already filled vector. - if !uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { - optional_services_uuids.push(uuid); - } - } - } - - Ok(RequestDeviceoptions::new(BluetoothScanfilterSequence::new(uuid_filters), - ServiceUUIDSequence::new(optional_services_uuids))) -} - -// https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices +// https://webbluetoothcg.github.io/web-bluetooth/#bluetoothlescanfilterinit-canonicalizing fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible<BluetoothScanfilter> { - // Step 2.4.1. + // Step 1. if filter.services.is_none() && filter.name.is_none() && filter.namePrefix.is_none() && @@ -222,13 +227,13 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible<Bluetooth return Err(Type(FILTER_ERROR.to_owned())); } - // Step 2.4.2: There is no empty canonicalizedFilter member, + // Step 2: There is no empty canonicalizedFilter member, // we create a BluetoothScanfilter instance at the end of the function. - // Step 2.4.3. + // Step 3. let services_vec = match filter.services { Some(ref services) => { - // Step 2.4.3.1. + // Step 3.1. if services.is_empty() { return Err(Type(SERVICE_ERROR.to_owned())); } @@ -236,27 +241,26 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible<Bluetooth let mut services_vec = vec!(); for service in services { - // Step 2.4.3.2 - 2.4.3.3. + // Step 3.2 - 3.3. let uuid = try!(BluetoothUUID::service(service.clone())).to_string(); - // Step 2.4.3.4. + // Step 3.4. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { return Err(Security) } services_vec.push(uuid); } - // Step 2.4.3.5. + // Step 3.5. services_vec - // Step 2.4.3.6: There is no requiredServiceUUIDS, we scan for all devices. }, None => vec!(), }; - // Step 2.4.4. + // Step 4. let name = match filter.name { Some(ref name) => { - // Step 2.4.4.1. + // Step 4.1. // Note: DOMString::len() gives back the size in bytes. if name.len() > MAX_DEVICE_NAME_LENGTH { return Err(Type(NAME_TOO_LONG_ERROR.to_owned())); @@ -265,16 +269,16 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible<Bluetooth return Err(NotFound); } - // Step 2.4.4.2. + // Step 4.2. Some(name.to_string()) }, None => None, }; - // Step 2.4.5. + // Step 5. let name_prefix = match filter.namePrefix { Some(ref name_prefix) => { - // Step 2.4.5.1. + // Step 5.1. if name_prefix.is_empty() { return Err(Type(NAME_PREFIX_ERROR.to_owned())); } @@ -285,24 +289,30 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible<Bluetooth return Err(NotFound); } - // Step 2.4.5.2. + // Step 5.2. name_prefix.to_string() }, None => String::new(), }; - // Step 2.4.6 - 2.4.7 + // Step 6 - 7. let manufacturer_data = match filter.manufacturerData { Some(ref manufacturer_data_map) => { + // Note: If manufacturer_data_map is empty, that means there are no key values in it. if manufacturer_data_map.is_empty() { return Err(Type(MANUFACTURER_DATA_ERROR.to_owned())); } let mut map = HashMap::new(); for (key, bdfi) in manufacturer_data_map.iter() { + // Step 7.1 - 7.2. let manufacturer_id = match u16::from_str(key.as_ref()) { Ok(id) => id, Err(err) => return Err(Type(format!("{} {} {}", KEY_CONVERSION_ERROR, key, err))), }; + + // Step 7.3: No need to convert to IDL values since this is only used by native code. + + // Step 7.4 - 7.5. map.insert(manufacturer_id, try!(canonicalize_bluetooth_data_filter_init(bdfi))); } Some(map) @@ -310,22 +320,33 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible<Bluetooth None => None, }; - // Step 2.4.8 -2.4.9 + // Step 8 - 9. let service_data = match filter.serviceData { Some(ref service_data_map) => { + // Note: If service_data_map is empty, that means there are no key values in it. if service_data_map.is_empty() { return Err(Type(SERVICE_DATA_ERROR.to_owned())); } let mut map = HashMap::new(); for (key, bdfi) in service_data_map.iter() { let service_name = match u32::from_str(key.as_ref()) { + // Step 9.1. Ok(number) => StringOrUnsignedLong::UnsignedLong(number), + // Step 9.2. _ => StringOrUnsignedLong::String(key.clone()) }; + + // Step 9.3 - 9.4. let service = try!(BluetoothUUID::service(service_name)).to_string(); + + // Step 9.5. if uuid_is_blocklisted(service.as_ref(), Blocklist::All) { return Err(Security); } + + // Step 9.6: No need to convert to IDL values since this is only used by native code. + + // Step 9.7 - 9.8. map.insert(service, try!(canonicalize_bluetooth_data_filter_init(bdfi))); } Some(map) @@ -341,14 +362,17 @@ fn canonicalize_filter(filter: &BluetoothLEScanFilterInit) -> Fallible<Bluetooth fn canonicalize_bluetooth_data_filter_init(bdfi: &BluetoothDataFilterInit) -> Fallible<(Vec<u8>, Vec<u8>)> { // Step 1. let data_prefix = bdfi.dataPrefix.clone().unwrap_or(vec![]); + // Step 2. // If no mask present, mask will be a sequence of 0xFF bytes the same length as dataPrefix. // Masking dataPrefix with this, leaves dataPrefix untouched. let mask = bdfi.mask.clone().unwrap_or(vec![0xFF; data_prefix.len()]); + // Step 3. if mask.len() != data_prefix.len() { return Err(Type(MASK_LENGTH_ERROR.to_owned())); } + // Step 4. Ok((data_prefix, mask)) } @@ -377,16 +401,22 @@ impl BluetoothMethods for Bluetooth { p.reject_error(p.global().get_cx(), Error::Type(OPTIONS_ERROR.to_owned())); return p; } + // Step 2. self.request_bluetooth_devices(&p, &option.filters, &option.optionalServices); - // TODO(#4282): Step 3-5: Reject and resolve promise. + //Note: Step 3 - 4. in response function, Step 5. in handle_response function. return p; } + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-onavailabilitychanged + event_handler!(availabilitychanged, GetOnavailabilitychanged, SetOnavailabilitychanged); } impl AsyncBluetoothListener for Bluetooth { fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) { match response { + // https://webbluetoothcg.github.io/web-bluetooth/#request-bluetooth-devices + // Step 13 - 14. BluetoothResponse::RequestDevice(device) => { let mut device_instance_map = self.device_instance_map.borrow_mut(); if let Some(existing_device) = device_instance_map.get(&device.id.clone()) { @@ -402,6 +432,8 @@ impl AsyncBluetoothListener for Bluetooth { &ad_data, &self); device_instance_map.insert(device.id, MutHeap::new(&bt_device)); + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetooth-requestdevice + // Step 5. promise.resolve_native(promise_cx, &bt_device); }, _ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())), diff --git a/components/script/dom/bluetoothdevice.rs b/components/script/dom/bluetoothdevice.rs index 39706befad6..4af6b2a4f59 100644 --- a/components/script/dom/bluetoothdevice.rs +++ b/components/script/dom/bluetoothdevice.rs @@ -4,18 +4,20 @@ use dom::bindings::codegen::Bindings::BluetoothDeviceBinding; use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods; +use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::js::{JS, Root, MutHeap, MutNullableHeap}; -use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; +use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::bluetooth::Bluetooth; use dom::bluetoothadvertisingdata::BluetoothAdvertisingData; use dom::bluetoothremotegattserver::BluetoothRemoteGATTServer; +use dom::eventtarget::EventTarget; use dom::globalscope::GlobalScope; // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothdevice #[dom_struct] pub struct BluetoothDevice { - reflector_: Reflector, + eventtarget: EventTarget, id: DOMString, name: Option<DOMString>, ad_data: MutHeap<JS<BluetoothAdvertisingData>>, @@ -30,7 +32,7 @@ impl BluetoothDevice { context: &Bluetooth) -> BluetoothDevice { BluetoothDevice { - reflector_: Reflector::new(), + eventtarget: EventTarget::new_inherited(), id: id, name: name, ad_data: MutHeap::new(ad_data), @@ -76,8 +78,12 @@ impl BluetoothDeviceMethods for BluetoothDevice { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdevice-gatt fn Gatt(&self) -> Root<BluetoothRemoteGATTServer> { + // TODO: Step 1 - 2: Implement the Permission API. self.gatt.or_init(|| { BluetoothRemoteGATTServer::new(&self.global(), self) }) } + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothdeviceeventhandlers-ongattserverdisconnected + event_handler!(gattserverdisconnected, GetOngattserverdisconnected, SetOngattserverdisconnected); } diff --git a/components/script/dom/bluetoothremotegattcharacteristic.rs b/components/script/dom/bluetoothremotegattcharacteristic.rs index 390878342db..3c3c86001fb 100644 --- a/components/script/dom/bluetoothremotegattcharacteristic.rs +++ b/components/script/dom/bluetoothremotegattcharacteristic.rs @@ -13,15 +13,18 @@ use dom::bindings::codegen::Bindings::BluetoothRemoteGATTCharacteristicBinding:: BluetoothRemoteGATTCharacteristicMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods; +use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::error::Error::{self, InvalidModification, Network, NotSupported, Security}; +use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutHeap, Root}; -use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; +use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::{ByteString, DOMString}; use dom::bluetooth::{AsyncBluetoothListener, response_async}; use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties; use dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor; use dom::bluetoothremotegattservice::BluetoothRemoteGATTService; use dom::bluetoothuuid::{BluetoothDescriptorUUID, BluetoothUUID}; +use dom::eventtarget::EventTarget; use dom::globalscope::GlobalScope; use dom::promise::Promise; use ipc_channel::ipc::IpcSender; @@ -35,7 +38,7 @@ pub const MAXIMUM_ATTRIBUTE_LENGTH: usize = 512; // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattcharacteristic #[dom_struct] pub struct BluetoothRemoteGATTCharacteristic { - reflector_: Reflector, + eventtarget: EventTarget, service: MutHeap<JS<BluetoothRemoteGATTService>>, uuid: DOMString, properties: MutHeap<JS<BluetoothCharacteristicProperties>>, @@ -50,7 +53,7 @@ impl BluetoothRemoteGATTCharacteristic { instance_id: String) -> BluetoothRemoteGATTCharacteristic { BluetoothRemoteGATTCharacteristic { - reflector_: Reflector::new(), + eventtarget: EventTarget::new_inherited(), service: MutHeap::new(service), uuid: uuid, properties: MutHeap::new(properties), @@ -100,9 +103,12 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetDescriptor(&self, descriptor: BluetoothDescriptorUUID) -> Rc<Promise> { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. let uuid = match BluetoothUUID::descriptor(descriptor) { Ok(uuid) => uuid.to_string(), Err(e) => { @@ -110,14 +116,23 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris return p; } }; + + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } + + // Step 3 - 4. if !self.Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetoothRemoteGATTService. + + // Note: Steps 6 - 7 are implemented in components/bluetooth/lib.rs in get_descriptor function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetDescriptor(self.get_instance_id(), uuid, sender)).unwrap(); @@ -126,6 +141,7 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptors + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetDescriptors(&self, descriptor: Option<BluetoothDescriptorUUID>) -> Rc<Promise> { @@ -133,6 +149,7 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris let p_cx = p.global().get_cx(); let mut uuid: Option<String> = None; if let Some(d) = descriptor { + // Step 1. uuid = match BluetoothUUID::descriptor(d) { Ok(uuid) => Some(uuid.to_string()), Err(e) => { @@ -141,16 +158,24 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris } }; if let Some(ref uuid) = uuid { + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } } }; + + // Step 3 - 4. if !self.Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetoothRemoteGATTService. + + // Note: Steps 6 - 7 are implemented in components/bluetooth/lib.rs in get_descriptors function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetDescriptors(self.get_instance_id(), uuid, sender)).unwrap(); @@ -167,18 +192,31 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris fn ReadValue(&self) -> Rc<Promise> { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Reads) { p.reject_error(p_cx, Security); return p; } + + // Step 2. if !self.Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 3 - 4: Implement representedCharacteristic internal slot for BluetoothRemoteGATTCharacteristic. + + // TODO: Step 5: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer. + + // Step 5.1. if !self.Properties().Read() { p.reject_error(p_cx, NotSupported); return p; } + + // Note: Remaining substeps of Step 5 are implemented in components/bluetooth/lib.rs in readValue function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::ReadValue(self.get_instance_id(), sender)).unwrap(); @@ -190,25 +228,39 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris fn WriteValue(&self, value: Vec<u8>) -> Rc<Promise> { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Writes) { p.reject_error(p_cx, Security); return p; } + + // Step 2 - 3. if value.len() > MAXIMUM_ATTRIBUTE_LENGTH { p.reject_error(p_cx, InvalidModification); return p; } + + // Step 4. if !self.Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + // TODO: Step 5 - 6: Implement representedCharacteristic internal slot for BluetoothRemoteGATTCharacteristic. + + // TODO: Step 7: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer. + + // Step 7.1. if !(self.Properties().Write() || self.Properties().WriteWithoutResponse() || self.Properties().AuthenticatedSignedWrites()) { p.reject_error(p_cx, NotSupported); return p; } + + // Note: Remaining substeps of Step 7 are implemented in components/bluetooth/lib.rs in writeValue function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::WriteValue(self.get_instance_id(), value, sender)).unwrap(); @@ -220,22 +272,32 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris fn StartNotifications(&self) -> Rc<Promise> { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + // Step 1. if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Reads) { p.reject_error(p_cx, Security); return p; } - // Step 3. + + // TODO: Step 2 - 3: Implement representedCharacteristic internal slot for BluetoothRemoteGATTCharacteristic. + + // Step 4. if !(self.Properties().Notify() || self.Properties().Indicate()) { p.reject_error(p_cx, NotSupported); return p; } + + // TODO: Step 5: Implement `active notification context set` for BluetoothRemoteGATTCharacteristic. + // Step 6. if !self.Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // Note: Steps 7 - 11 are implemented in components/bluetooth/lib.rs in enable_notification function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::EnableNotification(self.get_instance_id(), @@ -249,17 +311,29 @@ impl BluetoothRemoteGATTCharacteristicMethods for BluetoothRemoteGATTCharacteris fn StopNotifications(&self) -> Rc<Promise> { let p = Promise::new(&self.global()); let sender = response_async(&p, self); + + // TODO: Step 1 - 4: Implement representedCharacteristic internal slot and + // `active notification context set` for BluetoothRemoteGATTCharacteristic, + + // Note: Part of Step 4 and Step 5 are implemented in components/bluetooth/lib.rs in enable_notification + // function and in handle_response function. self.get_bluetooth_thread().send( BluetoothRequest::EnableNotification(self.get_instance_id(), false, sender)).unwrap(); return p; } + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-characteristiceventhandlers-oncharacteristicvaluechanged + event_handler!(characteristicvaluechanged, GetOncharacteristicvaluechanged, SetOncharacteristicvaluechanged); } impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic { fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) { match response { + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptor + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetDescriptor(descriptor) => { let context = self.service.get().get_device().get_context(); let mut descriptor_map = context.get_descriptor_map().borrow_mut(); @@ -273,6 +347,9 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic { descriptor_map.insert(descriptor.instance_id, MutHeap::new(&bt_descriptor)); promise.resolve_native(promise_cx, &bt_descriptor); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-getdescriptors + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetDescriptors(descriptors_vec) => { let mut descriptors = vec!(); let context = self.service.get().get_device().get_context(); @@ -294,17 +371,40 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTCharacteristic { } promise.resolve_native(promise_cx, &descriptors); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-readvalue BluetoothResponse::ReadValue(result) => { + // TODO: Step 5.5.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // Step 5.5.2. + // TODO(#5014): Replace ByteString with ArrayBuffer when it is implemented. let value = ByteString::new(result); *self.value.borrow_mut() = Some(value.clone()); + + // Step 5.5.3. + self.upcast::<EventTarget>().fire_bubbling_event(atom!("characteristicvaluechanged")); + + // Step 5.5.4. promise.resolve_native(promise_cx, &value); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue BluetoothResponse::WriteValue(result) => { - let value = ByteString::new(result); - *self.value.borrow_mut() = Some(value.clone()); - promise.resolve_native(promise_cx, &value); + // TODO: Step 7.5.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // Step 7.5.2. + // TODO(#5014): Replace ByteString with an ArrayBuffer wrapped in a DataView. + *self.value.borrow_mut() = Some(ByteString::new(result)); + + // Step 7.5.3. + promise.resolve_native(promise_cx, &()); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-startnotifications + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-stopnotifications BluetoothResponse::EnableNotification(_result) => { + // (StartNotification) TODO: Step 10: Implement `active notification context set` + // for BluetoothRemoteGATTCharacteristic. + + // (StartNotification) Step 11. + // (StopNotification) Step 5. promise.resolve_native(promise_cx, self); }, _ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())), diff --git a/components/script/dom/bluetoothremotegattdescriptor.rs b/components/script/dom/bluetoothremotegattdescriptor.rs index ffe66c23bec..8f092c6cad5 100644 --- a/components/script/dom/bluetoothremotegattdescriptor.rs +++ b/components/script/dom/bluetoothremotegattdescriptor.rs @@ -90,14 +90,24 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor { fn ReadValue(&self) -> Rc<Promise> { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Reads) { p.reject_error(p_cx, Security); return p; } + + // Step 2. if !self.Characteristic().Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 3 - 4: Implement representedDescriptor internal slot for BluetoothRemoteGATTDescriptor. + + // TODO: Step 5: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer. + // Note: Substeps of Step 5 are implemented in components/bluetooth/lib.rs in readValue function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::ReadValue(self.get_instance_id(), sender)).unwrap(); @@ -109,18 +119,30 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor { fn WriteValue(&self, value: Vec<u8>) -> Rc<Promise> { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. if uuid_is_blocklisted(self.uuid.as_ref(), Blocklist::Writes) { p.reject_error(p_cx, Security); return p; } + + // Step 2 - 3. if value.len() > MAXIMUM_ATTRIBUTE_LENGTH { p.reject_error(p_cx, InvalidModification); return p; } + + // Step 4. if !self.Characteristic().Service().Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5 - 6: Implement representedCharacteristic internal slot for BluetoothRemoteGATTCharacteristic. + + // TODO: Step 7: Implement the `connection-checking-wrapper` algorithm for BluetoothRemoteGATTServer. + // Note: Substeps of Step 7 are implemented in components/bluetooth/lib.rs in writeValue function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::WriteValue(self.get_instance_id(), value, sender)).unwrap(); @@ -131,15 +153,29 @@ impl BluetoothRemoteGATTDescriptorMethods for BluetoothRemoteGATTDescriptor { impl AsyncBluetoothListener for BluetoothRemoteGATTDescriptor { fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) { match response { + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-readvalue BluetoothResponse::ReadValue(result) => { + // TODO: Step 5.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // Step 5.4.2. + // TODO(#5014): Replace ByteString with ArrayBuffer when it is implemented. let value = ByteString::new(result); *self.value.borrow_mut() = Some(value.clone()); + + // Step 5.4.3. promise.resolve_native(promise_cx, &value); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattdescriptor-writevalue BluetoothResponse::WriteValue(result) => { - let value = ByteString::new(result); - *self.value.borrow_mut() = Some(value.clone()); - promise.resolve_native(promise_cx, &value); + // TODO: Step 7.4.1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // Step 7.4.2. + // TODO(#5014): Replace ByteString with an ArrayBuffer wrapped in a DataView. + *self.value.borrow_mut() = Some(ByteString::new(result)); + + // Step 7.4.3. + // TODO: Resolve promise with undefined instead of a value. + promise.resolve_native(promise_cx, &()); }, _ => promise.reject_error(promise_cx, Error::Type("Something went wrong...".to_owned())), } diff --git a/components/script/dom/bluetoothremotegattserver.rs b/components/script/dom/bluetoothremotegattserver.rs index f8424ad6241..06c11f0aa21 100644 --- a/components/script/dom/bluetoothremotegattserver.rs +++ b/components/script/dom/bluetoothremotegattserver.rs @@ -65,19 +65,42 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect fn Connect(&self) -> Rc<Promise> { + // Step 1. let p = Promise::new(&self.global()); let sender = response_async(&p, self); + + // TODO: Step 2: Implement representedDevice internal slot for BluetoothDevice. + + // TODO: Step 3: Check if the UA is currently using the Bluetooth system. + + // TODO: Step 4: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // TODO: Step 5.1 - 5.2: Implement activeAlgorithms, representedDevice internal slots + // and the` garbage-collect the connection` algorithm. + + // Note: Steps 5.1.1 and 5.1.3 are in components/bluetooth/lib.rs in the gatt_server_connect function. + // Steps 5.2.4 - 5.2.5 are in response function. self.get_bluetooth_thread().send( BluetoothRequest::GATTServerConnect(String::from(self.Device().Id()), sender)).unwrap(); + // Step 5: return promise. return p; } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-disconnect fn Disconnect(&self) -> ErrorResult { + // TODO: Step 1: Implement activeAlgorithms internal slot for BluetoothRemoteGATTServer. + + // TODO: Step 2: Check if this.connected is false here too. let (sender, receiver) = ipc::channel().unwrap(); self.get_bluetooth_thread().send( BluetoothRequest::GATTServerDisconnect(String::from(self.Device().Id()), sender)).unwrap(); let server = receiver.recv().unwrap(); + + // TODO: Step 3: Implement the `clean up the disconnected device` algorithm. + + // TODO: Step 4: Implement representedDevice internal slot for BluetoothDevice. + + // TODO: Step 5: Implement the `garbage-collect the connection` algorithm. match server { Ok(connected) => { self.connected.set(connected); @@ -92,8 +115,12 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice fn GetPrimaryService(&self, service: BluetoothServiceUUID) -> Rc<Promise> { + // TODO: Step 1: Implement the Permission API and the allowedServices BluetoothDevice internal slot. + // Subsequent steps are relative to https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. let uuid = match BluetoothUUID::service(service) { Ok(uuid) => uuid.to_string(), Err(e) => { @@ -101,14 +128,23 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { return p; } }; + + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedDevice internal slot for BluetoothDevice. + + // Note: Steps 6 - 7 are implemented in components/bluetooth/lib.rs in get_primary_service function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetPrimaryService(String::from(self.Device().Id()), uuid, sender)).unwrap(); @@ -118,10 +154,14 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices fn GetPrimaryServices(&self, service: Option<BluetoothServiceUUID>) -> Rc<Promise> { + // TODO: Step 1: Implement the Permission API and the allowedServices BluetoothDevice internal slot. + // Subsequent steps are relative to https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + let mut uuid: Option<String> = None; if let Some(s) = service { + // Step 1. uuid = match BluetoothUUID::service(s) { Ok(uuid) => Some(uuid.to_string()), Err(e) => { @@ -130,16 +170,24 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { } }; if let Some(ref uuid) = uuid { + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } } }; + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedDevice internal slot for BluetoothDevice. + + // Note: Steps 6 - 7 are implemented in components/bluetooth/lib.rs in get_primary_services function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetPrimaryServices(String::from(self.Device().Id()), uuid, sender)).unwrap(); @@ -150,10 +198,17 @@ impl BluetoothRemoteGATTServerMethods for BluetoothRemoteGATTServer { impl AsyncBluetoothListener for BluetoothRemoteGATTServer { fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) { match response { + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-connect BluetoothResponse::GATTServerConnect(connected) => { + // Step 5.2.4. self.connected.set(connected); + + // Step 5.2.5. promise.resolve_native(promise_cx, self); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservice + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetPrimaryService(service) => { let context = self.device.get().get_context(); let mut service_map = context.get_service_map().borrow_mut(); @@ -168,6 +223,9 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTServer { service_map.insert(service.instance_id, MutHeap::new(&bt_service)); promise.resolve_native(promise_cx, &bt_service); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattserver-getprimaryservices + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetPrimaryServices(services_vec) => { let mut services = vec!(); let context = self.device.get().get_context(); diff --git a/components/script/dom/bluetoothremotegattservice.rs b/components/script/dom/bluetoothremotegattservice.rs index 69593e2c390..ef922e9b385 100644 --- a/components/script/dom/bluetoothremotegattservice.rs +++ b/components/script/dom/bluetoothremotegattservice.rs @@ -8,15 +8,17 @@ use dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMet use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding; use dom::bindings::codegen::Bindings::BluetoothRemoteGATTServiceBinding::BluetoothRemoteGATTServiceMethods; +use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::error::Error::{self, Network, Security}; use dom::bindings::js::{JS, MutHeap, Root}; -use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; +use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::bluetooth::{AsyncBluetoothListener, response_async}; use dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties; use dom::bluetoothdevice::BluetoothDevice; use dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic; use dom::bluetoothuuid::{BluetoothCharacteristicUUID, BluetoothServiceUUID, BluetoothUUID}; +use dom::eventtarget::EventTarget; use dom::globalscope::GlobalScope; use dom::promise::Promise; use ipc_channel::ipc::IpcSender; @@ -26,7 +28,7 @@ use std::rc::Rc; // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice #[dom_struct] pub struct BluetoothRemoteGATTService { - reflector_: Reflector, + eventtarget: EventTarget, device: MutHeap<JS<BluetoothDevice>>, uuid: DOMString, is_primary: bool, @@ -40,7 +42,7 @@ impl BluetoothRemoteGATTService { instance_id: String) -> BluetoothRemoteGATTService { BluetoothRemoteGATTService { - reflector_: Reflector::new(), + eventtarget: EventTarget::new_inherited(), device: MutHeap::new(device), uuid: uuid, is_primary: is_primary, @@ -93,11 +95,14 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetCharacteristic(&self, characteristic: BluetoothCharacteristicUUID) -> Rc<Promise> { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. let uuid = match BluetoothUUID::characteristic(characteristic) { Ok(uuid) => uuid.to_string(), Err(e) => { @@ -105,14 +110,23 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { return p; } }; + + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetootRemoteGATTService. + + // Note: Steps 6 - 7 are implemented is components/bluetooth/lib.rs in get_characteristic function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetCharacteristic(self.get_instance_id(), uuid, sender)).unwrap(); @@ -121,6 +135,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetCharacteristics(&self, characteristic: Option<BluetoothCharacteristicUUID>) -> Rc<Promise> { @@ -128,6 +143,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { let p_cx = p.global().get_cx(); let mut uuid: Option<String> = None; if let Some(c) = characteristic { + // Step 1. uuid = match BluetoothUUID::characteristic(c) { Ok(uuid) => Some(uuid.to_string()), Err(e) => { @@ -136,16 +152,24 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { } }; if let Some(ref uuid) = uuid { + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } } }; + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetootRemoteGATTService. + + // Note: Steps 6 - 7 are implemented is components/bluetooth/lib.rs in get_characteristics function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetCharacteristics(self.get_instance_id(), uuid, sender)).unwrap(); @@ -154,11 +178,14 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetIncludedService(&self, service: BluetoothServiceUUID) -> Rc<Promise> { let p = Promise::new(&self.global()); let p_cx = p.global().get_cx(); + + // Step 1. let uuid = match BluetoothUUID::service(service) { Ok(uuid) => uuid.to_string(), Err(e) => { @@ -166,14 +193,23 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { return p; } }; + + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetootRemoteGATTService. + + // Note: Steps 6 - 7 are implemented is components/bluetooth/lib.rs in get_included_service function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetIncludedService(self.get_instance_id(), @@ -185,6 +221,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { #[allow(unrooted_must_root)] // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren fn GetIncludedServices(&self, service: Option<BluetoothServiceUUID>) -> Rc<Promise> { @@ -192,6 +229,7 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { let p_cx = p.global().get_cx(); let mut uuid: Option<String> = None; if let Some(s) = service { + // Step 1. uuid = match BluetoothUUID::service(s) { Ok(uuid) => Some(uuid.to_string()), Err(e) => { @@ -200,16 +238,24 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { } }; if let Some(ref uuid) = uuid { + // Step 2. if uuid_is_blocklisted(uuid.as_ref(), Blocklist::All) { p.reject_error(p_cx, Security); return p; } } }; + + // Step 3 - 4. if !self.Device().Gatt().Connected() { p.reject_error(p_cx, Network); return p; } + + // TODO: Step 5: Implement representedService internal slot for BluetootRemoteGATTService. + + // Note: Steps 6 - 7 are implemented is components/bluetooth/lib.rs in get_included_services function + // and in handle_response function. let sender = response_async(&p, self); self.get_bluetooth_thread().send( BluetoothRequest::GetIncludedServices(self.get_instance_id(), @@ -217,11 +263,23 @@ impl BluetoothRemoteGATTServiceMethods for BluetoothRemoteGATTService { sender)).unwrap(); return p; } + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-serviceeventhandlers-onserviceadded + event_handler!(serviceadded, GetOnserviceadded, SetOnserviceadded); + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-serviceeventhandlers-onservicechanged + event_handler!(servicechanged, GetOnservicechanged, SetOnservicechanged); + + // https://webbluetoothcg.github.io/web-bluetooth/#dom-serviceeventhandlers-onserviceremoved + event_handler!(serviceremoved, GetOnserviceremoved, SetOnserviceremoved); } impl AsyncBluetoothListener for BluetoothRemoteGATTService { fn handle_response(&self, response: BluetoothResponse, promise_cx: *mut JSContext, promise: &Rc<Promise>) { match response { + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristic + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetCharacteristic(characteristic) => { let context = self.device.get().get_context(); let mut characteristic_map = context.get_characteristic_map().borrow_mut(); @@ -247,6 +305,9 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTService { characteristic_map.insert(characteristic.instance_id, MutHeap::new(&bt_characteristic)); promise.resolve_native(promise_cx, &bt_characteristic); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getcharacteristics + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetCharacteristics(characteristics_vec) => { let mut characteristics = vec!(); let context = self.device.get().get_context(); @@ -281,6 +342,9 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTService { } promise.resolve_native(promise_cx, &characteristics); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservice + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetIncludedService(service) => { let s = BluetoothRemoteGATTService::new(&self.global(), @@ -290,6 +354,9 @@ impl AsyncBluetoothListener for BluetoothRemoteGATTService { service.instance_id); promise.resolve_native(promise_cx, &s); }, + // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattservice-getincludedservices + // https://webbluetoothcg.github.io/web-bluetooth/#getgattchildren + // Step 7. BluetoothResponse::GetIncludedServices(services_vec) => { let s: Vec<Root<BluetoothRemoteGATTService>> = services_vec.into_iter() diff --git a/components/script/dom/bluetoothuuid.rs b/components/script/dom/bluetoothuuid.rs index 11fb7af94cb..fbf49142aa3 100644 --- a/components/script/dom/bluetoothuuid.rs +++ b/components/script/dom/bluetoothuuid.rs @@ -7,7 +7,7 @@ use dom::bindings::error::Error::Syntax; use dom::bindings::error::Fallible; use dom::bindings::reflector::Reflector; use dom::bindings::str::DOMString; -use dom::globalscope::GlobalScope; +use dom::window::Window; use regex::Regex; pub type UUID = DOMString; @@ -271,22 +271,22 @@ const VALID_UUID_REGEX: &'static str = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0- impl BluetoothUUID { // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-canonicaluuid - pub fn CanonicalUUID(_: &GlobalScope, alias: u32) -> UUID { + pub fn CanonicalUUID(_: &Window, alias: u32) -> UUID { canonical_uuid(alias) } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-getservice - pub fn GetService(_: &GlobalScope, name: BluetoothServiceUUID) -> Fallible<UUID> { + pub fn GetService(_: &Window, name: BluetoothServiceUUID) -> Fallible<UUID> { Self::service(name) } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-getcharacteristic - pub fn GetCharacteristic(_: &GlobalScope, name: BluetoothCharacteristicUUID) -> Fallible<UUID> { + pub fn GetCharacteristic(_: &Window, name: BluetoothCharacteristicUUID) -> Fallible<UUID> { Self::characteristic(name) } // https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothuuid-getdescriptor - pub fn GetDescriptor(_: &GlobalScope, name: BluetoothDescriptorUUID) -> Fallible<UUID> { + pub fn GetDescriptor(_: &Window, name: BluetoothDescriptorUUID) -> Fallible<UUID> { Self::descriptor(name) } } diff --git a/components/script/dom/browsingcontext.rs b/components/script/dom/browsingcontext.rs index 1409c9c5dcf..2434ab76819 100644 --- a/components/script/dom/browsingcontext.rs +++ b/components/script/dom/browsingcontext.rs @@ -20,11 +20,12 @@ use js::glue::{GetProxyPrivate, SetProxyExtra, GetProxyExtra}; 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_ForwardGetPropertyTo, JS_ForwardSetPropertyTo}; use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById}; use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue}; use js::jsapi::{ObjectOpResult, PropertyDescriptor}; use js::jsval::{UndefinedValue, PrivateValue}; +use js::rust::get_object_class; use msg::constellation_msg::PipelineId; use std::cell::Cell; @@ -67,7 +68,7 @@ impl BrowsingContext { let cx = window.get_cx(); let parent = window.reflector().get_jsobject(); assert!(!parent.get().is_null()); - assert!(((*JS_GetClass(parent.get())).flags & JSCLASS_IS_GLOBAL) != 0); + assert!(((*get_object_class(parent.get())).flags & JSCLASS_IS_GLOBAL) != 0); let _ac = JSAutoCompartment::new(cx, parent.get()); rooted!(in(cx) let window_proxy = NewWindowProxy(cx, parent, handler)); assert!(!window_proxy.is_null()); diff --git a/components/script/dom/comment.rs b/components/script/dom/comment.rs index b135def6e2a..9efe8c8164c 100644 --- a/components/script/dom/comment.rs +++ b/components/script/dom/comment.rs @@ -9,8 +9,8 @@ use dom::bindings::js::Root; use dom::bindings::str::DOMString; use dom::characterdata::CharacterData; use dom::document::Document; -use dom::globalscope::GlobalScope; use dom::node::Node; +use dom::window::Window; /// An HTML comment. #[dom_struct] @@ -31,8 +31,8 @@ impl Comment { CommentBinding::Wrap) } - pub fn Constructor(global: &GlobalScope, data: DOMString) -> Fallible<Root<Comment>> { - let document = global.as_window().Document(); + pub fn Constructor(window: &Window, data: DOMString) -> Fallible<Root<Comment>> { + let document = window.Document(); Ok(Comment::new(data, &document)) } } diff --git a/components/script/dom/css.rs b/components/script/dom/css.rs index f2256007aeb..2e04aed50bc 100644 --- a/components/script/dom/css.rs +++ b/components/script/dom/css.rs @@ -6,7 +6,7 @@ use cssparser::serialize_identifier; use dom::bindings::error::Fallible; use dom::bindings::reflector::Reflector; use dom::bindings::str::DOMString; -use dom::globalscope::GlobalScope; +use dom::window::Window; #[dom_struct] pub struct CSS { @@ -15,7 +15,7 @@ pub struct CSS { impl CSS { // http://dev.w3.org/csswg/cssom/#serialize-an-identifier - pub fn Escape(_: &GlobalScope, ident: DOMString) -> Fallible<DOMString> { + pub fn Escape(_: &Window, ident: DOMString) -> Fallible<DOMString> { let mut escaped = String::new(); serialize_identifier(&ident, &mut escaped).unwrap(); Ok(DOMString::from(escaped)) diff --git a/components/script/dom/cssfontfacerule.rs b/components/script/dom/cssfontfacerule.rs index 60022b7ba8f..61234f8846a 100644 --- a/components/script/dom/cssfontfacerule.rs +++ b/components/script/dom/cssfontfacerule.rs @@ -22,17 +22,18 @@ pub struct CSSFontFaceRule { } impl CSSFontFaceRule { - fn new_inherited(parent: Option<&CSSStyleSheet>, fontfacerule: Arc<RwLock<FontFaceRule>>) -> CSSFontFaceRule { + fn new_inherited(parent_stylesheet: &CSSStyleSheet, fontfacerule: Arc<RwLock<FontFaceRule>>) + -> CSSFontFaceRule { CSSFontFaceRule { - cssrule: CSSRule::new_inherited(parent), + cssrule: CSSRule::new_inherited(parent_stylesheet), fontfacerule: fontfacerule, } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, parent: Option<&CSSStyleSheet>, + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet, fontfacerule: Arc<RwLock<FontFaceRule>>) -> Root<CSSFontFaceRule> { - reflect_dom_object(box CSSFontFaceRule::new_inherited(parent, fontfacerule), + reflect_dom_object(box CSSFontFaceRule::new_inherited(parent_stylesheet, fontfacerule), window, CSSFontFaceRuleBinding::Wrap) } diff --git a/components/script/dom/cssgroupingrule.rs b/components/script/dom/cssgroupingrule.rs index cd55cd358b6..7176dda8d12 100644 --- a/components/script/dom/cssgroupingrule.rs +++ b/components/script/dom/cssgroupingrule.rs @@ -4,7 +4,6 @@ use dom::bindings::codegen::Bindings::CSSGroupingRuleBinding; use dom::bindings::codegen::Bindings::CSSGroupingRuleBinding::CSSGroupingRuleMethods; -use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleBinding::CSSRuleMethods; use dom::bindings::error::{ErrorResult, Fallible}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root}; @@ -14,38 +13,40 @@ use dom::cssrule::CSSRule; use dom::cssrulelist::{CSSRuleList, RulesSource}; use dom::cssstylesheet::CSSStyleSheet; use dom::window::Window; +use parking_lot::RwLock; +use std::sync::Arc; use style::stylesheets::CssRules as StyleCssRules; #[dom_struct] pub struct CSSGroupingRule { cssrule: CSSRule, #[ignore_heap_size_of = "Arc"] - rules: StyleCssRules, + rules: Arc<RwLock<StyleCssRules>>, rulelist: MutNullableHeap<JS<CSSRuleList>>, } impl CSSGroupingRule { - pub fn new_inherited(parent: Option<&CSSStyleSheet>, - rules: StyleCssRules) -> CSSGroupingRule { + pub fn new_inherited(parent_stylesheet: &CSSStyleSheet, + rules: Arc<RwLock<StyleCssRules>>) -> CSSGroupingRule { CSSGroupingRule { - cssrule: CSSRule::new_inherited(parent), + cssrule: CSSRule::new_inherited(parent_stylesheet), rules: rules, rulelist: MutNullableHeap::new(None), } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, parent: Option<&CSSStyleSheet>, rules: StyleCssRules) -> Root<CSSGroupingRule> { - reflect_dom_object(box CSSGroupingRule::new_inherited(parent, rules), + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet, + rules: Arc<RwLock<StyleCssRules>>) -> Root<CSSGroupingRule> { + reflect_dom_object(box CSSGroupingRule::new_inherited(parent_stylesheet, rules), window, CSSGroupingRuleBinding::Wrap) } fn rulelist(&self) -> Root<CSSRuleList> { - let sheet = self.upcast::<CSSRule>().GetParentStyleSheet(); - let sheet = sheet.as_ref().map(|s| &**s); + let parent_stylesheet = self.upcast::<CSSRule>().parent_stylesheet(); self.rulelist.or_init(|| CSSRuleList::new(self.global().as_window(), - sheet, + parent_stylesheet, RulesSource::Rules(self.rules.clone()))) } } diff --git a/components/script/dom/csskeyframerule.rs b/components/script/dom/csskeyframerule.rs index e09099e1135..78916d339ab 100644 --- a/components/script/dom/csskeyframerule.rs +++ b/components/script/dom/csskeyframerule.rs @@ -22,17 +22,18 @@ pub struct CSSKeyframeRule { } impl CSSKeyframeRule { - fn new_inherited(parent: Option<&CSSStyleSheet>, keyframerule: Arc<RwLock<Keyframe>>) -> CSSKeyframeRule { + fn new_inherited(parent_stylesheet: &CSSStyleSheet, keyframerule: Arc<RwLock<Keyframe>>) + -> CSSKeyframeRule { CSSKeyframeRule { - cssrule: CSSRule::new_inherited(parent), + cssrule: CSSRule::new_inherited(parent_stylesheet), keyframerule: keyframerule, } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, parent: Option<&CSSStyleSheet>, + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet, keyframerule: Arc<RwLock<Keyframe>>) -> Root<CSSKeyframeRule> { - reflect_dom_object(box CSSKeyframeRule::new_inherited(parent, keyframerule), + reflect_dom_object(box CSSKeyframeRule::new_inherited(parent_stylesheet, keyframerule), window, CSSKeyframeRuleBinding::Wrap) } diff --git a/components/script/dom/csskeyframesrule.rs b/components/script/dom/csskeyframesrule.rs index b7baf6b85a9..259819189a9 100644 --- a/components/script/dom/csskeyframesrule.rs +++ b/components/script/dom/csskeyframesrule.rs @@ -5,8 +5,6 @@ use cssparser::Parser; use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding; use dom::bindings::codegen::Bindings::CSSKeyframesRuleBinding::CSSKeyframesRuleMethods; -use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleMethods; -use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::reflector::{Reflectable, reflect_dom_object}; @@ -20,7 +18,7 @@ use parking_lot::RwLock; use std::sync::Arc; use style::keyframes::{Keyframe, KeyframeSelector}; use style::parser::ParserContextExtraData; -use style::stylesheets::{KeyframesRule, Origin}; +use style::stylesheets::KeyframesRule; use style_traits::ToCss; #[dom_struct] @@ -32,28 +30,28 @@ pub struct CSSKeyframesRule { } impl CSSKeyframesRule { - fn new_inherited(parent: Option<&CSSStyleSheet>, keyframesrule: Arc<RwLock<KeyframesRule>>) -> CSSKeyframesRule { + fn new_inherited(parent_stylesheet: &CSSStyleSheet, keyframesrule: Arc<RwLock<KeyframesRule>>) + -> CSSKeyframesRule { CSSKeyframesRule { - cssrule: CSSRule::new_inherited(parent), + cssrule: CSSRule::new_inherited(parent_stylesheet), keyframesrule: keyframesrule, rulelist: MutNullableHeap::new(None), } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, parent: Option<&CSSStyleSheet>, + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet, keyframesrule: Arc<RwLock<KeyframesRule>>) -> Root<CSSKeyframesRule> { - reflect_dom_object(box CSSKeyframesRule::new_inherited(parent, keyframesrule), + reflect_dom_object(box CSSKeyframesRule::new_inherited(parent_stylesheet, keyframesrule), window, CSSKeyframesRuleBinding::Wrap) } fn rulelist(&self) -> Root<CSSRuleList> { self.rulelist.or_init(|| { - let sheet = self.upcast::<CSSRule>().GetParentStyleSheet(); - let sheet = sheet.as_ref().map(|s| &**s); + let parent_stylesheet = &self.upcast::<CSSRule>().parent_stylesheet(); CSSRuleList::new(self.global().as_window(), - sheet, + parent_stylesheet, RulesSource::Keyframes(self.keyframesrule.clone())) }) } @@ -82,10 +80,7 @@ impl CSSKeyframesRuleMethods for CSSKeyframesRule { // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-appendrule fn AppendRule(&self, rule: DOMString) { - let global = self.global(); - let window = global.as_window(); - let doc = window.Document(); - let rule = Keyframe::parse(&rule, Origin::Author, doc.url().clone(), + let rule = Keyframe::parse(&rule, self.cssrule.parent_stylesheet().style_stylesheet(), ParserContextExtraData::default()); if let Ok(rule) = rule { self.keyframesrule.write().keyframes.push(rule); diff --git a/components/script/dom/cssmediarule.rs b/components/script/dom/cssmediarule.rs index 67933fccd3b..7f4f43f3d5b 100644 --- a/components/script/dom/cssmediarule.rs +++ b/components/script/dom/cssmediarule.rs @@ -3,12 +3,14 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::Bindings::CSSMediaRuleBinding; -use dom::bindings::js::Root; -use dom::bindings::reflector::reflect_dom_object; +use dom::bindings::codegen::Bindings::CSSMediaRuleBinding::CSSMediaRuleMethods; +use dom::bindings::js::{JS, MutNullableHeap, Root}; +use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::cssgroupingrule::CSSGroupingRule; use dom::cssrule::SpecificCSSRule; use dom::cssstylesheet::CSSStyleSheet; +use dom::medialist::MediaList; use dom::window::Window; use parking_lot::RwLock; use std::sync::Arc; @@ -20,24 +22,32 @@ pub struct CSSMediaRule { cssrule: CSSGroupingRule, #[ignore_heap_size_of = "Arc"] mediarule: Arc<RwLock<MediaRule>>, + medialist: MutNullableHeap<JS<MediaList>>, } impl CSSMediaRule { - fn new_inherited(parent: Option<&CSSStyleSheet>, mediarule: Arc<RwLock<MediaRule>>) -> CSSMediaRule { + fn new_inherited(parent_stylesheet: &CSSStyleSheet, mediarule: Arc<RwLock<MediaRule>>) + -> CSSMediaRule { let list = mediarule.read().rules.clone(); CSSMediaRule { - cssrule: CSSGroupingRule::new_inherited(parent, list), + cssrule: CSSGroupingRule::new_inherited(parent_stylesheet, list), mediarule: mediarule, + medialist: MutNullableHeap::new(None), } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, parent: Option<&CSSStyleSheet>, + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet, mediarule: Arc<RwLock<MediaRule>>) -> Root<CSSMediaRule> { - reflect_dom_object(box CSSMediaRule::new_inherited(parent, mediarule), + reflect_dom_object(box CSSMediaRule::new_inherited(parent_stylesheet, mediarule), window, CSSMediaRuleBinding::Wrap) } + + fn medialist(&self) -> Root<MediaList> { + self.medialist.or_init(|| MediaList::new(self.global().as_window(), + self.mediarule.read().media_queries.clone())) + } } impl SpecificCSSRule for CSSMediaRule { @@ -50,3 +60,10 @@ impl SpecificCSSRule for CSSMediaRule { self.mediarule.read().to_css_string().into() } } + +impl CSSMediaRuleMethods for CSSMediaRule { + // https://drafts.csswg.org/cssom/#dom-cssgroupingrule-media + fn Media(&self) -> Root<MediaList> { + self.medialist() + } +} diff --git a/components/script/dom/cssnamespacerule.rs b/components/script/dom/cssnamespacerule.rs index 4e93eea2e1f..5f451d3b798 100644 --- a/components/script/dom/cssnamespacerule.rs +++ b/components/script/dom/cssnamespacerule.rs @@ -23,17 +23,18 @@ pub struct CSSNamespaceRule { } impl CSSNamespaceRule { - fn new_inherited(parent: Option<&CSSStyleSheet>, namespacerule: Arc<RwLock<NamespaceRule>>) -> CSSNamespaceRule { + fn new_inherited(parent_stylesheet: &CSSStyleSheet, namespacerule: Arc<RwLock<NamespaceRule>>) + -> CSSNamespaceRule { CSSNamespaceRule { - cssrule: CSSRule::new_inherited(parent), + cssrule: CSSRule::new_inherited(parent_stylesheet), namespacerule: namespacerule, } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, parent: Option<&CSSStyleSheet>, + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet, namespacerule: Arc<RwLock<NamespaceRule>>) -> Root<CSSNamespaceRule> { - reflect_dom_object(box CSSNamespaceRule::new_inherited(parent, namespacerule), + reflect_dom_object(box CSSNamespaceRule::new_inherited(parent_stylesheet, namespacerule), window, CSSNamespaceRuleBinding::Wrap) } diff --git a/components/script/dom/cssrule.rs b/components/script/dom/cssrule.rs index ca1e52b77f0..603354c8621 100644 --- a/components/script/dom/cssrule.rs +++ b/components/script/dom/cssrule.rs @@ -5,7 +5,7 @@ use dom::bindings::codegen::Bindings::CSSRuleBinding; use dom::bindings::codegen::Bindings::CSSRuleBinding::CSSRuleMethods; use dom::bindings::inheritance::Castable; -use dom::bindings::js::{JS, MutNullableHeap, Root}; +use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::cssfontfacerule::CSSFontFaceRule; @@ -17,27 +17,34 @@ use dom::cssstylerule::CSSStyleRule; use dom::cssstylesheet::CSSStyleSheet; use dom::cssviewportrule::CSSViewportRule; use dom::window::Window; +use std::cell::Cell; use style::stylesheets::CssRule as StyleCssRule; #[dom_struct] pub struct CSSRule { reflector_: Reflector, - parent: MutNullableHeap<JS<CSSStyleSheet>>, + parent_stylesheet: JS<CSSStyleSheet>, + + /// Whether the parentStyleSheet attribute should return null. + /// We keep parent_stylesheet in that case because insertRule needs it + /// for the stylesheet’s base URL and namespace prefixes. + parent_stylesheet_removed: Cell<bool>, } impl CSSRule { #[allow(unrooted_must_root)] - pub fn new_inherited(parent: Option<&CSSStyleSheet>) -> CSSRule { + pub fn new_inherited(parent_stylesheet: &CSSStyleSheet) -> CSSRule { CSSRule { reflector_: Reflector::new(), - parent: MutNullableHeap::new(parent), + parent_stylesheet: JS::from_ref(parent_stylesheet), + parent_stylesheet_removed: Cell::new(false), } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, parent: Option<&CSSStyleSheet>) -> Root<CSSRule> { - reflect_dom_object(box CSSRule::new_inherited(parent), + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet) -> Root<CSSRule> { + reflect_dom_object(box CSSRule::new_inherited(parent_stylesheet), window, CSSRuleBinding::Wrap) } @@ -64,16 +71,16 @@ impl CSSRule { // Given a StyleCssRule, create a new instance of a derived class of // CSSRule based on which rule it is - pub fn new_specific(window: &Window, parent: Option<&CSSStyleSheet>, + pub fn new_specific(window: &Window, parent_stylesheet: &CSSStyleSheet, rule: StyleCssRule) -> Root<CSSRule> { // be sure to update the match in as_specific when this is updated match rule { - StyleCssRule::Style(s) => Root::upcast(CSSStyleRule::new(window, parent, s)), - StyleCssRule::FontFace(s) => Root::upcast(CSSFontFaceRule::new(window, parent, s)), - StyleCssRule::Keyframes(s) => Root::upcast(CSSKeyframesRule::new(window, parent, s)), - StyleCssRule::Media(s) => Root::upcast(CSSMediaRule::new(window, parent, s)), - StyleCssRule::Namespace(s) => Root::upcast(CSSNamespaceRule::new(window, parent, s)), - StyleCssRule::Viewport(s) => Root::upcast(CSSViewportRule::new(window, parent, s)), + StyleCssRule::Style(s) => Root::upcast(CSSStyleRule::new(window, parent_stylesheet, s)), + StyleCssRule::FontFace(s) => Root::upcast(CSSFontFaceRule::new(window, parent_stylesheet, s)), + StyleCssRule::Keyframes(s) => Root::upcast(CSSKeyframesRule::new(window, parent_stylesheet, s)), + StyleCssRule::Media(s) => Root::upcast(CSSMediaRule::new(window, parent_stylesheet, s)), + StyleCssRule::Namespace(s) => Root::upcast(CSSNamespaceRule::new(window, parent_stylesheet, s)), + StyleCssRule::Viewport(s) => Root::upcast(CSSViewportRule::new(window, parent_stylesheet, s)), } } @@ -85,12 +92,16 @@ impl CSSRule { /// Sets owner sheet to null (and does the same for all children) pub fn deparent(&self) { - self.parent.set(None); + self.parent_stylesheet_removed.set(true); // https://github.com/w3c/csswg-drafts/issues/722 // Spec doesn't ask us to do this, but it makes sense // and browsers implement this behavior self.as_specific().deparent_children(); } + + pub fn parent_stylesheet(&self) -> &CSSStyleSheet { + &self.parent_stylesheet + } } impl CSSRuleMethods for CSSRule { @@ -101,7 +112,11 @@ impl CSSRuleMethods for CSSRule { // https://drafts.csswg.org/cssom/#dom-cssrule-parentstylesheet fn GetParentStyleSheet(&self) -> Option<Root<CSSStyleSheet>> { - self.parent.get() + if self.parent_stylesheet_removed.get() { + None + } else { + Some(Root::from_ref(&*self.parent_stylesheet)) + } } // https://drafts.csswg.org/cssom/#dom-cssrule-csstext @@ -118,7 +133,7 @@ impl CSSRuleMethods for CSSRule { pub trait SpecificCSSRule { fn ty(&self) -> u16; fn get_css(&self) -> DOMString; - /// Remove CSSStyleSheet parent from all transitive children + /// Remove parentStylesheet from all transitive children fn deparent_children(&self) { // most CSSRules do nothing here } diff --git a/components/script/dom/cssrulelist.rs b/components/script/dom/cssrulelist.rs index deb6b93e8b1..5f594c27d8c 100644 --- a/components/script/dom/cssrulelist.rs +++ b/components/script/dom/cssrulelist.rs @@ -5,7 +5,6 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::CSSRuleListBinding; use dom::bindings::codegen::Bindings::CSSRuleListBinding::CSSRuleListMethods; -use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; @@ -15,33 +14,42 @@ use dom::cssstylesheet::CSSStyleSheet; use dom::window::Window; use parking_lot::RwLock; use std::sync::Arc; -use style::parser::ParserContextExtraData; -use style::stylesheets::{CssRules, KeyframesRule, Origin}; -use style::stylesheets::CssRule as StyleCssRule; +use style::stylesheets::{CssRules, KeyframesRule, RulesMutateError}; no_jsmanaged_fields!(RulesSource); no_jsmanaged_fields!(CssRules); +impl From<RulesMutateError> for Error { + fn from(other: RulesMutateError) -> Self { + match other { + RulesMutateError::Syntax => Error::Syntax, + RulesMutateError::IndexSize => Error::IndexSize, + RulesMutateError::HierarchyRequest => Error::HierarchyRequest, + RulesMutateError::InvalidState => Error::InvalidState, + } + } +} + #[dom_struct] pub struct CSSRuleList { reflector_: Reflector, - sheet: MutNullableHeap<JS<CSSStyleSheet>>, + parent_stylesheet: JS<CSSStyleSheet>, #[ignore_heap_size_of = "Arc"] rules: RulesSource, dom_rules: DOMRefCell<Vec<MutNullableHeap<JS<CSSRule>>>> } pub enum RulesSource { - Rules(CssRules), + Rules(Arc<RwLock<CssRules>>), Keyframes(Arc<RwLock<KeyframesRule>>), } impl CSSRuleList { #[allow(unrooted_must_root)] - pub fn new_inherited(sheet: Option<&CSSStyleSheet>, rules: RulesSource) -> CSSRuleList { + pub fn new_inherited(parent_stylesheet: &CSSStyleSheet, rules: RulesSource) -> CSSRuleList { let dom_rules = match rules { RulesSource::Rules(ref rules) => { - rules.0.read().iter().map(|_| MutNullableHeap::new(None)).collect() + rules.read().0.iter().map(|_| MutNullableHeap::new(None)).collect() } RulesSource::Keyframes(ref rules) => { rules.read().keyframes.iter().map(|_| MutNullableHeap::new(None)).collect() @@ -50,35 +58,23 @@ impl CSSRuleList { CSSRuleList { reflector_: Reflector::new(), - sheet: MutNullableHeap::new(sheet), + parent_stylesheet: JS::from_ref(parent_stylesheet), rules: rules, dom_rules: DOMRefCell::new(dom_rules), } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, sheet: Option<&CSSStyleSheet>, + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet, rules: RulesSource) -> Root<CSSRuleList> { - reflect_dom_object(box CSSRuleList::new_inherited(sheet, rules), + reflect_dom_object(box CSSRuleList::new_inherited(parent_stylesheet, rules), window, CSSRuleListBinding::Wrap) } - /// https://drafts.csswg.org/cssom/#insert-a-css-rule - /// /// Should only be called for CssRules-backed rules. Use append_lazy_rule /// for keyframes-backed rules. pub fn insert_rule(&self, rule: &str, idx: u32, nested: bool) -> Fallible<u32> { - use style::stylesheets::SingleRuleParseError; - /// Insert an item into a vector, appending if it is out of bounds - fn insert<T>(vec: &mut Vec<T>, index: usize, item: T) { - if index >= vec.len() { - vec.push(item); - } else { - vec.insert(index, item); - } - } - let css_rules = if let RulesSource::Rules(ref rules) = self.rules { rules } else { @@ -87,95 +83,25 @@ impl CSSRuleList { let global = self.global(); let window = global.as_window(); - let doc = window.Document(); let index = idx as usize; + let parent_stylesheet = self.parent_stylesheet.style_stylesheet(); + let new_rule = css_rules.write().insert_rule(rule, parent_stylesheet, index, nested)?; - let new_rule = { - let rules = css_rules.0.read(); - let state = if nested { - None - } else { - Some(CssRules::state_at_index(&rules, index)) - }; - - let rev_state = CssRules::state_at_index_rev(&rules, index); - - // Step 1, 2 - // XXXManishearth get url from correct location - // XXXManishearth should we also store the namespace map? - let parse_result = StyleCssRule::parse(&rule, Origin::Author, - doc.url().clone(), - ParserContextExtraData::default(), - state); - - if let Err(SingleRuleParseError::Syntax) = parse_result { - return Err(Error::Syntax) - } - - // Step 3, 4 - if index > rules.len() { - return Err(Error::IndexSize); - } - - let (new_rule, new_state) = try!(parse_result.map_err(|_| Error::HierarchyRequest)); - - if new_state > rev_state { - // We inserted a rule too early, e.g. inserting - // a regular style rule before @namespace rules - return Err((Error::HierarchyRequest)); - } - - // Step 6 - if let StyleCssRule::Namespace(..) = new_rule { - if !CssRules::only_ns_or_import(&rules) { - return Err(Error::InvalidState); - } - } - - new_rule - }; - - insert(&mut css_rules.0.write(), index, new_rule.clone()); - let sheet = self.sheet.get(); - let sheet = sheet.as_ref().map(|sheet| &**sheet); - let dom_rule = CSSRule::new_specific(&window, sheet, new_rule); - insert(&mut self.dom_rules.borrow_mut(), - index, MutNullableHeap::new(Some(&*dom_rule))); + let parent_stylesheet = &*self.parent_stylesheet; + let dom_rule = CSSRule::new_specific(&window, parent_stylesheet, new_rule); + self.dom_rules.borrow_mut().insert(index, MutNullableHeap::new(Some(&*dom_rule))); Ok((idx)) } - // https://drafts.csswg.org/cssom/#remove-a-css-rule - // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-deleterule // In case of a keyframe rule, index must be valid. pub fn remove_rule(&self, index: u32) -> ErrorResult { let index = index as usize; match self.rules { RulesSource::Rules(ref css_rules) => { - // https://drafts.csswg.org/cssom/#remove-a-css-rule - { - let rules = css_rules.0.read(); - - // Step 1, 2 - if index >= rules.len() { - return Err(Error::IndexSize); - } - - // Step 3 - let ref rule = rules[index]; - - // Step 4 - if let StyleCssRule::Namespace(..) = *rule { - if !CssRules::only_ns_or_import(&rules) { - return Err(Error::InvalidState); - } - } - } - - // Step 5, 6 + css_rules.write().remove_rule(index)?; let mut dom_rules = self.dom_rules.borrow_mut(); - css_rules.0.write().remove(index); dom_rules[index].get().map(|r| r.detach()); dom_rules.remove(index); Ok(()) @@ -201,17 +127,16 @@ impl CSSRuleList { pub fn item(&self, idx: u32) -> Option<Root<CSSRule>> { self.dom_rules.borrow().get(idx as usize).map(|rule| { rule.or_init(|| { - let sheet = self.sheet.get(); - let sheet = sheet.as_ref().map(|sheet| &**sheet); + let parent_stylesheet = &self.parent_stylesheet; match self.rules { RulesSource::Rules(ref rules) => { CSSRule::new_specific(self.global().as_window(), - sheet, - rules.0.read()[idx as usize].clone()) + parent_stylesheet, + rules.read().0[idx as usize].clone()) } RulesSource::Keyframes(ref rules) => { Root::upcast(CSSKeyframeRule::new(self.global().as_window(), - sheet, + parent_stylesheet, rules.read() .keyframes[idx as usize] .clone())) diff --git a/components/script/dom/cssstylerule.rs b/components/script/dom/cssstylerule.rs index 7d776655147..091704f42d5 100644 --- a/components/script/dom/cssstylerule.rs +++ b/components/script/dom/cssstylerule.rs @@ -22,17 +22,18 @@ pub struct CSSStyleRule { } impl CSSStyleRule { - fn new_inherited(parent: Option<&CSSStyleSheet>, stylerule: Arc<RwLock<StyleRule>>) -> CSSStyleRule { + fn new_inherited(parent_stylesheet: &CSSStyleSheet, stylerule: Arc<RwLock<StyleRule>>) + -> CSSStyleRule { CSSStyleRule { - cssrule: CSSRule::new_inherited(parent), + cssrule: CSSRule::new_inherited(parent_stylesheet), stylerule: stylerule, } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, parent: Option<&CSSStyleSheet>, + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet, stylerule: Arc<RwLock<StyleRule>>) -> Root<CSSStyleRule> { - reflect_dom_object(box CSSStyleRule::new_inherited(parent, stylerule), + reflect_dom_object(box CSSStyleRule::new_inherited(parent_stylesheet, stylerule), window, CSSStyleRuleBinding::Wrap) } diff --git a/components/script/dom/cssstylesheet.rs b/components/script/dom/cssstylesheet.rs index 32413ea0298..93aefa8f852 100644 --- a/components/script/dom/cssstylesheet.rs +++ b/components/script/dom/cssstylesheet.rs @@ -53,7 +53,7 @@ impl CSSStyleSheet { fn rulelist(&self) -> Root<CSSRuleList> { self.rulelist.or_init(|| CSSRuleList::new(self.global().as_window(), - Some(self), + self, RulesSource::Rules(self.style_stylesheet .rules.clone()))) } @@ -67,6 +67,10 @@ impl CSSStyleSheet { self.global().as_window().Document().invalidate_stylesheets(); } } + + pub fn style_stylesheet(&self) -> &StyleStyleSheet { + &self.style_stylesheet + } } impl CSSStyleSheetMethods for CSSStyleSheet { diff --git a/components/script/dom/cssviewportrule.rs b/components/script/dom/cssviewportrule.rs index b324b0c64f5..25ca1b292c8 100644 --- a/components/script/dom/cssviewportrule.rs +++ b/components/script/dom/cssviewportrule.rs @@ -22,17 +22,17 @@ pub struct CSSViewportRule { } impl CSSViewportRule { - fn new_inherited(parent: Option<&CSSStyleSheet>, viewportrule: Arc<RwLock<ViewportRule>>) -> CSSViewportRule { + fn new_inherited(parent_stylesheet: &CSSStyleSheet, viewportrule: Arc<RwLock<ViewportRule>>) -> CSSViewportRule { CSSViewportRule { - cssrule: CSSRule::new_inherited(parent), + cssrule: CSSRule::new_inherited(parent_stylesheet), viewportrule: viewportrule, } } #[allow(unrooted_must_root)] - pub fn new(window: &Window, parent: Option<&CSSStyleSheet>, + pub fn new(window: &Window, parent_stylesheet: &CSSStyleSheet, viewportrule: Arc<RwLock<ViewportRule>>) -> Root<CSSViewportRule> { - reflect_dom_object(box CSSViewportRule::new_inherited(parent, viewportrule), + reflect_dom_object(box CSSViewportRule::new_inherited(parent_stylesheet, viewportrule), window, CSSViewportRuleBinding::Wrap) } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 5870e4c1320..8b66d2db75d 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -136,18 +136,12 @@ pub enum TouchEventResult { Forwarded, } -#[derive(JSTraceable, PartialEq, HeapSizeOf)] +#[derive(Clone, Copy, Debug, HeapSizeOf, JSTraceable, PartialEq)] pub enum IsHTMLDocument { HTMLDocument, NonHTMLDocument, } -#[derive(PartialEq)] -enum ParserBlockedByScript { - Blocked, - Unblocked, -} - #[derive(JSTraceable, HeapSizeOf)] #[must_root] pub struct StylesheetInDocument { @@ -287,6 +281,8 @@ pub struct Document { /// https://w3c.github.io/uievents/#event-type-dblclick #[ignore_heap_size_of = "Defined in std"] last_click_info: DOMRefCell<Option<(Instant, Point2D<f32>)>>, + /// https://html.spec.whatwg.org/multipage/#ignore-destructive-writes-counter + ignore_destructive_writes_counter: Cell<u32>, } #[derive(JSTraceable, HeapSizeOf)] @@ -378,15 +374,16 @@ impl Document { self.trigger_mozbrowser_event(MozBrowserEvent::SecurityChange(https_state)); } + // https://html.spec.whatwg.org/multipage/#active-document + pub fn is_active(&self) -> bool { + self.browsing_context().map_or(false, |context| { + self == &*context.active_document() + }) + } + // https://html.spec.whatwg.org/multipage/#fully-active pub fn is_fully_active(&self) -> bool { - let browsing_context = match self.browsing_context() { - Some(browsing_context) => browsing_context, - None => return false, - }; - let active_document = browsing_context.active_document(); - - if self != &*active_document { + if !self.is_active() { return false; } // FIXME: It should also check whether the browser context is top-level or not @@ -1545,15 +1542,13 @@ impl Document { self.process_asap_scripts(); } - if self.maybe_execute_parser_blocking_script() == ParserBlockedByScript::Blocked { - return; - } - - // A finished resource load can potentially unblock parsing. In that case, resume the - // parser so its loop can find out. if let Some(parser) = self.get_current_parser() { - if parser.is_suspended() { - parser.resume(); + if let Some(script) = self.pending_parsing_blocking_script.get() { + if self.script_blocking_stylesheets_count.get() > 0 || !script.is_ready_to_be_executed() { + return; + } + self.pending_parsing_blocking_script.set(None); + parser.resume_with_pending_parsing_blocking_script(&script); } } else if self.reflow_timeout.get().is_none() { // If we don't have a parser, and the reflow timer has been reset, explicitly @@ -1576,23 +1571,6 @@ impl Document { } } - /// If document parsing is blocked on a script, and that script is ready to run, - /// execute it. - /// https://html.spec.whatwg.org/multipage/#ready-to-be-parser-executed - fn maybe_execute_parser_blocking_script(&self) -> ParserBlockedByScript { - let script = match self.pending_parsing_blocking_script.get() { - None => return ParserBlockedByScript::Unblocked, - Some(script) => script, - }; - - if self.script_blocking_stylesheets_count.get() == 0 && script.is_ready_to_be_executed() { - self.pending_parsing_blocking_script.set(None); - script.execute(); - return ParserBlockedByScript::Unblocked; - } - ParserBlockedByScript::Blocked - } - /// https://html.spec.whatwg.org/multipage/#the-end step 3 pub fn process_deferred_scripts(&self) { if self.ready_state.get() != DocumentReadyState::Interactive { @@ -1901,15 +1879,15 @@ impl Document { referrer_policy: Cell::new(referrer_policy), target_element: MutNullableHeap::new(None), last_click_info: DOMRefCell::new(None), + ignore_destructive_writes_counter: Default::default(), } } // https://dom.spec.whatwg.org/#dom-document - pub fn Constructor(global: &GlobalScope) -> Fallible<Root<Document>> { - let win = global.as_window(); - let doc = win.Document(); + pub fn Constructor(window: &Window) -> Fallible<Root<Document>> { + let doc = window.Document(); let docloader = DocumentLoader::new(&*doc.loader()); - Ok(Document::new(win, + Ok(Document::new(window, None, None, IsHTMLDocument::NonHTMLDocument, @@ -2077,6 +2055,16 @@ impl Document { ReflowQueryType::NoQuery, ReflowReason::ElementStateChanged); } + + pub fn incr_ignore_destructive_writes_counter(&self) { + self.ignore_destructive_writes_counter.set( + self.ignore_destructive_writes_counter.get() + 1); + } + + pub fn decr_ignore_destructive_writes_counter(&self) { + self.ignore_destructive_writes_counter.set( + self.ignore_destructive_writes_counter.get() - 1); + } } @@ -2437,7 +2425,7 @@ impl DocumentMethods for Document { ) )), "webglcontextevent" => - Ok(Root::upcast(WebGLContextEvent::new_uninitialized(self.window.upcast()))), + Ok(Root::upcast(WebGLContextEvent::new_uninitialized(&self.window))), "storageevent" => { let USVString(url) = self.URL(); Ok(Root::upcast(StorageEvent::new_uninitialized(&self.window, DOMString::from(url)))) @@ -3043,6 +3031,55 @@ impl DocumentMethods for Document { elements } + // https://html.spec.whatwg.org/multipage/#dom-document-write + fn Write(&self, text: Vec<DOMString>) -> ErrorResult { + if !self.is_html_document() { + // Step 1. + return Err(Error::InvalidState); + } + + // Step 2. + // TODO: handle throw-on-dynamic-markup-insertion counter. + + if !self.is_active() { + // Step 3. + return Ok(()); + } + + let parser = self.get_current_parser(); + let parser = match parser.as_ref() { + Some(parser) if parser.script_nesting_level() > 0 => parser, + _ => { + // Either there is no parser, which means the parsing ended; + // or script nesting level is 0, which means the method was + // called from outside a parser-executed script. + if self.ignore_destructive_writes_counter.get() > 0 { + // Step 4. + // TODO: handle ignore-opens-during-unload counter. + return Ok(()); + } + // Step 5. + // TODO: call document.open(). + return Err(Error::InvalidState); + } + }; + + // Step 7. + // TODO: handle reload override buffer. + + // Steps 6-8. + parser.write(text); + + // Step 9. + Ok(()) + } + + // https://html.spec.whatwg.org/multipage/#dom-document-writeln + fn Writeln(&self, mut text: Vec<DOMString>) -> ErrorResult { + text.push("\n".into()); + self.Write(text) + } + // https://html.spec.whatwg.org/multipage/#documentandelementeventhandlers document_and_element_event_handlers!(); } diff --git a/components/script/dom/documentfragment.rs b/components/script/dom/documentfragment.rs index 334e083f1b7..554fb8fe3b3 100644 --- a/components/script/dom/documentfragment.rs +++ b/components/script/dom/documentfragment.rs @@ -12,10 +12,10 @@ use dom::bindings::js::Root; use dom::bindings::str::DOMString; use dom::document::Document; use dom::element::Element; -use dom::globalscope::GlobalScope; use dom::htmlcollection::HTMLCollection; use dom::node::{Node, window_from_node}; use dom::nodelist::NodeList; +use dom::window::Window; use servo_atoms::Atom; // https://dom.spec.whatwg.org/#documentfragment @@ -38,8 +38,8 @@ impl DocumentFragment { DocumentFragmentBinding::Wrap) } - pub fn Constructor(global: &GlobalScope) -> Fallible<Root<DocumentFragment>> { - let document = global.as_window().Document(); + pub fn Constructor(window: &Window) -> Fallible<Root<DocumentFragment>> { + let document = window.Document(); Ok(DocumentFragment::new(&document)) } diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs index 2a770aeb3a0..0ce2d50e09a 100644 --- a/components/script/dom/domparser.rs +++ b/components/script/dom/domparser.rs @@ -17,7 +17,6 @@ use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::document::{Document, IsHTMLDocument}; use dom::document::DocumentSource; -use dom::globalscope::GlobalScope; use dom::servoparser::ServoParser; use dom::window::Window; @@ -41,8 +40,8 @@ impl DOMParser { DOMParserBinding::Wrap) } - pub fn Constructor(global: &GlobalScope) -> Fallible<Root<DOMParser>> { - Ok(DOMParser::new(global.as_window())) + pub fn Constructor(window: &Window) -> Fallible<Root<DOMParser>> { + Ok(DOMParser::new(window)) } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index d8f0dd601a7..c384001e33a 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -30,6 +30,7 @@ use dom::bindings::xmlname::XMLName::InvalidXMLName; use dom::characterdata::CharacterData; use dom::create::create_element; use dom::document::{Document, LayoutDocumentHelpers}; +use dom::documentfragment::DocumentFragment; use dom::domrect::DOMRect; use dom::domrectlist::DOMRectList; use dom::domtokenlist::DOMTokenList; @@ -61,6 +62,7 @@ use dom::node::{CLICK_IN_PROGRESS, ChildrenMutation, LayoutNodeHelpers, Node}; use dom::node::{NodeDamage, SEQUENTIALLY_FOCUSABLE, UnbindContext}; use dom::node::{document_from_node, window_from_node}; use dom::nodelist::NodeList; +use dom::servoparser::ServoParser; use dom::text::Text; use dom::validation::Validatable; use dom::virtualmethods::{VirtualMethods, vtable_for}; @@ -71,6 +73,7 @@ use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks}; use html5ever_atoms::{Prefix, LocalName, Namespace, QualName}; use parking_lot::RwLock; +use ref_filter_map::ref_filter_map; 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}; @@ -712,12 +715,52 @@ impl Element { &self.namespace } - pub fn prefix(&self) -> &Option<DOMString> { - &self.prefix + pub fn prefix(&self) -> Option<&DOMString> { + self.prefix.as_ref() } - pub fn attrs(&self) -> Ref<Vec<JS<Attr>>> { - self.attrs.borrow() + pub fn attrs(&self) -> Ref<[JS<Attr>]> { + Ref::map(self.attrs.borrow(), |attrs| &**attrs) + } + + // Element branch of https://dom.spec.whatwg.org/#locate-a-namespace + pub fn locate_namespace(&self, prefix: Option<DOMString>) -> Namespace { + let prefix = prefix.map(String::from).map(LocalName::from); + + let inclusive_ancestor_elements = + self.upcast::<Node>() + .inclusive_ancestors() + .filter_map(Root::downcast::<Self>); + + // Steps 3-4. + for element in inclusive_ancestor_elements { + // Step 1. + if element.namespace() != &ns!() && element.prefix().map(|p| &**p) == prefix.as_ref().map(|p| &**p) { + return element.namespace().clone(); + } + + // Step 2. + let attr = ref_filter_map(self.attrs(), |attrs| { + attrs.iter().find(|attr| { + if attr.namespace() != &ns!(xmlns) { + return false; + } + match (attr.prefix(), prefix.as_ref()) { + (Some(&namespace_prefix!("xmlns")), Some(prefix)) => { + attr.local_name() == prefix + }, + (None, None) => attr.local_name() == &local_name!("xmlns"), + _ => false, + } + }) + }); + + if let Some(attr) = attr { + return (**attr.value()).into(); + } + } + + ns!() } pub fn style_attribute(&self) -> &DOMRefCell<Option<Arc<RwLock<PropertyDeclarationBlock>>>> { @@ -818,7 +861,7 @@ impl Element { // Step 2. for attr in element.attrs.borrow().iter() { - if *attr.prefix() == Some(namespace_prefix!("xmlns")) && + if attr.prefix() == Some(&namespace_prefix!("xmlns")) && **attr.value() == *namespace { return Some(attr.LocalName()); } @@ -1230,6 +1273,33 @@ impl Element { // Step 11 win.scroll_node(node.to_trusted_node_address(), x, y, behavior); } + + // https://w3c.github.io/DOM-Parsing/#parsing + pub fn parse_fragment(&self, markup: DOMString) -> Fallible<Root<DocumentFragment>> { + // Steps 1-2. + let context_document = document_from_node(self); + // TODO(#11995): XML case. + let new_children = ServoParser::parse_html_fragment(self, markup); + // Step 3. + let fragment = DocumentFragment::new(&context_document); + // Step 4. + for child in new_children { + fragment.upcast::<Node>().AppendChild(&child).unwrap(); + } + // Step 5. + Ok(fragment) + } + + pub fn fragment_parsing_context(owner_doc: &Document, element: Option<&Self>) -> Root<Self> { + match element { + Some(elem) if elem.local_name() != &local_name!("html") || !elem.html_element_in_html_document() => { + Root::from_ref(elem) + }, + _ => { + Root::upcast(HTMLBodyElement::new(local_name!("body"), None, owner_doc)) + } + } + } } impl ElementMethods for Element { @@ -1757,15 +1827,14 @@ impl ElementMethods for Element { /// https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML fn SetInnerHTML(&self, value: DOMString) -> ErrorResult { - let context_node = self.upcast::<Node>(); // Step 1. - let frag = try!(context_node.parse_fragment(value)); + let frag = try!(self.parse_fragment(value)); // Step 2. // https://github.com/w3c/DOM-Parsing/issues/1 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() { Root::upcast(template.Content()) } else { - Root::from_ref(context_node) + Root::from_ref(self.upcast()) }; Node::replace_all(Some(frag.upcast()), &target); Ok(()) @@ -1776,7 +1845,7 @@ impl ElementMethods for Element { self.serialize(IncludeNode) } - // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-outerHTML + // https://w3c.github.io/DOM-Parsing/#dom-element-outerhtml fn SetOuterHTML(&self, value: DOMString) -> ErrorResult { let context_document = document_from_node(self); let context_node = self.upcast::<Node>(); @@ -1800,7 +1869,7 @@ impl ElementMethods for Element { ElementCreator::ScriptCreated); Root::upcast(body_elem) }, - _ => context_node.GetParentNode().unwrap() + _ => context_node.GetParentElement().unwrap() }; // Step 5. @@ -1957,14 +2026,11 @@ impl ElementMethods for Element { }; // Step 2. - let context = match context.downcast::<Element>() { - Some(elem) if elem.local_name() != &local_name!("html") || - !elem.html_element_in_html_document() => Root::from_ref(elem), - _ => Root::upcast(HTMLBodyElement::new(local_name!("body"), None, &*context.owner_doc())), - }; + let context = Element::fragment_parsing_context( + &context.owner_doc(), context.downcast::<Element>()); // Step 3. - let fragment = try!(context.upcast::<Node>().parse_fragment(text)); + let fragment = try!(context.parse_fragment(text)); // Step 4. self.insert_adjacent(position, fragment.upcast()).map(|_| ()) diff --git a/components/script/dom/extendableevent.rs b/components/script/dom/extendableevent.rs index 55e9500a8e4..0a7db40b6d5 100644 --- a/components/script/dom/extendableevent.rs +++ b/components/script/dom/extendableevent.rs @@ -10,7 +10,7 @@ use dom::bindings::js::Root; use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; use dom::event::Event; -use dom::globalscope::GlobalScope; +use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; use js::jsapi::{HandleValue, JSContext}; use servo_atoms::Atom; @@ -28,12 +28,12 @@ impl ExtendableEvent { extensions_allowed: true } } - pub fn new(global: &GlobalScope, + pub fn new(worker: &ServiceWorkerGlobalScope, type_: Atom, bubbles: bool, cancelable: bool) -> Root<ExtendableEvent> { - let ev = reflect_dom_object(box ExtendableEvent::new_inherited(), global, ExtendableEventBinding::Wrap); + let ev = reflect_dom_object(box ExtendableEvent::new_inherited(), worker, ExtendableEventBinding::Wrap); { let event = ev.upcast::<Event>(); event.init_event(type_, bubbles, cancelable); @@ -41,10 +41,10 @@ impl ExtendableEvent { ev } - pub fn Constructor(global: &GlobalScope, + pub fn Constructor(worker: &ServiceWorkerGlobalScope, type_: DOMString, init: &ExtendableEventBinding::ExtendableEventInit) -> Fallible<Root<ExtendableEvent>> { - Ok(ExtendableEvent::new(global, + Ok(ExtendableEvent::new(worker, Atom::from(type_), init.parent.bubbles, init.parent.cancelable)) diff --git a/components/script/dom/extendablemessageevent.rs b/components/script/dom/extendablemessageevent.rs index fbbdd59cbd3..8f156a8f4d3 100644 --- a/components/script/dom/extendablemessageevent.rs +++ b/components/script/dom/extendablemessageevent.rs @@ -13,6 +13,7 @@ use dom::event::Event; use dom::eventtarget::EventTarget; use dom::extendableevent::ExtendableEvent; use dom::globalscope::GlobalScope; +use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope; use js::jsapi::{HandleValue, Heap, JSContext}; use js::jsval::JSVal; use servo_atoms::Atom; @@ -46,10 +47,11 @@ impl ExtendableMessageEvent { ev } - pub fn Constructor(global: &GlobalScope, + pub fn Constructor(worker: &ServiceWorkerGlobalScope, type_: DOMString, init: &ExtendableMessageEventBinding::ExtendableMessageEventInit) -> Fallible<Root<ExtendableMessageEvent>> { + let global = worker.upcast::<GlobalScope>(); rooted!(in(global.get_cx()) let data = init.data); let ev = ExtendableMessageEvent::new(global, Atom::from(type_), diff --git a/components/script/dom/focusevent.rs b/components/script/dom/focusevent.rs index d06b725286d..1eb3983737d 100644 --- a/components/script/dom/focusevent.rs +++ b/components/script/dom/focusevent.rs @@ -53,12 +53,12 @@ impl FocusEvent { ev } - pub fn Constructor(global: &GlobalScope, + pub fn Constructor(window: &Window, type_: DOMString, init: &FocusEventBinding::FocusEventInit) -> Fallible<Root<FocusEvent>> { let bubbles = EventBubbles::from(init.parent.parent.bubbles); let cancelable = EventCancelable::from(init.parent.parent.cancelable); - let event = FocusEvent::new(global.as_window(), + let event = FocusEvent::new(window, type_, bubbles, cancelable, diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 0251bd8143e..7cd092e0683 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -24,15 +24,15 @@ use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; use js::glue::{IsWrapper, UnwrapObject}; use js::jsapi::{CurrentGlobalOrNull, GetGlobalForObjectCrossCompartment}; use js::jsapi::{HandleValue, Evaluate2, JSAutoCompartment, JSContext}; -use js::jsapi::{JSObject, JS_GetClass, JS_GetContext}; +use js::jsapi::{JSObject, JS_GetContext}; use js::jsapi::{JS_GetObjectRuntime, MutableHandleValue}; -use js::rust::CompileOptionsWrapper; +use js::panic::maybe_resume_unwind; +use js::rust::{CompileOptionsWrapper, get_object_class}; use libc; use msg::constellation_msg::PipelineId; use net_traits::{CoreResourceThread, ResourceThreads, IpcSend}; use profile_traits::{mem, time}; -use script_runtime::{CommonScriptMsg, EnqueuedPromiseCallback, ScriptChan}; -use script_runtime::{ScriptPort, maybe_take_panic_result}; +use script_runtime::{CommonScriptMsg, EnqueuedPromiseCallback, ScriptChan, ScriptPort}; use script_thread::{MainThreadScriptChan, RunnableWrapper, ScriptThread}; use script_traits::{MsDuration, ScriptMsg as ConstellationMsg, TimerEvent}; use script_traits::{TimerEventId, TimerEventRequest, TimerSource}; @@ -41,7 +41,6 @@ use std::cell::Cell; use std::collections::HashMap; use std::collections::hash_map::Entry; use std::ffi::CString; -use std::panic; use task_source::file_reading::FileReadingTaskSource; use task_source::networking::NetworkingTaskSource; use time::{Timespec, get_time}; @@ -376,9 +375,7 @@ impl GlobalScope { } } - if let Some(error) = maybe_take_panic_result() { - panic::resume_unwind(error); - } + maybe_resume_unwind(); } ) } @@ -519,7 +516,7 @@ fn timestamp_in_ms(time: Timespec) -> u64 { #[allow(unsafe_code)] unsafe fn global_scope_from_global(global: *mut JSObject) -> Root<GlobalScope> { assert!(!global.is_null()); - let clasp = JS_GetClass(global); + let clasp = get_object_class(global); assert!(((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)) != 0); root_from_object(global).unwrap() } diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index b5621364c13..b9d7731d652 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -177,7 +177,7 @@ impl HTMLCanvasElement { GLContextAttributes::default() }; - let maybe_ctx = WebGLRenderingContext::new(window.upcast(), self, size, attrs); + let maybe_ctx = WebGLRenderingContext::new(&window, self, size, attrs); *self.context.borrow_mut() = maybe_ctx.map( |ctx| CanvasContext::WebGL(JS::from_ref(&*ctx))); } diff --git a/components/script/dom/htmlcollection.rs b/components/script/dom/htmlcollection.rs index 71fc6a1ecf1..959265e82e0 100644 --- a/components/script/dom/htmlcollection.rs +++ b/components/script/dom/htmlcollection.rs @@ -151,11 +151,11 @@ impl HTMLCollection { } fn match_element(elem: &Element, qualified_name: &LocalName) -> bool { - match *elem.prefix() { + match elem.prefix() { None => elem.local_name() == qualified_name, - Some(ref prefix) => qualified_name.starts_with(prefix as &str) && - qualified_name.find(":") == Some((prefix as &str).len()) && - qualified_name.ends_with(elem.local_name() as &str), + Some(prefix) => qualified_name.starts_with(&**prefix) && + qualified_name.find(":") == Some(prefix.len()) && + qualified_name.ends_with(&**elem.local_name()), } } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index a31baa9f045..c322b96a555 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -20,6 +20,7 @@ use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root}; +use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; use dom::browsingcontext::BrowsingContext; @@ -42,14 +43,16 @@ use js::jsval::{NullValue, UndefinedValue}; use msg::constellation_msg::{FrameType, FrameId, PipelineId, TraversalDirection}; use net_traits::response::HttpsState; use script_layout_interface::message::ReflowQueryType; -use script_thread::ScriptThread; -use script_traits::{IFrameLoadInfo, LoadData, MozBrowserEvent, ScriptMsg as ConstellationMsg}; +use script_thread::{ScriptThread, Runnable}; +use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, LoadData}; +use script_traits::{MozBrowserEvent, NewLayoutInfo, ScriptMsg as ConstellationMsg}; use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; use servo_atoms::Atom; use servo_url::ServoUrl; use std::cell::Cell; use style::attr::{AttrValue, LengthOrPercentageOrAuto}; use style::context::ReflowGoal; +use task_source::TaskSource; use util::prefs::PREFS; use util::servo_version; @@ -66,6 +69,12 @@ bitflags! { } } +#[derive(PartialEq)] +enum ProcessingMode { + FirstTime, + NotFirstTime, +} + #[dom_struct] pub struct HTMLIFrameElement { htmlelement: HTMLElement, @@ -131,20 +140,46 @@ impl HTMLIFrameElement { let global_scope = window.upcast::<GlobalScope>(); let load_info = IFrameLoadInfo { - load_data: load_data, parent_pipeline_id: global_scope.pipeline_id(), frame_id: self.frame_id, - old_pipeline_id: old_pipeline_id, new_pipeline_id: new_pipeline_id, - sandbox: sandboxed, is_private: private_iframe, frame_type: frame_type, replace: replace, }; - global_scope - .constellation_chan() - .send(ConstellationMsg::ScriptLoadedURLInIFrame(load_info)) - .unwrap(); + + if load_data.as_ref().map_or(false, |d| d.url.as_str() == "about:blank") { + let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap(); + + global_scope + .constellation_chan() + .send(ConstellationMsg::ScriptLoadedAboutBlankInIFrame(load_info, pipeline_sender)) + .unwrap(); + + let new_layout_info = NewLayoutInfo { + parent_info: Some((global_scope.pipeline_id(), frame_type)), + new_pipeline_id: new_pipeline_id, + frame_id: self.frame_id, + load_data: load_data.unwrap(), + pipeline_port: pipeline_receiver, + content_process_shutdown_chan: None, + window_size: None, + layout_threads: PREFS.get("layout.threads").as_u64().expect("count") as usize, + }; + + ScriptThread::process_attach_layout(new_layout_info); + } else { + let load_info = IFrameLoadInfoWithData { + info: load_info, + load_data: load_data, + old_pipeline_id: old_pipeline_id, + sandbox: sandboxed, + }; + global_scope + .constellation_chan() + .send(ConstellationMsg::ScriptLoadedURLInIFrame(load_info)) + .unwrap(); + } if PREFS.is_mozbrowser_enabled() { // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserloadstart @@ -152,9 +187,23 @@ impl HTMLIFrameElement { } } - pub fn process_the_iframe_attributes(&self) { + /// https://html.spec.whatwg.org/multipage/#process-the-iframe-attributes + fn process_the_iframe_attributes(&self, mode: ProcessingMode) { + // TODO: srcdoc + + // https://github.com/whatwg/html/issues/490 + if mode == ProcessingMode::FirstTime && !self.upcast::<Element>().has_attribute(&local_name!("src")) { + let window = window_from_node(self); + let event_loop = window.dom_manipulation_task_source(); + let _ = event_loop.queue(box IframeLoadEventSteps::new(self), + window.upcast()); + return; + } + let url = self.get_url(); + // TODO: check ancestor browsing contexts for same URL + let document = document_from_node(self); self.navigate_or_reload_child_browsing_context( Some(LoadData::new(url, document.get_referrer_policy(), Some(document.url()))), false); @@ -171,6 +220,16 @@ impl HTMLIFrameElement { } } + fn create_nested_browsing_context(&self) { + // Synchronously create a new context and navigate it to about:blank. + let url = ServoUrl::parse("about:blank").unwrap(); + let document = document_from_node(self); + let load_data = LoadData::new(url, + document.get_referrer_policy(), + Some(document.url().clone())); + self.navigate_or_reload_child_browsing_context(Some(load_data), false); + } + pub fn update_pipeline_id(&self, new_pipeline_id: PipelineId) { self.pipeline_id.set(Some(new_pipeline_id)); @@ -272,7 +331,11 @@ impl HTMLIFrameElement { self.pipeline_id.get() .and_then(|pipeline_id| ScriptThread::find_document(pipeline_id)) .and_then(|document| { - if self.global().get_url().origin() == document.global().get_url().origin() { + // FIXME(#10964): this should use the Document's origin and the + // origin of the incumbent settings object. + let contained_url = document.global().get_url(); + if self.global().get_url().origin() == contained_url.origin() || + contained_url.as_str() == "about:blank" { Some(Root::from_ref(document.window())) } else { None @@ -458,18 +521,7 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement { // https://html.spec.whatwg.org/multipage/#dom-iframe-contentdocument fn GetContentDocument(&self) -> Option<Root<Document>> { - self.get_content_window().and_then(|window| { - // FIXME(#10964): this should use the Document's origin and the - // origin of the incumbent settings object. - let self_url = self.get_url(); - let win_url = window_from_node(self).get_url(); - - if UrlHelper::SameOrigin(&self_url, &win_url) { - Some(window.Document()) - } else { - None - } - }) + self.get_content_window().map(|window| window.Document()) } // Experimental mozbrowser implementation is based on the webidl @@ -601,19 +653,17 @@ impl VirtualMethods for HTMLIFrameElement { })); }, &local_name!("src") => { - if let AttributeMutation::Set(_) = mutation { - // https://html.spec.whatwg.org/multipage/#the-iframe-element - // "Similarly, whenever an iframe element with a non-null nested browsing context - // but with no srcdoc attribute specified has its src attribute set, changed, or removed, - // the user agent must process the iframe attributes," - // but we can't check that directly, since the child browsing context - // may be in a different script thread. Instread, we check to see if the parent - // is in a document tree and has a browsing context, which is what causes - // the child browsing context to be created. - if self.upcast::<Node>().is_in_doc_with_browsing_context() { - debug!("iframe {} src set while in browsing context.", self.frame_id); - self.process_the_iframe_attributes(); - } + // https://html.spec.whatwg.org/multipage/#the-iframe-element + // "Similarly, whenever an iframe element with a non-null nested browsing context + // but with no srcdoc attribute specified has its src attribute set, changed, or removed, + // the user agent must process the iframe attributes," + // but we can't check that directly, since the child browsing context + // may be in a different script thread. Instread, we check to see if the parent + // is in a document tree and has a browsing context, which is what causes + // the child browsing context to be created. + if self.upcast::<Node>().is_in_doc_with_browsing_context() { + debug!("iframe {} src set while in browsing context.", self.frame_id); + self.process_the_iframe_attributes(ProcessingMode::NotFirstTime); } }, _ => {}, @@ -642,7 +692,8 @@ impl VirtualMethods for HTMLIFrameElement { // iframe attributes for the "first time"." if self.upcast::<Node>().is_in_doc_with_browsing_context() { debug!("iframe {} bound to browsing context.", self.frame_id); - self.process_the_iframe_attributes(); + self.create_nested_browsing_context(); + self.process_the_iframe_attributes(ProcessingMode::FirstTime); } } @@ -667,7 +718,7 @@ impl VirtualMethods for HTMLIFrameElement { // HTMLIFrameElement::contentDocument. let self_url = self.get_url(); let win_url = window_from_node(self).get_url(); - UrlHelper::SameOrigin(&self_url, &win_url) + UrlHelper::SameOrigin(&self_url, &win_url) || self_url.as_str() == "about:blank" }; let (sender, receiver) = if same_origin { (None, None) @@ -690,3 +741,24 @@ impl VirtualMethods for HTMLIFrameElement { } } } + +struct IframeLoadEventSteps { + frame_element: Trusted<HTMLIFrameElement>, + pipeline_id: PipelineId, +} + +impl IframeLoadEventSteps { + fn new(frame_element: &HTMLIFrameElement) -> IframeLoadEventSteps { + IframeLoadEventSteps { + frame_element: Trusted::new(frame_element), + pipeline_id: frame_element.pipeline_id().unwrap(), + } + } +} + +impl Runnable for IframeLoadEventSteps { + fn handler(self: Box<IframeLoadEventSteps>) { + let this = self.frame_element.root(); + this.iframe_load_event_steps(self.pipeline_id); + } +} diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 3b58b383bf4..a86f0258918 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -16,11 +16,11 @@ use dom::bindings::str::DOMString; use dom::document::Document; use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers}; use dom::eventtarget::EventTarget; -use dom::globalscope::GlobalScope; use dom::htmlelement::HTMLElement; use dom::node::{Node, NodeDamage, document_from_node, window_from_node}; use dom::values::UNSIGNED_LONG_MAX; use dom::virtualmethods::VirtualMethods; +use dom::window::Window; use html5ever_atoms::LocalName; use ipc_channel::ipc; use ipc_channel::router::ROUTER; @@ -220,10 +220,10 @@ impl HTMLImageElement { HTMLImageElementBinding::Wrap) } - pub fn Image(global: &GlobalScope, + pub fn Image(window: &Window, width: Option<u32>, height: Option<u32>) -> Fallible<Root<HTMLImageElement>> { - let document = global.as_window().Document(); + let document = window.Document(); let image = HTMLImageElement::new(local_name!("img"), None, &document); if let Some(w) = width { image.SetWidth(w); diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index fa2fe7846c1..bcf112cce94 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -24,7 +24,7 @@ use std::sync::Arc; use std::sync::atomic::AtomicBool; use style::attr::AttrValue; use style::str::HTML_SPACE_CHARACTERS; -use style::stylesheets::{Stylesheet, CssRule, Origin}; +use style::stylesheets::{Stylesheet, CssRule, CssRules, Origin}; use style::viewport::ViewportRule; #[dom_struct] @@ -98,8 +98,10 @@ 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(Arc::new(RwLock::new(translated_rule)))].into(), + rules: CssRules::new(vec![CssRule::Viewport(Arc::new(RwLock::new(translated_rule)))]), origin: Origin::Author, + base_url: window_from_node(self).get_url(), + namespaces: Default::default(), media: Default::default(), // Viewport constraints are always recomputed on resize; they don't need to // force all styles to be recomputed. diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 6dd6349b185..255137a939d 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -274,12 +274,10 @@ fn fetch_a_classic_script(script: &HTMLScriptElement, impl HTMLScriptElement { /// https://html.spec.whatwg.org/multipage/#prepare-a-script - /// - /// Returns true if tokenization should continue, false otherwise. - pub fn prepare(&self) -> bool { + pub fn prepare(&self) { // Step 1. if self.already_started.get() { - return true; + return; } // Step 2. @@ -297,17 +295,17 @@ impl HTMLScriptElement { // Step 4. let text = self.Text(); if text.is_empty() && !element.has_attribute(&local_name!("src")) { - return true; + return; } // Step 5. if !self.upcast::<Node>().is_in_doc() { - return true; + return; } // Step 6. if !self.is_javascript() { - return true; + return; } // Step 7. @@ -322,12 +320,12 @@ impl HTMLScriptElement { // Step 9. let doc = document_from_node(self); if self.parser_inserted.get() && &*self.parser_document != &*doc { - return true; + return; } // Step 10. if !doc.is_scripting_enabled() { - return true; + return; } // TODO(#4577): Step 11: CSP. @@ -340,13 +338,13 @@ impl HTMLScriptElement { let for_value = for_attribute.value().to_ascii_lowercase(); let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS); if for_value != "window" { - return true; + return; } let event_value = event_attribute.value().to_ascii_lowercase(); let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS); if event_value != "onload" && event_value != "onload()" { - return true; + return; } }, (_, _) => (), @@ -381,7 +379,7 @@ impl HTMLScriptElement { // Step 18.2. if src.is_empty() { self.queue_error_event(); - return true; + return; } // Step 18.4-18.5. @@ -389,7 +387,7 @@ impl HTMLScriptElement { Err(_) => { warn!("error parsing URL for script {}", &**src); self.queue_error_event(); - return true; + return; } Ok(url) => url, }; @@ -412,7 +410,6 @@ impl HTMLScriptElement { !async { doc.add_deferred_script(self); // Second part implemented in Document::process_deferred_scripts. - return true; // Step 20.b: classic, has src, was parser-inserted, is not async. } else if is_external && was_parser_inserted && @@ -432,7 +429,7 @@ impl HTMLScriptElement { // Step 20.e: doesn't have src, was parser-inserted, is blocked on stylesheet. } else if !is_external && was_parser_inserted && - // TODO: check for script nesting levels. + doc.get_current_parser().map_or(false, |parser| parser.script_nesting_level() <= 1) && doc.get_script_blocking_stylesheets_count() > 0 { doc.set_pending_parsing_blocking_script(Some(self)); *self.load.borrow_mut() = Some(Ok(ScriptOrigin::internal(text, base_url))); @@ -443,16 +440,7 @@ impl HTMLScriptElement { self.ready_to_be_parser_executed.set(true); *self.load.borrow_mut() = Some(Ok(ScriptOrigin::internal(text, base_url))); self.execute(); - return true; } - - // TODO: make this suspension happen automatically. - if was_parser_inserted { - if let Some(parser) = doc.get_current_parser() { - parser.suspend(); - } - } - false } pub fn is_ready_to_be_executed(&self) -> bool { @@ -481,19 +469,20 @@ impl HTMLScriptElement { Ok(script) => script, }; - if script.external { - debug!("loading external script, url = {}", script.url); - } - // TODO(#12446): beforescriptexecute. if self.dispatch_before_script_execute_event() == EventStatus::Canceled { return; } // Step 3. - // TODO: If the script is from an external file, then increment the - // ignore-destructive-writes counter of the script element's node - // document. Let neutralised doc be that Document. + let neutralized_doc = if script.external { + debug!("loading external script, url = {}", script.url); + let doc = document_from_node(self); + doc.incr_ignore_destructive_writes_counter(); + Some(doc) + } else { + None + }; // Step 4. let document = document_from_node(self); @@ -512,8 +501,9 @@ impl HTMLScriptElement { document.set_current_script(old_script.r()); // Step 7. - // TODO: Decrement the ignore-destructive-writes counter of neutralised - // doc, if it was incremented in the earlier step. + if let Some(doc) = neutralized_doc { + doc.decr_ignore_destructive_writes_counter(); + } // TODO(#12446): afterscriptexecute. self.dispatch_after_script_execute_event(); diff --git a/components/script/dom/keyboardevent.rs b/components/script/dom/keyboardevent.rs index 4da24309e18..99cc61e021f 100644 --- a/components/script/dom/keyboardevent.rs +++ b/components/script/dom/keyboardevent.rs @@ -12,7 +12,6 @@ use dom::bindings::js::{Root, RootedReference}; use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; use dom::event::Event; -use dom::globalscope::GlobalScope; use dom::uievent::UIEvent; use dom::window::Window; use msg::constellation_msg; @@ -101,10 +100,10 @@ impl KeyboardEvent { ev } - pub fn Constructor(global: &GlobalScope, + pub fn Constructor(window: &Window, type_: DOMString, init: &KeyboardEventBinding::KeyboardEventInit) -> Fallible<Root<KeyboardEvent>> { - let event = KeyboardEvent::new(global.as_window(), + let event = KeyboardEvent::new(window, type_, init.parent.parent.parent.bubbles, init.parent.parent.parent.cancelable, diff --git a/components/script/dom/medialist.rs b/components/script/dom/medialist.rs new file mode 100644 index 00000000000..b6e5fd9c1ee --- /dev/null +++ b/components/script/dom/medialist.rs @@ -0,0 +1,122 @@ +/* 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::default::Default; +use cssparser::Parser; +use dom::bindings::codegen::Bindings::MediaListBinding; +use dom::bindings::codegen::Bindings::MediaListBinding::MediaListMethods; +use dom::bindings::js::Root; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::window::Window; +use parking_lot::RwLock; +use std::sync::Arc; +use style::media_queries::{MediaQuery, parse_media_query_list}; +use style::media_queries::MediaList as StyleMediaList; +use style_traits::ToCss; + +#[dom_struct] +pub struct MediaList { + reflector_: Reflector, + #[ignore_heap_size_of = "Arc"] + media_queries: Arc<RwLock<StyleMediaList>>, +} + +impl MediaList { + #[allow(unrooted_must_root)] + pub fn new_inherited(media_queries: Arc<RwLock<StyleMediaList>>) -> MediaList { + MediaList { + reflector_: Reflector::new(), + media_queries: media_queries, + } + } + + #[allow(unrooted_must_root)] + pub fn new(window: &Window, media_queries: Arc<RwLock<StyleMediaList>>) + -> Root<MediaList> { + reflect_dom_object(box MediaList::new_inherited(media_queries), + window, + MediaListBinding::Wrap) + } + +} + +impl MediaListMethods for MediaList { + // https://drafts.csswg.org/cssom/#dom-medialist-mediatext + fn MediaText(&self) -> DOMString { + DOMString::from(self.media_queries.read().to_css_string()) + } + + // https://drafts.csswg.org/cssom/#dom-medialist-mediatext + fn SetMediaText(&self, value: DOMString) { + let mut media_queries = self.media_queries.write(); + // Step 2 + if value.is_empty() { + // Step 1 + *media_queries = StyleMediaList::default(); + return; + } + // Step 3 + let mut parser = Parser::new(&value); + *media_queries = parse_media_query_list(&mut parser); + } + + // https://drafts.csswg.org/cssom/#dom-medialist-length + fn Length(&self) -> u32 { + self.media_queries.read().media_queries.len() as u32 + } + + // https://drafts.csswg.org/cssom/#dom-medialist-item + fn Item(&self, index: u32) -> Option<DOMString> { + self.media_queries.read().media_queries.get(index as usize) + .and_then(|query| { + let mut s = String::new(); + query.to_css(&mut s).unwrap(); + Some(DOMString::from_string(s)) + }) + } + + // https://drafts.csswg.org/cssom/#dom-medialist-item + fn IndexedGetter(&self, index: u32) -> Option<DOMString> { + self.Item(index) + } + + // https://drafts.csswg.org/cssom/#dom-medialist-appendmedium + fn AppendMedium(&self, medium: DOMString) { + // Step 1 + let mut parser = Parser::new(&medium); + let m = MediaQuery::parse(&mut parser); + // Step 2 + if let Err(_) = m { + return; + } + // Step 3 + let m_serialized = m.clone().unwrap().to_css_string(); + let any = self.media_queries.read().media_queries.iter() + .any(|q| m_serialized == q.to_css_string()); + if any { + return; + } + // Step 4 + self.media_queries.write().media_queries.push(m.unwrap()); + } + + // https://drafts.csswg.org/cssom/#dom-medialist-deletemedium + fn DeleteMedium(&self, medium: DOMString) { + // Step 1 + let mut parser = Parser::new(&medium); + let m = MediaQuery::parse(&mut parser); + // Step 2 + if let Err(_) = m { + return; + } + // Step 3 + let m_serialized = m.unwrap().to_css_string(); + let mut media_list = self.media_queries.write(); + let new_vec = media_list.media_queries.drain(..) + .filter(|q| m_serialized != q.to_css_string()) + .collect(); + media_list.media_queries = new_vec; + } +} diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 23209d89e07..9a0190cb57e 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -368,6 +368,7 @@ pub mod imagedata; pub mod keyboardevent; pub mod location; pub mod mediaerror; +pub mod medialist; pub mod mediaquerylist; pub mod messageevent; pub mod mimetype; diff --git a/components/script/dom/mouseevent.rs b/components/script/dom/mouseevent.rs index 27a61c79400..2b39d555204 100644 --- a/components/script/dom/mouseevent.rs +++ b/components/script/dom/mouseevent.rs @@ -12,7 +12,6 @@ use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; use dom::event::{Event, EventBubbles, EventCancelable}; use dom::eventtarget::EventTarget; -use dom::globalscope::GlobalScope; use dom::uievent::UIEvent; use dom::window::Window; use std::cell::Cell; @@ -82,12 +81,12 @@ impl MouseEvent { ev } - pub fn Constructor(global: &GlobalScope, + pub fn Constructor(window: &Window, type_: DOMString, init: &MouseEventBinding::MouseEventInit) -> Fallible<Root<MouseEvent>> { let bubbles = EventBubbles::from(init.parent.parent.parent.bubbles); let cancelable = EventCancelable::from(init.parent.parent.parent.cancelable); - let event = MouseEvent::new(global.as_window(), + let event = MouseEvent::new(window, type_, bubbles, cancelable, diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 983e2e8d403..f08b1480eef 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -7,8 +7,6 @@ use app_units::Au; use devtools_traits::NodeInfo; use document_loader::DocumentLoader; -use dom::attr::Attr; -use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; @@ -51,7 +49,6 @@ use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHel use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::range::WeakRangeVec; -use dom::servoparser::ServoParser; use dom::svgsvgelement::{SVGSVGElement, LayoutSVGSVGElementHelpers}; use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; @@ -61,7 +58,7 @@ use euclid::rect::Rect; use euclid::size::Size2D; use heapsize::{HeapSizeOf, heap_size_of}; use html5ever::tree_builder::QuirksMode; -use html5ever_atoms::{Prefix, LocalName, Namespace, QualName}; +use html5ever_atoms::{Prefix, Namespace, QualName}; use js::jsapi::{JSContext, JSObject, JSRuntime}; use libc::{self, c_void, uintptr_t}; use msg::constellation_msg::PipelineId; @@ -799,19 +796,6 @@ impl Node { } } - // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#dfn-concept-parse-fragment - pub fn parse_fragment(&self, markup: DOMString) -> Fallible<Root<DocumentFragment>> { - let context_document = document_from_node(self); - let fragment = DocumentFragment::new(&context_document); - if context_document.is_html_document() { - ServoParser::parse_html_fragment(self.upcast(), markup, fragment.upcast()); - } else { - // FIXME: XML case - unimplemented!(); - } - Ok(fragment) - } - /// Used by `HTMLTableSectionElement::InsertRow` and `HTMLTableRowElement::InsertCell` pub fn insert_cell_or_row<F, G, I>(&self, index: i32, get_items: F, new_child: G) -> Fallible<Root<HTMLElement>> where F: Fn() -> Root<HTMLCollection>, @@ -1752,7 +1736,7 @@ impl Node { local: element.local_name().clone() }; let element = Element::create(name, - element.prefix().as_ref().map(|p| Prefix::from(&**p)), + element.prefix().map(|p| Prefix::from(&**p)), &document, ElementCreator::ScriptCreated); Root::upcast::<Node>(element) }, @@ -1782,7 +1766,7 @@ impl Node { attr.value().clone(), attr.name().clone(), attr.namespace().clone(), - attr.prefix().clone()); + attr.prefix().cloned()); } }, _ => () @@ -1825,68 +1809,20 @@ impl Node { // https://dom.spec.whatwg.org/#locate-a-namespace pub fn locate_namespace(node: &Node, prefix: Option<DOMString>) -> Namespace { - fn attr_defines_namespace(attr: &Attr, - defined_prefix: &Option<LocalName>) -> bool { - *attr.namespace() == ns!(xmlns) && - match (attr.prefix(), defined_prefix) { - (&Some(ref attr_prefix), &Some(ref defined_prefix)) => - attr_prefix == &namespace_prefix!("xmlns") && - attr.local_name() == defined_prefix, - (&None, &None) => *attr.local_name() == local_name!("xmlns"), - _ => false - } - } - match node.type_id() { NodeTypeId::Element(_) => { - let element = node.downcast::<Element>().unwrap(); - // Step 1. - if *element.namespace() != ns!() && *element.prefix() == prefix { - return element.namespace().clone() - } - - // Even though this is conceptually a namespace prefix, - // in the `xmlns:foo="https://example.net/namespace" declaration - // it is a local name. - // FIXME(ajeffrey): directly convert DOMString to LocalName - let prefix_atom = prefix.as_ref().map(|s| LocalName::from(&**s)); - - // Step 2. - let attrs = element.attrs(); - let namespace_attr = attrs.iter().find(|attr| { - attr_defines_namespace(attr, &prefix_atom) - }); - - // Steps 2.1-2. - if let Some(attr) = namespace_attr { - return namespace_from_domstring(Some(attr.Value())); - } - - match node.GetParentElement() { - // Step 3. - None => ns!(), - // Step 4. - Some(parent) => Node::locate_namespace(parent.upcast(), prefix) - } + node.downcast::<Element>().unwrap().locate_namespace(prefix) }, NodeTypeId::Document(_) => { - match node.downcast::<Document>().unwrap().GetDocumentElement().r() { - // Step 1. - None => ns!(), - // Step 2. - Some(document_element) => { - Node::locate_namespace(document_element.upcast(), prefix) - } - } + node.downcast::<Document>().unwrap() + .GetDocumentElement().as_ref() + .map_or(ns!(), |elem| elem.locate_namespace(prefix)) }, - NodeTypeId::DocumentType => ns!(), - NodeTypeId::DocumentFragment => ns!(), - _ => match node.GetParentElement() { - // Step 1. - None => ns!(), - // Step 2. - Some(parent) => Node::locate_namespace(parent.upcast(), prefix) - } + NodeTypeId::DocumentType | NodeTypeId::DocumentFragment => ns!(), + _ => { + node.GetParentElement().as_ref() + .map_or(ns!(), |elem| elem.locate_namespace(prefix)) + } } } } @@ -2250,7 +2186,7 @@ impl NodeMethods for Node { let element = node.downcast::<Element>().unwrap(); let other_element = other.downcast::<Element>().unwrap(); (*element.namespace() == *other_element.namespace()) && - (*element.prefix() == *other_element.prefix()) && + (element.prefix() == other_element.prefix()) && (*element.local_name() == *other_element.local_name()) && (element.attrs().len() == other_element.attrs().len()) } diff --git a/components/script/dom/range.rs b/components/script/dom/range.rs index 8e8b9888b87..b2507aeab3b 100644 --- a/components/script/dom/range.rs +++ b/components/script/dom/range.rs @@ -22,11 +22,10 @@ use dom::characterdata::CharacterData; use dom::document::Document; use dom::documentfragment::DocumentFragment; use dom::element::Element; -use dom::globalscope::GlobalScope; -use dom::htmlbodyelement::HTMLBodyElement; use dom::htmlscriptelement::HTMLScriptElement; use dom::node::{Node, UnbindContext}; use dom::text::Text; +use dom::window::Window; use heapsize::HeapSizeOf; use js::jsapi::JSTracer; use std::cell::{Cell, UnsafeCell}; @@ -70,8 +69,8 @@ impl Range { } // https://dom.spec.whatwg.org/#dom-range - pub fn Constructor(global: &GlobalScope) -> Fallible<Root<Range>> { - let document = global.as_window().Document(); + pub fn Constructor(window: &Window) -> Fallible<Root<Range>> { + let document = window.Document(); Ok(Range::new_with_doc(&document)) } @@ -901,6 +900,7 @@ impl RangeMethods for Range { fn CreateContextualFragment(&self, fragment: DOMString) -> Fallible<Root<DocumentFragment>> { // Step 1. let node = self.StartContainer(); + let owner_doc = node.owner_doc(); let element = match node.type_id() { NodeTypeId::Document(_) | NodeTypeId::DocumentFragment => None, NodeTypeId::Element(_) => Some(Root::downcast::<Element>(node).unwrap()), @@ -911,15 +911,7 @@ impl RangeMethods for Range { }; // Step 2. - let should_create_body = element.as_ref().map_or(true, |elem| { - let elem = elem.downcast::<Element>().unwrap(); - elem.local_name() == &local_name!("html") && elem.html_element_in_html_document() - }); - let element: Root<Node> = if should_create_body { - Root::upcast(HTMLBodyElement::new(local_name!("body"), None, &self.StartContainer().owner_doc())) - } else { - Root::upcast(element.unwrap()) - }; + let element = Element::fragment_parsing_context(&owner_doc, element.r()); // Step 3. let fragment_node = try!(element.parse_fragment(fragment)); diff --git a/components/script/dom/serviceworkerglobalscope.rs b/components/script/dom/serviceworkerglobalscope.rs index a3877064131..b39faeb5694 100644 --- a/components/script/dom/serviceworkerglobalscope.rs +++ b/components/script/dom/serviceworkerglobalscope.rs @@ -319,7 +319,7 @@ impl ServiceWorkerGlobalScope { } fn dispatch_activate(&self) { - let event = ExtendableEvent::new(self.upcast(), atom!("activate"), false, false); + let event = ExtendableEvent::new(self, atom!("activate"), false, false); let event = (&*event).upcast::<Event>(); self.upcast::<EventTarget>().dispatch_event(event); } diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs index fe650477123..c6f4364925b 100644 --- a/components/script/dom/servoparser/mod.rs +++ b/components/script/dom/servoparser/mod.rs @@ -14,11 +14,12 @@ use dom::bindings::refcounted::Trusted; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::document::{Document, DocumentSource, IsHTMLDocument}; +use dom::element::Element; use dom::globalscope::GlobalScope; use dom::htmlformelement::HTMLFormElement; use dom::htmlimageelement::HTMLImageElement; use dom::htmlscriptelement::HTMLScriptElement; -use dom::node::{Node, document_from_node, window_from_node}; +use dom::node::{Node, NodeSiblingIterator}; use encoding::all::UTF_8; use encoding::types::{DecoderTrap, Encoding}; use html5ever::tokenizer::buffer_queue::BufferQueue; @@ -33,12 +34,25 @@ use profile_traits::time::{TimerMetadataReflowType, ProfilerCategory, profile}; use script_thread::ScriptThread; use servo_url::ServoUrl; use std::cell::Cell; +use std::mem; use util::resource_files::read_resource_file; mod html; mod xml; #[dom_struct] +/// The parser maintains two input streams: one for input from script through +/// document.write(), and one for input from network. +/// +/// There is no concrete representation of the insertion point, instead it +/// always points to just before the next character from the network input, +/// with all of the script input before itself. +/// +/// ```text +/// ... script input ... | ... network input ... +/// ^ +/// insertion point +/// ``` pub struct ServoParser { reflector: Reflector, /// The document associated with this parser. @@ -46,15 +60,20 @@ pub struct ServoParser { /// The pipeline associated with this parse, unavailable if this parse /// does not correspond to a page load. pipeline: Option<PipelineId>, - /// Input chunks received but not yet passed to the parser. + /// Input received from network. #[ignore_heap_size_of = "Defined in html5ever"] - pending_input: DOMRefCell<BufferQueue>, + network_input: DOMRefCell<BufferQueue>, + /// Input received from script. Used only to support document.write(). + #[ignore_heap_size_of = "Defined in html5ever"] + script_input: DOMRefCell<BufferQueue>, /// The tokenizer of this parser. tokenizer: DOMRefCell<Tokenizer>, /// Whether to expect any further input from the associated network request. last_chunk_received: Cell<bool>, /// Whether this parser should avoid passing any further data to the tokenizer. suspended: Cell<bool>, + /// https://html.spec.whatwg.org/multipage/#script-nesting-level + script_nesting_level: Cell<usize>, } #[derive(PartialEq)] @@ -78,17 +97,15 @@ impl ServoParser { } // https://html.spec.whatwg.org/multipage/#parsing-html-fragments - pub fn parse_html_fragment( - context_node: &Node, - input: DOMString, - output: &Node) { - let window = window_from_node(context_node); - let context_document = document_from_node(context_node); + pub fn parse_html_fragment(context: &Element, input: DOMString) -> FragmentParsingResult { + let context_node = context.upcast::<Node>(); + let context_document = context_node.owner_doc(); + let window = context_document.window(); let url = context_document.url(); // Step 1. let loader = DocumentLoader::new(&*context_document.loader()); - let document = Document::new(&window, None, Some(url.clone()), + let document = Document::new(window, None, Some(url.clone()), IsHTMLDocument::HTMLDocument, None, None, DocumentSource::FromParser, @@ -116,9 +133,7 @@ impl ServoParser { // Step 14. let root_element = document.GetDocumentElement().expect("no document element"); - for child in root_element.upcast::<Node>().children() { - output.AppendChild(&child).unwrap(); - } + FragmentParsingResult { inner: root_element.upcast::<Node>().children() } } pub fn parse_xml_document( @@ -134,6 +149,84 @@ impl ServoParser { parser.parse_chunk(String::from(input)); } + pub fn script_nesting_level(&self) -> usize { + self.script_nesting_level.get() + } + + /// Corresponds to the latter part of the "Otherwise" branch of the 'An end + /// tag whose tag name is "script"' of + /// https://html.spec.whatwg.org/multipage/#parsing-main-incdata + /// + /// This first moves everything from the script input to the beginning of + /// the network input, effectively resetting the insertion point to just + /// before the next character to be consumed. + /// + /// + /// ```text + /// | ... script input ... network input ... + /// ^ + /// insertion point + /// ``` + pub fn resume_with_pending_parsing_blocking_script(&self, script: &HTMLScriptElement) { + assert!(self.suspended.get()); + self.suspended.set(false); + + mem::swap(&mut *self.script_input.borrow_mut(), &mut *self.network_input.borrow_mut()); + while let Some(chunk) = self.script_input.borrow_mut().pop_front() { + self.network_input.borrow_mut().push_back(chunk); + } + + let script_nesting_level = self.script_nesting_level.get(); + assert_eq!(script_nesting_level, 0); + + self.script_nesting_level.set(script_nesting_level + 1); + script.execute(); + self.script_nesting_level.set(script_nesting_level); + + if !self.suspended.get() { + self.parse_sync(); + } + } + + /// Steps 6-8 of https://html.spec.whatwg.org/multipage/#document.write() + pub fn write(&self, text: Vec<DOMString>) { + assert!(self.script_nesting_level.get() > 0); + + if self.document.get_pending_parsing_blocking_script().is_some() { + // There is already a pending parsing blocking script so the + // parser is suspended, we just append everything to the + // script input and abort these steps. + for chunk in text { + self.script_input.borrow_mut().push_back(String::from(chunk).into()); + } + return; + } + + // There is no pending parsing blocking script, so all previous calls + // to document.write() should have seen their entire input tokenized + // and process, with nothing pushed to the parser script input. + assert!(self.script_input.borrow().is_empty()); + + let mut input = BufferQueue::new(); + for chunk in text { + input.push_back(String::from(chunk).into()); + } + + self.tokenize(|tokenizer| tokenizer.feed(&mut input)); + + if self.suspended.get() { + // Parser got suspended, insert remaining input at end of + // script input, following anything written by scripts executed + // reentrantly during this call. + while let Some(chunk) = input.pop_front() { + self.script_input.borrow_mut().push_back(chunk); + } + return; + } + + assert!(input.is_empty()); + } + #[allow(unrooted_must_root)] fn new_inherited( document: &Document, @@ -145,10 +238,12 @@ impl ServoParser { reflector: Reflector::new(), document: JS::from_ref(document), pipeline: pipeline, - pending_input: DOMRefCell::new(BufferQueue::new()), + network_input: DOMRefCell::new(BufferQueue::new()), + script_input: DOMRefCell::new(BufferQueue::new()), tokenizer: DOMRefCell::new(tokenizer), last_chunk_received: Cell::new(last_chunk_state == LastChunkState::Received), suspended: Default::default(), + script_nesting_level: Default::default(), } } @@ -165,111 +260,109 @@ impl ServoParser { ServoParserBinding::Wrap) } - pub fn document(&self) -> &Document { - &self.document - } - - pub fn pipeline(&self) -> Option<PipelineId> { - self.pipeline - } - - fn has_pending_input(&self) -> bool { - !self.pending_input.borrow().is_empty() - } - fn push_input_chunk(&self, chunk: String) { - self.pending_input.borrow_mut().push_back(chunk.into()); - } - - fn last_chunk_received(&self) -> bool { - self.last_chunk_received.get() - } - - fn mark_last_chunk_received(&self) { - self.last_chunk_received.set(true) - } - - fn set_plaintext_state(&self) { - self.tokenizer.borrow_mut().set_plaintext_state() - } - - pub fn suspend(&self) { - assert!(!self.suspended.get()); - self.suspended.set(true); - } - - pub fn resume(&self) { - assert!(self.suspended.get()); - self.suspended.set(false); - self.parse_sync(); - } - - pub fn is_suspended(&self) -> bool { - self.suspended.get() + self.network_input.borrow_mut().push_back(chunk.into()); } fn parse_sync(&self) { let metadata = TimerMetadata { - url: self.document().url().as_str().into(), + url: self.document.url().as_str().into(), iframe: TimerMetadataFrameType::RootWindow, incremental: TimerMetadataReflowType::FirstReflow, }; let profiler_category = self.tokenizer.borrow().profiler_category(); profile(profiler_category, Some(metadata), - self.document().window().upcast::<GlobalScope>().time_profiler_chan().clone(), + self.document.window().upcast::<GlobalScope>().time_profiler_chan().clone(), || self.do_parse_sync()) } fn do_parse_sync(&self) { + assert!(self.script_input.borrow().is_empty()); + // This parser will continue to parse while there is either pending input or // the parser remains unsuspended. - loop { - self.document().reflow_if_reflow_timer_expired(); - if let Err(script) = self.tokenizer.borrow_mut().feed(&mut *self.pending_input.borrow_mut()) { - if script.prepare() { - continue; - } - } - // Document parsing is blocked on an external resource. - if self.suspended.get() { - return; - } + self.tokenize(|tokenizer| tokenizer.feed(&mut *self.network_input.borrow_mut())); - if !self.has_pending_input() { - break; - } + if self.suspended.get() { + return; } - if self.last_chunk_received() { + assert!(self.network_input.borrow().is_empty()); + + if self.last_chunk_received.get() { self.finish(); } } fn parse_chunk(&self, input: String) { - self.document().set_current_parser(Some(self)); + self.document.set_current_parser(Some(self)); self.push_input_chunk(input); - if !self.is_suspended() { + if !self.suspended.get() { self.parse_sync(); } } + fn tokenize<F>(&self, mut feed: F) + where F: FnMut(&mut Tokenizer) -> Result<(), Root<HTMLScriptElement>> + { + loop { + assert!(!self.suspended.get()); + + self.document.reflow_if_reflow_timer_expired(); + let script = match feed(&mut *self.tokenizer.borrow_mut()) { + Ok(()) => return, + Err(script) => script, + }; + + let script_nesting_level = self.script_nesting_level.get(); + + self.script_nesting_level.set(script_nesting_level + 1); + script.prepare(); + self.script_nesting_level.set(script_nesting_level); + + if self.document.get_pending_parsing_blocking_script().is_some() { + self.suspended.set(true); + return; + } + } + } + fn finish(&self) { assert!(!self.suspended.get()); - assert!(!self.has_pending_input()); + assert!(self.last_chunk_received.get()); + assert!(self.script_input.borrow().is_empty()); + assert!(self.network_input.borrow().is_empty()); self.tokenizer.borrow_mut().end(); debug!("finished parsing"); - self.document().set_current_parser(None); + self.document.set_current_parser(None); - if let Some(pipeline) = self.pipeline() { + if let Some(pipeline) = self.pipeline { ScriptThread::parsing_complete(pipeline); } } } +pub struct FragmentParsingResult { + inner: NodeSiblingIterator, +} + +impl Iterator for FragmentParsingResult { + type Item = Root<Node>; + + fn next(&mut self) -> Option<Root<Node>> { + let next = match self.inner.next() { + Some(next) => next, + None => return None, + }; + next.remove_self(); + Some(next) + } +} + #[derive(HeapSizeOf, JSTraceable)] #[must_root] enum Tokenizer { @@ -372,7 +465,7 @@ impl FetchResponseListener for ParserContext { parser.push_input_chunk(page); parser.parse_sync(); - let doc = parser.document(); + let doc = &parser.document; let doc_body = Root::upcast::<Node>(doc.GetBody().unwrap()); let img = HTMLImageElement::new(local_name!("img"), None, doc); img.SetSrc(DOMString::from(self.url.to_string())); @@ -384,7 +477,7 @@ impl FetchResponseListener for ParserContext { let page = "<pre>\n".into(); parser.push_input_chunk(page); parser.parse_sync(); - parser.set_plaintext_state(); + parser.tokenizer.borrow_mut().set_plaintext_state(); }, Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { // Handle text/html if let Some(reason) = ssl_error { @@ -449,11 +542,11 @@ impl FetchResponseListener for ParserContext { debug!("Failed to load page URL {}, error: {:?}", self.url, err); } - parser.document() + parser.document .finish_load(LoadType::PageSource(self.url.clone())); - parser.mark_last_chunk_received(); - if !parser.is_suspended() { + parser.last_chunk_received.set(true); + if !parser.suspended.get() { parser.parse_sync(); } } diff --git a/components/script/dom/servoparser/xml.rs b/components/script/dom/servoparser/xml.rs index 4f233bd80ec..9d527ce21d1 100644 --- a/components/script/dom/servoparser/xml.rs +++ b/components/script/dom/servoparser/xml.rs @@ -114,7 +114,7 @@ impl<'a> TreeSink for Sink { let elem = target.downcast::<Element>() .expect("tried to get name of non-Element in XML parsing"); QName { - prefix: elem.prefix().as_ref().map_or(namespace_prefix!(""), |p| Prefix::from(&**p)), + prefix: elem.prefix().map_or(namespace_prefix!(""), |p| Prefix::from(&**p)), namespace_url: elem.namespace().clone(), local: elem.local_name().clone(), } diff --git a/components/script/dom/text.rs b/components/script/dom/text.rs index a6034a7dad5..cfa670d9acb 100644 --- a/components/script/dom/text.rs +++ b/components/script/dom/text.rs @@ -14,8 +14,8 @@ use dom::bindings::js::RootedReference; use dom::bindings::str::DOMString; use dom::characterdata::CharacterData; use dom::document::Document; -use dom::globalscope::GlobalScope; use dom::node::Node; +use dom::window::Window; /// An HTML text node. #[dom_struct] @@ -35,8 +35,8 @@ impl Text { document, TextBinding::Wrap) } - pub fn Constructor(global: &GlobalScope, text: DOMString) -> Fallible<Root<Text>> { - let document = global.as_window().Document(); + pub fn Constructor(window: &Window, text: DOMString) -> Fallible<Root<Text>> { + let document = window.Document(); Ok(Text::new(text, &document)) } } diff --git a/components/script/dom/transitionevent.rs b/components/script/dom/transitionevent.rs index 1f487cb38a5..cc17e77eae1 100644 --- a/components/script/dom/transitionevent.rs +++ b/components/script/dom/transitionevent.rs @@ -13,6 +13,7 @@ use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; use dom::event::Event; use dom::globalscope::GlobalScope; +use dom::window::Window; use servo_atoms::Atom; #[dom_struct] @@ -46,9 +47,10 @@ impl TransitionEvent { ev } - pub fn Constructor(global: &GlobalScope, + pub fn Constructor(window: &Window, type_: DOMString, init: &TransitionEventInit) -> Fallible<Root<TransitionEvent>> { + let global = window.upcast::<GlobalScope>(); Ok(TransitionEvent::new(global, Atom::from(type_), init)) } } diff --git a/components/script/dom/uievent.rs b/components/script/dom/uievent.rs index 08e1b0c912c..289088b2977 100644 --- a/components/script/dom/uievent.rs +++ b/components/script/dom/uievent.rs @@ -12,7 +12,6 @@ use dom::bindings::js::Root; use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; use dom::event::{Event, EventBubbles, EventCancelable}; -use dom::globalscope::GlobalScope; use dom::window::Window; use servo_atoms::Atom; use std::cell::Cell; @@ -52,12 +51,12 @@ impl UIEvent { ev } - pub fn Constructor(global: &GlobalScope, + pub fn Constructor(window: &Window, type_: DOMString, init: &UIEventBinding::UIEventInit) -> Fallible<Root<UIEvent>> { let bubbles = EventBubbles::from(init.parent.bubbles); let cancelable = EventCancelable::from(init.parent.cancelable); - let event = UIEvent::new(global.as_window(), + let event = UIEvent::new(window, type_, bubbles, cancelable, init.view.r(), init.detail); diff --git a/components/script/dom/webglactiveinfo.rs b/components/script/dom/webglactiveinfo.rs index ff82ce3b05f..515d6afa4a0 100644 --- a/components/script/dom/webglactiveinfo.rs +++ b/components/script/dom/webglactiveinfo.rs @@ -8,7 +8,7 @@ use dom::bindings::codegen::Bindings::WebGLActiveInfoBinding::WebGLActiveInfoMet use dom::bindings::js::Root; use dom::bindings::reflector::{Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; -use dom::globalscope::GlobalScope; +use dom::window::Window; #[dom_struct] pub struct WebGLActiveInfo { @@ -29,8 +29,8 @@ impl WebGLActiveInfo { } } - pub fn new(global: &GlobalScope, size: i32, ty: u32, name: DOMString) -> Root<WebGLActiveInfo> { - reflect_dom_object(box WebGLActiveInfo::new_inherited(size, ty, name), global, WebGLActiveInfoBinding::Wrap) + pub fn new(window: &Window, size: i32, ty: u32, name: DOMString) -> Root<WebGLActiveInfo> { + reflect_dom_object(box WebGLActiveInfo::new_inherited(size, ty, name), window, WebGLActiveInfoBinding::Wrap) } } diff --git a/components/script/dom/webglbuffer.rs b/components/script/dom/webglbuffer.rs index 6e19c00ae9f..752511df67a 100644 --- a/components/script/dom/webglbuffer.rs +++ b/components/script/dom/webglbuffer.rs @@ -7,8 +7,8 @@ use canvas_traits::CanvasMsg; use dom::bindings::codegen::Bindings::WebGLBufferBinding; use dom::bindings::js::Root; use dom::bindings::reflector::reflect_dom_object; -use dom::globalscope::GlobalScope; use dom::webglobject::WebGLObject; +use dom::window::Window; use ipc_channel::ipc::IpcSender; use std::cell::Cell; use webrender_traits; @@ -40,21 +40,21 @@ impl WebGLBuffer { } } - pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>) + pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>) -> Option<Root<WebGLBuffer>> { let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateBuffer(sender))).unwrap(); let result = receiver.recv().unwrap(); - result.map(|buffer_id| WebGLBuffer::new(global, renderer, buffer_id)) + result.map(|buffer_id| WebGLBuffer::new(window, renderer, buffer_id)) } - pub fn new(global: &GlobalScope, + pub fn new(window: &Window, renderer: IpcSender<CanvasMsg>, id: WebGLBufferId) -> Root<WebGLBuffer> { reflect_dom_object(box WebGLBuffer::new_inherited(renderer, id), - global, WebGLBufferBinding::Wrap) + window, WebGLBufferBinding::Wrap) } } diff --git a/components/script/dom/webglcontextevent.rs b/components/script/dom/webglcontextevent.rs index c128515e81a..9d13fc8f64a 100644 --- a/components/script/dom/webglcontextevent.rs +++ b/components/script/dom/webglcontextevent.rs @@ -12,7 +12,7 @@ use dom::bindings::js::Root; use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; use dom::event::{Event, EventBubbles, EventCancelable}; -use dom::globalscope::GlobalScope; +use dom::window::Window; use servo_atoms::Atom; #[dom_struct] @@ -41,25 +41,25 @@ impl WebGLContextEvent { } } - pub fn new_uninitialized(global_ref: &GlobalScope) -> Root<WebGLContextEvent> { + pub fn new_uninitialized(window: &Window) -> Root<WebGLContextEvent> { // according to https://www.khronos.org/registry/webgl/specs/1.0/#5.15 this is // additional information or the empty string if no additional information is // available. let status_message = DOMString::new(); reflect_dom_object( box WebGLContextEvent::new_inherited(status_message), - global_ref, + window, WebGLContextEventBinding::Wrap) } - pub fn new(global: &GlobalScope, + pub fn new(window: &Window, type_: Atom, bubbles: EventBubbles, cancelable: EventCancelable, status_message: DOMString) -> Root<WebGLContextEvent> { let event = reflect_dom_object( box WebGLContextEvent::new_inherited(status_message), - global, + window, WebGLContextEventBinding::Wrap); { @@ -70,7 +70,7 @@ impl WebGLContextEvent { event } - pub fn Constructor(global: &GlobalScope, + pub fn Constructor(window: &Window, type_: DOMString, init: &WebGLContextEventInit) -> Fallible<Root<WebGLContextEvent>> { let status_message = match init.statusMessage.as_ref() { @@ -82,7 +82,7 @@ impl WebGLContextEvent { let cancelable = EventCancelable::from(init.parent.cancelable); - Ok(WebGLContextEvent::new(global, + Ok(WebGLContextEvent::new(window, Atom::from(type_), bubbles, cancelable, diff --git a/components/script/dom/webglframebuffer.rs b/components/script/dom/webglframebuffer.rs index 643661d67d4..02a767958e2 100644 --- a/components/script/dom/webglframebuffer.rs +++ b/components/script/dom/webglframebuffer.rs @@ -9,10 +9,10 @@ use dom::bindings::codegen::Bindings::WebGLFramebufferBinding; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; use dom::bindings::js::{HeapGCValue, JS, Root}; use dom::bindings::reflector::reflect_dom_object; -use dom::globalscope::GlobalScope; use dom::webglobject::WebGLObject; use dom::webglrenderbuffer::WebGLRenderbuffer; use dom::webgltexture::WebGLTexture; +use dom::window::Window; use ipc_channel::ipc::IpcSender; use std::cell::Cell; use webrender_traits; @@ -66,21 +66,21 @@ impl WebGLFramebuffer { } } - pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>) + pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>) -> Option<Root<WebGLFramebuffer>> { let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateFramebuffer(sender))).unwrap(); let result = receiver.recv().unwrap(); - result.map(|fb_id| WebGLFramebuffer::new(global, renderer, fb_id)) + result.map(|fb_id| WebGLFramebuffer::new(window, renderer, fb_id)) } - pub fn new(global: &GlobalScope, + pub fn new(window: &Window, renderer: IpcSender<CanvasMsg>, id: WebGLFramebufferId) -> Root<WebGLFramebuffer> { reflect_dom_object(box WebGLFramebuffer::new_inherited(renderer, id), - global, + window, WebGLFramebufferBinding::Wrap) } } diff --git a/components/script/dom/webglobject.rs b/components/script/dom/webglobject.rs index 0964fc5d0cf..9c7382ce5b5 100644 --- a/components/script/dom/webglobject.rs +++ b/components/script/dom/webglobject.rs @@ -6,7 +6,7 @@ use dom::bindings::codegen::Bindings::WebGLObjectBinding; use dom::bindings::js::Root; use dom::bindings::reflector::{Reflector, reflect_dom_object}; -use dom::globalscope::GlobalScope; +use dom::window::Window; #[dom_struct] pub struct WebGLObject { @@ -20,7 +20,7 @@ impl WebGLObject { } } - pub fn new(global: &GlobalScope) -> Root<WebGLObject> { - reflect_dom_object(box WebGLObject::new_inherited(), global, WebGLObjectBinding::Wrap) + pub fn new(window: &Window) -> Root<WebGLObject> { + reflect_dom_object(box WebGLObject::new_inherited(), window, WebGLObjectBinding::Wrap) } } diff --git a/components/script/dom/webglprogram.rs b/components/script/dom/webglprogram.rs index 47807a8086e..2e5188269fa 100644 --- a/components/script/dom/webglprogram.rs +++ b/components/script/dom/webglprogram.rs @@ -9,11 +9,11 @@ use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderi use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::str::DOMString; -use dom::globalscope::GlobalScope; use dom::webglactiveinfo::WebGLActiveInfo; use dom::webglobject::WebGLObject; use dom::webglrenderingcontext::MAX_UNIFORM_AND_ATTRIBUTE_LEN; use dom::webglshader::WebGLShader; +use dom::window::Window; use ipc_channel::ipc::IpcSender; use std::cell::Cell; use webrender_traits; @@ -49,21 +49,21 @@ impl WebGLProgram { } } - pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>) + pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>) -> Option<Root<WebGLProgram>> { let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateProgram(sender))).unwrap(); let result = receiver.recv().unwrap(); - result.map(|program_id| WebGLProgram::new(global, renderer, program_id)) + result.map(|program_id| WebGLProgram::new(window, renderer, program_id)) } - pub fn new(global: &GlobalScope, + pub fn new(window: &Window, renderer: IpcSender<CanvasMsg>, id: WebGLProgramId) -> Root<WebGLProgram> { reflect_dom_object(box WebGLProgram::new_inherited(renderer, id), - global, + window, WebGLProgramBinding::Wrap) } } @@ -231,7 +231,7 @@ impl WebGLProgram { .unwrap(); receiver.recv().unwrap().map(|(size, ty, name)| - WebGLActiveInfo::new(&self.global(), size, ty, DOMString::from(name))) + WebGLActiveInfo::new(self.global().as_window(), size, ty, DOMString::from(name))) } /// glGetActiveAttrib @@ -245,7 +245,7 @@ impl WebGLProgram { .unwrap(); receiver.recv().unwrap().map(|(size, ty, name)| - WebGLActiveInfo::new(&self.global(), size, ty, DOMString::from(name))) + WebGLActiveInfo::new(self.global().as_window(), size, ty, DOMString::from(name))) } /// glGetAttribLocation diff --git a/components/script/dom/webglrenderbuffer.rs b/components/script/dom/webglrenderbuffer.rs index d74d045363a..b9d36f33d1c 100644 --- a/components/script/dom/webglrenderbuffer.rs +++ b/components/script/dom/webglrenderbuffer.rs @@ -8,8 +8,8 @@ use dom::bindings::codegen::Bindings::WebGLRenderbufferBinding; use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; use dom::bindings::js::Root; use dom::bindings::reflector::reflect_dom_object; -use dom::globalscope::GlobalScope; use dom::webglobject::WebGLObject; +use dom::window::Window; use ipc_channel::ipc::IpcSender; use std::cell::Cell; use webrender_traits; @@ -42,21 +42,21 @@ impl WebGLRenderbuffer { } } - pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>) + pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>) -> Option<Root<WebGLRenderbuffer>> { let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateRenderbuffer(sender))).unwrap(); let result = receiver.recv().unwrap(); - result.map(|renderbuffer_id| WebGLRenderbuffer::new(global, renderer, renderbuffer_id)) + result.map(|renderbuffer_id| WebGLRenderbuffer::new(window, renderer, renderbuffer_id)) } - pub fn new(global: &GlobalScope, + pub fn new(window: &Window, renderer: IpcSender<CanvasMsg>, id: WebGLRenderbufferId) -> Root<WebGLRenderbuffer> { reflect_dom_object(box WebGLRenderbuffer::new_inherited(renderer, id), - global, + window, WebGLRenderbufferBinding::Wrap) } } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index f43576fba63..76bfd9e10ce 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -34,6 +34,7 @@ use dom::webglrenderbuffer::WebGLRenderbuffer; use dom::webglshader::WebGLShader; use dom::webgltexture::{TexParameterValue, WebGLTexture}; use dom::webgluniformlocation::WebGLUniformLocation; +use dom::window::Window; use euclid::size::Size2D; use ipc_channel::ipc::{self, IpcSender}; use js::conversions::ConversionBehavior; @@ -132,16 +133,20 @@ pub struct WebGLRenderingContext { current_program: MutNullableHeap<JS<WebGLProgram>>, #[ignore_heap_size_of = "Because it's small"] current_vertex_attrib_0: Cell<(f32, f32, f32, f32)>, + #[ignore_heap_size_of = "Because it's small"] + current_scissor: Cell<(i32, i32, i32, i32)>, + #[ignore_heap_size_of = "Because it's small"] + current_clear_color: Cell<(f32, f32, f32, f32)>, } impl WebGLRenderingContext { - fn new_inherited(global: &GlobalScope, + fn new_inherited(window: &Window, canvas: &HTMLCanvasElement, size: Size2D<i32>, attrs: GLContextAttributes) -> Result<WebGLRenderingContext, String> { let (sender, receiver) = ipc::channel().unwrap(); - let constellation_chan = global.constellation_chan(); + let constellation_chan = window.upcast::<GlobalScope>().constellation_chan(); constellation_chan.send(ConstellationMsg::CreateWebGLPaintThread(size, attrs, sender)) .unwrap(); let result = receiver.recv().unwrap(); @@ -162,18 +167,20 @@ impl WebGLRenderingContext { bound_renderbuffer: MutNullableHeap::new(None), current_program: MutNullableHeap::new(None), current_vertex_attrib_0: Cell::new((0f32, 0f32, 0f32, 1f32)), + current_scissor: Cell::new((0, 0, size.width, size.height)), + current_clear_color: Cell::new((0.0, 0.0, 0.0, 0.0)) } }) } #[allow(unrooted_must_root)] - pub fn new(global: &GlobalScope, canvas: &HTMLCanvasElement, size: Size2D<i32>, attrs: GLContextAttributes) + pub fn new(window: &Window, canvas: &HTMLCanvasElement, size: Size2D<i32>, attrs: GLContextAttributes) -> Option<Root<WebGLRenderingContext>> { - match WebGLRenderingContext::new_inherited(global, canvas, size, attrs) { - Ok(ctx) => Some(reflect_dom_object(box ctx, global, WebGLRenderingContextBinding::Wrap)), + match WebGLRenderingContext::new_inherited(window, canvas, size, attrs) { + Ok(ctx) => Some(reflect_dom_object(box ctx, window, WebGLRenderingContextBinding::Wrap)), Err(msg) => { error!("Couldn't create WebGLRenderingContext: {}", msg); - let event = WebGLContextEvent::new(global, + let event = WebGLContextEvent::new(window, atom!("webglcontextcreationerror"), EventBubbles::DoesNotBubble, EventCancelable::Cancelable, @@ -202,6 +209,22 @@ impl WebGLRenderingContext { pub fn recreate(&self, size: Size2D<i32>) { self.ipc_renderer.send(CanvasMsg::Common(CanvasCommonMsg::Recreate(size))).unwrap(); + + // ClearColor needs to be restored because after a resize the GLContext is recreated + // and the framebuffer is cleared using the default black transparent color. + let color = self.current_clear_color.get(); + self.ipc_renderer + .send(CanvasMsg::WebGL(WebGLCommand::ClearColor(color.0, color.1, color.2, color.3))) + .unwrap(); + + // WebGL Spec: Scissor rect must not change if the canvas is resized. + // See: webgl/conformance-1.0.3/conformance/rendering/gl-scissor-canvas-dimensions.html + // NativeContext handling library changes the scissor after a resize, so we need to reset the + // default scissor when the canvas was created or the last scissor that the user set. + let rect = self.current_scissor.get(); + self.ipc_renderer + .send(CanvasMsg::WebGL(WebGLCommand::Scissor(rect.0, rect.1, rect.2, rect.3))) + .unwrap() } pub fn ipc_renderer(&self) -> IpcSender<CanvasMsg> { @@ -1135,6 +1158,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 fn ClearColor(&self, red: f32, green: f32, blue: f32, alpha: f32) { + self.current_clear_color.set((red, green, blue, alpha)); self.ipc_renderer .send(CanvasMsg::WebGL(WebGLCommand::ClearColor(red, green, blue, alpha))) .unwrap() @@ -1248,27 +1272,27 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // generated objects, either here or in the webgl thread // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn CreateBuffer(&self) -> Option<Root<WebGLBuffer>> { - WebGLBuffer::maybe_new(&self.global(), self.ipc_renderer.clone()) + WebGLBuffer::maybe_new(self.global().as_window(), self.ipc_renderer.clone()) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 fn CreateFramebuffer(&self) -> Option<Root<WebGLFramebuffer>> { - WebGLFramebuffer::maybe_new(&self.global(), self.ipc_renderer.clone()) + WebGLFramebuffer::maybe_new(self.global().as_window(), self.ipc_renderer.clone()) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 fn CreateRenderbuffer(&self) -> Option<Root<WebGLRenderbuffer>> { - WebGLRenderbuffer::maybe_new(&self.global(), self.ipc_renderer.clone()) + WebGLRenderbuffer::maybe_new(self.global().as_window(), self.ipc_renderer.clone()) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8 fn CreateTexture(&self) -> Option<Root<WebGLTexture>> { - WebGLTexture::maybe_new(&self.global(), self.ipc_renderer.clone()) + WebGLTexture::maybe_new(self.global().as_window(), self.ipc_renderer.clone()) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 fn CreateProgram(&self) -> Option<Root<WebGLProgram>> { - WebGLProgram::maybe_new(&self.global(), self.ipc_renderer.clone()) + WebGLProgram::maybe_new(self.global().as_window(), self.ipc_renderer.clone()) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 @@ -1280,7 +1304,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return None; } } - WebGLShader::maybe_new(&self.global(), self.ipc_renderer.clone(), shader_type) + WebGLShader::maybe_new(self.global().as_window(), self.ipc_renderer.clone(), shader_type) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 @@ -1612,7 +1636,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { name: DOMString) -> Option<Root<WebGLUniformLocation>> { program.and_then(|p| { handle_potential_webgl_error!(self, p.get_uniform_location(name), None) - .map(|location| WebGLUniformLocation::new(&self.global(), location, p.id())) + .map(|location| WebGLUniformLocation::new(self.global().as_window(), location, p.id())) }) } @@ -1902,6 +1926,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { return self.webgl_error(InvalidValue) } + self.current_scissor.set((x, y, width, height)); self.ipc_renderer .send(CanvasMsg::WebGL(WebGLCommand::Scissor(x, y, width, height))) .unwrap() diff --git a/components/script/dom/webglshader.rs b/components/script/dom/webglshader.rs index 93da1ca0bc5..d846ac34aa7 100644 --- a/components/script/dom/webglshader.rs +++ b/components/script/dom/webglshader.rs @@ -10,8 +10,8 @@ use dom::bindings::codegen::Bindings::WebGLShaderBinding; use dom::bindings::js::Root; use dom::bindings::reflector::reflect_dom_object; use dom::bindings::str::DOMString; -use dom::globalscope::GlobalScope; use dom::webglobject::WebGLObject; +use dom::window::Window; use ipc_channel::ipc::IpcSender; use std::cell::Cell; use std::sync::{ONCE_INIT, Once}; @@ -66,23 +66,24 @@ impl WebGLShader { } } - pub fn maybe_new(global: &GlobalScope, + pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>, - shader_type: u32) -> Option<Root<WebGLShader>> { + shader_type: u32) + -> Option<Root<WebGLShader>> { let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateShader(shader_type, sender))).unwrap(); let result = receiver.recv().unwrap(); - result.map(|shader_id| WebGLShader::new(global, renderer, shader_id, shader_type)) + result.map(|shader_id| WebGLShader::new(window, renderer, shader_id, shader_type)) } - pub fn new(global: &GlobalScope, + pub fn new(window: &Window, renderer: IpcSender<CanvasMsg>, id: WebGLShaderId, shader_type: u32) -> Root<WebGLShader> { reflect_dom_object(box WebGLShader::new_inherited(renderer, id, shader_type), - global, + window, WebGLShaderBinding::Wrap) } } diff --git a/components/script/dom/webglshaderprecisionformat.rs b/components/script/dom/webglshaderprecisionformat.rs index 18ba8c189a5..c0f954c8d4d 100644 --- a/components/script/dom/webglshaderprecisionformat.rs +++ b/components/script/dom/webglshaderprecisionformat.rs @@ -7,7 +7,7 @@ use dom::bindings::codegen::Bindings::WebGLShaderPrecisionFormatBinding; use dom::bindings::codegen::Bindings::WebGLShaderPrecisionFormatBinding::WebGLShaderPrecisionFormatMethods; use dom::bindings::js::Root; use dom::bindings::reflector::{Reflector, reflect_dom_object}; -use dom::globalscope::GlobalScope; +use dom::window::Window; #[dom_struct] pub struct WebGLShaderPrecisionFormat { @@ -27,13 +27,13 @@ impl WebGLShaderPrecisionFormat { } } - pub fn new(global: &GlobalScope, + pub fn new(window: &Window, range_min: i32, range_max: i32, precision: i32) -> Root<WebGLShaderPrecisionFormat> { reflect_dom_object( box WebGLShaderPrecisionFormat::new_inherited(range_min, range_max, precision), - global, + window, WebGLShaderPrecisionFormatBinding::Wrap) } } diff --git a/components/script/dom/webgltexture.rs b/components/script/dom/webgltexture.rs index 26cf14f7972..9a1e4663152 100644 --- a/components/script/dom/webgltexture.rs +++ b/components/script/dom/webgltexture.rs @@ -9,9 +9,9 @@ use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderi use dom::bindings::codegen::Bindings::WebGLTextureBinding; use dom::bindings::js::Root; use dom::bindings::reflector::reflect_dom_object; -use dom::globalscope::GlobalScope; use dom::webgl_validations::types::{TexImageTarget, TexFormat, TexDataType}; use dom::webglobject::WebGLObject; +use dom::window::Window; use ipc_channel::ipc::IpcSender; use std::cell::Cell; use std::cmp; @@ -61,21 +61,21 @@ impl WebGLTexture { } } - pub fn maybe_new(global: &GlobalScope, renderer: IpcSender<CanvasMsg>) + pub fn maybe_new(window: &Window, renderer: IpcSender<CanvasMsg>) -> Option<Root<WebGLTexture>> { let (sender, receiver) = webrender_traits::channel::msg_channel().unwrap(); renderer.send(CanvasMsg::WebGL(WebGLCommand::CreateTexture(sender))).unwrap(); let result = receiver.recv().unwrap(); - result.map(|texture_id| WebGLTexture::new(global, renderer, texture_id)) + result.map(|texture_id| WebGLTexture::new(window, renderer, texture_id)) } - pub fn new(global: &GlobalScope, + pub fn new(window: &Window, renderer: IpcSender<CanvasMsg>, id: WebGLTextureId) -> Root<WebGLTexture> { reflect_dom_object(box WebGLTexture::new_inherited(renderer, id), - global, + window, WebGLTextureBinding::Wrap) } } diff --git a/components/script/dom/webgluniformlocation.rs b/components/script/dom/webgluniformlocation.rs index 6e0683ec833..1b355bd8582 100644 --- a/components/script/dom/webgluniformlocation.rs +++ b/components/script/dom/webgluniformlocation.rs @@ -6,7 +6,7 @@ use dom::bindings::codegen::Bindings::WebGLUniformLocationBinding; use dom::bindings::js::Root; use dom::bindings::reflector::{Reflector, reflect_dom_object}; -use dom::globalscope::GlobalScope; +use dom::window::Window; use webrender_traits::WebGLProgramId; #[dom_struct] @@ -27,12 +27,12 @@ impl WebGLUniformLocation { } } - pub fn new(global: &GlobalScope, + pub fn new(window: &Window, id: i32, program_id: WebGLProgramId) -> Root<WebGLUniformLocation> { reflect_dom_object(box WebGLUniformLocation::new_inherited(id, program_id), - global, + window, WebGLUniformLocationBinding::Wrap) } diff --git a/components/script/dom/webidls/Bluetooth.webidl b/components/script/dom/webidls/Bluetooth.webidl index b925e0b0b25..de2b95c3fbb 100644 --- a/components/script/dom/webidls/Bluetooth.webidl +++ b/components/script/dom/webidls/Bluetooth.webidl @@ -28,9 +28,10 @@ dictionary RequestDeviceOptions { }; [Pref="dom.bluetooth.enabled"] -interface Bluetooth { +interface Bluetooth : EventTarget { // [SecureContext] // readonly attribute BluetoothDevice? referringDevice; + attribute EventHandler onavailabilitychanged; // [SecureContext] // Promise<boolean> getAvailability(); // [SecureContext] diff --git a/components/script/dom/webidls/BluetoothDevice.webidl b/components/script/dom/webidls/BluetoothDevice.webidl index 34a77230850..0e7843db109 100644 --- a/components/script/dom/webidls/BluetoothDevice.webidl +++ b/components/script/dom/webidls/BluetoothDevice.webidl @@ -5,7 +5,7 @@ // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothdevice [Pref="dom.bluetooth.enabled"] -interface BluetoothDevice { +interface BluetoothDevice : EventTarget { readonly attribute DOMString id; readonly attribute DOMString? name; // TODO: remove this after BluetoothAdvertisingEvent implemented. @@ -17,7 +17,12 @@ interface BluetoothDevice { // readonly attribute boolean watchingAdvertisements; }; +[NoInterfaceObject] +interface BluetoothDeviceEventHandlers { + attribute EventHandler ongattserverdisconnected; +}; + // BluetoothDevice implements EventTarget; -// BluetoothDevice implements BluetoothDeviceEventHandlers; +BluetoothDevice implements BluetoothDeviceEventHandlers; // BluetoothDevice implements CharacteristicEventHandlers; // BluetoothDevice implements ServiceEventHandlers; diff --git a/components/script/dom/webidls/BluetoothRemoteGATTCharacteristic.webidl b/components/script/dom/webidls/BluetoothRemoteGATTCharacteristic.webidl index d1d937475b4..55d7bf0a43c 100644 --- a/components/script/dom/webidls/BluetoothRemoteGATTCharacteristic.webidl +++ b/components/script/dom/webidls/BluetoothRemoteGATTCharacteristic.webidl @@ -5,7 +5,7 @@ // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattcharacteristic [Pref="dom.bluetooth.enabled"] -interface BluetoothRemoteGATTCharacteristic { +interface BluetoothRemoteGATTCharacteristic : EventTarget { readonly attribute BluetoothRemoteGATTService service; readonly attribute DOMString uuid; readonly attribute BluetoothCharacteristicProperties properties; @@ -21,5 +21,10 @@ interface BluetoothRemoteGATTCharacteristic { Promise<BluetoothRemoteGATTCharacteristic> stopNotifications(); }; -//BluetootRemoteGATTCharacteristic implements EventTarget; -//BluetootRemoteGATTCharacteristic implements CharacteristicEventHandlers; +[NoInterfaceObject] +interface CharacteristicEventHandlers { + attribute EventHandler oncharacteristicvaluechanged; +}; + +// BluetoothRemoteGATTCharacteristic implements EventTarget; +BluetoothRemoteGATTCharacteristic implements CharacteristicEventHandlers; diff --git a/components/script/dom/webidls/BluetoothRemoteGATTService.webidl b/components/script/dom/webidls/BluetoothRemoteGATTService.webidl index 715c2acbe1e..c39dcc1447a 100644 --- a/components/script/dom/webidls/BluetoothRemoteGATTService.webidl +++ b/components/script/dom/webidls/BluetoothRemoteGATTService.webidl @@ -5,7 +5,7 @@ // https://webbluetoothcg.github.io/web-bluetooth/#bluetoothremotegattservice [Pref="dom.bluetooth.enabled"] -interface BluetoothRemoteGATTService { +interface BluetoothRemoteGATTService : EventTarget { readonly attribute BluetoothDevice device; readonly attribute DOMString uuid; readonly attribute boolean isPrimary; @@ -15,3 +15,14 @@ interface BluetoothRemoteGATTService { Promise<BluetoothRemoteGATTService> getIncludedService(BluetoothServiceUUID service); Promise<sequence<BluetoothRemoteGATTService>> getIncludedServices(optional BluetoothServiceUUID service); }; + +[NoInterfaceObject] +interface ServiceEventHandlers { + attribute EventHandler onserviceadded; + attribute EventHandler onservicechanged; + attribute EventHandler onserviceremoved; +}; + +// BluetoothRemoteGATTService implements EventTarget; +// BluetoothRemoteGATTService implements CharacteristicEventHandlers; +BluetoothRemoteGATTService implements ServiceEventHandlers; diff --git a/components/script/dom/webidls/CSSMediaRule.webidl b/components/script/dom/webidls/CSSMediaRule.webidl index e2b89dee723..9ed133fb065 100644 --- a/components/script/dom/webidls/CSSMediaRule.webidl +++ b/components/script/dom/webidls/CSSMediaRule.webidl @@ -5,5 +5,5 @@ // https://drafts.csswg.org/cssom/#the-cssmediarule-interface [Exposed=Window] interface CSSMediaRule : CSSGroupingRule { - // [SameObject, PutForwards=mediaText] readonly attribute MediaList media; + [SameObject, PutForwards=mediaText] readonly attribute MediaList media; }; diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index 0f0b483d0b1..4314eca74f9 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -321,6 +321,14 @@ partial interface CSSStyleDeclaration { [SetterThrows, TreatNullAs=EmptyString] attribute DOMString right; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString left; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString bottom; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString offset-block-start; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString offsetBlockStart; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString offset-block-end; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString offsetBlockEnd; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString offset-inline-start; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString offsetInlineStart; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString offset-inline-end; + [SetterThrows, TreatNullAs=EmptyString] attribute DOMString offsetInlineEnd; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString height; [SetterThrows, TreatNullAs=EmptyString] attribute DOMString minHeight; diff --git a/components/script/dom/webidls/DOMMatrix.webidl b/components/script/dom/webidls/DOMMatrix.webidl index d8306d315fb..d1dd65e8a63 100644 --- a/components/script/dom/webidls/DOMMatrix.webidl +++ b/components/script/dom/webidls/DOMMatrix.webidl @@ -11,10 +11,9 @@ */ [Constructor, -// Constructor(DOMString transformList), -Constructor(sequence<unrestricted double> numberSequence) -// Exposed=(Window,Worker) -] + // Constructor(DOMString transformList), + Constructor(sequence<unrestricted double> numberSequence), + Exposed=(Window,Worker)] interface DOMMatrix : DOMMatrixReadOnly { [NewObject, Throws] static DOMMatrix fromMatrix(optional DOMMatrixInit other); diff --git a/components/script/dom/webidls/DOMMatrixReadOnly.webidl b/components/script/dom/webidls/DOMMatrixReadOnly.webidl index 6d92a6b0828..9261002e348 100644 --- a/components/script/dom/webidls/DOMMatrixReadOnly.webidl +++ b/components/script/dom/webidls/DOMMatrixReadOnly.webidl @@ -11,10 +11,9 @@ */ [Constructor, -// Constructor(DOMString transformList) -Constructor(sequence<unrestricted double> numberSequence), -// Exposed=(Window,Worker) -] + // Constructor(DOMString transformList) + Constructor(sequence<unrestricted double> numberSequence), + Exposed=(Window,Worker)] interface DOMMatrixReadOnly { [NewObject, Throws] static DOMMatrixReadOnly fromMatrix(optional DOMMatrixInit other); diff --git a/components/script/dom/webidls/Document.webidl b/components/script/dom/webidls/Document.webidl index 192c3d03714..1e8725d8b86 100644 --- a/components/script/dom/webidls/Document.webidl +++ b/components/script/dom/webidls/Document.webidl @@ -114,8 +114,10 @@ partial /*sealed*/ interface Document { // Document open(optional DOMString type = "text/html", optional DOMString replace = ""); // WindowProxy open(DOMString url, DOMString name, DOMString features, optional boolean replace = false); // void close(); - // void write(DOMString... text); - // void writeln(DOMString... text); + [Throws] + void write(DOMString... text); + [Throws] + void writeln(DOMString... text); // user interaction readonly attribute Window?/*Proxy?*/ defaultView; diff --git a/components/script/dom/webidls/MediaList.webidl b/components/script/dom/webidls/MediaList.webidl new file mode 100644 index 00000000000..f397badfd52 --- /dev/null +++ b/components/script/dom/webidls/MediaList.webidl @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// https://drafts.csswg.org/cssom/#the-medialist-interface +// [LegacyArrayClass] +interface MediaList { + [TreatNullAs=EmptyString] /* stringifier */ attribute DOMString mediaText; + readonly attribute unsigned long length; + getter DOMString? item(unsigned long index); + void appendMedium(DOMString medium); + void deleteMedium(DOMString medium); +}; diff --git a/components/script/dom/webidls/WebGLActiveInfo.webidl b/components/script/dom/webidls/WebGLActiveInfo.webidl index 7195e8bacaa..be9e6f4e2db 100644 --- a/components/script/dom/webidls/WebGLActiveInfo.webidl +++ b/components/script/dom/webidls/WebGLActiveInfo.webidl @@ -6,6 +6,7 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.7 // +[Exposed=Window] interface WebGLActiveInfo { readonly attribute GLint size; readonly attribute GLenum type; diff --git a/components/script/dom/webidls/WebGLBuffer.webidl b/components/script/dom/webidls/WebGLBuffer.webidl index 344850d42e6..ca2697bb9be 100644 --- a/components/script/dom/webidls/WebGLBuffer.webidl +++ b/components/script/dom/webidls/WebGLBuffer.webidl @@ -6,5 +6,6 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.4 // +[Exposed=Window] interface WebGLBuffer : WebGLObject { }; diff --git a/components/script/dom/webidls/WebGLContextEvent.webidl b/components/script/dom/webidls/WebGLContextEvent.webidl index 6a699754d2c..b5c70b8b17f 100644 --- a/components/script/dom/webidls/WebGLContextEvent.webidl +++ b/components/script/dom/webidls/WebGLContextEvent.webidl @@ -3,7 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15 -[Constructor(DOMString type, optional WebGLContextEventInit eventInit)] +[Constructor(DOMString type, optional WebGLContextEventInit eventInit), + Exposed=Window] interface WebGLContextEvent : Event { readonly attribute DOMString statusMessage; }; diff --git a/components/script/dom/webidls/WebGLFramebuffer.webidl b/components/script/dom/webidls/WebGLFramebuffer.webidl index 2f21edafc0a..306e2c479ed 100644 --- a/components/script/dom/webidls/WebGLFramebuffer.webidl +++ b/components/script/dom/webidls/WebGLFramebuffer.webidl @@ -6,5 +6,6 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.7 // +[Exposed=Window] interface WebGLFramebuffer : WebGLObject { }; diff --git a/components/script/dom/webidls/WebGLObject.webidl b/components/script/dom/webidls/WebGLObject.webidl index 040c76be0c7..3ac7514830a 100644 --- a/components/script/dom/webidls/WebGLObject.webidl +++ b/components/script/dom/webidls/WebGLObject.webidl @@ -6,5 +6,6 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.3 // +[Exposed=Window] interface WebGLObject { }; diff --git a/components/script/dom/webidls/WebGLProgram.webidl b/components/script/dom/webidls/WebGLProgram.webidl index 0c9ede907e0..2ee21b2a6a1 100644 --- a/components/script/dom/webidls/WebGLProgram.webidl +++ b/components/script/dom/webidls/WebGLProgram.webidl @@ -6,5 +6,6 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.6 // +[Exposed=Window] interface WebGLProgram : WebGLObject { }; diff --git a/components/script/dom/webidls/WebGLRenderbuffer.webidl b/components/script/dom/webidls/WebGLRenderbuffer.webidl index 451621ec30c..3024dc7513e 100644 --- a/components/script/dom/webidls/WebGLRenderbuffer.webidl +++ b/components/script/dom/webidls/WebGLRenderbuffer.webidl @@ -6,5 +6,6 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.5 // +[Exposed=Window] interface WebGLRenderbuffer : WebGLObject { }; diff --git a/components/script/dom/webidls/WebGLRenderingContext.webidl b/components/script/dom/webidls/WebGLRenderingContext.webidl index 3f20d89ce96..dd91d53d2d7 100644 --- a/components/script/dom/webidls/WebGLRenderingContext.webidl +++ b/components/script/dom/webidls/WebGLRenderingContext.webidl @@ -41,7 +41,7 @@ dictionary WebGLContextAttributes { GLboolean failIfMajorPerformanceCaveat = false; }; -[NoInterfaceObject] +[Exposed=Window, NoInterfaceObject] interface WebGLRenderingContextBase { @@ -762,6 +762,7 @@ interface WebGLRenderingContextBase void viewport(GLint x, GLint y, GLsizei width, GLsizei height); }; +[Exposed=Window] interface WebGLRenderingContext { }; diff --git a/components/script/dom/webidls/WebGLShader.webidl b/components/script/dom/webidls/WebGLShader.webidl index f160602cba7..671da6405ff 100644 --- a/components/script/dom/webidls/WebGLShader.webidl +++ b/components/script/dom/webidls/WebGLShader.webidl @@ -6,5 +6,6 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.8 // +[Exposed=Window] interface WebGLShader : WebGLObject { }; diff --git a/components/script/dom/webidls/WebGLShaderPrecisionFormat.webidl b/components/script/dom/webidls/WebGLShaderPrecisionFormat.webidl index 2d299232e90..eb7b1370b31 100644 --- a/components/script/dom/webidls/WebGLShaderPrecisionFormat.webidl +++ b/components/script/dom/webidls/WebGLShaderPrecisionFormat.webidl @@ -6,6 +6,7 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.7 // +[Exposed=Window] interface WebGLShaderPrecisionFormat { readonly attribute GLint rangeMin; readonly attribute GLint rangeMax; diff --git a/components/script/dom/webidls/WebGLTexture.webidl b/components/script/dom/webidls/WebGLTexture.webidl index f1b7fa20f1e..42313c98683 100644 --- a/components/script/dom/webidls/WebGLTexture.webidl +++ b/components/script/dom/webidls/WebGLTexture.webidl @@ -6,5 +6,6 @@ // https://www.khronos.org/registry/webgl/specs/latest/#5.9 // +[Exposed=Window] interface WebGLTexture : WebGLObject { }; diff --git a/components/script/dom/webidls/WebGLUniformLocation.webidl b/components/script/dom/webidls/WebGLUniformLocation.webidl index 467c2d00572..f068eead6e2 100644 --- a/components/script/dom/webidls/WebGLUniformLocation.webidl +++ b/components/script/dom/webidls/WebGLUniformLocation.webidl @@ -6,5 +6,6 @@ // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.10 // +[Exposed=Window] interface WebGLUniformLocation { }; diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 81004a9dbd2..7b140b14d8f 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -26,17 +26,17 @@ use fetch; use ipc_channel::ipc::IpcSender; use js::jsapi::{HandleValue, JSAutoCompartment, JSContext, JSRuntime}; use js::jsval::UndefinedValue; +use js::panic::maybe_resume_unwind; use js::rust::Runtime; use net_traits::{IpcSend, load_whole_resource}; use net_traits::request::{CredentialsMode, Destination, RequestInit as NetRequestInit, Type as RequestType}; -use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, maybe_take_panic_result}; +use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort}; use script_runtime::{ScriptThreadEventCategory, PromiseJobQueue, EnqueuedPromiseCallback}; use script_thread::{Runnable, RunnableWrapper}; use script_traits::{TimerEvent, TimerEventId}; use script_traits::WorkerGlobalScopeInit; use servo_url::ServoUrl; use std::default::Default; -use std::panic; use std::rc::Rc; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; @@ -232,9 +232,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { let result = self.runtime.evaluate_script( self.reflector().get_jsobject(), &source, url.as_str(), 1, rval.handle_mut()); - if let Some(error) = maybe_take_panic_result() { - panic::resume_unwind(error); - } + maybe_resume_unwind(); match result { Ok(_) => (), diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index a386d5c8563..8838787defc 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -175,14 +175,6 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { unsafe { self.get_jsmanaged().opaque() } } - fn layout_parent_element(self, reflow_root: OpaqueNode) -> Option<ServoLayoutElement<'ln>> { - if self.opaque() == reflow_root { - None - } else { - self.parent_node().and_then(|x| x.as_element()) - } - } - fn debug_id(self) -> usize { self.opaque().0 } diff --git a/components/script/lib.rs b/components/script/lib.rs index 0d726a723af..cf3aadf1658 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -74,6 +74,7 @@ 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; diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs index 603e24a1468..154353c71e8 100644 --- a/components/script/script_runtime.rs +++ b/components/script/script_runtime.rs @@ -20,17 +20,17 @@ use js::jsapi::{JSGCInvocationKind, JSGCStatus, JS_AddExtraGCRootsTracer, JS_Set use js::jsapi::{JSGCMode, JSGCParamKey, JS_SetGCParameter, JS_SetGlobalJitCompilerOption}; use js::jsapi::{JSJitCompilerOption, JS_SetOffthreadIonCompilationEnabled, JS_SetParallelParsingEnabled}; use js::jsapi::{JSObject, RuntimeOptionsRef, SetPreserveWrapperCallback, SetEnqueuePromiseJobCallback}; +use js::panic::wrap_panic; use js::rust::Runtime; use msg::constellation_msg::PipelineId; use profile_traits::mem::{Report, ReportKind, ReportsChan}; use script_thread::{Runnable, STACK_ROOTS, trace_thread}; -use std::any::Any; -use std::cell::{RefCell, Cell}; +use std::cell::Cell; use std::io::{Write, stdout}; use std::marker::PhantomData; use std::os; use std::os::raw::c_void; -use std::panic::{self, AssertUnwindSafe}; +use std::panic::AssertUnwindSafe; use std::ptr; use std::rc::Rc; use style::thread_state; @@ -176,7 +176,7 @@ unsafe extern "C" fn enqueue_job(_cx: *mut JSContext, job: HandleObject, _allocation_site: HandleObject, _data: *mut c_void) -> bool { - let result = panic::catch_unwind(AssertUnwindSafe(|| { + wrap_panic(AssertUnwindSafe(|| { let global = GlobalScope::from_object(job.get()); let pipeline = global.pipeline_id(); global.enqueue_promise_job(EnqueuedPromiseCallback { @@ -184,14 +184,7 @@ unsafe extern "C" fn enqueue_job(_cx: *mut JSContext, pipeline: pipeline, }); true - })); - match result { - Ok(result) => result, - Err(error) => { - store_panic_result(error); - return false; - } - } + }), false) } #[allow(unsafe_code)] @@ -421,21 +414,6 @@ pub fn get_reports(cx: *mut JSContext, path_seg: String) -> Vec<Report> { reports } -thread_local!(static PANIC_RESULT: RefCell<Option<Box<Any + Send>>> = RefCell::new(None)); - -pub fn store_panic_result(error: Box<Any + Send>) { - PANIC_RESULT.with(|result| { - assert!(result.borrow().is_none()); - *result.borrow_mut() = Some(error); - }); -} - -pub fn maybe_take_panic_result() -> Option<Box<Any + Send>> { - PANIC_RESULT.with(|result| { - result.borrow_mut().take() - }) -} - thread_local!(static GC_CYCLE_START: Cell<Option<Tm>> = Cell::new(None)); thread_local!(static GC_SLICE_START: Cell<Option<Tm>> = Cell::new(None)); diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index b902c6df101..983d6fc8e36 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -71,7 +71,8 @@ use js::rust::Runtime; use layout_wrapper::ServoLayoutNode; use mem::heap_size_of_self_and_children; use msg::constellation_msg::{FrameId, FrameType, PipelineId, PipelineNamespace}; -use net_traits::{CoreResourceMsg, IpcSend, Metadata, ReferrerPolicy, ResourceThreads}; +use net_traits::{CoreResourceMsg, FetchMetadata, FetchResponseListener}; +use net_traits::{IpcSend, Metadata, ReferrerPolicy, ResourceThreads}; use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCacheThread}; use net_traits::request::{CredentialsMode, Destination, RequestInit}; use net_traits::storage_thread::StorageType; @@ -82,7 +83,7 @@ use script_layout_interface::message::{self, NewLayoutThreadInfo, ReflowQueryTyp use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory, EnqueuedPromiseCallback}; use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx, PromiseJobQueue}; use script_traits::{CompositorEvent, ConstellationControlMsg, EventResult}; -use script_traits::{InitialScriptState, LoadData, MouseButton, MouseEventType, MozBrowserEvent}; +use script_traits::{InitialScriptState, LayoutMsg, LoadData, MouseButton, MouseEventType, MozBrowserEvent}; use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg}; use script_traits::{ScriptThreadFactory, TimerEvent, TimerEventRequest, TimerSource}; use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData, WindowSizeType}; @@ -434,6 +435,9 @@ pub struct ScriptThread { /// For communicating load url messages to the constellation constellation_chan: IpcSender<ConstellationMsg>, + /// A sender for new layout threads to communicate to the constellation. + layout_to_constellation_chan: IpcSender<LayoutMsg>, + /// The port on which we receive messages from the image cache image_cache_port: Receiver<ImageCacheResult>, @@ -602,6 +606,17 @@ impl ScriptThread { }); } + pub fn process_attach_layout(new_layout_info: NewLayoutInfo) { + SCRIPT_THREAD_ROOT.with(|root| { + if let Some(script_thread) = root.get() { + let script_thread = unsafe { &*script_thread }; + script_thread.profile_event(ScriptThreadEventCategory::AttachLayout, || { + script_thread.handle_new_layout(new_layout_info); + }) + } + }); + } + pub fn find_document(id: PipelineId) -> Option<Root<Document>> { SCRIPT_THREAD_ROOT.with(|root| root.get().and_then(|script_thread| { let script_thread = unsafe { &*script_thread }; @@ -681,6 +696,8 @@ impl ScriptThread { content_process_shutdown_chan: state.content_process_shutdown_chan, promise_job_queue: PromiseJobQueue::new(), + + layout_to_constellation_chan: state.layout_to_constellation_chan, } } @@ -1179,7 +1196,6 @@ impl ScriptThread { load_data, window_size, pipeline_port, - layout_to_constellation_chan, content_process_shutdown_chan, layout_threads, } = new_layout_info; @@ -1193,7 +1209,7 @@ impl ScriptThread { is_parent: false, layout_pair: layout_pair, pipeline_port: pipeline_port, - constellation_chan: layout_to_constellation_chan, + constellation_chan: self.layout_to_constellation_chan.clone(), script_chan: self.control_chan.clone(), image_cache_thread: self.image_cache_thread.clone(), content_process_shutdown_chan: content_process_shutdown_chan, @@ -1215,7 +1231,11 @@ impl ScriptThread { let new_load = InProgressLoad::new(new_pipeline_id, frame_id, parent_info, layout_chan, window_size, load_data.url.clone()); - self.start_page_load(new_load, load_data); + if load_data.url.as_str() == "about:blank" { + self.start_page_load_about_blank(new_load); + } else { + self.start_page_load(new_load, load_data); + } } fn handle_loads_complete(&self, pipeline: PipelineId) { @@ -1641,7 +1661,8 @@ impl ScriptThread { /// Notify the containing document of a child frame that has completed loading. fn handle_frame_load_event(&self, parent_id: PipelineId, frame_id: FrameId, child_id: PipelineId) { - match self.documents.borrow().find_iframe(parent_id, frame_id) { + let iframe = self.documents.borrow().find_iframe(parent_id, frame_id); + match iframe { Some(iframe) => iframe.iframe_load_event_steps(child_id), None => warn!("Message sent to closed pipeline {}.", parent_id), } @@ -1730,9 +1751,12 @@ impl ScriptThread { Some(incomplete.url.clone())); let is_html_document = match metadata.content_type { + Some(Serde(ContentType(Mime(TopLevel::Application, SubLevel::Ext(ref sub_level), _)))) + if sub_level.ends_with("+xml") => IsHTMLDocument::NonHTMLDocument, + Some(Serde(ContentType(Mime(TopLevel::Application, SubLevel::Xml, _)))) | - Some(Serde(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _)))) => - IsHTMLDocument::NonHTMLDocument, + Some(Serde(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _)))) => IsHTMLDocument::NonHTMLDocument, + _ => IsHTMLDocument::HTMLDocument, }; @@ -1823,17 +1847,7 @@ impl ScriptThread { document.set_https_state(metadata.https_state); - let is_xml = match metadata.content_type { - Some(Serde(ContentType(Mime(TopLevel::Application, SubLevel::Ext(ref sub_level), _)))) - if sub_level.ends_with("+xml") => true, - - Some(Serde(ContentType(Mime(TopLevel::Application, SubLevel::Xml, _)))) | - Some(Serde(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _)))) => true, - - _ => false, - }; - - if is_xml { + if is_html_document == IsHTMLDocument::NonHTMLDocument { ServoParser::parse_xml_document( &document, parse_input, @@ -2014,7 +2028,8 @@ impl ScriptThread { replace: bool) { match frame_id { Some(frame_id) => { - if let Some(iframe) = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id) { + let iframe = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id); + if let Some(iframe) = iframe { iframe.navigate_or_reload_child_browsing_context(Some(load_data), replace); } } @@ -2092,6 +2107,23 @@ impl ScriptThread { self.incomplete_loads.borrow_mut().push(incomplete); } + /// Synchronously fetch `about:blank`. Stores the `InProgressLoad` + /// argument until a notification is received that the fetch is complete. + fn start_page_load_about_blank(&self, incomplete: InProgressLoad) { + let id = incomplete.pipeline_id; + + self.incomplete_loads.borrow_mut().push(incomplete); + + let url = ServoUrl::parse("about:blank").unwrap(); + let mut context = ParserContext::new(id, url.clone()); + + let mut meta = Metadata::default(url); + meta.set_content_type(Some(&mime!(Text / Html))); + context.process_response(Ok(FetchMetadata::Unfiltered(meta))); + context.process_response_chunk(vec![]); + context.process_response_eof(Ok(())); + } + fn handle_parsing_complete(&self, id: PipelineId) { let document = match self.documents.borrow().find_document(id) { Some(document) => document, |