Auto merge of #13185 - nox:namespaces, r=jdm,Ms2ger

Make console a namespace (fixes #13010)

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13185)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-09-10 07:33:25 -05:00 committed by GitHub
commit 84f3cf22bf
16 changed files with 223 additions and 120 deletions

View file

@ -26,6 +26,7 @@ from WebIDL import (
) )
from Configuration import ( from Configuration import (
MakeNativeName,
MemberIsUnforgeable, MemberIsUnforgeable,
getModuleFromObject, getModuleFromObject,
getTypesFromCallback, getTypesFromCallback,
@ -80,7 +81,7 @@ def toStringBool(arg):
def toBindingNamespace(arg): def toBindingNamespace(arg):
return re.sub("((_workers)?$)", "Binding\\1", arg) return re.sub("((_workers)?$)", "Binding\\1", MakeNativeName(arg))
def stripTrailingWhitespace(text): def stripTrailingWhitespace(text):
@ -96,9 +97,6 @@ def innerSequenceType(type):
return type.inner.inner if type.nullable() else type.inner return type.inner.inner if type.nullable() else type.inner
def MakeNativeName(name):
return name[0].upper() + name[1:]
builtinNames = { builtinNames = {
IDLType.Tags.bool: 'bool', IDLType.Tags.bool: 'bool',
IDLType.Tags.int8: 'i8', IDLType.Tags.int8: 'i8',
@ -1811,7 +1809,8 @@ class CGImports(CGWrapper):
def isImportable(type): def isImportable(type):
if not type.isType(): 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 True
return not (type.builtin or type.isSequence() or type.isUnion()) return not (type.builtin or type.isSequence() or type.isUnion())
@ -1830,7 +1829,7 @@ class CGImports(CGWrapper):
if t.isCallback(): if t.isCallback():
return t.callback.identifier return t.callback.identifier
return t.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 return t.identifier
def removeWrapperAndNullableTypes(types): def removeWrapperAndNullableTypes(types):
@ -1881,7 +1880,7 @@ class CGImports(CGWrapper):
# Importing these types in the same module that defines them is an error. # Importing these types in the same module that defines them is an error.
if t in dictionaries or t in enums: if t in dictionaries or t in enums:
continue continue
if t.isInterface(): if t.isInterface() or t.isNamespace():
descriptor = descriptorProvider.getDescriptor(getIdentifier(t).name) descriptor = descriptorProvider.getDescriptor(getIdentifier(t).name)
extras += [descriptor.path] extras += [descriptor.path]
if descriptor.interface.parent: if descriptor.interface.parent:
@ -2060,6 +2059,17 @@ class CGInterfaceObjectJSClass(CGThing):
self.descriptor = descriptor self.descriptor = descriptor
def define(self): 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(): if self.descriptor.interface.ctor():
constructorBehavior = "InterfaceConstructorBehavior::call(%s)" % CONSTRUCT_HOOK_NAME constructorBehavior = "InterfaceConstructorBehavior::call(%s)" % CONSTRUCT_HOOK_NAME
else: else:
@ -2659,6 +2669,28 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
def definition_body(self): def definition_body(self):
name = self.descriptor.interface.identifier.name 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(): if self.descriptor.interface.isCallback():
assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants() assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants()
return CGGeneric("""\ return CGGeneric("""\
@ -2873,7 +2905,7 @@ class CGGetPerInterfaceObject(CGAbstractMethod):
Argument('MutableHandleObject', 'rval')] Argument('MutableHandleObject', 'rval')]
CGAbstractMethod.__init__(self, descriptor, name, CGAbstractMethod.__init__(self, descriptor, name,
'void', args, pub=pub, unsafe=True) 'void', args, pub=pub, unsafe=True)
self.id = idPrefix + "::" + self.descriptor.name self.id = idPrefix + "::" + MakeNativeName(self.descriptor.name)
def definition_body(self): def definition_body(self):
return CGGeneric(""" return CGGeneric("""
@ -3016,7 +3048,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
return CGAbstractMethod.define(self) return CGAbstractMethod.define(self)
def definition_body(self): def definition_body(self):
if self.descriptor.interface.isCallback(): if self.descriptor.interface.isCallback() or self.descriptor.interface.isNamespace():
function = "GetConstructorObject" function = "GetConstructorObject"
else: else:
function = "GetProtoObject" function = "GetProtoObject"
@ -3047,7 +3079,7 @@ class CGCallGenerator(CGThing):
exception from the native code, or None if no error reporting is needed. exception from the native code, or None if no error reporting is needed.
""" """
def __init__(self, errorResult, arguments, argsPre, returnType, def __init__(self, errorResult, arguments, argsPre, returnType,
extendedAttributes, descriptorProvider, nativeMethodName, extendedAttributes, descriptor, nativeMethodName,
static, object="this"): static, object="this"):
CGThing.__init__(self) CGThing.__init__(self)
@ -3055,7 +3087,7 @@ class CGCallGenerator(CGThing):
isFallible = errorResult is not None isFallible = errorResult is not None
result = getRetvalDeclarationForType(returnType, descriptorProvider) result = getRetvalDeclarationForType(returnType, descriptor)
if isFallible: if isFallible:
result = CGWrapper(result, pre="Result<", post=", Error>") result = CGWrapper(result, pre="Result<", post=", Error>")
@ -3076,7 +3108,7 @@ class CGCallGenerator(CGThing):
call = CGGeneric(nativeMethodName) call = CGGeneric(nativeMethodName)
if static: if static:
call = CGWrapper(call, pre="%s::" % descriptorProvider.interface.identifier.name) call = CGWrapper(call, pre="%s::" % MakeNativeName(descriptor.interface.identifier.name))
else: else:
call = CGWrapper(call, pre="%s." % object) call = CGWrapper(call, pre="%s." % object)
call = CGList([call, CGWrapper(args, pre="(", post=")")]) call = CGList([call, CGWrapper(args, pre="(", post=")")])
@ -5454,6 +5486,8 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'dom::bindings::js::OptionalRootedReference', 'dom::bindings::js::OptionalRootedReference',
'dom::bindings::js::Root', 'dom::bindings::js::Root',
'dom::bindings::js::RootedReference', 'dom::bindings::js::RootedReference',
'dom::bindings::namespace::NamespaceObjectClass',
'dom::bindings::namespace::create_namespace_object',
'dom::bindings::reflector::MutReflectable', 'dom::bindings::reflector::MutReflectable',
'dom::bindings::reflector::Reflectable', 'dom::bindings::reflector::Reflectable',
'dom::bindings::utils::DOMClass', 'dom::bindings::utils::DOMClass',
@ -5561,7 +5595,7 @@ class CGDescriptor(CGThing):
return name return name
cgThings = [] cgThings = []
if not descriptor.interface.isCallback(): if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace():
cgThings.append(CGGetProtoObjectMethod(descriptor)) cgThings.append(CGGetProtoObjectMethod(descriptor))
reexports.append('GetProtoObject') reexports.append('GetProtoObject')
if (descriptor.interface.hasInterfaceObject() and if (descriptor.interface.hasInterfaceObject() and
@ -5622,7 +5656,7 @@ class CGDescriptor(CGThing):
if not descriptor.interface.isCallback(): if not descriptor.interface.isCallback():
cgThings.append(CGInterfaceObjectJSClass(descriptor)) cgThings.append(CGInterfaceObjectJSClass(descriptor))
if not descriptor.interface.isCallback(): if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace():
cgThings.append(CGPrototypeJSClass(descriptor)) cgThings.append(CGPrototypeJSClass(descriptor))
# If there are no constant members, don't make a module for constants # If there are no constant members, don't make a module for constants
@ -5679,7 +5713,7 @@ class CGDescriptor(CGThing):
reexports.append('Wrap') reexports.append('Wrap')
haveUnscopables = False haveUnscopables = False
if not descriptor.interface.isCallback(): if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace():
if unscopableNames: if unscopableNames:
haveUnscopables = True haveUnscopables = True
cgThings.append( cgThings.append(
@ -5706,7 +5740,7 @@ class CGDescriptor(CGThing):
cgThings, public=True), cgThings, public=True),
post='\n') post='\n')
reexports = ', '.join(map(lambda name: reexportedName(name), reexports)) 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') cgThings], '\n')
def define(self): def define(self):
@ -6760,10 +6794,12 @@ class GlobalGenRoots():
@staticmethod @staticmethod
def PrototypeList(config): def PrototypeList(config):
# Prototype ID enum. # Prototype ID enum.
interfaces = config.getDescriptors(isCallback=False) interfaces = config.getDescriptors(isCallback=False, isNamespace=False)
protos = [d.name for d in interfaces] protos = [d.name for d in interfaces]
constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True) constructors = sorted([MakeNativeName(d.name)
if d.shouldHaveGetConstructorObjectMethod()] for d in config.getDescriptors(hasInterfaceObject=True)
if d.shouldHaveGetConstructorObjectMethod()])
proxies = [d.name for d in config.getDescriptors(proxy=True)] proxies = [d.name for d in config.getDescriptors(proxy=True)]
return CGList([ return CGList([
@ -6800,10 +6836,13 @@ class GlobalGenRoots():
@staticmethod @staticmethod
def InterfaceTypes(config): 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, isCallback=False,
isIteratorInterface=False)] isIteratorInterface=False)])
curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), name)) for name in descriptors]) curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(),
MakeNativeName(name)))
for name in descriptors])
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
return curr return curr
@ -6814,7 +6853,7 @@ class GlobalGenRoots():
return getModuleFromObject(d).split('::')[-1] return getModuleFromObject(d).split('::')[-1]
descriptors = config.getDescriptors(register=True, isIteratorInterface=False) 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.callbacks) |
set(leafModule(d) for d in config.getDictionaries())) set(leafModule(d) for d in config.getDictionaries()))
curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)]) curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)])

View file

@ -4,7 +4,7 @@
import os import os
from WebIDL import IDLExternalInterface, IDLInterface, IDLWrapperType, WebIDLError from WebIDL import IDLExternalInterface, IDLWrapperType, WebIDLError
class Configuration: class Configuration:
@ -30,10 +30,9 @@ class Configuration:
raise WebIDLError("Servo does not support external interfaces.", raise WebIDLError("Servo does not support external interfaces.",
[thing.location]) [thing.location])
# Some toplevel things are sadly types, and those have an assert not thing.isType()
# isInterface that doesn't mean the same thing as IDLObject's
# isInterface()... if not thing.isInterface() and not thing.isNamespace():
if not isinstance(thing, IDLInterface):
continue continue
iface = thing iface = thing
@ -83,6 +82,8 @@ class Configuration:
getter = lambda x: x.interface.hasInterfaceObject() getter = lambda x: x.interface.hasInterfaceObject()
elif key == 'isCallback': elif key == 'isCallback':
getter = lambda x: x.interface.isCallback() getter = lambda x: x.interface.isCallback()
elif key == 'isNamespace':
getter = lambda x: x.interface.isNamespace()
elif key == 'isJSImplemented': elif key == 'isJSImplemented':
getter = lambda x: x.interface.isJSImplemented() getter = lambda x: x.interface.isJSImplemented()
elif key == 'isGlobal': elif key == 'isGlobal':
@ -210,7 +211,7 @@ class Descriptor(DescriptorProvider):
if self.interface.isIteratorInterface(): if self.interface.isIteratorInterface():
pathDefault = 'dom::bindings::iterable::IterableIterator' pathDefault = 'dom::bindings::iterable::IterableIterator'
else: else:
pathDefault = 'dom::types::%s' % typeName pathDefault = 'dom::types::%s' % MakeNativeName(typeName)
self.concreteType = typeName self.concreteType = typeName
self.register = desc.get('register', True) self.register = desc.get('register', True)
@ -223,6 +224,7 @@ class Descriptor(DescriptorProvider):
# If we're concrete, we need to crawl our ancestor interfaces and mark # If we're concrete, we need to crawl our ancestor interfaces and mark
# them as having a concrete descendant. # them as having a concrete descendant.
self.concrete = (not self.interface.isCallback() and self.concrete = (not self.interface.isCallback() and
not self.interface.isNamespace() and
not self.interface.getExtendedAttribute("Abstract")) not self.interface.getExtendedAttribute("Abstract"))
self.hasUnforgeableMembers = (self.concrete and self.hasUnforgeableMembers = (self.concrete and
any(MemberIsUnforgeable(m, self) for m in any(MemberIsUnforgeable(m, self) for m in
@ -381,7 +383,7 @@ class Descriptor(DescriptorProvider):
def shouldHaveGetConstructorObjectMethod(self): def shouldHaveGetConstructorObjectMethod(self):
assert self.interface.hasInterfaceObject() 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): def isExposedConditionally(self):
return self.interface.isExposedConditionally() return self.interface.isExposedConditionally()
@ -396,6 +398,12 @@ class Descriptor(DescriptorProvider):
# Some utility methods # Some utility methods
def MakeNativeName(name):
return name[0].upper() + name[1:]
def getModuleFromObject(object): def getModuleFromObject(object):
return ('dom::bindings::codegen::Bindings::' + return ('dom::bindings::codegen::Bindings::' +
os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding') os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding')

View file

@ -13,6 +13,7 @@ use dom::bindings::conversions::root_from_object;
use dom::bindings::error::ErrorInfo; use dom::bindings::error::ErrorInfo;
use dom::bindings::js::Root; use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector}; use dom::bindings::reflector::{Reflectable, Reflector};
use dom::console::TimerSet;
use dom::window::{self, ScriptHelpers}; use dom::window::{self, ScriptHelpers};
use dom::workerglobalscope::WorkerGlobalScope; use dom::workerglobalscope::WorkerGlobalScope;
use ipc_channel::ipc::IpcSender; 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 /// Returns a wrapper for runnables to ensure they are cancelled if the global
/// is being destroyed. /// is being destroyed.
pub fn get_runnable_wrapper(&self) -> RunnableWrapper { pub fn get_runnable_wrapper(&self) -> RunnableWrapper {

View file

@ -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, cx: *mut JSContext,
proto: HandleObject, proto: HandleObject,
class: &'static JSClass, class: &'static JSClass,
@ -316,7 +317,9 @@ pub unsafe fn is_exposed_in(object: HandleObject, globals: Globals) -> bool {
globals.contains(dom_class.global) 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, cx: *mut JSContext,
global: HandleObject, global: HandleObject,
name: &[u8], name: &[u8],

View file

@ -140,6 +140,7 @@ pub mod inheritance;
pub mod interface; pub mod interface;
pub mod iterable; pub mod iterable;
pub mod js; pub mod js;
pub mod namespace;
pub mod num; pub mod num;
pub mod proxyhandler; pub mod proxyhandler;
pub mod refcounted; pub mod refcounted;

View file

@ -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());
}

View file

@ -5,7 +5,7 @@
//! The `ByteString` struct. //! The `ByteString` struct.
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::borrow::{ToOwned, Cow}; use std::borrow::{Borrow, Cow, ToOwned};
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::ops; use std::ops;
@ -180,6 +180,13 @@ impl DOMString {
} }
} }
impl Borrow<str> for DOMString {
#[inline]
fn borrow(&self) -> &str {
&self.0
}
}
impl Default for DOMString { impl Default for DOMString {
fn default() -> Self { fn default() -> Self {
DOMString(String::new()) DOMString(String::new())

View file

@ -4,39 +4,17 @@
use devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg}; use devtools_traits::{ConsoleMessage, LogLevel, ScriptToDevtoolsControlMsg};
use dom::bindings::cell::DOMRefCell; 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::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::hash_map::Entry;
use time::{Timespec, get_time}; use time::{Timespec, get_time};
// https://developer.mozilla.org/en-US/docs/Web/API/Console // https://developer.mozilla.org/en-US/docs/Web/API/Console
#[dom_struct] pub struct Console(());
pub struct Console {
reflector_: Reflector,
timers: DOMRefCell<HashMap<DOMString, u64>>,
}
impl Console { impl Console {
fn new_inherited() -> Console { fn send_to_devtools(global: GlobalRef, level: LogLevel, message: DOMString) {
Console {
reflector_: Reflector::new(),
timers: DOMRefCell::new(HashMap::new()),
}
}
pub fn new(global: GlobalRef) -> Root<Console> {
reflect_dom_object(box Console::new_inherited(),
global,
ConsoleBinding::Wrap)
}
fn send_to_devtools(&self, level: LogLevel, message: DOMString) {
let global = self.global();
let global = global.r();
if let Some(chan) = global.devtools_chan() { if let Some(chan) = global.devtools_chan() {
let console_message = prepare_message(level, message); let console_message = prepare_message(level, message);
let devtools_message = ScriptToDevtoolsControlMsg::ConsoleAPI( let devtools_message = ScriptToDevtoolsControlMsg::ConsoleAPI(
@ -48,83 +26,73 @@ impl Console {
} }
} }
impl ConsoleMethods for Console { impl Console {
// https://developer.mozilla.org/en-US/docs/Web/API/Console/log // https://developer.mozilla.org/en-US/docs/Web/API/Console/log
fn Log(&self, messages: Vec<DOMString>) { pub fn Log(global: GlobalRef, messages: Vec<DOMString>) {
for message in messages { for message in messages {
println!("{}", message); 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 // https://developer.mozilla.org/en-US/docs/Web/API/Console
fn Debug(&self, messages: Vec<DOMString>) { pub fn Debug(global: GlobalRef, messages: Vec<DOMString>) {
for message in messages { for message in messages {
println!("{}", message); 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 // https://developer.mozilla.org/en-US/docs/Web/API/Console/info
fn Info(&self, messages: Vec<DOMString>) { pub fn Info(global: GlobalRef, messages: Vec<DOMString>) {
for message in messages { for message in messages {
println!("{}", message); 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 // https://developer.mozilla.org/en-US/docs/Web/API/Console/warn
fn Warn(&self, messages: Vec<DOMString>) { pub fn Warn(global: GlobalRef, messages: Vec<DOMString>) {
for message in messages { for message in messages {
println!("{}", message); 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 // https://developer.mozilla.org/en-US/docs/Web/API/Console/error
fn Error(&self, messages: Vec<DOMString>) { pub fn Error(global: GlobalRef, messages: Vec<DOMString>) {
for message in messages { for message in messages {
println!("{}", message); 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 // https://developer.mozilla.org/en-US/docs/Web/API/Console/assert
fn Assert(&self, condition: bool, message: Option<DOMString>) { pub fn Assert(global: GlobalRef, condition: bool, message: Option<DOMString>) {
if !condition { if !condition {
let message = message.unwrap_or_else(|| DOMString::from("no message")); let message = message.unwrap_or_else(|| DOMString::from("no message"));
println!("Assertion failed: {}", 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 // https://developer.mozilla.org/en-US/docs/Web/API/Console/time
fn Time(&self, label: DOMString) { pub fn Time(global: GlobalRef, label: DOMString) {
let mut timers = self.timers.borrow_mut(); if let Ok(()) = global.console_timers().time(label.clone()) {
if timers.contains_key(&label) { let message = DOMString::from(format!("{}: timer started", label));
// Timer already started println!("{}", message);
return; Self::send_to_devtools(global, LogLevel::Log, message);
} }
if timers.len() >= 10000 {
// Too many timers on page
return;
}
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 // https://developer.mozilla.org/en-US/docs/Web/API/Console/timeEnd
fn TimeEnd(&self, label: DOMString) { pub fn TimeEnd(global: GlobalRef, label: DOMString) {
let mut timers = self.timers.borrow_mut(); if let Ok(delta) = global.console_timers().time_end(&label) {
if let Some(start) = timers.remove(&label) {
let message = DOMString::from( let message = DOMString::from(
format!("{}: {}ms", label, timestamp_in_ms(get_time()) - start) format!("{}: {}ms", label, delta)
); );
println!("{}", message); println!("{}", message);
self.send_to_devtools(LogLevel::Log, message); Self::send_to_devtools(global, LogLevel::Log, message);
}; };
} }
} }
@ -143,3 +111,32 @@ fn prepare_message(logLevel: LogLevel, message: DOMString) -> ConsoleMessage {
columnNumber: 1, columnNumber: 1,
} }
} }
#[derive(HeapSizeOf, JSTraceable)]
pub struct TimerSet(DOMRefCell<HashMap<DOMString, u64>>);
impl TimerSet {
pub fn new() -> Self {
TimerSet(DOMRefCell::new(Default::default()))
}
fn time(&self, label: DOMString) -> Result<(), ()> {
let mut timers = self.0.borrow_mut();
if timers.len() >= 10000 {
return Err(());
}
match timers.entry(label) {
Entry::Vacant(entry) => {
entry.insert(timestamp_in_ms(get_time()));
Ok(())
},
Entry::Occupied(_) => Err(()),
}
}
fn time_end(&self, label: &str) -> Result<u64, ()> {
self.0.borrow_mut().remove(label).ok_or(()).map(|start| {
timestamp_in_ms(get_time()) - start
})
}
}

View file

@ -9,8 +9,10 @@
* © Copyright 2014 Mozilla Foundation. * © Copyright 2014 Mozilla Foundation.
*/ */
[Exposed=(Window,Worker)] [ClassString="Console",
interface Console { Exposed=(Window,Worker),
ProtoObjectHack]
namespace console {
// These should be DOMString message, DOMString message2, ... // These should be DOMString message, DOMString message2, ...
void log(DOMString... messages); void log(DOMString... messages);
void debug(DOMString... messages); void debug(DOMString... messages);

View file

@ -161,7 +161,6 @@ partial interface Window {
// Proprietary extensions. // Proprietary extensions.
partial interface Window { partial interface Window {
readonly attribute Console console;
void debug(DOMString arg); void debug(DOMString arg);
void gc(); void gc();
void trap(); void trap();

View file

@ -24,10 +24,3 @@ partial interface WorkerGlobalScope { // not obsolete
}; };
WorkerGlobalScope implements WindowTimers; WorkerGlobalScope implements WindowTimers;
WorkerGlobalScope implements WindowBase64; WorkerGlobalScope implements WindowBase64;
// Proprietary
[Exposed=Worker]
partial interface WorkerGlobalScope {
[Replaceable]
readonly attribute Console console;
};

View file

@ -25,7 +25,7 @@ use dom::bindings::str::DOMString;
use dom::bindings::structuredclone::StructuredCloneData; use dom::bindings::structuredclone::StructuredCloneData;
use dom::bindings::utils::{GlobalStaticData, WindowProxyHandler}; use dom::bindings::utils::{GlobalStaticData, WindowProxyHandler};
use dom::browsingcontext::BrowsingContext; use dom::browsingcontext::BrowsingContext;
use dom::console::Console; use dom::console::TimerSet;
use dom::crypto::Crypto; use dom::crypto::Crypto;
use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration}; use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration};
use dom::document::Document; use dom::document::Document;
@ -157,7 +157,6 @@ pub struct Window {
history_traversal_task_source: HistoryTraversalTaskSource, history_traversal_task_source: HistoryTraversalTaskSource,
#[ignore_heap_size_of = "task sources are hard"] #[ignore_heap_size_of = "task sources are hard"]
file_reading_task_source: FileReadingTaskSource, file_reading_task_source: FileReadingTaskSource,
console: MutNullableHeap<JS<Console>>,
crypto: MutNullableHeap<JS<Crypto>>, crypto: MutNullableHeap<JS<Crypto>>,
navigator: MutNullableHeap<JS<Navigator>>, navigator: MutNullableHeap<JS<Navigator>>,
#[ignore_heap_size_of = "channels are hard"] #[ignore_heap_size_of = "channels are hard"]
@ -276,7 +275,10 @@ pub struct Window {
scroll_offsets: DOMRefCell<HashMap<UntrustedNodeAddress, Point2D<f32>>>, scroll_offsets: DOMRefCell<HashMap<UntrustedNodeAddress, Point2D<f32>>>,
/// https://html.spec.whatwg.org/multipage/#in-error-reporting-mode /// https://html.spec.whatwg.org/multipage/#in-error-reporting-mode
in_error_reporting_mode: Cell<bool> in_error_reporting_mode: Cell<bool>,
/// Timers used by the Console API.
console_timers: TimerSet,
} }
impl Window { impl Window {
@ -508,11 +510,6 @@ impl WindowMethods for Window {
self.local_storage.or_init(|| Storage::new(&GlobalRef::Window(self), StorageType::Local)) self.local_storage.or_init(|| Storage::new(&GlobalRef::Window(self), StorageType::Local))
} }
// https://developer.mozilla.org/en-US/docs/Web/API/Console
fn Console(&self) -> Root<Console> {
self.console.or_init(|| Console::new(GlobalRef::Window(self)))
}
// https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-GlobalCrypto // https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#dfn-GlobalCrypto
fn Crypto(&self) -> Root<Crypto> { fn Crypto(&self) -> Root<Crypto> {
self.crypto.or_init(|| Crypto::new(GlobalRef::Window(self))) self.crypto.or_init(|| Crypto::new(GlobalRef::Window(self)))
@ -1701,7 +1698,6 @@ impl Window {
history_traversal_task_source: history_task_source, history_traversal_task_source: history_task_source,
file_reading_task_source: file_task_source, file_reading_task_source: file_task_source,
image_cache_chan: image_cache_chan, image_cache_chan: image_cache_chan,
console: Default::default(),
crypto: Default::default(), crypto: Default::default(),
navigator: Default::default(), navigator: Default::default(),
image_cache_thread: image_cache_thread, image_cache_thread: image_cache_thread,
@ -1747,10 +1743,16 @@ impl Window {
error_reporter: error_reporter, error_reporter: error_reporter,
scroll_offsets: DOMRefCell::new(HashMap::new()), scroll_offsets: DOMRefCell::new(HashMap::new()),
in_error_reporting_mode: Cell::new(false), in_error_reporting_mode: Cell::new(false),
console_timers: TimerSet::new(),
}; };
WindowBinding::Wrap(runtime.cx(), win) WindowBinding::Wrap(runtime.cx(), win)
} }
pub fn console_timers(&self) -> &TimerSet {
&self.console_timers
}
pub fn live_devtools_updates(&self) -> bool { pub fn live_devtools_updates(&self) -> bool {
return self.devtools_wants_updates.get(); return self.devtools_wants_updates.get();
} }

View file

@ -12,7 +12,7 @@ use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, MutNullableHeap, Root}; use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::reflector::Reflectable; use dom::bindings::reflector::Reflectable;
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use dom::console::Console; use dom::console::TimerSet;
use dom::crypto::Crypto; use dom::crypto::Crypto;
use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
use dom::eventtarget::EventTarget; use dom::eventtarget::EventTarget;
@ -80,7 +80,6 @@ pub struct WorkerGlobalScope {
resource_threads: ResourceThreads, resource_threads: ResourceThreads,
location: MutNullableHeap<JS<WorkerLocation>>, location: MutNullableHeap<JS<WorkerLocation>>,
navigator: MutNullableHeap<JS<WorkerNavigator>>, navigator: MutNullableHeap<JS<WorkerNavigator>>,
console: MutNullableHeap<JS<Console>>,
crypto: MutNullableHeap<JS<Crypto>>, crypto: MutNullableHeap<JS<Crypto>>,
timers: OneshotTimers, timers: OneshotTimers,
@ -110,6 +109,9 @@ pub struct WorkerGlobalScope {
#[ignore_heap_size_of = "Defined in std"] #[ignore_heap_size_of = "Defined in std"]
scheduler_chan: IpcSender<TimerEventRequest>, scheduler_chan: IpcSender<TimerEventRequest>,
/// Timers used by the Console API.
console_timers: TimerSet,
} }
impl WorkerGlobalScope { impl WorkerGlobalScope {
@ -130,7 +132,6 @@ impl WorkerGlobalScope {
resource_threads: init.resource_threads, resource_threads: init.resource_threads,
location: Default::default(), location: Default::default(),
navigator: Default::default(), navigator: Default::default(),
console: Default::default(),
crypto: Default::default(), crypto: Default::default(),
timers: OneshotTimers::new(timer_event_chan, init.scheduler_chan.clone()), timers: OneshotTimers::new(timer_event_chan, init.scheduler_chan.clone()),
mem_profiler_chan: init.mem_profiler_chan, mem_profiler_chan: init.mem_profiler_chan,
@ -141,9 +142,14 @@ impl WorkerGlobalScope {
devtools_wants_updates: Cell::new(false), devtools_wants_updates: Cell::new(false),
constellation_chan: init.constellation_chan, constellation_chan: init.constellation_chan,
scheduler_chan: init.scheduler_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 { pub fn mem_profiler_chan(&self) -> &mem::ProfilerChan {
&self.mem_profiler_chan &self.mem_profiler_chan
} }
@ -299,11 +305,6 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
self.navigator.or_init(|| WorkerNavigator::new(self)) self.navigator.or_init(|| WorkerNavigator::new(self))
} }
// https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/console
fn Console(&self) -> Root<Console> {
self.console.or_init(|| Console::new(GlobalRef::Worker(self)))
}
// https://html.spec.whatwg.org/multipage/#dfn-Crypto // https://html.spec.whatwg.org/multipage/#dfn-Crypto
fn Crypto(&self) -> Root<Crypto> { fn Crypto(&self) -> Root<Crypto> {
self.crypto.or_init(|| Crypto::new(GlobalRef::Worker(self))) self.crypto.or_init(|| Crypto::new(GlobalRef::Worker(self)))

View file

@ -27,7 +27,6 @@ test_interfaces([
"DOMRect", "DOMRect",
"DOMRectReadOnly", "DOMRectReadOnly",
"Comment", "Comment",
"Console",
"CustomEvent", "CustomEvent",
"Document", "Document",
"DocumentFragment", "DocumentFragment",
@ -184,5 +183,6 @@ test_interfaces([
"XMLHttpRequest", "XMLHttpRequest",
"XMLHttpRequestEventTarget", "XMLHttpRequestEventTarget",
"XMLHttpRequestUpload", "XMLHttpRequestUpload",
"console",
]); ]);
</script> </script>

View file

@ -65,7 +65,7 @@ function test_interfaces(interfaceNamesInGlobalScope) {
} }
for (var name of Object.getOwnPropertyNames(self)) { for (var name of Object.getOwnPropertyNames(self)) {
if (!/^[A-Z]/.test(name)) { if (!/^[A-Z]/.test(name) && name != 'console') {
continue; continue;
} }
assert_true(name in interfaceMap, assert_true(name in interfaceMap,

View file

@ -23,7 +23,6 @@ test_interfaces([
"DOMRect", "DOMRect",
"DOMRectReadOnly", "DOMRectReadOnly",
"Comment", "Comment",
"Console",
"CustomEvent", "CustomEvent",
"DedicatedWorkerGlobalScope", "DedicatedWorkerGlobalScope",
"Document", "Document",
@ -129,6 +128,7 @@ test_interfaces([
"XMLHttpRequest", "XMLHttpRequest",
"XMLHttpRequestEventTarget", "XMLHttpRequestEventTarget",
"XMLHttpRequestUpload", "XMLHttpRequestUpload",
"console",
]); ]);
done(); done();