From 20b131af172ac07ebc817bc24b3601d27a939646 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 6 Sep 2016 15:31:43 +0200 Subject: [PATCH 1/5] Implement Borrow for DOMString Useful to use DOMString as a HashMap key type. --- components/script/dom/bindings/str.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs index c73a08d182b7..2e28a1b314e7 100644 --- a/components/script/dom/bindings/str.rs +++ b/components/script/dom/bindings/str.rs @@ -5,7 +5,7 @@ //! The `ByteString` struct. use std::ascii::AsciiExt; -use std::borrow::{ToOwned, Cow}; +use std::borrow::{Borrow, Cow, ToOwned}; use std::fmt; use std::hash::{Hash, Hasher}; use std::ops; @@ -180,6 +180,13 @@ impl DOMString { } } +impl Borrow for DOMString { + #[inline] + fn borrow(&self) -> &str { + &self.0 + } +} + impl Default for DOMString { fn default() -> Self { DOMString(String::new()) From 7a942b1742db82b6425edc2cb0579fa1a3b003ad Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 6 Sep 2016 15:32:09 +0200 Subject: [PATCH 2/5] Store Console timers in globals --- components/script/dom/bindings/global.rs | 9 ++++ components/script/dom/console.rs | 56 +++++++++++++++------- components/script/dom/window.rs | 13 ++++- components/script/dom/workerglobalscope.rs | 10 +++- 4 files changed, 67 insertions(+), 21 deletions(-) diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index 54efb11250d4..1eed5473a390 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -13,6 +13,7 @@ use dom::bindings::conversions::root_from_object; use dom::bindings::error::ErrorInfo; use dom::bindings::js::Root; use dom::bindings::reflector::{Reflectable, Reflector}; +use dom::console::TimerSet; use dom::window::{self, ScriptHelpers}; use dom::workerglobalscope::WorkerGlobalScope; use ipc_channel::ipc::IpcSender; @@ -271,6 +272,14 @@ impl<'a> GlobalRef<'a> { } } + /// Returns the global's timers for the Console API. + pub fn console_timers(&self) -> &TimerSet { + match *self { + GlobalRef::Window(ref window) => window.console_timers(), + GlobalRef::Worker(ref worker) => worker.console_timers(), + } + } + /// Returns a wrapper for runnables to ensure they are cancelled if the global /// is being destroyed. pub fn get_runnable_wrapper(&self) -> RunnableWrapper { diff --git a/components/script/dom/console.rs b/components/script/dom/console.rs index 9382bf936a83..252cf2435b8b 100644 --- a/components/script/dom/console.rs +++ b/components/script/dom/console.rs @@ -11,20 +11,19 @@ use dom::bindings::js::Root; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use std::collections::HashMap; +use std::collections::hash_map::Entry; use time::{Timespec, get_time}; // https://developer.mozilla.org/en-US/docs/Web/API/Console #[dom_struct] pub struct Console { reflector_: Reflector, - timers: DOMRefCell>, } impl Console { fn new_inherited() -> Console { Console { reflector_: Reflector::new(), - timers: DOMRefCell::new(HashMap::new()), } } @@ -100,28 +99,20 @@ impl ConsoleMethods for Console { // https://developer.mozilla.org/en-US/docs/Web/API/Console/time fn Time(&self, label: DOMString) { - let mut timers = self.timers.borrow_mut(); - if timers.contains_key(&label) { - // Timer already started - return; - } - if timers.len() >= 10000 { - // Too many timers on page - return; + let global = self.global(); + if let Ok(()) = global.r().console_timers().time(label.clone()) { + let message = DOMString::from(format!("{}: timer started", label)); + println!("{}", message); + self.send_to_devtools(LogLevel::Log, message); } - - timers.insert(label.clone(), timestamp_in_ms(get_time())); - let message = DOMString::from(format!("{}: timer started", label)); - println!("{}", message); - self.send_to_devtools(LogLevel::Log, message); } // https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd fn TimeEnd(&self, label: DOMString) { - let mut timers = self.timers.borrow_mut(); - if let Some(start) = timers.remove(&label) { + let global = self.global(); + if let Ok(delta) = global.r().console_timers().time_end(&label) { let message = DOMString::from( - format!("{}: {}ms", label, timestamp_in_ms(get_time()) - start) + format!("{}: {}ms", label, delta) ); println!("{}", message); self.send_to_devtools(LogLevel::Log, message); @@ -143,3 +134,32 @@ fn prepare_message(logLevel: LogLevel, message: DOMString) -> ConsoleMessage { columnNumber: 1, } } + +#[derive(HeapSizeOf, JSTraceable)] +pub struct TimerSet(DOMRefCell>); + +impl TimerSet { + pub fn new() -> Self { + TimerSet(DOMRefCell::new(Default::default())) + } + + fn time(&self, label: DOMString) -> Result<(), ()> { + let mut timers = self.0.borrow_mut(); + if timers.len() >= 10000 { + return Err(()); + } + match timers.entry(label) { + Entry::Vacant(entry) => { + entry.insert(timestamp_in_ms(get_time())); + Ok(()) + }, + Entry::Occupied(_) => Err(()), + } + } + + fn time_end(&self, label: &str) -> Result { + self.0.borrow_mut().remove(label).ok_or(()).map(|start| { + timestamp_in_ms(get_time()) - start + }) + } +} diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index d1529b44b19a..5d57cd679af9 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -25,7 +25,7 @@ use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; use dom::bindings::utils::{GlobalStaticData, WindowProxyHandler}; use dom::browsingcontext::BrowsingContext; -use dom::console::Console; +use dom::console::{Console, TimerSet}; use dom::crypto::Crypto; use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration}; use dom::document::Document; @@ -276,7 +276,10 @@ pub struct Window { scroll_offsets: DOMRefCell>>, /// https://html.spec.whatwg.org/multipage/#in-error-reporting-mode - in_error_reporting_mode: Cell + in_error_reporting_mode: Cell, + + /// Timers used by the Console API. + console_timers: TimerSet, } impl Window { @@ -1747,10 +1750,16 @@ impl Window { error_reporter: error_reporter, scroll_offsets: DOMRefCell::new(HashMap::new()), in_error_reporting_mode: Cell::new(false), + console_timers: TimerSet::new(), }; WindowBinding::Wrap(runtime.cx(), win) } + + pub fn console_timers(&self) -> &TimerSet { + &self.console_timers + } + pub fn live_devtools_updates(&self) -> bool { return self.devtools_wants_updates.get(); } diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 9f61753ca644..78fea9b50c8f 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -11,7 +11,7 @@ use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; -use dom::console::Console; +use dom::console::{Console, TimerSet}; use dom::crypto::Crypto; use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use dom::eventtarget::EventTarget; @@ -109,6 +109,9 @@ pub struct WorkerGlobalScope { #[ignore_heap_size_of = "Defined in std"] scheduler_chan: IpcSender, + + /// Timers used by the Console API. + console_timers: TimerSet, } impl WorkerGlobalScope { @@ -140,9 +143,14 @@ impl WorkerGlobalScope { devtools_wants_updates: Cell::new(false), constellation_chan: init.constellation_chan, scheduler_chan: init.scheduler_chan, + console_timers: TimerSet::new(), } } + pub fn console_timers(&self) -> &TimerSet { + &self.console_timers + } + pub fn mem_profiler_chan(&self) -> &mem::ProfilerChan { &self.mem_profiler_chan } From 8ba0cf30a169ec62efbdd8e8d8ae530f441f42f1 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 6 Sep 2016 16:19:19 +0200 Subject: [PATCH 3/5] Fix descriptor argument in CGCallGenerator.__init__ It's a descriptor, not a descriptor provider. --- components/script/dom/bindings/codegen/CodegenRust.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 71331dfe503f..dd6d6276bfdb 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -3045,7 +3045,7 @@ class CGCallGenerator(CGThing): exception from the native code, or None if no error reporting is needed. """ def __init__(self, errorResult, arguments, argsPre, returnType, - extendedAttributes, descriptorProvider, nativeMethodName, + extendedAttributes, descriptor, nativeMethodName, static, object="this"): CGThing.__init__(self) @@ -3053,7 +3053,7 @@ def __init__(self, errorResult, arguments, argsPre, returnType, isFallible = errorResult is not None - result = getRetvalDeclarationForType(returnType, descriptorProvider) + result = getRetvalDeclarationForType(returnType, descriptor) if isFallible: result = CGWrapper(result, pre="Result<", post=", Error>") @@ -3074,7 +3074,7 @@ def __init__(self, errorResult, arguments, argsPre, returnType, call = CGGeneric(nativeMethodName) if static: - call = CGWrapper(call, pre="%s::" % descriptorProvider.interface.identifier.name) + call = CGWrapper(call, pre="%s::" % descriptor.interface.identifier.name) else: call = CGWrapper(call, pre="%s." % object) call = CGList([call, CGWrapper(args, pre="(", post=")")]) From 0b689a8a312a2da095b2a8cfcc1bf827aac5d4ac Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Fri, 2 Sep 2016 17:26:54 +0200 Subject: [PATCH 4/5] Implement WebIDL namespaces --- .../dom/bindings/codegen/CodegenRust.py | 81 ++++++++++++++----- .../dom/bindings/codegen/Configuration.py | 22 +++-- components/script/dom/bindings/interface.rs | 7 +- components/script/dom/bindings/mod.rs | 1 + components/script/dom/bindings/namespace.rs | 42 ++++++++++ 5 files changed, 123 insertions(+), 30 deletions(-) create mode 100644 components/script/dom/bindings/namespace.rs diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index dd6d6276bfdb..3555073bea80 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -26,6 +26,7 @@ ) from Configuration import ( + MakeNativeName, MemberIsUnforgeable, getModuleFromObject, getTypesFromCallback, @@ -80,7 +81,7 @@ def toStringBool(arg): def toBindingNamespace(arg): - return re.sub("((_workers)?$)", "Binding\\1", arg) + return re.sub("((_workers)?$)", "Binding\\1", MakeNativeName(arg)) def stripTrailingWhitespace(text): @@ -96,9 +97,6 @@ def innerSequenceType(type): return type.inner.inner if type.nullable() else type.inner -def MakeNativeName(name): - return name[0].upper() + name[1:] - builtinNames = { IDLType.Tags.bool: 'bool', IDLType.Tags.int8: 'i8', @@ -1811,7 +1809,8 @@ def componentTypes(type): def isImportable(type): if not type.isType(): - assert type.isInterface() or type.isDictionary() or type.isEnum() + assert (type.isInterface() or type.isDictionary() or + type.isEnum() or type.isNamespace()) return True return not (type.builtin or type.isSequence() or type.isUnion()) @@ -1830,7 +1829,7 @@ def getIdentifier(t): if t.isCallback(): return t.callback.identifier return t.identifier - assert t.isInterface() or t.isDictionary() or t.isEnum() + assert t.isInterface() or t.isDictionary() or t.isEnum() or t.isNamespace() return t.identifier def removeWrapperAndNullableTypes(types): @@ -1881,7 +1880,7 @@ def removeWrapperAndNullableTypes(types): # Importing these types in the same module that defines them is an error. if t in dictionaries or t in enums: continue - if t.isInterface(): + if t.isInterface() or t.isNamespace(): descriptor = descriptorProvider.getDescriptor(getIdentifier(t).name) extras += [descriptor.path] if descriptor.interface.parent: @@ -2060,6 +2059,17 @@ def __init__(self, descriptor): self.descriptor = descriptor def define(self): + if self.descriptor.interface.isNamespace(): + classString = self.descriptor.interface.getExtendedAttribute("ClassString") + if classString: + classString = classString[0] + else: + classString = "Object" + return """\ +static NAMESPACE_OBJECT_CLASS: NamespaceObjectClass = unsafe { + NamespaceObjectClass::new(%s) +}; +""" % str_to_const_array(classString) if self.descriptor.interface.ctor(): constructorBehavior = "InterfaceConstructorBehavior::call(%s)" % CONSTRUCT_HOOK_NAME else: @@ -2657,6 +2667,28 @@ def __init__(self, descriptor, properties, haveUnscopables): def definition_body(self): name = self.descriptor.interface.identifier.name + if self.descriptor.interface.isNamespace(): + if self.descriptor.interface.getExtendedAttribute("ProtoObjectHack"): + proto = "JS_GetObjectPrototype(cx, global)" + else: + proto = "JS_NewPlainObject(cx)" + if self.properties.static_methods.length(): + methods = self.properties.static_methods.variableName() + else: + methods = "&[]" + return CGGeneric("""\ +rooted!(in(cx) let proto = %(proto)s); +assert!(!proto.is_null()); +rooted!(in(cx) let mut namespace = ptr::null_mut()); +create_namespace_object(cx, global, proto.handle(), &NAMESPACE_OBJECT_CLASS, + %(methods)s, %(name)s, namespace.handle_mut()); +assert!(!namespace.is_null()); +assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null()); +(*cache)[PrototypeList::Constructor::%(id)s as usize] = namespace.get(); +<*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize), + ptr::null_mut(), + namespace.get()); +""" % {"id": MakeNativeName(name), "methods": methods, "name": str_to_const_array(name), "proto": proto}) if self.descriptor.interface.isCallback(): assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants() return CGGeneric("""\ @@ -2871,7 +2903,7 @@ def __init__(self, descriptor, name, idPrefix="", pub=False): Argument('MutableHandleObject', 'rval')] CGAbstractMethod.__init__(self, descriptor, name, 'void', args, pub=pub, unsafe=True) - self.id = idPrefix + "::" + self.descriptor.name + self.id = idPrefix + "::" + MakeNativeName(self.descriptor.name) def definition_body(self): return CGGeneric(""" @@ -3014,7 +3046,7 @@ def define(self): return CGAbstractMethod.define(self) def definition_body(self): - if self.descriptor.interface.isCallback(): + if self.descriptor.interface.isCallback() or self.descriptor.interface.isNamespace(): function = "GetConstructorObject" else: function = "GetProtoObject" @@ -3074,7 +3106,7 @@ def __init__(self, errorResult, arguments, argsPre, returnType, call = CGGeneric(nativeMethodName) if static: - call = CGWrapper(call, pre="%s::" % descriptor.interface.identifier.name) + call = CGWrapper(call, pre="%s::" % MakeNativeName(descriptor.interface.identifier.name)) else: call = CGWrapper(call, pre="%s." % object) call = CGList([call, CGWrapper(args, pre="(", post=")")]) @@ -5452,6 +5484,8 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'dom::bindings::js::OptionalRootedReference', 'dom::bindings::js::Root', 'dom::bindings::js::RootedReference', + 'dom::bindings::namespace::NamespaceObjectClass', + 'dom::bindings::namespace::create_namespace_object', 'dom::bindings::reflector::MutReflectable', 'dom::bindings::reflector::Reflectable', 'dom::bindings::utils::DOMClass', @@ -5559,7 +5593,7 @@ def reexportedName(name): return name cgThings = [] - if not descriptor.interface.isCallback(): + if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace(): cgThings.append(CGGetProtoObjectMethod(descriptor)) reexports.append('GetProtoObject') if (descriptor.interface.hasInterfaceObject() and @@ -5620,7 +5654,7 @@ def reexportedName(name): if not descriptor.interface.isCallback(): cgThings.append(CGInterfaceObjectJSClass(descriptor)) - if not descriptor.interface.isCallback(): + if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace(): cgThings.append(CGPrototypeJSClass(descriptor)) # If there are no constant members, don't make a module for constants @@ -5677,7 +5711,7 @@ def reexportedName(name): reexports.append('Wrap') haveUnscopables = False - if not descriptor.interface.isCallback(): + if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace(): if unscopableNames: haveUnscopables = True cgThings.append( @@ -5704,7 +5738,7 @@ def reexportedName(name): cgThings, public=True), post='\n') reexports = ', '.join(map(lambda name: reexportedName(name), reexports)) - self.cgRoot = CGList([CGGeneric('pub use self::%sBinding::{%s};' % (descriptor.name, reexports)), + self.cgRoot = CGList([CGGeneric('pub use self::%s::{%s};' % (toBindingNamespace(descriptor.name), reexports)), cgThings], '\n') def define(self): @@ -6758,10 +6792,12 @@ def InterfaceObjectMap(config): @staticmethod def PrototypeList(config): # Prototype ID enum. - interfaces = config.getDescriptors(isCallback=False) + interfaces = config.getDescriptors(isCallback=False, isNamespace=False) protos = [d.name for d in interfaces] - constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True) - if d.shouldHaveGetConstructorObjectMethod()] + constructors = sorted([MakeNativeName(d.name) + for d in config.getDescriptors(hasInterfaceObject=True) + if d.shouldHaveGetConstructorObjectMethod()]) + proxies = [d.name for d in config.getDescriptors(proxy=True)] return CGList([ @@ -6798,10 +6834,13 @@ def RegisterBindings(config): @staticmethod def InterfaceTypes(config): - descriptors = [d.name for d in config.getDescriptors(register=True, + descriptors = sorted([MakeNativeName(d.name) + for d in config.getDescriptors(register=True, isCallback=False, - isIteratorInterface=False)] - curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), name)) for name in descriptors]) + isIteratorInterface=False)]) + curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), + MakeNativeName(name))) + for name in descriptors]) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) return curr @@ -6812,7 +6851,7 @@ def leafModule(d): return getModuleFromObject(d).split('::')[-1] descriptors = config.getDescriptors(register=True, isIteratorInterface=False) - descriptors = (set(d.name + "Binding" for d in descriptors) | + descriptors = (set(toBindingNamespace(d.name) for d in descriptors) | set(leafModule(d) for d in config.callbacks) | set(leafModule(d) for d in config.getDictionaries())) curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)]) diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py index c8f924726181..6e71bd4bd00f 100644 --- a/components/script/dom/bindings/codegen/Configuration.py +++ b/components/script/dom/bindings/codegen/Configuration.py @@ -4,7 +4,7 @@ import os -from WebIDL import IDLExternalInterface, IDLInterface, IDLWrapperType, WebIDLError +from WebIDL import IDLExternalInterface, IDLWrapperType, WebIDLError class Configuration: @@ -30,10 +30,9 @@ def __init__(self, filename, parseData): raise WebIDLError("Servo does not support external interfaces.", [thing.location]) - # Some toplevel things are sadly types, and those have an - # isInterface that doesn't mean the same thing as IDLObject's - # isInterface()... - if not isinstance(thing, IDLInterface): + assert not thing.isType() + + if not thing.isInterface() and not thing.isNamespace(): continue iface = thing @@ -83,6 +82,8 @@ def getDescriptors(self, **filters): getter = lambda x: x.interface.hasInterfaceObject() elif key == 'isCallback': getter = lambda x: x.interface.isCallback() + elif key == 'isNamespace': + getter = lambda x: x.interface.isNamespace() elif key == 'isJSImplemented': getter = lambda x: x.interface.isJSImplemented() elif key == 'isGlobal': @@ -210,7 +211,7 @@ def __init__(self, config, interface, desc): if self.interface.isIteratorInterface(): pathDefault = 'dom::bindings::iterable::IterableIterator' else: - pathDefault = 'dom::types::%s' % typeName + pathDefault = 'dom::types::%s' % MakeNativeName(typeName) self.concreteType = typeName self.register = desc.get('register', True) @@ -223,6 +224,7 @@ def __init__(self, config, interface, desc): # If we're concrete, we need to crawl our ancestor interfaces and mark # them as having a concrete descendant. self.concrete = (not self.interface.isCallback() and + not self.interface.isNamespace() and not self.interface.getExtendedAttribute("Abstract")) self.hasUnforgeableMembers = (self.concrete and any(MemberIsUnforgeable(m, self) for m in @@ -381,7 +383,7 @@ def hasDescendants(self): def shouldHaveGetConstructorObjectMethod(self): assert self.interface.hasInterfaceObject() - return self.interface.isCallback() or self.hasDescendants() + return self.interface.isCallback() or self.interface.isNamespace() or self.hasDescendants() def isExposedConditionally(self): return self.interface.isExposedConditionally() @@ -396,6 +398,12 @@ def isGlobal(self): # Some utility methods + + +def MakeNativeName(name): + return name[0].upper() + name[1:] + + def getModuleFromObject(object): return ('dom::bindings::codegen::Bindings::' + os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding') diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs index f7786163cc58..cae5da97887d 100644 --- a/components/script/dom/bindings/interface.rs +++ b/components/script/dom/bindings/interface.rs @@ -257,7 +257,8 @@ pub unsafe fn create_named_constructors( } } -unsafe fn create_object( +/// Create a new object with a unique type. +pub unsafe fn create_object( cx: *mut JSContext, proto: HandleObject, class: &'static JSClass, @@ -316,7 +317,9 @@ pub unsafe fn is_exposed_in(object: HandleObject, globals: Globals) -> bool { globals.contains(dom_class.global) } -unsafe fn define_on_global_object( +/// Define a property with a given name on the global object. Should be called +/// through the resolve hook. +pub unsafe fn define_on_global_object( cx: *mut JSContext, global: HandleObject, name: &[u8], diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index 8aefc71d5774..16607c9045f1 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -140,6 +140,7 @@ pub mod inheritance; pub mod interface; pub mod iterable; pub mod js; +pub mod namespace; pub mod num; pub mod proxyhandler; pub mod refcounted; diff --git a/components/script/dom/bindings/namespace.rs b/components/script/dom/bindings/namespace.rs new file mode 100644 index 000000000000..38055dea1800 --- /dev/null +++ b/components/script/dom/bindings/namespace.rs @@ -0,0 +1,42 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! Machinery to initialise namespace objects. + +use dom::bindings::guard::Guard; +use dom::bindings::interface::{create_object, define_on_global_object}; +use js::jsapi::{HandleObject, JSClass, JSContext, JSFunctionSpec, MutableHandleObject}; +use libc; +use std::ptr; + +/// The class of a namespace object. +#[derive(Copy, Clone)] +pub struct NamespaceObjectClass(JSClass); + +unsafe impl Sync for NamespaceObjectClass {} + +impl NamespaceObjectClass { + /// Create a new `NamespaceObjectClass` structure. + pub const unsafe fn new(name: &'static [u8]) -> Self { + NamespaceObjectClass(JSClass { + name: name as *const _ as *const libc::c_char, + flags: 0, + cOps: ptr::null_mut(), + reserved: [ptr::null_mut(); 3], + }) + } +} + +/// Create a new namespace object. +pub unsafe fn create_namespace_object( + cx: *mut JSContext, + global: HandleObject, + proto: HandleObject, + class: &'static NamespaceObjectClass, + methods: &[Guard<&'static [JSFunctionSpec]>], + name: &[u8], + rval: MutableHandleObject) { + create_object(cx, proto, &class.0, methods, &[], &[], rval); + define_on_global_object(cx, global, name, rval.handle()); +} From 2bc0862f47a937eaf96aec929e884bddd23c6447 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Tue, 6 Sep 2016 15:47:53 +0200 Subject: [PATCH 5/5] Make console a namespace (fixes #13010) --- components/script/dom/console.rs | 65 ++++++------------- components/script/dom/webidls/Console.webidl | 6 +- components/script/dom/webidls/Window.webidl | 1 - .../dom/webidls/WorkerGlobalScope.webidl | 7 -- components/script/dom/window.rs | 9 +-- components/script/dom/workerglobalscope.rs | 9 +-- .../wpt/mozilla/tests/mozilla/interfaces.html | 2 +- tests/wpt/mozilla/tests/mozilla/interfaces.js | 2 +- .../tests/mozilla/interfaces.worker.js | 2 +- 9 files changed, 30 insertions(+), 73 deletions(-) diff --git a/components/script/dom/console.rs b/components/script/dom/console.rs index 252cf2435b8b..37d549befd91 100644 --- a/components/script/dom/console.rs +++ b/components/script/dom/console.rs @@ -4,38 +4,17 @@ use devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg}; use dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::ConsoleBinding; -use dom::bindings::codegen::Bindings::ConsoleBinding::ConsoleMethods; use dom::bindings::global::GlobalRef; -use dom::bindings::js::Root; -use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use std::collections::HashMap; use std::collections::hash_map::Entry; use time::{Timespec, get_time}; // https://developer.mozilla.org/en-US/docs/Web/API/Console -#[dom_struct] -pub struct Console { - reflector_: Reflector, -} +pub struct Console(()); impl Console { - fn new_inherited() -> Console { - Console { - reflector_: Reflector::new(), - } - } - - pub fn new(global: GlobalRef) -> Root { - reflect_dom_object(box Console::new_inherited(), - global, - ConsoleBinding::Wrap) - } - - fn send_to_devtools(&self, level: LogLevel, message: DOMString) { - let global = self.global(); - let global = global.r(); + fn send_to_devtools(global: GlobalRef, level: LogLevel, message: DOMString) { if let Some(chan) = global.devtools_chan() { let console_message = prepare_message(level, message); let devtools_message = ScriptToDevtoolsControlMsg::ConsoleAPI( @@ -47,75 +26,73 @@ impl Console { } } -impl ConsoleMethods for Console { +impl Console { // https://developer.mozilla.org/en-US/docs/Web/API/Console/log - fn Log(&self, messages: Vec) { + pub fn Log(global: GlobalRef, messages: Vec) { for message in messages { println!("{}", message); - self.send_to_devtools(LogLevel::Log, message); + Self::send_to_devtools(global, LogLevel::Log, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console - fn Debug(&self, messages: Vec) { + pub fn Debug(global: GlobalRef, messages: Vec) { for message in messages { println!("{}", message); - self.send_to_devtools(LogLevel::Debug, message); + Self::send_to_devtools(global, LogLevel::Debug, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/info - fn Info(&self, messages: Vec) { + pub fn Info(global: GlobalRef, messages: Vec) { for message in messages { println!("{}", message); - self.send_to_devtools(LogLevel::Info, message); + Self::send_to_devtools(global, LogLevel::Info, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/warn - fn Warn(&self, messages: Vec) { + pub fn Warn(global: GlobalRef, messages: Vec) { for message in messages { println!("{}", message); - self.send_to_devtools(LogLevel::Warn, message); + Self::send_to_devtools(global, LogLevel::Warn, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/error - fn Error(&self, messages: Vec) { + pub fn Error(global: GlobalRef, messages: Vec) { for message in messages { println!("{}", message); - self.send_to_devtools(LogLevel::Error, message); + Self::send_to_devtools(global, LogLevel::Error, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/assert - fn Assert(&self, condition: bool, message: Option) { + pub fn Assert(global: GlobalRef, condition: bool, message: Option) { if !condition { let message = message.unwrap_or_else(|| DOMString::from("no message")); println!("Assertion failed: {}", message); - self.send_to_devtools(LogLevel::Error, message); + Self::send_to_devtools(global, LogLevel::Error, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/time - fn Time(&self, label: DOMString) { - let global = self.global(); - if let Ok(()) = global.r().console_timers().time(label.clone()) { + pub fn Time(global: GlobalRef, label: DOMString) { + if let Ok(()) = global.console_timers().time(label.clone()) { let message = DOMString::from(format!("{}: timer started", label)); println!("{}", message); - self.send_to_devtools(LogLevel::Log, message); + Self::send_to_devtools(global, LogLevel::Log, message); } } // https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd - fn TimeEnd(&self, label: DOMString) { - let global = self.global(); - if let Ok(delta) = global.r().console_timers().time_end(&label) { + pub fn TimeEnd(global: GlobalRef, label: DOMString) { + if let Ok(delta) = global.console_timers().time_end(&label) { let message = DOMString::from( format!("{}: {}ms", label, delta) ); println!("{}", message); - self.send_to_devtools(LogLevel::Log, message); + Self::send_to_devtools(global, LogLevel::Log, message); }; } } diff --git a/components/script/dom/webidls/Console.webidl b/components/script/dom/webidls/Console.webidl index 09273db78b89..90f9bb9f58ee 100644 --- a/components/script/dom/webidls/Console.webidl +++ b/components/script/dom/webidls/Console.webidl @@ -9,8 +9,10 @@ * © Copyright 2014 Mozilla Foundation. */ -[Exposed=(Window,Worker)] -interface Console { +[ClassString="Console", + Exposed=(Window,Worker), + ProtoObjectHack] +namespace console { // These should be DOMString message, DOMString message2, ... void log(DOMString... messages); void debug(DOMString... messages); diff --git a/components/script/dom/webidls/Window.webidl b/components/script/dom/webidls/Window.webidl index dcf79a61c7ec..dbe73ca48314 100644 --- a/components/script/dom/webidls/Window.webidl +++ b/components/script/dom/webidls/Window.webidl @@ -161,7 +161,6 @@ partial interface Window { // Proprietary extensions. partial interface Window { - readonly attribute Console console; void debug(DOMString arg); void gc(); void trap(); diff --git a/components/script/dom/webidls/WorkerGlobalScope.webidl b/components/script/dom/webidls/WorkerGlobalScope.webidl index ec65bcb09a65..d7c403e51760 100644 --- a/components/script/dom/webidls/WorkerGlobalScope.webidl +++ b/components/script/dom/webidls/WorkerGlobalScope.webidl @@ -24,10 +24,3 @@ partial interface WorkerGlobalScope { // not obsolete }; WorkerGlobalScope implements WindowTimers; WorkerGlobalScope implements WindowBase64; - -// Proprietary -[Exposed=Worker] -partial interface WorkerGlobalScope { - [Replaceable] - readonly attribute Console console; -}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 5d57cd679af9..fa2bd072910a 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -25,7 +25,7 @@ use dom::bindings::str::DOMString; use dom::bindings::structuredclone::StructuredCloneData; use dom::bindings::utils::{GlobalStaticData, WindowProxyHandler}; use dom::browsingcontext::BrowsingContext; -use dom::console::{Console, TimerSet}; +use dom::console::TimerSet; use dom::crypto::Crypto; use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration}; use dom::document::Document; @@ -157,7 +157,6 @@ pub struct Window { history_traversal_task_source: HistoryTraversalTaskSource, #[ignore_heap_size_of = "task sources are hard"] file_reading_task_source: FileReadingTaskSource, - console: MutNullableHeap>, crypto: MutNullableHeap>, navigator: MutNullableHeap>, #[ignore_heap_size_of = "channels are hard"] @@ -511,11 +510,6 @@ impl WindowMethods for Window { self.local_storage.or_init(|| Storage::new(&GlobalRef::Window(self), StorageType::Local)) } - // https://developer.mozilla.org/en-US/docs/Web/API/Console - fn Console(&self) -> Root { - self.console.or_init(|| Console::new(GlobalRef::Window(self))) - } - // https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-GlobalCrypto fn Crypto(&self) -> Root { self.crypto.or_init(|| Crypto::new(GlobalRef::Window(self))) @@ -1704,7 +1698,6 @@ impl Window { history_traversal_task_source: history_task_source, file_reading_task_source: file_task_source, image_cache_chan: image_cache_chan, - console: Default::default(), crypto: Default::default(), navigator: Default::default(), image_cache_thread: image_cache_thread, diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index 78fea9b50c8f..2bd8a0b083b2 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -11,7 +11,7 @@ use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::reflector::Reflectable; use dom::bindings::str::DOMString; -use dom::console::{Console, TimerSet}; +use dom::console::TimerSet; use dom::crypto::Crypto; use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use dom::eventtarget::EventTarget; @@ -79,7 +79,6 @@ pub struct WorkerGlobalScope { resource_threads: ResourceThreads, location: MutNullableHeap>, navigator: MutNullableHeap>, - console: MutNullableHeap>, crypto: MutNullableHeap>, timers: OneshotTimers, @@ -132,7 +131,6 @@ impl WorkerGlobalScope { resource_threads: init.resource_threads, location: Default::default(), navigator: Default::default(), - console: Default::default(), crypto: Default::default(), timers: OneshotTimers::new(timer_event_chan, init.scheduler_chan.clone()), mem_profiler_chan: init.mem_profiler_chan, @@ -303,11 +301,6 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope { self.navigator.or_init(|| WorkerNavigator::new(self)) } - // https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/console - fn Console(&self) -> Root { - self.console.or_init(|| Console::new(GlobalRef::Worker(self))) - } - // https://html.spec.whatwg.org/multipage/#dfn-Crypto fn Crypto(&self) -> Root { self.crypto.or_init(|| Crypto::new(GlobalRef::Worker(self))) diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index 89f844292898..de27752caaff 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -27,7 +27,6 @@ "DOMRect", "DOMRectReadOnly", "Comment", - "Console", "CustomEvent", "Document", "DocumentFragment", @@ -183,5 +182,6 @@ "XMLHttpRequest", "XMLHttpRequestEventTarget", "XMLHttpRequestUpload", + "console", ]); diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.js b/tests/wpt/mozilla/tests/mozilla/interfaces.js index 4e1ecf8b9183..7501325dd94d 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.js @@ -65,7 +65,7 @@ function test_interfaces(interfaceNamesInGlobalScope) { } for (var name of Object.getOwnPropertyNames(self)) { - if (!/^[A-Z]/.test(name)) { + if (!/^[A-Z]/.test(name) && name != 'console') { continue; } assert_true(name in interfaceMap, diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js index cd70470a1204..c8ee191bc7b0 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js @@ -23,7 +23,6 @@ test_interfaces([ "DOMRect", "DOMRectReadOnly", "Comment", - "Console", "CustomEvent", "DedicatedWorkerGlobalScope", "Document", @@ -128,6 +127,7 @@ test_interfaces([ "XMLHttpRequest", "XMLHttpRequestEventTarget", "XMLHttpRequestUpload", + "console", ]); done();