aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
Diffstat (limited to 'components/script')
-rw-r--r--components/script/Cargo.toml3
-rw-r--r--components/script/devtools.rs41
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py183
-rw-r--r--components/script/dom/bindings/codegen/Configuration.py5
-rw-r--r--components/script/dom/bindings/conversions.rs51
-rw-r--r--components/script/dom/bindings/error.rs14
-rw-r--r--components/script/dom/bindings/global.rs9
-rw-r--r--components/script/dom/bindings/js.rs16
-rw-r--r--components/script/dom/bindings/trace.rs16
-rw-r--r--components/script/dom/bindings/utils.rs102
-rw-r--r--components/script/dom/canvasrenderingcontext2d.rs294
-rw-r--r--components/script/dom/characterdata.rs94
-rw-r--r--components/script/dom/comment.rs5
-rw-r--r--components/script/dom/document.rs166
-rw-r--r--components/script/dom/documentfragment.rs18
-rw-r--r--components/script/dom/domexception.rs2
-rw-r--r--components/script/dom/domimplementation.rs21
-rw-r--r--components/script/dom/element.rs220
-rw-r--r--components/script/dom/event.rs13
-rw-r--r--components/script/dom/eventdispatcher.rs3
-rw-r--r--components/script/dom/htmlbuttonelement.rs12
-rw-r--r--components/script/dom/htmlcollection.rs24
-rw-r--r--components/script/dom/htmlfieldsetelement.rs14
-rw-r--r--components/script/dom/htmlformelement.rs30
-rw-r--r--components/script/dom/htmlimageelement.rs10
-rw-r--r--components/script/dom/htmlinputelement.rs33
-rw-r--r--components/script/dom/htmlscriptelement.rs3
-rw-r--r--components/script/dom/htmlselectelement.rs2
-rw-r--r--components/script/dom/htmltextareaelement.rs2
-rw-r--r--components/script/dom/htmltitleelement.rs5
-rw-r--r--components/script/dom/mod.rs1
-rw-r--r--components/script/dom/namednodemap.rs53
-rw-r--r--components/script/dom/node.rs329
-rw-r--r--components/script/dom/nodelist.rs5
-rw-r--r--components/script/dom/processinginstruction.rs13
-rw-r--r--components/script/dom/storage.rs2
-rw-r--r--components/script/dom/testbinding.rs13
-rw-r--r--components/script/dom/text.rs28
-rw-r--r--components/script/dom/textdecoder.rs99
-rw-r--r--components/script/dom/treewalker.rs26
-rw-r--r--components/script/dom/webidls/CanvasRenderingContext2D.webidl27
-rw-r--r--components/script/dom/webidls/CharacterData.webidl4
-rw-r--r--components/script/dom/webidls/ChildNode.webidl14
-rw-r--r--components/script/dom/webidls/Document.webidl22
-rw-r--r--components/script/dom/webidls/Element.webidl1
-rw-r--r--components/script/dom/webidls/NamedNodeMap.webidl10
-rw-r--r--components/script/dom/webidls/NodeFilter.webidl34
-rw-r--r--components/script/dom/webidls/ParentNode.webidl3
-rw-r--r--components/script/dom/webidls/TestBinding.webidl12
-rw-r--r--components/script/dom/webidls/Text.webidl2
-rw-r--r--components/script/dom/webidls/TextDecoder.webidl21
-rw-r--r--components/script/dom/window.rs50
-rw-r--r--components/script/lib.rs4
-rw-r--r--components/script/page.rs2
-rw-r--r--components/script/parse/html.rs29
-rw-r--r--components/script/script_task.rs146
-rw-r--r--components/script/tests.rs48
-rw-r--r--components/script/textinput.rs188
58 files changed, 1613 insertions, 984 deletions
diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml
index 9e7db847a86..63eaea80142 100644
--- a/components/script/Cargo.toml
+++ b/components/script/Cargo.toml
@@ -57,6 +57,9 @@ git = "https://github.com/servo/html5ever"
[dependencies.js]
git = "https://github.com/servo/rust-mozjs"
+[dependencies.png]
+git = "https://github.com/servo/rust-png"
+
[dependencies.uuid]
git = "https://github.com/rust-lang/uuid"
diff --git a/components/script/devtools.rs b/components/script/devtools.rs
index 56588941667..271cc39ab35 100644
--- a/components/script/devtools.rs
+++ b/components/script/devtools.rs
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use devtools_traits::{EvaluateJSReply, NodeInfo, Modification};
+use devtools_traits::{EvaluateJSReply, NodeInfo, Modification, TimelineMarker, TimelineMarkerType};
use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::conversions::StringificationBehavior;
use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
@@ -16,7 +16,7 @@ use dom::element::Element;
use dom::document::DocumentHelpers;
use page::Page;
use msg::constellation_msg::PipelineId;
-use script_task::get_page;
+use script_task::{get_page, ScriptTask};
use std::sync::mpsc::Sender;
use std::rc::Rc;
@@ -69,8 +69,8 @@ fn find_node_by_unique_id(page: &Rc<Page>, pipeline: PipelineId, node_id: String
let node: JSRef<Node> = NodeCast::from_ref(document.r());
for candidate in node.traverse_preorder() {
- if candidate.get_unique_id() == node_id {
- return Temporary::from_rooted(candidate);
+ if candidate.root().r().get_unique_id() == node_id {
+ return candidate;
}
}
@@ -114,3 +114,36 @@ pub fn handle_wants_live_notifications(page: &Rc<Page>, pipeline_id: PipelineId,
let window = page.window().root();
window.r().set_devtools_wants_updates(send_notifications);
}
+
+pub fn handle_set_timeline_markers(page: &Rc<Page>,
+ script_task: &ScriptTask,
+ marker_types: Vec<TimelineMarkerType>,
+ reply: Sender<TimelineMarker>) {
+ for marker_type in &marker_types {
+ match *marker_type {
+ TimelineMarkerType::Reflow => {
+ let window = page.window().root();
+ window.r().set_devtools_timeline_marker(TimelineMarkerType::Reflow, reply.clone());
+ }
+ TimelineMarkerType::DOMEvent => {
+ script_task.set_devtools_timeline_marker(TimelineMarkerType::DOMEvent, reply.clone());
+ }
+ }
+ }
+}
+
+pub fn handle_drop_timeline_markers(page: &Rc<Page>,
+ script_task: &ScriptTask,
+ marker_types: Vec<TimelineMarkerType>) {
+ let window = page.window().root();
+ for marker_type in &marker_types {
+ match *marker_type {
+ TimelineMarkerType::Reflow => {
+ window.r().drop_devtools_timeline_markers();
+ }
+ TimelineMarkerType::DOMEvent => {
+ script_task.drop_devtools_timeline_markers();
+ }
+ }
+ }
+}
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
index b828ccb74bd..960a84b1717 100644
--- a/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -582,7 +582,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
# A helper function for wrapping up the template body for
# possibly-nullable objecty stuff
- def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
+ def wrapObjectTemplate(templateBody, nullValue, isDefinitelyObject, type,
failureCode=None):
if not isDefinitelyObject:
# Handle the non-object cases by wrapping up the whole
@@ -593,7 +593,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.nullable():
templateBody += (
"} else if (${val}).is_null_or_undefined() {\n"
- " None\n")
+ " %s\n") % nullValue
templateBody += (
"} else {\n" +
CGIndenter(onFailureNotAnObject(failureCode)).define() +
@@ -632,8 +632,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
template = "%s::new((${val}).to_object())" % name
if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=">")
- template = wrapObjectTemplate("Some(%s)" % template, isDefinitelyObject, type,
- failureCode)
+ template = wrapObjectTemplate("Some(%s)" % template, "None",
+ isDefinitelyObject, type,
+ failureCode)
return handleOptional(template, declType, handleDefaultNull("None"))
@@ -675,8 +676,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if isMember:
templateBody += ".root()"
- templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
- type, failureCode)
+ templateBody = wrapObjectTemplate(templateBody, "None",
+ isDefinitelyObject, type, failureCode)
return handleOptional(templateBody, declType, handleDefaultNull("None"))
@@ -821,6 +822,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
onFailureNotCallable(failureCode)).define()
template = wrapObjectTemplate(
template,
+ "None",
isDefinitelyObject,
type,
failureCode)
@@ -853,7 +855,15 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
return handleOptional("${val}", declType, default)
if type.isObject():
- raise TypeError("Can't handle object arguments yet")
+ assert not isEnforceRange and not isClamp
+
+ declType = CGGeneric("*mut JSObject")
+ templateBody = wrapObjectTemplate("${val}.to_object()",
+ "ptr::null_mut()",
+ isDefinitelyObject, type, failureCode)
+
+ return handleOptional(templateBody, declType,
+ handleDefaultNull("ptr::null_mut()"))
if type.isDictionary():
if failureCode is not None:
@@ -887,23 +897,12 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=">")
- template = ""
- if type.isFloat() and not type.isUnrestricted():
- template = (
- "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
- " Ok(v) => v,\n"
- " Err(_) => {\n"
- " throw_type_error(cx, \"this argument is not a finite floating-point value\");\n"
- " %s\n"
- " }\n"
- "}" % exceptionCode)
- else:
- #XXXjdm support conversionBehavior here
- template = (
- "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
- " Ok(v) => v,\n"
- " Err(_) => { %s }\n"
- "}" % exceptionCode)
+ #XXXjdm support conversionBehavior here
+ template = (
+ "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ " Ok(v) => v,\n"
+ " Err(_) => { %s }\n"
+ "}" % exceptionCode)
if defaultValue is not None:
if isinstance(defaultValue, IDLNullValue):
@@ -1241,9 +1240,14 @@ class MethodDefiner(PropertyDefiner):
# FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
# We should be able to check for special operations without an
# identifier. For now we check if the name starts with __
- methods = [m for m in descriptor.interface.members if
- m.isMethod() and m.isStatic() == static and
- not m.isIdentifierLess()]
+
+ # Ignore non-static methods for callback interfaces
+ if not descriptor.interface.isCallback() or static:
+ methods = [m for m in descriptor.interface.members if
+ m.isMethod() and m.isStatic() == static and
+ not m.isIdentifierLess()]
+ else:
+ methods = []
self.regular = [{"name": m.identifier.name,
"methodInfo": not m.isStatic(),
"length": methodLength(m),
@@ -1441,7 +1445,7 @@ class CGImports(CGWrapper):
"""
Generates the appropriate import/use statements.
"""
- def __init__(self, child, descriptors, imports):
+ def __init__(self, child, descriptors, callbacks, imports):
"""
Adds a set of imports.
"""
@@ -1461,8 +1465,55 @@ class CGImports(CGWrapper):
'dead_code',
]
+ def componentTypes(type):
+ if type.nullable():
+ type = type.unroll()
+ if type.isUnion():
+ return type.flatMemberTypes
+ return [type]
+
+ def isImportable(type):
+ if not type.isType():
+ assert type.isInterface()
+ return not type.isCallback()
+ return type.isNonCallbackInterface() and not type.builtin
+
+ def relatedTypesForSignatures(method):
+ types = []
+ for (returnType, arguments) in method.signatures():
+ types += componentTypes(returnType)
+ for arg in arguments:
+ types += componentTypes(arg.type)
+ return types
+
+ def getIdentifier(t):
+ if t.isType():
+ return t.inner.identifier
+ assert t.isInterface()
+ return t.identifier
+
+ types = []
+ for d in descriptors:
+ types += [d.interface]
+
+ members = d.interface.members + d.interface.namedConstructors
+ constructor = d.interface.ctor()
+ if constructor:
+ members += [constructor]
+
+ for m in members:
+ if m.isMethod():
+ types += relatedTypesForSignatures(m)
+ elif m.isAttr():
+ types += componentTypes(m.type)
+
+ for c in callbacks:
+ types += relatedTypesForSignatures(c)
+
+ imports += ['dom::types::%s' % getIdentifier(t).name for t in types if isImportable(t)]
+
statements = ['#![allow(%s)]' % ','.join(ignored_warnings)]
- statements.extend('use %s;' % i for i in sorted(imports))
+ statements.extend('use %s;' % i for i in sorted(set(imports)))
CGWrapper.__init__(self, child,
pre='\n'.join(statements) + '\n\n')
@@ -1784,7 +1835,7 @@ def UnionTypes(descriptors, dictionaries, callbacks, config):
CGUnionConversionStruct(t, provider)
])
- return CGImports(CGList(SortedDictValues(unionStructs), "\n\n"), [], imports)
+ return CGImports(CGList(SortedDictValues(unionStructs), "\n\n"), [], [], imports)
class Argument():
@@ -2049,7 +2100,6 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
properties should be a PropertyArrays instance.
"""
def __init__(self, descriptor, properties):
- assert not descriptor.interface.isCallback()
args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'global'),
Argument('*mut JSObject', 'receiver')]
CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', '*mut JSObject', args)
@@ -2066,6 +2116,11 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
getParentProto = ("let parent_proto: *mut JSObject = %s;\n"
"assert!(!parent_proto.is_null());\n") % getParentProto
+ if self.descriptor.interface.isCallback():
+ protoClass = "None"
+ else:
+ protoClass = "Some(&PrototypeClass)"
+
if self.descriptor.concrete:
if self.descriptor.proxy:
domClass = "&Class"
@@ -2090,9 +2145,9 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
call = """\
return do_create_interface_objects(cx, global, receiver, parent_proto,
- &PrototypeClass, %s,
+ %s, %s,
%s,
- &sNativeProperties);""" % (constructor, domClass)
+ &sNativeProperties);""" % (protoClass, constructor, domClass)
return CGList([
CGGeneric(getParentProto),
@@ -2240,9 +2295,11 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
return CGAbstractMethod.define(self)
def definition_body(self):
- return CGGeneric("""\
-assert!(!global.is_null());
-assert!(!GetProtoObject(cx, global, global).is_null());""")
+ if self.descriptor.interface.isCallback():
+ code = "CreateInterfaceObjects(cx, global, global);"
+ else:
+ code = "assert!(!GetProtoObject(cx, global, global).is_null());"
+ return CGGeneric("assert!(!global.is_null());\n" + code)
def needCx(returnType, arguments, considerTypes):
return (considerTypes and
@@ -4222,10 +4279,11 @@ class CGDescriptor(CGThing):
def __init__(self, descriptor):
CGThing.__init__(self)
- assert not descriptor.interface.isCallback()
+ assert not descriptor.concrete or not descriptor.interface.isCallback()
cgThings = []
- cgThings.append(CGGetProtoObjectMethod(descriptor))
+ if not descriptor.interface.isCallback():
+ cgThings.append(CGGetProtoObjectMethod(descriptor))
if descriptor.interface.hasInterfaceObject():
# https://github.com/mozilla/servo/issues/2665
# cgThings.append(CGGetConstructorObjectMethod(descriptor))
@@ -4239,7 +4297,7 @@ class CGDescriptor(CGThing):
if m.isStatic():
assert descriptor.interface.hasInterfaceObject()
cgThings.append(CGStaticMethod(descriptor, m))
- else:
+ elif not descriptor.interface.isCallback():
cgThings.append(CGSpecializedMethod(descriptor, m))
cgThings.append(CGMemberJITInfo(descriptor, m))
hasMethod = True
@@ -4252,7 +4310,7 @@ class CGDescriptor(CGThing):
if m.isStatic():
assert descriptor.interface.hasInterfaceObject()
cgThings.append(CGStaticGetter(descriptor, m))
- else:
+ elif not descriptor.interface.isCallback():
cgThings.append(CGSpecializedGetter(descriptor, m))
if m.hasLenientThis():
hasLenientGetter = True
@@ -4263,14 +4321,15 @@ class CGDescriptor(CGThing):
if m.isStatic():
assert descriptor.interface.hasInterfaceObject()
cgThings.append(CGStaticSetter(descriptor, m))
- else:
+ elif not descriptor.interface.isCallback():
cgThings.append(CGSpecializedSetter(descriptor, m))
if m.hasLenientThis():
hasLenientSetter = True
else:
hasSetter = True
- if not m.isStatic():
+ if (not m.isStatic() and
+ not descriptor.interface.isCallback()):
cgThings.append(CGMemberJITInfo(descriptor, m))
if hasMethod:
cgThings.append(CGGenericMethod(descriptor))
@@ -4291,7 +4350,8 @@ class CGDescriptor(CGThing):
cgThings.append(CGClassConstructHook(descriptor))
cgThings.append(CGInterfaceObjectJSClass(descriptor))
- cgThings.append(CGPrototypeJSClass(descriptor))
+ if not descriptor.interface.isCallback():
+ cgThings.append(CGPrototypeJSClass(descriptor))
properties = PropertyArrays(descriptor)
cgThings.append(CGGeneric(str(properties)))
@@ -4337,8 +4397,9 @@ class CGDescriptor(CGThing):
cgThings.append(CGWrapMethod(descriptor))
- cgThings.append(CGIDLInterface(descriptor))
- cgThings.append(CGInterfaceTrait(descriptor))
+ if not descriptor.interface.isCallback():
+ cgThings.append(CGIDLInterface(descriptor))
+ cgThings.append(CGInterfaceTrait(descriptor))
cgThings = CGList(cgThings, "\n")
#self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
@@ -4592,7 +4653,14 @@ class CGBindingRoot(CGThing):
"""
def __init__(self, config, prefix, webIDLFile):
descriptors = config.getDescriptors(webIDLFile=webIDLFile,
- isCallback=False)
+ hasInterfaceObject=True)
+ # We also want descriptors that have an interface prototype object
+ # (isCallback=False), but we don't want to include a second copy
+ # of descriptors that we also matched in the previous line
+ # (hence hasInterfaceObject=False).
+ descriptors.extend(config.getDescriptors(webIDLFile=webIDLFile,
+ hasInterfaceObject=False,
+ isCallback=False))
dictionaries = config.getDictionaries(webIDLFile=webIDLFile)
cgthings = []
@@ -4628,9 +4696,7 @@ class CGBindingRoot(CGThing):
# CGWrapper(curr, pre="\n"))
# Add imports
- #XXXjdm This should only import the namespace for the current binding,
- # not every binding ever.
- curr = CGImports(curr, descriptors, [
+ curr = CGImports(curr, descriptors + callbackDescriptors, mainCallbacks, [
'js',
'js::{JS_ARGV, JS_CALLEE, JS_THIS_OBJECT}',
'js::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS}',
@@ -4658,7 +4724,6 @@ class CGBindingRoot(CGThing):
'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}',
'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING}',
'js::rust::with_compartment',
- 'dom::types::*',
'dom::bindings',
'dom::bindings::global::GlobalRef',
'dom::bindings::global::global_object_for_js_object',
@@ -5317,7 +5382,7 @@ class GlobalGenRoots():
CGRegisterProxyHandlers(config),
], "\n")
- return CGImports(code, [], [
+ return CGImports(code, [], [], [
'dom::bindings::codegen',
'dom::bindings::codegen::PrototypeList::Proxies',
'js::jsapi::JSContext',
@@ -5409,6 +5474,16 @@ impl ${name}Cast {
}
#[inline(always)]
+ pub fn to_temporary<T: ${toBound}+Reflectable>(base: Temporary<T>) -> Option<Temporary<${name}>> {
+ let base = base.root();
+ let base = base.r();
+ match base.${checkFn}() {
+ true => Some(Temporary::from_rooted(unsafe { base.transmute() })),
+ false => None
+ }
+ }
+
+ #[inline(always)]
pub fn from_ref<'a, T: ${fromBound}+Reflectable>(derived: JSRef<'a, T>) -> JSRef<'a, ${name}> {
unsafe { derived.transmute() }
}
@@ -5419,6 +5494,12 @@ impl ${name}Cast {
}
#[inline(always)]
+ #[allow(unrooted_must_root)]
+ pub fn from_layout_js<T: ${fromBound}+Reflectable>(derived: &LayoutJS<T>) -> LayoutJS<${name}> {
+ unsafe { derived.transmute_copy() }
+ }
+
+ #[inline(always)]
pub fn from_temporary<T: ${fromBound}+Reflectable>(derived: Temporary<T>) -> Temporary<${name}> {
unsafe { derived.transmute() }
}
diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py
index e6712357d35..5ea521e15b8 100644
--- a/components/script/dom/bindings/codegen/Configuration.py
+++ b/components/script/dom/bindings/codegen/Configuration.py
@@ -162,10 +162,12 @@ class Descriptor(DescriptorProvider):
self.concreteType = ifaceName
self.register = desc.get('register', True)
self.outerObjectHook = desc.get('outerObjectHook', 'None')
+ self.proxy = False
# If we're concrete, we need to crawl our ancestor interfaces and mark
# them as having a concrete descendant.
- self.concrete = desc.get('concrete', True)
+ self.concrete = (not self.interface.isCallback() and
+ desc.get('concrete', True))
self.operations = {
'IndexedGetter': None,
@@ -190,7 +192,6 @@ class Descriptor(DescriptorProvider):
addOperation('Stringifier', m)
if self.concrete:
- self.proxy = False
iface = self.interface
while iface:
for m in iface.members:
diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs
index 1a96dc7c948..eb6d7d60680 100644
--- a/components/script/dom/bindings/conversions.rs
+++ b/components/script/dom/bindings/conversions.rs
@@ -33,6 +33,7 @@
//! | union types | `T` |
use dom::bindings::codegen::PrototypeList;
+use dom::bindings::error::throw_type_error;
use dom::bindings::js::{JSRef, Root, Unrooted};
use dom::bindings::num::Finite;
use dom::bindings::str::{ByteString, USVString};
@@ -58,6 +59,7 @@ use libc;
use std::borrow::ToOwned;
use std::default;
use std::marker::MarkerTrait;
+use std::num::Float;
use std::slice;
/// A trait to retrieve the constants necessary to check if a `JSObject`
@@ -257,24 +259,6 @@ impl FromJSValConvertible for f32 {
}
}
-impl ToJSValConvertible for Finite<f32> {
- fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
- let value = **self;
- value.to_jsval(cx)
- }
-}
-
-impl FromJSValConvertible for Finite<f32> {
- type Config = ();
- fn from_jsval(cx: *mut JSContext, val: JSVal, option: ()) -> Result<Finite<f32>, ()> {
- let result = FromJSValConvertible::from_jsval(cx, val, option);
- let result = result.and_then(|v| {
- Finite::<f32>::new(v).ok_or(())
- });
- result
- }
-}
-
impl ToJSValConvertible for f64 {
fn to_jsval(&self, _cx: *mut JSContext) -> JSVal {
unsafe {
@@ -290,7 +274,7 @@ impl FromJSValConvertible for f64 {
}
}
-impl ToJSValConvertible for Finite<f64> {
+impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> {
#[inline]
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
let value = **self;
@@ -298,14 +282,18 @@ impl ToJSValConvertible for Finite<f64> {
}
}
-impl FromJSValConvertible for Finite<f64> {
+impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite<T> {
type Config = ();
- fn from_jsval(cx: *mut JSContext, val: JSVal, option: ()) -> Result<Finite<f64>, ()> {
- let result = FromJSValConvertible::from_jsval(cx, val, option);
- let result = result.and_then(|v| {
- Finite::<f64>::new(v).ok_or(())
- });
- result
+
+ fn from_jsval(cx: *mut JSContext, value: JSVal, option: ()) -> Result<Finite<T>, ()> {
+ let result = try!(FromJSValConvertible::from_jsval(cx, value, option));
+ match Finite::new(result) {
+ Some(v) => Ok(v),
+ None => {
+ throw_type_error(cx, "this argument is not a finite floating-point value");
+ Err(())
+ },
+ }
}
}
@@ -569,17 +557,6 @@ pub fn native_from_reflector_jsmanaged<T>(mut obj: *mut JSObject) -> Result<Unro
}
}
-impl<T: Reflectable+IDLInterface> FromJSValConvertible for Unrooted<T> {
- type Config = ();
- fn from_jsval(_cx: *mut JSContext, value: JSVal, _option: ())
- -> Result<Unrooted<T>, ()> {
- if !value.is_object() {
- return Err(());
- }
- native_from_reflector_jsmanaged(value.to_object())
- }
-}
-
impl<T: Reflectable> ToJSValConvertible for Root<T> {
fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
self.r().reflector().to_jsval(cx)
diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs
index 4908fa3b03d..426bff64d95 100644
--- a/components/script/dom/bindings/error.rs
+++ b/components/script/dom/bindings/error.rs
@@ -34,6 +34,8 @@ pub enum Error {
InvalidCharacter,
/// NotSupportedError DOMException
NotSupported,
+ /// InUseAttributeError DOMException
+ InUseAttribute,
/// InvalidStateError DOMException
InvalidState,
/// SyntaxError DOMException
@@ -69,16 +71,16 @@ pub type Fallible<T> = Result<T, Error>;
/// return `()`.
pub type ErrorResult = Fallible<()>;
-/// Set a pending DOM exception for the given `result` on `cx`.
+/// Set a pending exception for the given `result` on `cx`.
pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef,
result: Error) {
- assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
let code = match result {
Error::IndexSize => DOMErrorName::IndexSizeError,
Error::NotFound => DOMErrorName::NotFoundError,
Error::HierarchyRequest => DOMErrorName::HierarchyRequestError,
Error::InvalidCharacter => DOMErrorName::InvalidCharacterError,
Error::NotSupported => DOMErrorName::NotSupportedError,
+ Error::InUseAttribute => DOMErrorName::InUseAttributeError,
Error::InvalidState => DOMErrorName::InvalidStateError,
Error::Syntax => DOMErrorName::SyntaxError,
Error::Namespace => DOMErrorName::NamespaceError,
@@ -90,11 +92,17 @@ pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef,
Error::DataClone => DOMErrorName::DataCloneError,
Error::NoModificationAllowed => DOMErrorName::NoModificationAllowedError,
Error::Type(message) => {
+ assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
throw_type_error(cx, &message);
return;
}
- Error::JSFailed => panic!(),
+ Error::JSFailed => {
+ assert!(unsafe { JS_IsExceptionPending(cx) } == 1);
+ return;
+ }
};
+
+ assert!(unsafe { JS_IsExceptionPending(cx) } == 0);
let exception = DOMException::new(global, code).root();
let thrown = exception.to_jsval(cx);
unsafe {
diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs
index 316c51a67e3..233143851e6 100644
--- a/components/script/dom/bindings/global.rs
+++ b/components/script/dom/bindings/global.rs
@@ -7,7 +7,7 @@
//! This module contains smart pointers to global scopes, to simplify writing
//! code that works in workers as well as window scopes.
-use dom::bindings::conversions::FromJSValConvertible;
+use dom::bindings::conversions::native_from_reflector_jsmanaged;
use dom::bindings::js::{JS, JSRef, Root, Unrooted};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
@@ -22,11 +22,8 @@ use js::{JSCLASS_IS_GLOBAL, JSCLASS_IS_DOMJSCLASS};
use js::glue::{GetGlobalForObjectCrossCompartment};
use js::jsapi::{JSContext, JSObject};
use js::jsapi::{JS_GetClass};
-use js::jsval::ObjectOrNullValue;
use url::Url;
-use std::ptr;
-
/// A freely-copyable reference to a rooted global object.
#[derive(Copy)]
pub enum GlobalRef<'a> {
@@ -189,12 +186,12 @@ pub fn global_object_for_js_object(obj: *mut JSObject) -> GlobalUnrooted {
let global = GetGlobalForObjectCrossCompartment(obj);
let clasp = JS_GetClass(global);
assert!(((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)) != 0);
- match FromJSValConvertible::from_jsval(ptr::null_mut(), ObjectOrNullValue(global), ()) {
+ match native_from_reflector_jsmanaged(global) {
Ok(window) => return GlobalUnrooted::Window(window),
Err(_) => (),
}
- match FromJSValConvertible::from_jsval(ptr::null_mut(), ObjectOrNullValue(global), ()) {
+ match native_from_reflector_jsmanaged(global) {
Ok(worker) => return GlobalUnrooted::Worker(worker),
Err(_) => (),
}
diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs
index 357a3282f9d..6b3ce4a555c 100644
--- a/components/script/dom/bindings/js.rs
+++ b/components/script/dom/bindings/js.rs
@@ -50,6 +50,7 @@
//! `Option<Root<T>>` easy
use dom::bindings::trace::JSTraceable;
+use dom::bindings::trace::RootedVec;
use dom::bindings::utils::{Reflector, Reflectable};
use dom::node::Node;
use js::jsapi::JSObject;
@@ -57,11 +58,11 @@ use js::jsval::JSVal;
use layout_interface::TrustedNodeAddress;
use script_task::STACK_ROOTS;
-use util::smallvec::{SmallVec, SmallVec32};
-
use core::nonzero::NonZero;
+use libc;
use std::cell::{Cell, UnsafeCell};
use std::default::Default;
+use std::intrinsics::return_address;
use std::marker::PhantomData;
use std::mem;
use std::ops::Deref;
@@ -173,7 +174,7 @@ impl<T: Reflectable> Temporary<T> {
}
/// Create a stack-bounded root for this value.
- pub fn root(self) -> Root<T> {
+ pub fn root(&self) -> Root<T> {
STACK_ROOTS.with(|ref collection| {
let RootCollectionPtr(collection) = collection.get().unwrap();
unsafe {
@@ -610,7 +611,7 @@ impl<T: Assignable<U>, U: Reflectable> TemporaryPushable<T> for Vec<JS<U>> {
/// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*]
/// (https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting).
pub struct RootCollection {
- roots: UnsafeCell<SmallVec32<*mut JSObject>>,
+ roots: UnsafeCell<RootedVec<*mut JSObject>>,
}
/// A pointer to a RootCollection, for use in global variables.
@@ -621,8 +622,12 @@ impl Copy for RootCollectionPtr {}
impl RootCollection {
/// Create an empty collection of roots
pub fn new() -> RootCollection {
+ let addr = unsafe {
+ return_address() as *const libc::c_void
+ };
+
RootCollection {
- roots: UnsafeCell::new(SmallVec32::new()),
+ roots: UnsafeCell::new(RootedVec::new_with_destination_address(addr)),
}
}
@@ -632,7 +637,6 @@ impl RootCollection {
let roots = self.roots.get();
(*roots).push(untracked_js_ptr);
debug!(" rooting {:?}", untracked_js_ptr);
- assert!(!(*roots).spilled());
}
}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index bfdb1e83a6c..9d4e548e161 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -241,6 +241,7 @@ no_jsmanaged_fields!(ImageCacheTask, ScriptControlChan);
no_jsmanaged_fields!(Atom, Namespace, Timer);
no_jsmanaged_fields!(Trusted<T>);
no_jsmanaged_fields!(PropertyDeclarationBlock);
+no_jsmanaged_fields!(HashSet<T>);
// These three are interdependent, if you plan to put jsmanaged data
// in one of these make sure it is propagated properly to containing structs
no_jsmanaged_fields!(SubpageId, WindowSizeData, PipelineId);
@@ -389,12 +390,21 @@ impl<T: VecRootableType> RootedVec<T> {
/// Create a vector of items of type T that is rooted for
/// the lifetime of this struct
pub fn new() -> RootedVec<T> {
+ let addr = unsafe {
+ return_address() as *const libc::c_void
+ };
+
+ RootedVec::new_with_destination_address(addr)
+ }
+
+ /// Create a vector of items of type T. This constructor is specific
+ /// for RootCollection.
+ pub fn new_with_destination_address(addr: *const libc::c_void) -> RootedVec<T> {
unsafe {
- RootedCollectionSet::add::<T>(&*(return_address() as *const _));
+ RootedCollectionSet::add::<T>(&*(addr as *const _));
}
RootedVec::<T> { v: vec!() }
}
-
}
#[unsafe_destructor]
@@ -419,7 +429,7 @@ impl<T> DerefMut for RootedVec<T> {
/// SM Callback that traces the rooted collections
-pub unsafe extern fn trace_collections(tracer: *mut JSTracer, _data: *mut libc::c_void) {
+pub unsafe fn trace_collections(tracer: *mut JSTracer) {
ROOTED_COLLECTIONS.with(|ref collections| {
let collections = collections.borrow();
collections.trace(tracer);
diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs
index 7faca76b6c6..7d4bf9b88c7 100644
--- a/components/script/dom/bindings/utils.rs
+++ b/components/script/dom/bindings/utils.rs
@@ -7,14 +7,17 @@
use dom::bindings::codegen::PrototypeList;
use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH;
use dom::bindings::conversions::{native_from_reflector_jsmanaged, is_dom_class};
-use dom::bindings::error::throw_type_error;
+use dom::bindings::error::{Error, ErrorResult, Fallible, throw_type_error};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{Temporary, Root};
use dom::browsercontext;
use dom::window;
+use util::namespace;
+use util::str::DOMString;
use libc;
use libc::c_uint;
+use std::borrow::ToOwned;
use std::boxed;
use std::cell::Cell;
use std::ffi::CString;
@@ -43,6 +46,7 @@ use js::rust::with_compartment;
use js::{JSPROP_ENUMERATE, JSPROP_READONLY, JSPROP_PERMANENT};
use js::JSFUN_CONSTRUCTOR;
use js;
+use string_cache::{Atom, Namespace};
/// Proxy handler for a WindowProxy.
pub struct WindowProxyHandler(pub *const libc::c_void);
@@ -176,36 +180,38 @@ unsafe impl Sync for NativeProperties {}
pub type NonNullJSNative =
unsafe extern "C" fn (arg1: *mut JSContext, arg2: c_uint, arg3: *mut JSVal) -> JSBool;
-/// Creates the *interface prototype object* and the *interface object* (if
-/// needed).
+/// Creates the *interface prototype object* (if a `proto_class` is given)
+/// and the *interface object* (if a `constructor` is given).
/// Fails on JSAPI failure.
pub fn do_create_interface_objects(cx: *mut JSContext, global: *mut JSObject,
receiver: *mut JSObject,
proto_proto: *mut JSObject,
- proto_class: &'static JSClass,
+ proto_class: Option<&'static JSClass>,
constructor: Option<(NonNullJSNative, &'static str, u32)>,
dom_class: *const DOMClass,
members: &'static NativeProperties)
-> *mut JSObject {
- let proto = create_interface_prototype_object(cx, global, proto_proto,
- proto_class, members);
-
unsafe {
- JS_SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
- PrivateValue(dom_class as *const libc::c_void));
- }
-
- match constructor {
- Some((native, name, nargs)) => {
+ let proto = match proto_class {
+ Some(proto_class) => {
+ let proto = create_interface_prototype_object(cx, global, proto_proto,
+ proto_class, members);
+ JS_SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
+ PrivateValue(dom_class as *const libc::c_void));
+ proto
+ },
+ None => ptr::null_mut()
+ };
+
+ if let Some((native, name, nargs)) = constructor {
let s = CString::new(name).unwrap();
create_interface_object(cx, global, receiver,
native, nargs, proto,
members, s.as_ptr())
- },
- None => (),
- }
+ }
- proto
+ proto
+ }
}
/// Creates the *interface object*.
@@ -604,6 +610,68 @@ pub unsafe fn delete_property_by_id(cx: *mut JSContext, object: *mut JSObject,
return true;
}
+/// Validate a qualified name. See https://dom.spec.whatwg.org/#validate for details.
+pub fn validate_qualified_name(qualified_name: &str) -> ErrorResult {
+ match xml_name_type(qualified_name) {
+ XMLName::InvalidXMLName => {
+ // Step 1.
+ return Err(Error::InvalidCharacter);
+ },
+ XMLName::Name => {
+ // Step 2.
+ return Err(Error::Namespace);
+ },
+ XMLName::QName => Ok(())
+ }
+}
+
+/// Validate a namespace and qualified name and extract their parts.
+/// See https://dom.spec.whatwg.org/#validate-and-extract for details.
+pub fn validate_and_extract(namespace: Option<DOMString>, qualified_name: &str)
+ -> Fallible<(Namespace, Option<DOMString>, Atom)> {
+ // Step 1.
+ let namespace = namespace::from_domstring(namespace);
+
+ // Step 2.
+ try!(validate_qualified_name(qualified_name));
+
+ let (prefix, local_name) = if qualified_name.contains(":") {
+ // Step 5.
+ let mut parts = qualified_name.splitn(1, ':');
+ let prefix = parts.next().unwrap();
+ debug_assert!(!prefix.is_empty());
+ let local_name = parts.next().unwrap();
+ debug_assert!(!local_name.contains(":"));
+ (Some(prefix), local_name)
+ } else {
+ (None, qualified_name)
+ };
+
+ match (namespace, prefix) {
+ (ns!(""), Some(_)) => {
+ // Step 6.
+ Err(Error::Namespace)
+ },
+ (ref ns, Some("xml")) if ns != &ns!(XML) => {
+ // Step 7.
+ Err(Error::Namespace)
+ },
+ (ref ns, p) if ns != &ns!(XMLNS) &&
+ (qualified_name == "xmlns" || p == Some("xmlns")) => {
+ // Step 8.
+ Err(Error::Namespace)
+ },
+ (ns!(XMLNS), p) if qualified_name != "xmlns" && p != Some("xmlns") => {
+ // Step 9.
+ Err(Error::Namespace)
+ },
+ (ns, p) => {
+ // Step 10.
+ Ok((ns, p.map(|s| s.to_owned()), Atom::from_slice(local_name)))
+ }
+ }
+}
+
/// Results of `xml_name_type`.
#[derive(PartialEq)]
#[allow(missing_docs)]
diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs
index fc8573923d0..17dda025def 100644
--- a/components/script/dom/canvasrenderingcontext2d.rs
+++ b/components/script/dom/canvasrenderingcontext2d.rs
@@ -6,9 +6,9 @@ use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding;
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasRenderingContext2DMethods;
use dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::CanvasWindingRule;
use dom::bindings::codegen::Bindings::ImageDataBinding::ImageDataMethods;
-use dom::bindings::codegen::UnionTypes::HTMLCanvasElementOrCanvasRenderingContext2D;
+use dom::bindings::codegen::UnionTypes::HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D;
use dom::bindings::codegen::UnionTypes::StringOrCanvasGradientOrCanvasPattern;
-use dom::bindings::error::Error::{IndexSize, NotSupported, Type};
+use dom::bindings::error::Error::{IndexSize, NotSupported, Type, InvalidState};
use dom::bindings::error::Fallible;
use dom::bindings::global::{GlobalRef, GlobalField};
use dom::bindings::js::{JS, JSRef, LayoutJS, Temporary};
@@ -16,7 +16,9 @@ use dom::bindings::num::Finite;
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::canvasgradient::{CanvasGradient, CanvasGradientStyle, ToFillOrStrokeStyle};
use dom::htmlcanvaselement::{HTMLCanvasElement, HTMLCanvasElementHelpers};
+use dom::htmlimageelement::{HTMLImageElement, HTMLImageElementHelpers};
use dom::imagedata::{ImageData, ImageDataHelpers};
+use dom::node::{window_from_node};
use cssparser::Color as CSSColor;
use cssparser::{Parser, RGBA, ToCss};
@@ -28,19 +30,30 @@ use geom::size::Size2D;
use canvas::canvas_paint_task::{CanvasMsg, CanvasPaintTask, FillOrStrokeStyle};
use canvas::canvas_paint_task::{LinearGradientStyle, RadialGradientStyle};
+use net_traits::image::base::Image;
+use net_traits::image_cache_task::{ImageResponseMsg, Msg};
+use png::PixelsByColorType;
+
use std::borrow::ToOwned;
use std::cell::Cell;
use std::num::{Float, ToPrimitive};
+use std::sync::{Arc};
use std::sync::mpsc::{channel, Sender};
+use url::Url;
+use util::vec::byte_swap;
+
#[dom_struct]
pub struct CanvasRenderingContext2D {
reflector_: Reflector,
global: GlobalField,
renderer: Sender<CanvasMsg>,
canvas: JS<HTMLCanvasElement>,
+ global_alpha: Cell<f64>,
image_smoothing_enabled: Cell<bool>,
stroke_color: Cell<RGBA>,
+ line_width: Cell<f64>,
+ miter_limit: Cell<f64>,
fill_color: Cell<RGBA>,
transform: Cell<Matrix2D<f32>>,
}
@@ -59,8 +72,11 @@ impl CanvasRenderingContext2D {
global: GlobalField::from_rooted(&global),
renderer: CanvasPaintTask::start(size),
canvas: JS::from_rooted(canvas),
+ global_alpha: Cell::new(1.0),
image_smoothing_enabled: Cell::new(true),
stroke_color: Cell::new(black),
+ line_width: Cell::new(1.0),
+ miter_limit: Cell::new(10.0),
fill_color: Cell::new(black),
transform: Cell::new(Matrix2D::identity()),
}
@@ -85,10 +101,9 @@ impl CanvasRenderingContext2D {
// source rectangle = area of the original image to be copied
// destination rectangle = area of the destination canvas where the source image is going to be drawn
fn adjust_source_dest_rects(&self,
- canvas: JSRef<HTMLCanvasElement>,
+ image_size: Size2D<f64>,
sx: f64, sy: f64, sw: f64, sh: f64,
- dx: f64, dy: f64, dw: f64, dh: f64) -> (Rect<i32>, Rect<i32>) {
- let image_size = canvas.get_size();
+ dx: f64, dy: f64, dw: f64, dh: f64) -> (Rect<f64>, Rect<f64>) {
let image_rect = Rect(Point2D(0f64, 0f64),
Size2D(image_size.width as f64, image_size.height as f64));
@@ -112,15 +127,13 @@ impl CanvasRenderingContext2D {
// The destination rectangle is the rectangle whose corners are the four points (dx, dy),
// (dx+dw, dy), (dx+dw, dy+dh), (dx, dy+dh).
- let dest_rect = Rect(Point2D(dx.to_i32().unwrap(),
- dy.to_i32().unwrap()),
- Size2D(dest_rect_width_scaled.to_i32().unwrap(),
- dest_rect_height_scaled.to_i32().unwrap()));
+ let dest_rect = Rect(Point2D(dx, dy),
+ Size2D(dest_rect_width_scaled, dest_rect_height_scaled));
- let source_rect = Rect(Point2D(source_rect_clipped.origin.x.to_i32().unwrap(),
- source_rect_clipped.origin.y.to_i32().unwrap()),
- Size2D(source_rect_clipped.size.width.to_i32().unwrap(),
- source_rect_clipped.size.height.to_i32().unwrap()));
+ let source_rect = Rect(Point2D(source_rect_clipped.origin.x,
+ source_rect_clipped.origin.y),
+ Size2D(source_rect_clipped.size.width,
+ source_rect_clipped.size.height));
return (source_rect, dest_rect)
}
@@ -150,39 +163,115 @@ impl CanvasRenderingContext2D {
canvas: JSRef<HTMLCanvasElement>,
sx: f64, sy: f64, sw: f64, sh: f64,
dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> {
-
// 1. Check the usability of the image argument
if !canvas.is_valid() {
- return Ok(())
+ return Err(InvalidState)
}
+ let canvas_size = canvas.get_size();
+ let image_size = Size2D(canvas_size.width as f64, canvas_size.height as f64);
// 2. Establish the source and destination rectangles
- let (source_rect, dest_rect) = self.adjust_source_dest_rects(canvas, sx, sy, sw, sh, dx, dy, dw, dh);
+ let (source_rect, dest_rect) = self.adjust_source_dest_rects(image_size, sx, sy, sw, sh, dx, dy, dw, dh);
if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) {
return Err(IndexSize)
}
let smoothing_enabled = self.image_smoothing_enabled.get();
- let canvas_size = canvas.get_size();
// If the source and target canvas are the same
let msg = if self.canvas.root().r() == canvas {
- CanvasMsg::DrawImageSelf(canvas_size, dest_rect, source_rect, smoothing_enabled)
+ CanvasMsg::DrawImageSelf(image_size, dest_rect, source_rect, smoothing_enabled)
} else { // Source and target canvases are different
let context = canvas.get_2d_context().root();
let renderer = context.r().get_renderer();
let (sender, receiver) = channel::<Vec<u8>>();
// Reads pixels from source image
- renderer.send(CanvasMsg::GetImageData(source_rect, canvas_size, sender)).unwrap();
+ renderer.send(CanvasMsg::GetImageData(source_rect, image_size, sender)).unwrap();
let imagedata = receiver.recv().unwrap();
// Writes pixels to destination canvas
- CanvasMsg::DrawImage(imagedata, dest_rect, source_rect, smoothing_enabled)
+ CanvasMsg::DrawImage(imagedata, source_rect.size, dest_rect, source_rect, smoothing_enabled)
};
self.renderer.send(msg).unwrap();
Ok(())
}
+
+ fn draw_image_data(&self,
+ image_data: Vec<u8>,
+ image_size: Size2D<f64>,
+ sx: f64, sy: f64, sw: f64, sh: f64,
+ dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> {
+ // Establish the source and destination rectangles
+ let (source_rect, dest_rect) = self.adjust_source_dest_rects(image_size, sx, sy, sw, sh, dx, dy, dw, dh);
+
+ if !is_rect_valid(source_rect) || !is_rect_valid(dest_rect) {
+ return Err(IndexSize)
+ }
+
+ let smoothing_enabled = self.image_smoothing_enabled.get();
+ self.renderer.send(CanvasMsg::DrawImage(
+ image_data, image_size, dest_rect,
+ source_rect, smoothing_enabled)).unwrap();
+ Ok(())
+ }
+
+ fn fetch_image_data(&self,
+ image_element: &JSRef<HTMLImageElement>)
+ -> Option<(Vec<u8>, Size2D<f64>)> {
+ let url = match image_element.get_url() {
+ Some(url) => url,
+ None => return None,
+ };
+
+ let img = match self.request_image_from_cache(url) {
+ Some(img) => img,
+ None => return None,
+ };
+
+ let image_size = Size2D(img.width as f64, img.height as f64);
+ let mut image_data = match img.pixels {
+ PixelsByColorType::RGBA8(ref pixels) => pixels.to_vec(),
+ PixelsByColorType::K8(_) => panic!("K8 color type not supported"),
+ PixelsByColorType::RGB8(_) => panic!("RGB8 color type not supported"),
+ PixelsByColorType::KA8(_) => panic!("KA8 color type not supported"),
+ };
+ // Pixels come from cache in BGRA order and drawImage expects RGBA so we
+ // have to swap the color values
+ {
+ let mut pixel_colors = image_data.as_mut_slice();
+ byte_swap(pixel_colors);
+ }
+ return Some((image_data, image_size));
+ }
+
+ fn request_image_from_cache(&self, url: Url) -> Option<Arc<Box<Image>>> {
+ let canvas = self.canvas.root();
+ let window = window_from_node(canvas.r()).root();
+ let window = window.r();
+ let image_cache_task = window.image_cache_task().clone();
+ image_cache_task.send(Msg::Prefetch(url.clone()));
+ image_cache_task.send(Msg::Decode(url.clone()));
+ let (response_chan, response_port) = channel();
+ image_cache_task.send(Msg::WaitForImage(url, response_chan));
+ match response_port.recv().unwrap() {
+ ImageResponseMsg::ImageReady(image) => Some(image),
+ ImageResponseMsg::ImageFailed => None,
+ _ => panic!("Image Cache: Unknown Result")
+ }
+ }
+
+ fn create_drawable_rect(&self, x: f64, y: f64, w: f64, h: f64) -> Option<Rect<f32>> {
+ if !([x, y, w, h].iter().all(|val| val.is_finite())) {
+ return None;
+ }
+
+ if w == 0.0 && h == 0.0 {
+ return None;
+ }
+
+ Some(Rect(Point2D(x as f32, y as f32), Size2D(w as f32, h as f32)))
+ }
}
pub trait CanvasRenderingContext2DHelpers {
@@ -269,34 +358,35 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
self.update_transform()
}
- fn FillRect(self, x: f64, y: f64, width: f64, height: f64) {
- if !(x.is_finite() && y.is_finite() &&
- width.is_finite() && height.is_finite()) {
+ fn GlobalAlpha(self) -> f64 {
+ self.global_alpha.get()
+ }
+
+ fn SetGlobalAlpha(self, alpha: f64) {
+ if !alpha.is_finite() || alpha > 1.0 || alpha < 0.0 {
return;
}
- let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32));
- self.renderer.send(CanvasMsg::FillRect(rect)).unwrap();
+ self.global_alpha.set(alpha);
+ self.renderer.send(CanvasMsg::SetGlobalAlpha(alpha as f32)).unwrap()
}
- fn ClearRect(self, x: f64, y: f64, width: f64, height: f64) {
- if !(x.is_finite() && y.is_finite() &&
- width.is_finite() && height.is_finite()) {
- return;
+ fn FillRect(self, x: f64, y: f64, width: f64, height: f64) {
+ if let Some(rect) = self.create_drawable_rect(x, y, width, height) {
+ self.renderer.send(CanvasMsg::FillRect(rect)).unwrap();
}
+ }
- let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32));
- self.renderer.send(CanvasMsg::ClearRect(rect)).unwrap();
+ fn ClearRect(self, x: f64, y: f64, width: f64, height: f64) {
+ if let Some(rect) = self.create_drawable_rect(x, y, width, height) {
+ self.renderer.send(CanvasMsg::ClearRect(rect)).unwrap();
+ }
}
fn StrokeRect(self, x: f64, y: f64, width: f64, height: f64) {
- if !(x.is_finite() && y.is_finite() &&
- width.is_finite() && height.is_finite()) {
- return;
+ if let Some(rect) = self.create_drawable_rect(x, y, width, height) {
+ self.renderer.send(CanvasMsg::StrokeRect(rect)).unwrap();
}
-
- let rect = Rect(Point2D(x as f32, y as f32), Size2D(width as f32, height as f32));
- self.renderer.send(CanvasMsg::StrokeRect(rect)).unwrap();
}
fn BeginPath(self) {
@@ -316,7 +406,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
}
// https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage
- fn DrawImage(self, image: HTMLCanvasElementOrCanvasRenderingContext2D,
+ fn DrawImage(self, image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
dx: f64, dy: f64) -> Fallible<()> {
if !(dx.is_finite() && dy.is_finite()) {
return Ok(());
@@ -330,7 +420,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
let sy: f64 = 0f64;
match image {
- HTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
+ HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
let canvas = image.root();
let canvas_size = canvas.r().get_size();
let dw: f64 = canvas_size.width as f64;
@@ -341,7 +431,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
sx, sy, sw, sh,
dx, dy, dw, dh)
}
- HTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
+ HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
let image = image.root();
let context = image.r();
let canvas = context.Canvas().root();
@@ -354,11 +444,31 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
sx, sy, sw, sh,
dx, dy, dw, dh)
}
+ HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
+ let image = image.root();
+ let image_element = image.r();
+ // https://html.spec.whatwg.org/multipage/embedded-content.html#img-error
+ // If the image argument is an HTMLImageElement object that is in the broken state,
+ // then throw an InvalidStateError exception
+ let (image_data, image_size) = match self.fetch_image_data(&image_element) {
+ Some((data, size)) => (data, size),
+ None => return Err(InvalidState),
+ };
+ let dw: f64 = image_size.width as f64;
+ let dh: f64 = image_size.height as f64;
+ let sw: f64 = dw;
+ let sh: f64 = dh;
+ return self.draw_image_data(image_data,
+ image_size,
+ sx, sy, sw, sh,
+ dx, dy, dw, dh)
+ }
+
}
}
// https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage
- fn DrawImage_(self, image: HTMLCanvasElementOrCanvasRenderingContext2D,
+ fn DrawImage_(self, image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> {
if !(dx.is_finite() && dy.is_finite() &&
dw.is_finite() && dh.is_finite()) {
@@ -373,7 +483,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
let sy: f64 = 0f64;
match image {
- HTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
+ HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
let canvas = image.root();
let canvas_size = canvas.r().get_size();
let sw: f64 = canvas_size.width as f64;
@@ -382,7 +492,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
sx, sy, sw, sh,
dx, dy, dw, dh)
}
- HTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
+ HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
let image = image.root();
let context = image.r();
let canvas = context.Canvas().root();
@@ -393,11 +503,28 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
sx, sy, sw, sh,
dx, dy, dw, dh)
}
+ HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
+ let image = image.root();
+ let image_element = image.r();
+ // https://html.spec.whatwg.org/multipage/embedded-content.html#img-error
+ // If the image argument is an HTMLImageElement object that is in the broken state,
+ // then throw an InvalidStateError exception
+ let (image_data, image_size) = match self.fetch_image_data(&image_element) {
+ Some((data, size)) => (data, size),
+ None => return Err(InvalidState),
+ };
+ let sw: f64 = image_size.width as f64;
+ let sh: f64 = image_size.height as f64;
+ return self.draw_image_data(image_data,
+ image_size,
+ sx, sy, sw, sh,
+ dx, dy, dw, dh)
+ }
}
}
// https://html.spec.whatwg.org/multipage/scripting.html#dom-context-2d-drawimage
- fn DrawImage__(self, image: HTMLCanvasElementOrCanvasRenderingContext2D,
+ fn DrawImage__(self, image: HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D,
sx: f64, sy: f64, sw: f64, sh: f64,
dx: f64, dy: f64, dw: f64, dh: f64) -> Fallible<()> {
if !(sx.is_finite() && sy.is_finite() && sw.is_finite() && sh.is_finite() &&
@@ -406,13 +533,13 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
}
match image {
- HTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
+ HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLCanvasElement(image) => {
let canvas = image.root();
return self.draw_html_canvas_element(canvas.r(),
sx, sy, sw, sh,
dx, dy, dw, dh)
}
- HTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
+ HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eCanvasRenderingContext2D(image) => {
let image = image.root();
let context = image.r();
let canvas = context.Canvas().root();
@@ -420,6 +547,21 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
sx, sy, sw, sh,
dx, dy, dw, dh)
}
+ HTMLImageElementOrHTMLCanvasElementOrCanvasRenderingContext2D::eHTMLImageElement(image) => {
+ let image = image.root();
+ let image_element = image.r();
+ // https://html.spec.whatwg.org/multipage/embedded-content.html#img-error
+ // If the image argument is an HTMLImageElement object that is in the broken state,
+ // then throw an InvalidStateError exception
+ let (image_data, image_size) = match self.fetch_image_data(&image_element) {
+ Some((data, size)) => (data, size),
+ None => return Err(InvalidState),
+ };
+ return self.draw_image_data(image_data,
+ image_size,
+ sx, sy, sw, sh,
+ dx, dy, dw, dh)
+ }
}
}
@@ -574,6 +716,8 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
}
fn GetImageData(self, sx: Finite<f64>, sy: Finite<f64>, sw: Finite<f64>, sh: Finite<f64>) -> Fallible<Temporary<ImageData>> {
+ let sx = *sx;
+ let sy = *sy;
let sw = *sw;
let sh = *sh;
@@ -582,14 +726,18 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
}
let (sender, receiver) = channel::<Vec<u8>>();
- let dest_rect = Rect(Point2D(sx.to_i32().unwrap(), sy.to_i32().unwrap()), Size2D(sw.to_i32().unwrap(), sh.to_i32().unwrap()));
+ let dest_rect = Rect(Point2D(sx as f64, sy as f64), Size2D(sw as f64, sh as f64));
let canvas_size = self.canvas.root().r().get_size();
+ let canvas_size = Size2D(canvas_size.width as f64, canvas_size.height as f64);
self.renderer.send(CanvasMsg::GetImageData(dest_rect, canvas_size, sender)).unwrap();
let data = receiver.recv().unwrap();
Ok(ImageData::new(self.global.root().r(), sw.abs().to_u32().unwrap(), sh.abs().to_u32().unwrap(), Some(data)))
}
fn PutImageData(self, imagedata: JSRef<ImageData>, dx: Finite<f64>, dy: Finite<f64>) {
+ let dx = *dx;
+ let dy = *dy;
+
// XXX:
// By the spec: http://www.w3.org/html/wg/drafts/2dcontext/html5_canvas_CR/#dom-context-2d-putimagedata
// "If any of the arguments to the method are infinite or NaN, the method must throw a NotSupportedError exception"
@@ -597,13 +745,22 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
// they will be TypeError by WebIDL spec before call this methods.
let data = imagedata.get_data_array(&self.global.root().r());
- let image_data_rect = Rect(Point2D(dx.to_i32().unwrap(), dy.to_i32().unwrap()), imagedata.get_size());
+ let image_data_size = imagedata.get_size();
+ let image_data_size = Size2D(image_data_size.width as f64, image_data_size.height as f64);
+ let image_data_rect = Rect(Point2D(dx, dy), image_data_size);
let dirty_rect = None;
self.renderer.send(CanvasMsg::PutImageData(data, image_data_rect, dirty_rect)).unwrap()
}
fn PutImageData_(self, imagedata: JSRef<ImageData>, dx: Finite<f64>, dy: Finite<f64>,
dirtyX: Finite<f64>, dirtyY: Finite<f64>, dirtyWidth: Finite<f64>, dirtyHeight: Finite<f64>) {
+ let dx = *dx;
+ let dy = *dy;
+ let dirtyX = *dirtyX;
+ let dirtyY = *dirtyY;
+ let dirtyWidth = *dirtyWidth;
+ let dirtyHeight = *dirtyHeight;
+
// XXX:
// By the spec: http://www.w3.org/html/wg/drafts/2dcontext/html5_canvas_CR/#dom-context-2d-putimagedata
// "If any of the arguments to the method are infinite or NaN, the method must throw a NotSupportedError exception"
@@ -611,12 +768,11 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
// they will be TypeError by WebIDL spec before call this methods.
let data = imagedata.get_data_array(&self.global.root().r());
- let image_data_rect = Rect(Point2D(dx.to_i32().unwrap(), dy.to_i32().unwrap()),
- Size2D(imagedata.Width().to_i32().unwrap(),
- imagedata.Height().to_i32().unwrap()));
- let dirty_rect = Some(Rect(Point2D(dirtyX.to_i32().unwrap(), dirtyY.to_i32().unwrap()),
- Size2D(dirtyWidth.to_i32().unwrap(),
- dirtyHeight.to_i32().unwrap())));
+ let image_data_rect = Rect(Point2D(dx, dy),
+ Size2D(imagedata.Width() as f64,
+ imagedata.Height() as f64));
+ let dirty_rect = Some(Rect(Point2D(dirtyX, dirtyY),
+ Size2D(dirtyWidth, dirtyHeight)));
self.renderer.send(CanvasMsg::PutImageData(data, image_data_rect, dirty_rect)).unwrap()
}
@@ -649,6 +805,32 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D>
Ok(CanvasGradient::new(self.global.root().r(),
CanvasGradientStyle::Radial(RadialGradientStyle::new(x0, y0, r0, x1, y1, r1, Vec::new()))))
}
+
+ fn LineWidth(self) -> f64 {
+ self.line_width.get()
+ }
+
+ fn SetLineWidth(self, width: f64) {
+ if !width.is_finite() || width <= 0.0 {
+ return;
+ }
+
+ self.line_width.set(width);
+ self.renderer.send(CanvasMsg::SetLineWidth(width as f32)).unwrap()
+ }
+
+ fn MiterLimit(self) -> f64 {
+ self.miter_limit.get()
+ }
+
+ fn SetMiterLimit(self, limit: f64) {
+ if !limit.is_finite() || limit <= 0.0 {
+ return;
+ }
+
+ self.miter_limit.set(limit);
+ self.renderer.send(CanvasMsg::SetMiterLimit(limit as f32)).unwrap()
+ }
}
#[unsafe_destructor]
@@ -667,6 +849,6 @@ pub fn parse_color(string: &str) -> Result<RGBA,()> {
// Used by drawImage to determine if a source or destination rectangle is valid
// Origin coordinates and size cannot be negative. Size has to be greater than zero
-fn is_rect_valid(rect: Rect<i32>) -> bool {
- rect.origin.x >= 0 && rect.origin.y >= 0 && rect.size.width > 0 && rect.size.height > 0
+fn is_rect_valid(rect: Rect<f64>) -> bool {
+ rect.size.width > 0.0 && rect.size.height > 0.0
}
diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs
index 1f2b18f1b99..92f3283c759 100644
--- a/components/script/dom/characterdata.rs
+++ b/components/script/dom/characterdata.rs
@@ -6,11 +6,13 @@
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
-use dom::bindings::codegen::InheritTypes::{CharacterDataDerived, NodeCast};
+use dom::bindings::codegen::InheritTypes::{CharacterDataDerived, ElementCast};
+use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::error::{Fallible, ErrorResult};
use dom::bindings::error::Error::IndexSize;
-use dom::bindings::js::JSRef;
+use dom::bindings::js::{JSRef, LayoutJS, Temporary};
use dom::document::Document;
+use dom::element::Element;
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::node::{Node, NodeHelpers, NodeTypeId};
@@ -18,6 +20,7 @@ use util::str::DOMString;
use std::borrow::ToOwned;
use std::cell::Ref;
+use std::cmp;
#[dom_struct]
pub struct CharacterData {
@@ -43,67 +46,59 @@ impl CharacterData {
data: DOMRefCell::new(data),
}
}
-
- #[inline]
- pub fn node<'a>(&'a self) -> &'a Node {
- &self.node
- }
-
- #[inline]
- pub fn data(&self) -> Ref<DOMString> {
- self.data.borrow()
- }
-
- #[inline]
- pub fn set_data(&self, data: DOMString) {
- *self.data.borrow_mut() = data;
- }
-
- #[inline]
- #[allow(unsafe_code)]
- pub unsafe fn data_for_layout<'a>(&'a self) -> &'a str {
- self.data.borrow_for_layout().as_slice()
- }
-
}
impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> {
+ // https://dom.spec.whatwg.org/#dom-characterdata-data
fn Data(self) -> DOMString {
// FIXME(https://github.com/rust-lang/rust/issues/23338)
let data = self.data.borrow();
data.clone()
}
- fn SetData(self, arg: DOMString) -> ErrorResult {
- *self.data.borrow_mut() = arg;
- Ok(())
+ // https://dom.spec.whatwg.org/#dom-characterdata-data
+ fn SetData(self, data: DOMString) {
+ *self.data.borrow_mut() = data;
}
+ // https://dom.spec.whatwg.org/#dom-characterdata-length
fn Length(self) -> u32 {
// FIXME(https://github.com/rust-lang/rust/issues/23338)
let data = self.data.borrow();
data.chars().count() as u32
}
+ // https://dom.spec.whatwg.org/#dom-characterdata-substringdata
fn SubstringData(self, offset: u32, count: u32) -> Fallible<DOMString> {
- // FIXME(https://github.com/rust-lang/rust/issues/23338)
let data = self.data.borrow();
- Ok(data.slice_chars(offset as usize, (offset + count) as usize).to_owned())
+ // Step 1.
+ let len = data.chars().count();
+ if offset as usize > len {
+ // Step 2.
+ return Err(IndexSize);
+ }
+ // Step 3.
+ let end = cmp::min((offset + count) as usize, len);
+ // Step 4.
+ Ok(data.slice_chars(offset as usize, end).to_owned())
}
- fn AppendData(self, arg: DOMString) -> ErrorResult {
- self.data.borrow_mut().push_str(arg.as_slice());
- Ok(())
+ // https://dom.spec.whatwg.org/#dom-characterdata-appenddata
+ fn AppendData(self, data: DOMString) {
+ self.data.borrow_mut().push_str(&data);
}
+ // https://dom.spec.whatwg.org/#dom-characterdata-insertdata
fn InsertData(self, offset: u32, arg: DOMString) -> ErrorResult {
self.ReplaceData(offset, 0, arg)
}
+ // https://dom.spec.whatwg.org/#dom-characterdata-deletedata
fn DeleteData(self, offset: u32, count: u32) -> ErrorResult {
self.ReplaceData(offset, count, "".to_owned())
}
+ // https://dom.spec.whatwg.org/#dom-characterdata-replacedata
fn ReplaceData(self, offset: u32, count: u32, arg: DOMString) -> ErrorResult {
let length = self.data.borrow().chars().count() as u32;
if offset > length {
@@ -127,5 +122,40 @@ impl<'a> CharacterDataMethods for JSRef<'a, CharacterData> {
let node: JSRef<Node> = NodeCast::from_ref(self);
node.remove_self();
}
+
+ // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling
+ fn GetPreviousElementSibling(self) -> Option<Temporary<Element>> {
+ NodeCast::from_ref(self).preceding_siblings()
+ .filter_map(ElementCast::to_temporary).next()
+ }
+
+ // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-nextelementsibling
+ fn GetNextElementSibling(self) -> Option<Temporary<Element>> {
+ NodeCast::from_ref(self).following_siblings()
+ .filter_map(ElementCast::to_temporary).next()
+ }
+}
+
+pub trait CharacterDataHelpers<'a> {
+ fn data(self) -> Ref<'a, DOMString>;
}
+impl<'a> CharacterDataHelpers<'a> for JSRef<'a, CharacterData> {
+ #[inline]
+ fn data(self) -> Ref<'a, DOMString> {
+ self.extended_deref().data.borrow()
+ }
+}
+
+#[allow(unsafe_code)]
+pub trait LayoutCharacterDataHelpers {
+ unsafe fn data_for_layout<'a>(&'a self) -> &'a str;
+}
+
+#[allow(unsafe_code)]
+impl LayoutCharacterDataHelpers for LayoutJS<CharacterData> {
+ #[inline]
+ unsafe fn data_for_layout<'a>(&'a self) -> &'a str {
+ &(*self.unsafe_get()).data.borrow_for_layout()
+ }
+}
diff --git a/components/script/dom/comment.rs b/components/script/dom/comment.rs
index 85cca18b2b2..743e935db01 100644
--- a/components/script/dom/comment.rs
+++ b/components/script/dom/comment.rs
@@ -42,10 +42,5 @@ impl Comment {
let document = global.as_window().Document().root();
Ok(Comment::new(data, document.r()))
}
-
- #[inline]
- pub fn characterdata<'a>(&'a self) -> &'a CharacterData {
- &self.characterdata
- }
}
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index 7e8a570960d..f001f0268f3 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -18,24 +18,26 @@ use dom::bindings::codegen::InheritTypes::{EventTargetCast, HTMLAnchorElementCas
use dom::bindings::codegen::InheritTypes::{HTMLAnchorElementDerived, HTMLAppletElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLAreaElementDerived, HTMLEmbedElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, HTMLImageElementDerived};
-use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived};
+use dom::bindings::codegen::InheritTypes::{HTMLScriptElementDerived, CharacterDataCast};
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::error::Error::{NotSupported, InvalidCharacter, Security};
-use dom::bindings::error::Error::{HierarchyRequest, Namespace};
+use dom::bindings::error::Error::HierarchyRequest;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable};
use dom::bindings::js::{OptionalRootable, RootedReference};
use dom::bindings::refcounted::Trusted;
+use dom::bindings::trace::RootedVec;
use dom::bindings::utils::reflect_dom_object;
-use dom::bindings::utils::xml_name_type;
-use dom::bindings::utils::XMLName::{QName, Name, InvalidXMLName};
+use dom::bindings::utils::{xml_name_type, validate_and_extract};
+use dom::bindings::utils::XMLName::InvalidXMLName;
+use dom::characterdata::CharacterDataHelpers;
use dom::comment::Comment;
use dom::customevent::CustomEvent;
use dom::documentfragment::DocumentFragment;
use dom::documenttype::DocumentType;
use dom::domimplementation::DOMImplementation;
-use dom::element::{Element, ElementCreator, AttributeHandlers, get_attribute_parts};
-use dom::element::{ElementTypeId, ActivationElementHelpers};
+use dom::element::{Element, ElementCreator, AttributeHandlers};
+use dom::element::{ElementTypeId, ActivationElementHelpers, FocusElementHelpers};
use dom::event::{Event, EventBubbles, EventCancelable, EventHelpers};
use dom::eventtarget::{EventTarget, EventTargetTypeId, EventTargetHelpers};
use dom::htmlanchorelement::HTMLAnchorElement;
@@ -67,7 +69,7 @@ use net_traits::CookieSource::NonHTTP;
use net_traits::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl};
use script_task::Runnable;
use script_traits::{MouseButton, UntrustedNodeAddress};
-use util::{opts, namespace};
+use util::opts;
use util::str::{DOMString, split_html_space_chars};
use layout_interface::{ReflowGoal, ReflowQueryType};
@@ -342,12 +344,12 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
let mut head: usize = 0;
let root: JSRef<Node> = NodeCast::from_ref(root.r());
for node in root.traverse_preorder() {
- let elem: Option<JSRef<Element>> = ElementCast::to_ref(node);
- if let Some(elem) = elem {
+ let node = node.root();
+ if let Some(elem) = ElementCast::to_ref(node.r()) {
if (*elements)[head].root().r() == elem {
head += 1;
}
- if new_node == node || head == elements.len() {
+ if new_node == node.r() || head == elements.len() {
break;
}
}
@@ -378,9 +380,9 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
};
let doc_node: JSRef<Node> = NodeCast::from_ref(self);
doc_node.traverse_preorder()
- .filter_map(HTMLAnchorElementCast::to_ref)
- .find(check_anchor)
- .map(|node| Temporary::from_rooted(ElementCast::from_ref(node)))
+ .filter_map(HTMLAnchorElementCast::to_temporary)
+ .find(|node| check_anchor(&node.root().r()))
+ .map(ElementCast::from_temporary)
})
}
@@ -447,7 +449,9 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
/// Request that the given element receive focus once the current transaction is complete.
fn request_focus(self, elem: JSRef<Element>) {
- self.possibly_focused.assign(Some(elem))
+ if elem.is_focusable_area() {
+ self.possibly_focused.assign(Some(elem))
+ }
}
/// Reassign the focus context to the element that last requested focus during this
@@ -490,7 +494,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
fn dirty_all_nodes(self) {
let root: JSRef<Node> = NodeCast::from_ref(self);
for node in root.traverse_preorder() {
- node.dirty(NodeDamage::OtherNodeDamage)
+ node.root().r().dirty(NodeDamage::OtherNodeDamage)
}
}
@@ -506,20 +510,17 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
}.root();
let el = match ElementCast::to_ref(node.r()) {
- Some(el) => el,
+ Some(el) => Temporary::from_rooted(el),
None => {
- let ancestor = node.r()
- .ancestors()
- .filter_map(ElementCast::to_ref)
- .next();
- match ancestor {
- Some(ancestor) => ancestor,
+ let parent = node.r().parent_node();
+ match parent.and_then(ElementCast::to_temporary) {
+ Some(parent) => parent,
None => return,
}
},
- };
+ }.root();
- let node: JSRef<Node> = NodeCast::from_ref(el);
+ let node: JSRef<Node> = NodeCast::from_ref(el.r());
debug!("clicked on {:?}", node.debug_str());
// Prevent click event if form control element is disabled.
if node.click_event_filter_by_disabled_state() {
@@ -548,7 +549,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
// https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#trusted-events
event.set_trusted(true);
// https://html.spec.whatwg.org/multipage/interaction.html#run-authentic-click-activation-steps
- el.authentic_click_activation(event);
+ el.r().authentic_click_activation(event);
self.commit_focus_transaction();
window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::MouseEvent);
@@ -563,7 +564,10 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
let mouse_over_targets: Vec<JS<Node>> = mouse_over_addresses.iter()
.filter_map(|node_address| {
let node = node::from_untrusted_node_address(js_runtime, *node_address);
- node.root().r().inclusive_ancestors().find(|node| node.is_element()).map(JS::from_rooted)
+ node.root().r().inclusive_ancestors()
+ .map(|node| node.root())
+ .find(|node| node.r().is_element())
+ .map(|node| JS::from_rooted(node.r()))
}).collect();
// Remove hover from any elements in the previous list that are no longer
@@ -814,22 +818,24 @@ impl Document {
}
trait PrivateDocumentHelpers {
- fn createNodeList<F: Fn(JSRef<Node>) -> bool>(self, callback: F) -> Temporary<NodeList>;
+ fn create_node_list<F: Fn(JSRef<Node>) -> bool>(self, callback: F) -> Temporary<NodeList>;
fn get_html_element(self) -> Option<Temporary<HTMLHtmlElement>>;
}
impl<'a> PrivateDocumentHelpers for JSRef<'a, Document> {
- fn createNodeList<F: Fn(JSRef<Node>) -> bool>(self, callback: F) -> Temporary<NodeList> {
+ fn create_node_list<F: Fn(JSRef<Node>) -> bool>(self, callback: F) -> Temporary<NodeList> {
let window = self.window.root();
let document_element = self.GetDocumentElement().root();
- let nodes = match document_element {
- None => vec!(),
- Some(ref root) => {
- let root: JSRef<Node> = NodeCast::from_ref(root.r());
- root.traverse_preorder().filter(|&node| callback(node)).collect()
+ let mut nodes = RootedVec::new();
+ if let Some(ref root) = document_element {
+ for node in NodeCast::from_ref(root.r()).traverse_preorder() {
+ let node = node.root();
+ if callback(node.r()) {
+ nodes.push(node.r().unrooted());
+ }
}
};
- NodeList::new_simple_list(window.r(), nodes)
+ NodeList::new_simple_list(window.r(), &nodes)
}
fn get_html_element(self) -> Option<Temporary<HTMLHtmlElement>> {
@@ -927,7 +933,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
// http://dom.spec.whatwg.org/#dom-document-documentelement
fn GetDocumentElement(self) -> Option<Temporary<Element>> {
let node: JSRef<Node> = NodeCast::from_ref(self);
- node.child_elements().next().map(Temporary::from_rooted)
+ node.child_elements().next()
}
// http://dom.spec.whatwg.org/#dom-document-getelementsbytagname
@@ -975,45 +981,10 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
fn CreateElementNS(self,
namespace: Option<DOMString>,
qualified_name: DOMString) -> Fallible<Temporary<Element>> {
- let ns = namespace::from_domstring(namespace);
- match xml_name_type(&qualified_name) {
- InvalidXMLName => {
- debug!("Not a valid element name");
- return Err(InvalidCharacter);
- },
- Name => {
- debug!("Not a valid qualified element name");
- return Err(Namespace);
- },
- QName => {}
- }
-
- let (prefix_from_qname, local_name_from_qname) = get_attribute_parts(&qualified_name);
- match (&ns, prefix_from_qname, local_name_from_qname) {
- // throw if prefix is not null and namespace is null
- (&ns!(""), Some(_), _) => {
- debug!("Namespace can't be null with a non-null prefix");
- return Err(Namespace);
- },
- // throw if prefix is "xml" and namespace is not the XML namespace
- (_, Some(ref prefix), _) if "xml" == *prefix && ns != ns!(XML) => {
- debug!("Namespace must be the xml namespace if the prefix is 'xml'");
- return Err(Namespace);
- },
- // throw if namespace is the XMLNS namespace and neither qualifiedName nor prefix is
- // "xmlns"
- (&ns!(XMLNS), Some(ref prefix), _) if "xmlns" == *prefix => {},
- (&ns!(XMLNS), _, "xmlns") => {},
- (&ns!(XMLNS), _, _) => {
- debug!("The prefix or the qualified name must be 'xmlns' if namespace is the XMLNS namespace ");
- return Err(Namespace);
- },
- _ => {}
- }
-
- let name = QualName::new(ns, Atom::from_slice(local_name_from_qname));
- Ok(Element::create(name, prefix_from_qname.map(|s| s.to_owned()), self,
- ElementCreator::ScriptCreated))
+ let (namespace, prefix, local_name) =
+ try!(validate_and_extract(namespace, &qualified_name));
+ let name = QualName::new(namespace, local_name);
+ Ok(Element::create(name, prefix, self, ElementCreator::ScriptCreated))
}
// http://dom.spec.whatwg.org/#dom-document-createattribute
@@ -1032,6 +1003,18 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
Ok(Attr::new(window.r(), name, value, l_name, ns!(""), None, None))
}
+ // http://dom.spec.whatwg.org/#dom-document-createattributens
+ fn CreateAttributeNS(self, namespace: Option<DOMString>, qualified_name: DOMString)
+ -> Fallible<Temporary<Attr>> {
+ let (namespace, prefix, local_name) =
+ try!(validate_and_extract(namespace, &qualified_name));
+ let window = self.window.root();
+ let value = AttrValue::String("".to_owned());
+ let qualified_name = Atom::from_slice(&qualified_name);
+ Ok(Attr::new(window.r(), local_name, value, qualified_name,
+ namespace, prefix, None))
+ }
+
// http://dom.spec.whatwg.org/#dom-document-createdocumentfragment
fn CreateDocumentFragment(self) -> Temporary<DocumentFragment> {
DocumentFragment::new(self)
@@ -1138,17 +1121,17 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
// http://www.whatwg.org/specs/web-apps/current-work/#document.title
fn Title(self) -> DOMString {
let title_element = self.GetDocumentElement().root().and_then(|root| {
- NodeCast::from_ref(root.get_unsound_ref_forever())
- .traverse_preorder()
- .find(|node| node.type_id() == NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTitleElement)))
- });
+ NodeCast::from_ref(root.r()).traverse_preorder().find(|node| {
+ node.root().r().type_id() == NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTitleElement))
+ })
+ }).root();
let mut title = String::new();
if let Some(title_element) = title_element {
- for child in title_element.children() {
+ for child in title_element.r().children() {
let child = child.root();
if let Some(text) = TextCast::to_ref(child.r()) {
- title.push_str(&text.characterdata().data());
+ title.push_str(&CharacterDataCast::from_ref(text).data());
}
}
}
@@ -1162,9 +1145,9 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
self.GetDocumentElement().root().map(|root| {
let root: JSRef<Node> = NodeCast::from_ref(root.r());
let head_node = root.traverse_preorder().find(|child| {
- child.type_id() == NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLHeadElement))
- });
- head_node.map(|head| {
+ child.root().r().type_id() == NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLHeadElement))
+ }).root();
+ head_node.r().map(|head| {
let title_node = head.children().map(|c| c.root()).find(|child| {
child.r().type_id() == NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTitleElement))
});
@@ -1275,7 +1258,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
// http://www.whatwg.org/specs/web-apps/current-work/#dom-document-getelementsbyname
fn GetElementsByName(self, name: DOMString) -> Temporary<NodeList> {
- self.createNodeList(|node| {
+ self.create_node_list(|node| {
let element: JSRef<Element> = match ElementCast::to_ref(node) {
Some(element) => element,
None => return false,
@@ -1378,6 +1361,21 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
HTMLCollection::children(window.r(), NodeCast::from_ref(self))
}
+ // https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild
+ fn GetFirstElementChild(self) -> Option<Temporary<Element>> {
+ NodeCast::from_ref(self).child_elements().next()
+ }
+
+ // https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild
+ fn GetLastElementChild(self) -> Option<Temporary<Element>> {
+ NodeCast::from_ref(self).rev_children().filter_map(ElementCast::to_temporary).next()
+ }
+
+ // https://dom.spec.whatwg.org/#dom-parentnode-childelementcount
+ fn ChildElementCount(self) -> u32 {
+ NodeCast::from_ref(self).child_elements().count() as u32
+ }
+
// http://dom.spec.whatwg.org/#dom-parentnode-queryselector
fn QuerySelector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
let root: JSRef<Node> = NodeCast::from_ref(self);
diff --git a/components/script/dom/documentfragment.rs b/components/script/dom/documentfragment.rs
index f8e76103d7d..2a61aa98460 100644
--- a/components/script/dom/documentfragment.rs
+++ b/components/script/dom/documentfragment.rs
@@ -5,7 +5,8 @@
use dom::bindings::codegen::Bindings::DocumentFragmentBinding;
use dom::bindings::codegen::Bindings::DocumentFragmentBinding::DocumentFragmentMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
-use dom::bindings::codegen::InheritTypes::{DocumentFragmentDerived, NodeCast};
+use dom::bindings::codegen::InheritTypes::DocumentFragmentDerived;
+use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
@@ -56,6 +57,21 @@ impl<'a> DocumentFragmentMethods for JSRef<'a, DocumentFragment> {
HTMLCollection::children(window.r(), NodeCast::from_ref(self))
}
+ // https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild
+ fn GetFirstElementChild(self) -> Option<Temporary<Element>> {
+ NodeCast::from_ref(self).child_elements().next()
+ }
+
+ // https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild
+ fn GetLastElementChild(self) -> Option<Temporary<Element>> {
+ NodeCast::from_ref(self).rev_children().filter_map(ElementCast::to_temporary).next()
+ }
+
+ // https://dom.spec.whatwg.org/#dom-parentnode-childelementcount
+ fn ChildElementCount(self) -> u32 {
+ NodeCast::from_ref(self).child_elements().count() as u32
+ }
+
// http://dom.spec.whatwg.org/#dom-parentnode-queryselector
fn QuerySelector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
let root: JSRef<Node> = NodeCast::from_ref(self);
diff --git a/components/script/dom/domexception.rs b/components/script/dom/domexception.rs
index 59f8c0a0743..d066021e304 100644
--- a/components/script/dom/domexception.rs
+++ b/components/script/dom/domexception.rs
@@ -23,6 +23,7 @@ pub enum DOMErrorName {
NoModificationAllowedError = DOMExceptionConstants::NO_MODIFICATION_ALLOWED_ERR,
NotFoundError = DOMExceptionConstants::NOT_FOUND_ERR,
NotSupportedError = DOMExceptionConstants::NOT_SUPPORTED_ERR,
+ InUseAttributeError = DOMExceptionConstants::INUSE_ATTRIBUTE_ERR,
InvalidStateError = DOMExceptionConstants::INVALID_STATE_ERR,
SyntaxError = DOMExceptionConstants::SYNTAX_ERR,
InvalidModificationError = DOMExceptionConstants::INVALID_MODIFICATION_ERR,
@@ -83,6 +84,7 @@ impl<'a> DOMExceptionMethods for JSRef<'a, DOMException> {
DOMErrorName::NoModificationAllowedError => "The object can not be modified.",
DOMErrorName::NotFoundError => "The object can not be found here.",
DOMErrorName::NotSupportedError => "The operation is not supported.",
+ DOMErrorName::InUseAttributeError => "The attribute already in use.",
DOMErrorName::InvalidStateError => "The object is in an invalid state.",
DOMErrorName::SyntaxError => "The string did not match the expected pattern.",
DOMErrorName::InvalidModificationError => "The object can not be modified in this way.",
diff --git a/components/script/dom/domimplementation.rs b/components/script/dom/domimplementation.rs
index 7e2a3dba03a..e5cac62f4e7 100644
--- a/components/script/dom/domimplementation.rs
+++ b/components/script/dom/domimplementation.rs
@@ -8,12 +8,10 @@ use dom::bindings::codegen::Bindings::DOMImplementationBinding::DOMImplementatio
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::error::Fallible;
-use dom::bindings::error::Error::{InvalidCharacter, Namespace};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, JSRef, Root, Temporary, OptionalRootable};
use dom::bindings::utils::{Reflector, reflect_dom_object};
-use dom::bindings::utils::xml_name_type;
-use dom::bindings::utils::XMLName::{QName, Name, InvalidXMLName};
+use dom::bindings::utils::validate_qualified_name;
use dom::document::{Document, DocumentHelpers, IsHTMLDocument};
use dom::document::DocumentSource;
use dom::documenttype::DocumentType;
@@ -52,18 +50,11 @@ impl DOMImplementation {
// http://dom.spec.whatwg.org/#domimplementation
impl<'a> DOMImplementationMethods for JSRef<'a, DOMImplementation> {
// http://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype
- fn CreateDocumentType(self, qname: DOMString, pubid: DOMString, sysid: DOMString) -> Fallible<Temporary<DocumentType>> {
- match xml_name_type(&qname) {
- // Step 1.
- InvalidXMLName => Err(InvalidCharacter),
- // Step 2.
- Name => Err(Namespace),
- // Step 3.
- QName => {
- let document = self.document.root();
- Ok(DocumentType::new(qname, Some(pubid), Some(sysid), document.r()))
- }
- }
+ fn CreateDocumentType(self, qualified_name: DOMString, pubid: DOMString, sysid: DOMString)
+ -> Fallible<Temporary<DocumentType>> {
+ try!(validate_qualified_name(&qualified_name));
+ let document = self.document.root();
+ Ok(DocumentType::new(qualified_name, Some(pubid), Some(sysid), document.r()))
}
// http://dom.spec.whatwg.org/#dom-domimplementation-createdocument
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index d8d5a9ecf93..d7c7e1908cb 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -24,14 +24,13 @@ use dom::bindings::codegen::InheritTypes::{HTMLTableRowElementDerived, HTMLTextA
use dom::bindings::codegen::InheritTypes::{HTMLTableSectionElementDerived, NodeCast};
use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast;
use dom::bindings::error::{ErrorResult, Fallible};
-use dom::bindings::error::Error;
use dom::bindings::error::Error::{InvalidCharacter, Syntax};
use dom::bindings::error::Error::NoModificationAllowed;
use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable};
-use dom::bindings::js::OptionalRootable;
+use dom::bindings::js::{OptionalRootable, RootedReference};
use dom::bindings::trace::RootedVec;
-use dom::bindings::utils::xml_name_type;
-use dom::bindings::utils::XMLName::{QName, Name, InvalidXMLName};
+use dom::bindings::utils::{xml_name_type, validate_and_extract};
+use dom::bindings::utils::XMLName::InvalidXMLName;
use dom::create::create_element;
use dom::domrect::DOMRect;
use dom::domrectlist::DOMRectList;
@@ -508,7 +507,7 @@ impl<'a> ElementHelpers<'a> for JSRef<'a, Element> {
fn remove_inline_style_property(self, property: DOMString) {
let mut inline_declarations = self.style_attribute.borrow_mut();
- inline_declarations.as_mut().map(|declarations| {
+ if let &mut Some(ref mut declarations) = &mut *inline_declarations {
let index = declarations.normal
.iter()
.position(|decl| decl.name() == property);
@@ -524,7 +523,7 @@ impl<'a> ElementHelpers<'a> for JSRef<'a, Element> {
declarations.important.make_unique().remove(index);
return;
}
- });
+ }
}
fn update_inline_style(self, property_decl: PropertyDeclaration, style_priority: StylePriority) {
@@ -595,13 +594,60 @@ impl<'a> ElementHelpers<'a> for JSRef<'a, Element> {
// https://html.spec.whatwg.org/multipage/infrastructure.html#root-element
fn get_root_element(self) -> Option<Temporary<Element>> {
let node: JSRef<Node> = NodeCast::from_ref(self);
- match node.ancestors().last().map(ElementCast::to_ref) {
- Some(n) => n.map(Temporary::from_rooted),
+ match node.ancestors().last().map(ElementCast::to_temporary) {
+ Some(n) => n,
None => Some(self).map(Temporary::from_rooted),
}
}
}
+pub trait FocusElementHelpers {
+ /// https://html.spec.whatwg.org/multipage/interaction.html#focusable-area
+ fn is_focusable_area(self) -> bool;
+
+ /// https://html.spec.whatwg.org/multipage/scripting.html#concept-element-disabled
+ fn is_actually_disabled(self) -> bool;
+}
+
+impl<'a> FocusElementHelpers for JSRef<'a, Element> {
+ fn is_focusable_area(self) -> bool {
+ if self.is_actually_disabled() {
+ return false;
+ }
+ // TODO: Check whether the element is being rendered (i.e. not hidden).
+ // TODO: Check the tabindex focus flag.
+ // https://html.spec.whatwg.org/multipage/interaction.html#specially-focusable
+ let node: JSRef<Node> = NodeCast::from_ref(self);
+ match node.type_id() {
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) |
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
+ true
+ }
+ _ => false
+ }
+ }
+
+ fn is_actually_disabled(self) -> bool {
+ let node: JSRef<Node> = NodeCast::from_ref(self);
+ match node.type_id() {
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) |
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) |
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) |
+ NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement)) => {
+ node.get_disabled_state()
+ }
+ // TODO:
+ // an optgroup element that has a disabled attribute
+ // a menuitem element that has a disabled attribute
+ // a fieldset element that is a disabled fieldset
+ _ => false
+ }
+ }
+}
+
pub trait AttributeHandlers {
/// Returns the attribute with given namespace and case-sensitive local
/// name, if any.
@@ -627,11 +673,13 @@ pub trait AttributeHandlers {
/// Removes the first attribute with any given namespace and case-sensitive local
/// name, if any.
- fn remove_attribute(self, namespace: &Namespace, local_name: &Atom);
+ fn remove_attribute(self, namespace: &Namespace, local_name: &Atom)
+ -> Option<Temporary<Attr>>;
/// Removes the first attribute with any namespace and given case-sensitive name.
- fn remove_attribute_by_name(self, name: &Atom);
+ fn remove_attribute_by_name(self, name: &Atom) -> Option<Temporary<Attr>>;
/// Removes the first attribute that satisfies `find`.
- fn do_remove_attribute<F>(self, find: F) where F: Fn(JSRef<Attr>) -> bool;
+ fn do_remove_attribute<F>(self, find: F) -> Option<Temporary<Attr>>
+ where F: Fn(JSRef<Attr>) -> bool;
fn has_class(self, name: &Atom) -> bool;
@@ -764,22 +812,25 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
}
}
- fn remove_attribute(self, namespace: &Namespace, local_name: &Atom) {
+ fn remove_attribute(self, namespace: &Namespace, local_name: &Atom)
+ -> Option<Temporary<Attr>> {
self.do_remove_attribute(|attr| {
attr.namespace() == namespace && attr.local_name() == local_name
- });
+ })
}
- fn remove_attribute_by_name(self, name: &Atom) {
- self.do_remove_attribute(|attr| attr.name() == name);
+ fn remove_attribute_by_name(self, name: &Atom) -> Option<Temporary<Attr>> {
+ self.do_remove_attribute(|attr| attr.name() == name)
}
- fn do_remove_attribute<F>(self, find: F) where F: Fn(JSRef<Attr>) -> bool {
+ fn do_remove_attribute<F>(self, find: F) -> Option<Temporary<Attr>>
+ where F: Fn(JSRef<Attr>) -> bool
+ {
let idx = self.attrs.borrow().iter()
.map(|attr| attr.root())
.position(|attr| find(attr.r()));
- if let Some(idx) = idx {
+ idx.map(|idx| {
let attr = (*self.attrs.borrow())[idx].root();
if attr.r().namespace() == &ns!("") {
vtable_for(&NodeCast::from_ref(self)).before_remove_attr(attr.r());
@@ -798,7 +849,8 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
};
document.r().content_changed(node, damage);
}
- };
+ Temporary::from_rooted(attr.r())
+ })
}
fn has_class(self, name: &Atom) -> bool {
@@ -929,6 +981,7 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
}
}
+ // https://dom.spec.whatwg.org/#dom-element-localname
fn LocalName(self) -> DOMString {
self.local_name.as_slice().to_owned()
}
@@ -1031,58 +1084,14 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
// http://dom.spec.whatwg.org/#dom-element-setattributens
fn SetAttributeNS(self,
- namespace_url: Option<DOMString>,
- name: DOMString,
+ namespace: Option<DOMString>,
+ qualified_name: DOMString,
value: DOMString) -> ErrorResult {
- // Step 1.
- let namespace = namespace::from_domstring(namespace_url);
-
- let name_type = xml_name_type(&name);
- match name_type {
- // Step 2.
- InvalidXMLName => return Err(InvalidCharacter),
- // Step 3.
- Name => return Err(Error::Namespace),
- QName => {}
- }
-
- // Step 4.
- let (prefix, local_name) = get_attribute_parts(&name);
-
- if let Some(ref prefix_str) = prefix {
- // Step 5.
- if namespace == ns!("") {
- return Err(Error::Namespace);
- }
-
- // Step 6.
- if "xml" == *prefix_str && namespace != ns!(XML) {
- return Err(Error::Namespace);
- }
-
- // Step 7b.
- if "xmlns" == *prefix_str && namespace != ns!(XMLNS) {
- return Err(Error::Namespace);
- }
- }
-
- let name = Atom::from_slice(&name);
- let local_name = Atom::from_slice(local_name);
- let xmlns = atom!("xmlns");
-
- // Step 7a.
- if xmlns == name && namespace != ns!(XMLNS) {
- return Err(Error::Namespace);
- }
-
- // Step 8.
- if namespace == ns!(XMLNS) && xmlns != name && Some("xmlns") != prefix {
- return Err(Error::Namespace);
- }
-
- // Step 9.
+ let (namespace, prefix, local_name) =
+ try!(validate_and_extract(namespace, &qualified_name));
+ let qualified_name = Atom::from_slice(&qualified_name);
let value = self.parse_attribute(&namespace, &local_name, value);
- self.do_set_attribute(local_name.clone(), value, name,
+ self.do_set_attribute(local_name.clone(), value, qualified_name,
namespace.clone(), prefix.map(|s| s.to_owned()),
|attr| {
*attr.local_name() == local_name &&
@@ -1118,17 +1127,20 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
self.GetAttributeNS(namespace, local_name).is_some()
}
+ // https://dom.spec.whatwg.org/#dom-element-getelementsbytagname
fn GetElementsByTagName(self, localname: DOMString) -> Temporary<HTMLCollection> {
let window = window_from_node(self).root();
HTMLCollection::by_tag_name(window.r(), NodeCast::from_ref(self), localname)
}
+ // https://dom.spec.whatwg.org/#dom-element-getelementsbytagnamens
fn GetElementsByTagNameNS(self, maybe_ns: Option<DOMString>,
localname: DOMString) -> Temporary<HTMLCollection> {
let window = window_from_node(self).root();
HTMLCollection::by_tag_name_ns(window.r(), NodeCast::from_ref(self), localname, maybe_ns)
}
+ // https://dom.spec.whatwg.org/#dom-element-getelementsbyclassname
fn GetElementsByClassName(self, classes: DOMString) -> Temporary<HTMLCollection> {
let window = window_from_node(self).root();
HTMLCollection::by_class_name(window.r(), NodeCast::from_ref(self), classes)
@@ -1165,12 +1177,13 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
rect.origin.x + rect.size.width)
}
- // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#extensions-to-the-element-interface
+ // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-innerHTML
fn GetInnerHTML(self) -> Fallible<DOMString> {
//XXX TODO: XML case
self.serialize(ChildrenOnly)
}
+ // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-innerHTML
fn SetInnerHTML(self, value: DOMString) -> Fallible<()> {
let context_node: JSRef<Node> = NodeCast::from_ref(self);
// Step 1.
@@ -1180,10 +1193,12 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
Ok(())
}
+ // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-outerHTML
fn GetOuterHTML(self) -> Fallible<DOMString> {
self.serialize(IncludeNode)
}
+ // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-outerHTML
fn SetOuterHTML(self, value: DOMString) -> Fallible<()> {
let context_document = document_from_node(self).root();
let context_node: JSRef<Node> = NodeCast::from_ref(self);
@@ -1217,12 +1232,39 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
Ok(())
}
+ // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling
+ fn GetPreviousElementSibling(self) -> Option<Temporary<Element>> {
+ NodeCast::from_ref(self).preceding_siblings()
+ .filter_map(ElementCast::to_temporary).next()
+ }
+
+ // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-nextelementsibling
+ fn GetNextElementSibling(self) -> Option<Temporary<Element>> {
+ NodeCast::from_ref(self).following_siblings()
+ .filter_map(ElementCast::to_temporary).next()
+ }
+
// http://dom.spec.whatwg.org/#dom-parentnode-children
fn Children(self) -> Temporary<HTMLCollection> {
let window = window_from_node(self).root();
HTMLCollection::children(window.r(), NodeCast::from_ref(self))
}
+ // https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild
+ fn GetFirstElementChild(self) -> Option<Temporary<Element>> {
+ NodeCast::from_ref(self).child_elements().next()
+ }
+
+ // https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild
+ fn GetLastElementChild(self) -> Option<Temporary<Element>> {
+ NodeCast::from_ref(self).rev_children().filter_map(ElementCast::to_temporary).next()
+ }
+
+ // https://dom.spec.whatwg.org/#dom-parentnode-childelementcount
+ fn ChildElementCount(self) -> u32 {
+ NodeCast::from_ref(self).child_elements().count() as u32
+ }
+
// http://dom.spec.whatwg.org/#dom-parentnode-queryselector
fn QuerySelector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> {
let root: JSRef<Node> = NodeCast::from_ref(self);
@@ -1258,26 +1300,20 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
Err(()) => Err(Syntax),
Ok(ref selectors) => {
let root: JSRef<Node> = NodeCast::from_ref(self);
- Ok(root.inclusive_ancestors()
- .filter_map(ElementCast::to_ref)
- .find(|element| matches(selectors, &NodeCast::from_ref(*element), &mut None))
- .map(Temporary::from_rooted))
+ for element in root.inclusive_ancestors() {
+ let element = element.root();
+ if let Some(element) = ElementCast::to_ref(element.r()) {
+ if matches(selectors, &NodeCast::from_ref(element), &mut None) {
+ return Ok(Some(Temporary::from_rooted(element)));
+ }
+ }
+ }
+ Ok(None)
}
}
}
}
-pub fn get_attribute_parts<'a>(name: &'a str) -> (Option<&'a str>, &'a str) {
- //FIXME: Throw for XML-invalid names
- //FIXME: Throw for XMLNS-invalid names
- if name.contains(":") {
- let mut parts = name.splitn(1, ':');
- (Some(parts.next().unwrap()), parts.next().unwrap())
- } else {
- (None, name)
- }
-}
-
impl<'a> VirtualMethods for JSRef<'a, Element> {
fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> {
let node: &JSRef<Node> = NodeCast::from_borrowed_ref(self);
@@ -1599,13 +1635,15 @@ impl<'a> ActivationElementHelpers<'a> for JSRef<'a, Element> {
Some(el) => Some(Temporary::from_rooted(el.as_element().root().r())),
None => {
let node: JSRef<Node> = NodeCast::from_ref(self);
- node.ancestors()
- .filter_map(|node| {
- let e: Option<JSRef<Element>> = ElementCast::to_ref(node);
- e
- })
- .filter(|e| e.as_maybe_activatable().is_some()).next()
- .map(|r| Temporary::from_rooted(r))
+ for node in node.ancestors() {
+ let node = node.root();
+ if let Some(node) = ElementCast::to_ref(node.r()) {
+ if node.as_maybe_activatable().is_some() {
+ return Some(Temporary::from_rooted(node))
+ }
+ }
+ }
+ None
}
}
}
diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs
index aa588d09984..6c2e6fa860f 100644
--- a/components/script/dom/event.rs
+++ b/components/script/dom/event.rs
@@ -174,55 +174,67 @@ impl Event {
}
impl<'a> EventMethods for JSRef<'a, Event> {
+ // https://dom.spec.whatwg.org/#dom-event-eventphase
fn EventPhase(self) -> u16 {
self.phase.get() as u16
}
+ // https://dom.spec.whatwg.org/#dom-event-type
fn Type(self) -> DOMString {
// FIXME(https://github.com/rust-lang/rust/issues/23338)
let type_ = self.type_.borrow();
type_.clone()
}
+ // https://dom.spec.whatwg.org/#dom-event-target
fn GetTarget(self) -> Option<Temporary<EventTarget>> {
self.target.get()
}
+ // https://dom.spec.whatwg.org/#dom-event-currenttarget
fn GetCurrentTarget(self) -> Option<Temporary<EventTarget>> {
self.current_target.get()
}
+ // https://dom.spec.whatwg.org/#dom-event-defaultprevented
fn DefaultPrevented(self) -> bool {
self.canceled.get()
}
+ // https://dom.spec.whatwg.org/#dom-event-preventdefault
fn PreventDefault(self) {
if self.cancelable.get() {
self.canceled.set(true)
}
}
+ // https://dom.spec.whatwg.org/#dom-event-stoppropagation
fn StopPropagation(self) {
self.stop_propagation.set(true);
}
+ // https://dom.spec.whatwg.org/#dom-event-stopimmediatepropagation
fn StopImmediatePropagation(self) {
self.stop_immediate.set(true);
self.stop_propagation.set(true);
}
+ // https://dom.spec.whatwg.org/#dom-event-bubbles
fn Bubbles(self) -> bool {
self.bubbles.get()
}
+ // https://dom.spec.whatwg.org/#dom-event-cancelable
fn Cancelable(self) -> bool {
self.cancelable.get()
}
+ // https://dom.spec.whatwg.org/#dom-event-timestamp
fn TimeStamp(self) -> u64 {
self.timestamp
}
+ // https://dom.spec.whatwg.org/#dom-event-initevent
fn InitEvent(self,
type_: DOMString,
bubbles: bool,
@@ -242,6 +254,7 @@ impl<'a> EventMethods for JSRef<'a, Event> {
self.cancelable.set(cancelable);
}
+ // https://dom.spec.whatwg.org/#dom-event-istrusted
fn IsTrusted(self) -> bool {
self.trusted.get()
}
diff --git a/components/script/dom/eventdispatcher.rs b/components/script/dom/eventdispatcher.rs
index 029691f702c..6882c3f4599 100644
--- a/components/script/dom/eventdispatcher.rs
+++ b/components/script/dom/eventdispatcher.rs
@@ -31,7 +31,8 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>,
let mut chain: RootedVec<JS<EventTarget>> = RootedVec::new();
if let Some(target_node) = NodeCast::to_ref(target) {
for ancestor in target_node.ancestors() {
- let ancestor_target: JSRef<EventTarget> = EventTargetCast::from_ref(ancestor);
+ let ancestor = ancestor.root();
+ let ancestor_target = EventTargetCast::from_ref(ancestor.r());
chain.push(JS::from_rooted(ancestor_target))
}
}
diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs
index 7355b1448b1..b79c966d993 100644
--- a/components/script/dom/htmlbuttonelement.rs
+++ b/components/script/dom/htmlbuttonelement.rs
@@ -162,7 +162,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLButtonElement> {
}
let node: JSRef<Node> = NodeCast::from_ref(*self);
- if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
+ if node.ancestors().any(|ancestor| ancestor.root().r().is_htmlfieldsetelement()) {
node.check_ancestors_disabled_state_for_form_control();
} else {
node.check_disabled_attribute();
@@ -225,12 +225,10 @@ impl<'a> Activatable for JSRef<'a, HTMLButtonElement> {
// and only then performing actions which may modify the DOM tree
unsafe {
node.query_selector_iter("button[type=submit]".to_owned()).unwrap()
- .filter_map(|t| {
- let h: Option<JSRef<HTMLButtonElement>> = HTMLButtonElementCast::to_ref(t);
- h
- })
- .find(|r| r.form_owner() == owner)
- .map(|s| s.synthetic_click_activation(ctrlKey, shiftKey, altKey, metaKey));
+ .filter_map(HTMLButtonElementCast::to_temporary)
+ .map(|t| t.root())
+ .find(|r| r.r().form_owner() == owner)
+ .map(|s| s.r().synthetic_click_activation(ctrlKey, shiftKey, altKey, metaKey));
}
}
}
diff --git a/components/script/dom/htmlcollection.rs b/components/script/dom/htmlcollection.rs
index a6e216cffdb..a2cf605bc7e 100644
--- a/components/script/dom/htmlcollection.rs
+++ b/components/script/dom/htmlcollection.rs
@@ -164,12 +164,13 @@ impl HTMLCollection {
HTMLCollection::create(window, root, box ElementChildFilter)
}
- fn traverse<'a>(root: JSRef<'a, Node>)
- -> FilterMap<Skip<TreeIterator<'a>>,
- fn(JSRef<Node>) -> Option<JSRef<Element>>> {
+ fn traverse(root: JSRef<Node>)
+ -> FilterMap<Skip<TreeIterator>,
+ fn(Temporary<Node>) -> Option<Temporary<Element>>> {
root.traverse_preorder()
.skip(1)
- .filter_map(ElementCast::to_ref as fn(JSRef<Node>) -> Option<JSRef<Element>>)
+ .filter_map(ElementCast::to_temporary as
+ fn(Temporary<Node>) -> Option<Temporary<Element>>)
}
}
@@ -181,7 +182,7 @@ impl<'a> HTMLCollectionMethods for JSRef<'a, HTMLCollection> {
CollectionTypeId::Live(ref root, ref filter) => {
let root = root.root();
HTMLCollection::traverse(root.r())
- .filter(|element| filter.filter(*element, root.r()))
+ .filter(|element| filter.filter(element.root().r(), root.r()))
.count() as u32
}
}
@@ -198,10 +199,8 @@ impl<'a> HTMLCollectionMethods for JSRef<'a, HTMLCollection> {
CollectionTypeId::Live(ref root, ref filter) => {
let root = root.root();
HTMLCollection::traverse(root.r())
- .filter(|element| filter.filter(*element, root.r()))
+ .filter(|element| filter.filter(element.root().r(), root.r()))
.nth(index)
- .clone()
- .map(Temporary::from_rooted)
}
}
}
@@ -224,11 +223,12 @@ impl<'a> HTMLCollectionMethods for JSRef<'a, HTMLCollection> {
CollectionTypeId::Live(ref root, ref filter) => {
let root = root.root();
HTMLCollection::traverse(root.r())
- .filter(|element| filter.filter(*element, root.r()))
+ .map(|element| element.root())
+ .filter(|element| filter.filter(element.r(), root.r()))
.find(|elem| {
- elem.get_string_attribute(&atom!("name")) == key ||
- elem.get_string_attribute(&atom!("id")) == key })
- .map(Temporary::from_rooted)
+ elem.r().get_string_attribute(&atom!("name")) == key ||
+ elem.r().get_string_attribute(&atom!("id")) == key })
+ .map(|elem| Temporary::from_rooted(elem.r()))
}
}
}
diff --git a/components/script/dom/htmlfieldsetelement.rs b/components/script/dom/htmlfieldsetelement.rs
index 39088810eab..e3b8397e4bc 100644
--- a/components/script/dom/htmlfieldsetelement.rs
+++ b/components/script/dom/htmlfieldsetelement.rs
@@ -104,13 +104,14 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLFieldSetElement> {
}
for descendant in child.r().traverse_preorder() {
- match descendant.type_id() {
+ let descendant = descendant.root();
+ match descendant.r().type_id() {
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
- descendant.set_disabled_state(true);
- descendant.set_enabled_state(false);
+ descendant.r().set_disabled_state(true);
+ descendant.r().set_enabled_state(false);
},
_ => ()
}
@@ -142,13 +143,14 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLFieldSetElement> {
}
for descendant in child.r().traverse_preorder() {
- match descendant.type_id() {
+ let descendant = descendant.root();
+ match descendant.r().type_id() {
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) |
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
- descendant.check_disabled_attribute();
- descendant.check_ancestors_disabled_state_for_form_control();
+ descendant.r().check_disabled_attribute();
+ descendant.r().check_ancestors_disabled_state_for_form_control();
},
_ => ()
}
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index ebe033cea37..7bd9925ffd2 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -20,7 +20,7 @@ use dom::element::ElementTypeId;
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::htmlinputelement::{HTMLInputElement, HTMLInputElementHelpers};
use dom::htmlbuttonelement::{HTMLButtonElement};
-use dom::htmltextareaelement::{HTMLTextAreaElement, HTMLTextAreaElementHelpers};
+use dom::htmltextareaelement::HTMLTextAreaElementHelpers;
use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node};
use hyper::method::Method;
use hyper::header::ContentType;
@@ -255,16 +255,18 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
// TODO: This is an incorrect way of getting controls owned
// by the form, but good enough until html5ever lands
let data_set = node.traverse_preorder().filter_map(|child| {
- if child.get_disabled_state() {
+ let child = child.root();
+ if child.r().get_disabled_state() {
return None;
}
- if child.ancestors().any(|a| a.type_id() == NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLDataListElement))) {
+ if child.r().ancestors()
+ .any(|a| a.root().r().type_id() == NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLDataListElement))) {
return None;
}
// XXXManishearth don't include it if it is a button but not the submitter
- match child.type_id() {
+ match child.r().type_id() {
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) => {
- let input: JSRef<HTMLInputElement> = HTMLInputElementCast::to_ref(child).unwrap();
+ let input = HTMLInputElementCast::to_ref(child.r()).unwrap();
let ty = input.Type();
let name = input.Name();
match ty.as_slice() {
@@ -368,10 +370,10 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
// TODO: This is an incorrect way of getting controls owned
// by the form, but good enough until html5ever lands
for child in node.traverse_preorder() {
- match child.type_id() {
+ let child = child.root();
+ match child.r().type_id() {
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) => {
- let input: JSRef<HTMLInputElement> = HTMLInputElementCast::to_ref(child)
- .unwrap();
+ let input = HTMLInputElementCast::to_ref(child.r()).unwrap();
input.reset()
}
// TODO HTMLKeygenElement unimplemented
@@ -384,8 +386,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> {
{}
}
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
- let textarea: JSRef<HTMLTextAreaElement> = HTMLTextAreaElementCast::to_ref(child)
- .unwrap();
+ let textarea = HTMLTextAreaElementCast::to_ref(child.r()).unwrap();
textarea.reset()
}
NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOutputElement)) => {
@@ -531,8 +532,13 @@ pub trait FormControl<'a> : Copy + Sized {
}
}
let node: JSRef<Node> = NodeCast::from_ref(elem);
- node.ancestors().filter_map(|a| HTMLFormElementCast::to_ref(a)).next()
- .map(Temporary::from_rooted)
+ for ancestor in node.ancestors() {
+ let ancestor = ancestor.root();
+ if let Some(ancestor) = HTMLFormElementCast::to_ref(ancestor.r()) {
+ return Some(Temporary::from_rooted(ancestor))
+ }
+ }
+ None
}
fn get_form_attribute<InputFn, OwnerFn>(self,
diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs
index dadcd776238..f93d525f1fc 100644
--- a/components/script/dom/htmlimageelement.rs
+++ b/components/script/dom/htmlimageelement.rs
@@ -39,6 +39,16 @@ impl HTMLImageElementDerived for EventTarget {
}
}
+pub trait HTMLImageElementHelpers {
+ fn get_url(&self) -> Option<Url>;
+}
+
+impl<'a> HTMLImageElementHelpers for JSRef<'a, HTMLImageElement> {
+ fn get_url(&self) -> Option<Url>{
+ self.image.borrow().clone()
+ }
+}
+
trait PrivateHTMLImageElementHelpers {
fn update_image(self, value: Option<(DOMString, &Url)>);
}
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 3748ce9bac5..5bd170ebea7 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -346,12 +346,13 @@ fn broadcast_radio_checked(broadcaster: JSRef<HTMLInputElement>, group: Option<&
// There is no DOM tree manipulation here, so this is safe
let iter = unsafe {
doc_node.query_selector_iter("input[type=radio]".to_owned()).unwrap()
- .filter_map(|t| HTMLInputElementCast::to_ref(t))
- .filter(|&r| in_same_group(r, owner, group) && broadcaster != r)
+ .filter_map(HTMLInputElementCast::to_temporary)
+ .map(|t| t.root())
+ .filter(|r| in_same_group(r.r(), owner, group) && broadcaster != r.r())
};
for r in iter {
- if r.Checked() {
- r.SetChecked(false);
+ if r.r().Checked() {
+ r.r().SetChecked(false);
}
}
}
@@ -583,7 +584,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
}
let node: JSRef<Node> = NodeCast::from_ref(*self);
- if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
+ if node.ancestors().any(|ancestor| ancestor.root().r().is_htmlfieldsetelement()) {
node.check_ancestors_disabled_state_for_form_control();
} else {
node.check_disabled_attribute();
@@ -684,12 +685,14 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
// Safe since we only manipulate the DOM tree after finding an element
let checked_member = unsafe {
doc_node.query_selector_iter("input[type=radio]".to_owned()).unwrap()
- .filter_map(|t| HTMLInputElementCast::to_ref(t))
- .filter(|&r| in_same_group(r, owner.r(),
- group.as_ref().map(|gr| gr.as_slice())))
- .find(|r| r.Checked())
+ .filter_map(HTMLInputElementCast::to_temporary)
+ .map(|t| t.root())
+ .find(|r| {
+ in_same_group(r.r(), owner.r(), group.as_ref().map(|gr| gr.as_slice())) &&
+ r.r().Checked()
+ })
};
- cache.checked_radio.assign(checked_member);
+ cache.checked_radio.assign(checked_member.r());
cache.checked_changed = self.checked_changed.get();
self.SetChecked(true);
}
@@ -815,12 +818,10 @@ impl<'a> Activatable for JSRef<'a, HTMLInputElement> {
// and only then performing actions which may modify the DOM tree
unsafe {
node.query_selector_iter("input[type=submit]".to_owned()).unwrap()
- .filter_map(|t| {
- let h: Option<JSRef<HTMLInputElement>> = HTMLInputElementCast::to_ref(t);
- h
- })
- .find(|r| r.form_owner() == owner)
- .map(|s| s.synthetic_click_activation(ctrlKey, shiftKey, altKey, metaKey));
+ .filter_map(HTMLInputElementCast::to_temporary)
+ .map(|t| t.root())
+ .find(|r| r.r().form_owner() == owner)
+ .map(|s| s.r().synthetic_click_activation(ctrlKey, shiftKey, altKey, metaKey));
}
}
}
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index 7b4f7a8aea0..49cf5f257ed 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -524,8 +524,7 @@ impl<'a> HTMLScriptElementMethods for JSRef<'a, HTMLScriptElement> {
// http://www.whatwg.org/html/#dom-script-text
fn Text(self) -> DOMString {
- let node: JSRef<Node> = NodeCast::from_ref(self);
- Node::collect_text_contents(node.children().map(|c| c.root()).map(|c| c.get_unsound_ref_forever()))
+ Node::collect_text_contents(NodeCast::from_ref(self).children())
}
// http://www.whatwg.org/html/#dom-script-text
diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs
index 14459ca841b..12f88281eba 100644
--- a/components/script/dom/htmlselectelement.rs
+++ b/components/script/dom/htmlselectelement.rs
@@ -129,7 +129,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLSelectElement> {
}
let node: JSRef<Node> = NodeCast::from_ref(*self);
- if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
+ if node.ancestors().any(|ancestor| ancestor.root().r().is_htmlfieldsetelement()) {
node.check_ancestors_disabled_state_for_form_control();
} else {
node.check_disabled_attribute();
diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs
index 2b53e09a1a2..f45bedd791d 100644
--- a/components/script/dom/htmltextareaelement.rs
+++ b/components/script/dom/htmltextareaelement.rs
@@ -319,7 +319,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLTextAreaElement> {
}
let node: JSRef<Node> = NodeCast::from_ref(*self);
- if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
+ if node.ancestors().any(|ancestor| ancestor.root().r().is_htmlfieldsetelement()) {
node.check_ancestors_disabled_state_for_form_control();
} else {
node.check_disabled_attribute();
diff --git a/components/script/dom/htmltitleelement.rs b/components/script/dom/htmltitleelement.rs
index df2592acef9..5fb9cd3b25f 100644
--- a/components/script/dom/htmltitleelement.rs
+++ b/components/script/dom/htmltitleelement.rs
@@ -6,8 +6,9 @@ use dom::bindings::codegen::Bindings::HTMLTitleElementBinding;
use dom::bindings::codegen::Bindings::HTMLTitleElementBinding::HTMLTitleElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTitleElementDerived, NodeCast};
-use dom::bindings::codegen::InheritTypes::{TextCast};
+use dom::bindings::codegen::InheritTypes::{CharacterDataCast, TextCast};
use dom::bindings::js::{JSRef, Temporary};
+use dom::characterdata::CharacterDataHelpers;
use dom::document::{Document, DocumentHelpers};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::element::ElementTypeId;
@@ -51,7 +52,7 @@ impl<'a> HTMLTitleElementMethods for JSRef<'a, HTMLTitleElement> {
let child = child.root();
let text: Option<JSRef<Text>> = TextCast::to_ref(child.r());
match text {
- Some(text) => content.push_str(text.characterdata().data().as_slice()),
+ Some(text) => content.push_str(&CharacterDataCast::from_ref(text).data()),
None => (),
}
}
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index d1dd72a30b6..6c7204f7765 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -315,6 +315,7 @@ pub mod servohtmlparser;
pub mod storage;
pub mod storageevent;
pub mod text;
+pub mod textdecoder;
pub mod textencoder;
pub mod treewalker;
pub mod uievent;
diff --git a/components/script/dom/namednodemap.rs b/components/script/dom/namednodemap.rs
index bfa8f1652f1..50b24aafcc9 100644
--- a/components/script/dom/namednodemap.rs
+++ b/components/script/dom/namednodemap.rs
@@ -5,11 +5,16 @@
use dom::attr::Attr;
use dom::bindings::codegen::Bindings::NamedNodeMapBinding;
use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods;
+use dom::bindings::error::{Error, Fallible};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::utils::{Reflector, reflect_dom_object};
-use dom::element::{Element, ElementHelpers};
+use dom::element::{AttributeHandlers, Element, ElementHelpers};
use dom::window::Window;
+use util::namespace;
+use util::str::DOMString;
+
+use string_cache::Atom;
#[dom_struct]
pub struct NamedNodeMap {
@@ -32,6 +37,7 @@ impl NamedNodeMap {
}
impl<'a> NamedNodeMapMethods for JSRef<'a, NamedNodeMap> {
+ // https://dom.spec.whatwg.org/#dom-namednodemap-length
fn Length(self) -> u32 {
let owner = self.owner.root();
// FIXME(https://github.com/rust-lang/rust/issues/23338)
@@ -40,6 +46,7 @@ impl<'a> NamedNodeMapMethods for JSRef<'a, NamedNodeMap> {
attrs.len() as u32
}
+ // https://dom.spec.whatwg.org/#dom-namednodemap-item
fn Item(self, index: u32) -> Option<Temporary<Attr>> {
let owner = self.owner.root();
// FIXME(https://github.com/rust-lang/rust/issues/23338)
@@ -48,10 +55,54 @@ impl<'a> NamedNodeMapMethods for JSRef<'a, NamedNodeMap> {
attrs.as_slice().get(index as usize).map(|x| Temporary::new(x.clone()))
}
+ // https://dom.spec.whatwg.org/#dom-namednodemap-getnameditem
+ fn GetNamedItem(self, name: DOMString) -> Option<Temporary<Attr>> {
+ let owner = self.owner.root();
+ // FIXME(https://github.com/rust-lang/rust/issues/23338)
+ let owner = owner.r();
+ let name = owner.parsed_name(name);
+ owner.get_attribute_by_name(&Atom::from_slice(&name))
+ }
+
+ // https://dom.spec.whatwg.org/#dom-namednodemap-getnameditemns
+ fn GetNamedItemNS(self, namespace: Option<DOMString>, local_name: DOMString)
+ -> Option<Temporary<Attr>> {
+ let owner = self.owner.root();
+ // FIXME(https://github.com/rust-lang/rust/issues/23338)
+ let owner = owner.r();
+ let ns = namespace::from_domstring(namespace);
+ owner.get_attribute(&ns, &Atom::from_slice(&local_name))
+ }
+
+ // https://dom.spec.whatwg.org/#dom-namednodemap-removenameditem
+ fn RemoveNamedItem(self, name: DOMString) -> Fallible<Temporary<Attr>> {
+ let owner = self.owner.root();
+ // FIXME(https://github.com/rust-lang/rust/issues/23338)
+ let owner = owner.r();
+ let name = owner.parsed_name(name);
+ owner.remove_attribute_by_name(&Atom::from_slice(&name)).ok_or(Error::NotFound)
+ }
+
+ // https://dom.spec.whatwg.org/#dom-namednodemap-removenameditemns
+ fn RemoveNamedItemNS(self, namespace: Option<DOMString>, local_name: DOMString)
+ -> Fallible<Temporary<Attr>> {
+ let owner = self.owner.root();
+ // FIXME(https://github.com/rust-lang/rust/issues/23338)
+ let owner = owner.r();
+ let ns = namespace::from_domstring(namespace);
+ owner.remove_attribute(&ns, &Atom::from_slice(&local_name)).ok_or(Error::NotFound)
+ }
+
fn IndexedGetter(self, index: u32, found: &mut bool) -> Option<Temporary<Attr>> {
let item = self.Item(index);
*found = item.is_some();
item
}
+
+ fn NamedGetter(self, name: DOMString, found: &mut bool) -> Option<Temporary<Attr>> {
+ let item = self.GetNamedItem(name);
+ *found = item.is_some();
+ item
+ }
}
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 7cd01875b15..6e96e560101 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -13,12 +13,11 @@ use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods;
use dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
-use dom::bindings::codegen::InheritTypes::{CommentCast, DocumentCast, DocumentTypeCast};
-use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, NodeCast, ElementDerived};
-use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeBase, NodeDerived};
-use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, EventTargetCast};
+use dom::bindings::codegen::InheritTypes::{CharacterDataCast, DocumentCast, DocumentTypeCast};
+use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast, ElementDerived, EventTargetCast};
use dom::bindings::codegen::InheritTypes::{HTMLLegendElementDerived, HTMLFieldSetElementDerived};
-use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementDerived;
+use dom::bindings::codegen::InheritTypes::{HTMLOptGroupElementDerived, NodeBase, NodeDerived};
+use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, TextCast};
use dom::bindings::conversions;
use dom::bindings::error::Fallible;
use dom::bindings::error::Error::{NotFound, HierarchyRequest, Syntax};
@@ -29,7 +28,7 @@ use dom::bindings::js::{ResultRootable, OptionalRootable, MutNullableJS};
use dom::bindings::trace::JSTraceable;
use dom::bindings::trace::RootedVec;
use dom::bindings::utils::{Reflectable, reflect_dom_object};
-use dom::characterdata::CharacterData;
+use dom::characterdata::{CharacterData, CharacterDataHelpers};
use dom::comment::Comment;
use dom::document::{Document, DocumentHelpers, IsHTMLDocument, DocumentSource};
use dom::documentfragment::DocumentFragment;
@@ -39,7 +38,7 @@ use dom::element::ElementHelpers;
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::htmlelement::HTMLElementTypeId;
use dom::nodelist::NodeList;
-use dom::processinginstruction::ProcessingInstruction;
+use dom::processinginstruction::{ProcessingInstruction, ProcessingInstructionHelpers};
use dom::text::Text;
use dom::virtualmethods::{VirtualMethods, vtable_for};
use dom::window::{Window, WindowHelpers};
@@ -291,7 +290,8 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> {
let is_in_doc = self.is_in_doc();
for node in self.traverse_preorder() {
- vtable_for(&node).bind_to_tree(is_in_doc);
+ let node = node.root();
+ vtable_for(&node.r()).bind_to_tree(is_in_doc);
}
let parent = self.parent_node().root();
@@ -303,7 +303,8 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> {
fn node_removed(self, parent_in_doc: bool) {
assert!(self.parent_node().is_none());
for node in self.traverse_preorder() {
- vtable_for(&node).unbind_from_tree(parent_in_doc);
+ let node = node.root();
+ vtable_for(&node.r()).unbind_from_tree(parent_in_doc);
}
self.layout_data.dispose();
}
@@ -382,14 +383,15 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> {
}
}
-pub struct QuerySelectorIterator<'a> {
+pub struct QuerySelectorIterator {
selectors: Vec<Selector>,
- iterator: TreeIterator<'a>,
+ iterator: TreeIterator,
}
-impl<'a> QuerySelectorIterator<'a> {
+impl<'a> QuerySelectorIterator {
#[allow(unsafe_code)]
- unsafe fn new(iter: TreeIterator<'a>, selectors: Vec<Selector>) -> QuerySelectorIterator<'a> {
+ unsafe fn new(iter: TreeIterator, selectors: Vec<Selector>)
+ -> QuerySelectorIterator {
QuerySelectorIterator {
selectors: selectors,
iterator: iter,
@@ -397,24 +399,28 @@ impl<'a> QuerySelectorIterator<'a> {
}
}
-impl<'a> Iterator for QuerySelectorIterator<'a> {
- type Item = JSRef<'a, Node>;
+impl<'a> Iterator for QuerySelectorIterator {
+ type Item = Temporary<Node>;
- fn next(&mut self) -> Option<JSRef<'a, Node>> {
+ fn next(&mut self) -> Option<Temporary<Node>> {
let selectors = &self.selectors;
// TODO(cgaebel): Is it worth it to build a bloom filter here
// (instead of passing `None`)? Probably.
- self.iterator.find(|node| node.is_element() && matches(selectors, node, &mut None))
+ self.iterator.find(|node| {
+ let node = node.root();
+ node.r().is_element() && matches(selectors, &node.r(), &mut None)
+ })
}
}
-pub trait NodeHelpers<'a> {
- fn ancestors(self) -> AncestorIterator<'a>;
- fn inclusive_ancestors(self) -> AncestorIterator<'a>;
- fn children(self) -> NodeChildrenIterator;
- fn rev_children(self) -> ReverseChildrenIterator;
- fn child_elements(self) -> ChildElementIterator<'a>;
- fn following_siblings(self) -> NodeChildrenIterator;
+pub trait NodeHelpers {
+ fn ancestors(self) -> AncestorIterator;
+ fn inclusive_ancestors(self) -> AncestorIterator;
+ fn children(self) -> NodeSiblingIterator;
+ fn rev_children(self) -> ReverseSiblingIterator;
+ fn child_elements(self) -> ChildElementIterator;
+ fn following_siblings(self) -> NodeSiblingIterator;
+ fn preceding_siblings(self) -> ReverseSiblingIterator;
fn is_in_doc(self) -> bool;
fn is_inclusive_ancestor_of(self, parent: JSRef<Node>) -> bool;
fn is_parent_of(self, child: JSRef<Node>) -> bool;
@@ -484,8 +490,9 @@ pub trait NodeHelpers<'a> {
fn dump_indent(self, indent: u32);
fn debug_str(self) -> String;
- fn traverse_preorder(self) -> TreeIterator<'a>;
- fn inclusively_following_siblings(self) -> NodeChildrenIterator;
+ fn traverse_preorder(self) -> TreeIterator;
+ fn inclusively_following_siblings(self) -> NodeSiblingIterator;
+ fn inclusively_preceding_siblings(self) -> ReverseSiblingIterator;
fn to_trusted_node_address(self) -> TrustedNodeAddress;
@@ -494,7 +501,7 @@ pub trait NodeHelpers<'a> {
fn query_selector(self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>>;
#[allow(unsafe_code)]
- unsafe fn query_selector_iter(self, selectors: DOMString) -> Fallible<QuerySelectorIterator<'a>>;
+ unsafe fn query_selector_iter(self, selectors: DOMString) -> Fallible<QuerySelectorIterator>;
fn query_selector_all(self, selectors: DOMString) -> Fallible<Temporary<NodeList>>;
fn remove_self(self);
@@ -507,7 +514,7 @@ pub trait NodeHelpers<'a> {
fn parse_fragment(self, markup: DOMString) -> Fallible<Temporary<DocumentFragment>>;
}
-impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
+impl<'a> NodeHelpers for JSRef<'a, Node> {
fn teardown(self) {
self.layout_data.dispose();
for kid in self.children() {
@@ -623,7 +630,8 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
}
fn set_hover_state(self, state: bool) {
- self.set_flag(IN_HOVER_STATE, state)
+ self.set_flag(IN_HOVER_STATE, state);
+ self.dirty(NodeDamage::OtherNodeDamage);
}
fn get_focus_state(self) -> bool {
@@ -631,7 +639,8 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
}
fn set_focus_state(self, state: bool) {
- self.set_flag(IN_FOCUS_STATE, state)
+ self.set_flag(IN_FOCUS_STATE, state);
+ self.dirty(NodeDamage::OtherNodeDamage);
}
fn get_disabled_state(self) -> bool {
@@ -735,32 +744,45 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
// 4. Dirty ancestors.
for ancestor in self.ancestors() {
- if !force_ancestors && ancestor.get_has_dirty_descendants() { break }
- ancestor.set_has_dirty_descendants(true);
+ let ancestor = ancestor.root();
+ if !force_ancestors && ancestor.r().get_has_dirty_descendants() { break }
+ ancestor.r().set_has_dirty_descendants(true);
}
}
/// Iterates over this node and all its descendants, in preorder.
- fn traverse_preorder(self) -> TreeIterator<'a> {
+ fn traverse_preorder(self) -> TreeIterator {
TreeIterator::new(self)
}
- fn inclusively_following_siblings(self) -> NodeChildrenIterator {
- NodeChildrenIterator {
+ fn inclusively_following_siblings(self) -> NodeSiblingIterator {
+ NodeSiblingIterator {
+ current: Some(Temporary::from_rooted(self)),
+ }
+ }
+
+ fn inclusively_preceding_siblings(self) -> ReverseSiblingIterator {
+ ReverseSiblingIterator {
current: Some(Temporary::from_rooted(self)),
}
}
fn is_inclusive_ancestor_of(self, parent: JSRef<Node>) -> bool {
- self == parent || parent.ancestors().any(|ancestor| ancestor == self)
+ self == parent || parent.ancestors().any(|ancestor| ancestor.root().r() == self)
}
- fn following_siblings(self) -> NodeChildrenIterator {
- NodeChildrenIterator {
+ fn following_siblings(self) -> NodeSiblingIterator {
+ NodeSiblingIterator {
current: self.next_sibling(),
}
}
+ fn preceding_siblings(self) -> ReverseSiblingIterator {
+ ReverseSiblingIterator {
+ current: self.prev_sibling(),
+ }
+ }
+
fn is_parent_of(self, child: JSRef<Node>) -> bool {
match child.parent_node() {
Some(ref parent) if parent == &Temporary::from_rooted(self) => true,
@@ -788,11 +810,11 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
Err(()) => return Err(Syntax),
// Step 3.
Ok(ref selectors) => {
- let root = self.ancestors().last().unwrap_or(self.clone());
- Ok(root.traverse_preorder()
- .filter_map(ElementCast::to_ref)
- .find(|element| matches(selectors, &NodeCast::from_ref(*element), &mut None))
- .map(Temporary::from_rooted))
+ let root = self.ancestors().last().root();
+ let root = root.r().unwrap_or(self.clone());
+ Ok(root.traverse_preorder().filter_map(ElementCast::to_temporary).find(|element| {
+ matches(selectors, &NodeCast::from_ref(element.root().r()), &mut None)
+ }))
}
}
}
@@ -802,10 +824,12 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
/// the iterator may be invalidated
#[allow(unsafe_code)]
unsafe fn query_selector_iter(self, selectors: DOMString)
- -> Fallible<QuerySelectorIterator<'a>> {
+ -> Fallible<QuerySelectorIterator> {
// Step 1.
let nodes;
- let root = self.ancestors().last().unwrap_or(self.clone());
+ let root = self.ancestors().last().root()
+ .map(|node| node.get_unsound_ref_forever())
+ .unwrap_or(self.clone());
match parse_author_origin_selector_list_from_str(selectors.as_slice()) {
// Step 2.
Err(()) => return Err(Syntax),
@@ -820,25 +844,24 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
// http://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
#[allow(unsafe_code)]
fn query_selector_all(self, selectors: DOMString) -> Fallible<Temporary<NodeList>> {
- // Step 1.
- unsafe {
- self.query_selector_iter(selectors).map(|iter| {
- let window = window_from_node(self).root();
- NodeList::new_simple_list(window.r(), iter.collect())
- })
+ let mut nodes = RootedVec::new();
+ for node in try!(unsafe { self.query_selector_iter(selectors) }) {
+ nodes.push(JS::from_rooted(node));
}
+ let window = window_from_node(self).root();
+ Ok(NodeList::new_simple_list(window.r(), &nodes))
}
- fn ancestors(self) -> AncestorIterator<'a> {
+ fn ancestors(self) -> AncestorIterator {
AncestorIterator {
- current: self.parent_node.get().map(|node| node.root().get_unsound_ref_forever()),
+ current: self.parent_node()
}
}
- fn inclusive_ancestors(self) -> AncestorIterator<'a> {
+ fn inclusive_ancestors(self) -> AncestorIterator {
AncestorIterator {
- current: Some(self.clone())
+ current: Some(Temporary::from_rooted(self))
}
}
@@ -854,26 +877,21 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
self.owner_doc().root().r().is_html_document()
}
- fn children(self) -> NodeChildrenIterator {
- NodeChildrenIterator {
+ fn children(self) -> NodeSiblingIterator {
+ NodeSiblingIterator {
current: self.first_child.get(),
}
}
- fn rev_children(self) -> ReverseChildrenIterator {
- ReverseChildrenIterator {
- current: self.last_child.get().root(),
+ fn rev_children(self) -> ReverseSiblingIterator {
+ ReverseSiblingIterator {
+ current: self.last_child(),
}
}
- fn child_elements(self) -> ChildElementIterator<'a> {
- fn cast<'a>(n: Temporary<Node>) -> Option<JSRef<'a, Element>> {
- let n = n.root();
- ElementCast::to_ref(n.get_unsound_ref_forever())
- }
-
+ fn child_elements(self) -> ChildElementIterator {
self.children()
- .filter_map(cast as fn(_) -> _)
+ .filter_map(ElementCast::to_temporary as fn(_) -> _)
.peekable()
}
@@ -902,7 +920,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
uniqueId: unique_id.clone(),
baseURI: self.GetBaseURI().unwrap_or("".to_owned()),
parent: self.GetParentNode().root().map(|node| node.r().get_unique_id()).unwrap_or("".to_owned()),
- nodeType: self.NodeType() as usize,
+ nodeType: self.NodeType(),
namespaceURI: "".to_owned(), //FIXME
nodeName: self.NodeName(),
numChildren: self.ChildNodes().root().r().Length() as usize,
@@ -1110,15 +1128,15 @@ impl RawLayoutNodeHelpers for Node {
// Iteration and traversal
//
-pub type ChildElementIterator<'a> =
- Peekable<FilterMap<NodeChildrenIterator,
- fn(Temporary<Node>) -> Option<JSRef<'a, Element>>>>;
+pub type ChildElementIterator =
+ Peekable<FilterMap<NodeSiblingIterator,
+ fn(Temporary<Node>) -> Option<Temporary<Element>>>>;
-pub struct NodeChildrenIterator {
+pub struct NodeSiblingIterator {
current: Option<Temporary<Node>>,
}
-impl Iterator for NodeChildrenIterator {
+impl Iterator for NodeSiblingIterator {
type Item = Temporary<Node>;
fn next(&mut self) -> Option<Temporary<Node>> {
@@ -1131,58 +1149,82 @@ impl Iterator for NodeChildrenIterator {
}
}
-pub struct ReverseChildrenIterator {
- current: Option<Root<Node>>,
+pub struct ReverseSiblingIterator {
+ current: Option<Temporary<Node>>,
}
-impl Iterator for ReverseChildrenIterator {
+impl Iterator for ReverseSiblingIterator {
type Item = Temporary<Node>;
fn next(&mut self) -> Option<Temporary<Node>> {
- let node = self.current.r().map(Temporary::from_rooted);
- self.current = self.current.take().and_then(|node| node.r().prev_sibling()).root();
- node
+ let current = match self.current.take() {
+ None => return None,
+ Some(current) => current,
+ }.root();
+ self.current = current.r().prev_sibling();
+ Some(Temporary::from_rooted(current.r()))
}
}
-pub struct AncestorIterator<'a> {
- current: Option<JSRef<'a, Node>>,
+pub struct AncestorIterator {
+ current: Option<Temporary<Node>>,
}
-impl<'a> Iterator for AncestorIterator<'a> {
- type Item = JSRef<'a, Node>;
+impl Iterator for AncestorIterator {
+ type Item = Temporary<Node>;
- fn next(&mut self) -> Option<JSRef<'a, Node>> {
- let node = self.current;
- self.current = node.and_then(|node| node.parent_node().map(|node| node.root().get_unsound_ref_forever()));
- node
+ fn next(&mut self) -> Option<Temporary<Node>> {
+ let current = match self.current.take() {
+ None => return None,
+ Some(current) => current,
+ }.root();
+ self.current = current.r().parent_node();
+ Some(Temporary::from_rooted(current.r()))
}
}
-pub struct TreeIterator<'a> {
- stack: Vec<JSRef<'a, Node>>,
+pub struct TreeIterator {
+ current: Option<Temporary<Node>>,
+ depth: usize,
}
-impl<'a> TreeIterator<'a> {
- fn new(root: JSRef<'a, Node>) -> TreeIterator<'a> {
- let mut stack = vec!();
- stack.push(root);
-
+impl<'a> TreeIterator {
+ fn new(root: JSRef<'a, Node>) -> TreeIterator {
TreeIterator {
- stack: stack,
+ current: Some(Temporary::from_rooted(root)),
+ depth: 0,
}
}
}
-impl<'a> Iterator for TreeIterator<'a> {
- type Item = JSRef<'a, Node>;
+impl Iterator for TreeIterator {
+ type Item = Temporary<Node>;
- fn next(&mut self) -> Option<JSRef<'a, Node>> {
- let ret = self.stack.pop();
- ret.map(|node| {
- self.stack.extend(node.rev_children().map(|c| c.root().get_unsound_ref_forever()))
- });
- ret
+ // https://dom.spec.whatwg.org/#concept-tree-order
+ fn next(&mut self) -> Option<Temporary<Node>> {
+ let current = match self.current.take() {
+ None => return None,
+ Some(current) => current,
+ };
+ let node = current.root();
+ if let Some(first_child) = node.r().first_child() {
+ self.current = Some(first_child);
+ self.depth += 1;
+ return Some(current);
+ };
+ for ancestor in node.r().inclusive_ancestors() {
+ if self.depth == 0 {
+ break;
+ }
+ if let Some(next_sibling) = ancestor.root().r().next_sibling() {
+ self.current = Some(next_sibling);
+ return Some(current);
+ }
+ self.depth -= 1;
+ }
+ debug_assert!(self.depth == 0);
+ self.current = None;
+ Some(current)
}
}
@@ -1264,7 +1306,7 @@ impl Node {
let node_doc = document_from_node(node).root();
if node_doc.r() != document {
for descendant in node.traverse_preorder() {
- descendant.set_owner_doc(document);
+ descendant.root().r().set_owner_doc(document);
}
}
@@ -1419,13 +1461,14 @@ impl Node {
parent.add_child(node, child);
let is_in_doc = parent.is_in_doc();
for kid in node.traverse_preorder() {
- let mut flags = kid.flags.get();
+ let kid = kid.root();
+ let mut flags = kid.r().flags.get();
if is_in_doc {
flags.insert(IS_IN_DOC);
} else {
flags.remove(IS_IN_DOC);
}
- kid.flags.set(flags);
+ kid.r().flags.set(flags);
}
}
@@ -1587,8 +1630,8 @@ impl Node {
NodeCast::from_temporary(doc_fragment)
},
NodeTypeId::Comment => {
- let comment: JSRef<Comment> = CommentCast::to_ref(node).unwrap();
- let comment = Comment::new(comment.characterdata().data().clone(), document.r());
+ let cdata = CharacterDataCast::to_ref(node).unwrap();
+ let comment = Comment::new(cdata.Data(), document.r());
NodeCast::from_temporary(comment)
},
NodeTypeId::Document => {
@@ -1615,14 +1658,14 @@ impl Node {
NodeCast::from_temporary(element)
},
NodeTypeId::Text => {
- let text: JSRef<Text> = TextCast::to_ref(node).unwrap();
- let text = Text::new(text.characterdata().data().clone(), document.r());
+ let cdata = CharacterDataCast::to_ref(node).unwrap();
+ let text = Text::new(cdata.Data(), document.r());
NodeCast::from_temporary(text)
},
NodeTypeId::ProcessingInstruction => {
let pi: JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(node).unwrap();
- let pi = ProcessingInstruction::new(pi.target().clone(),
- pi.characterdata().data().clone(), document.r());
+ let pi = ProcessingInstruction::new(pi.Target(),
+ CharacterDataCast::from_ref(pi).Data(), document.r());
NodeCast::from_temporary(pi)
},
}.root();
@@ -1676,12 +1719,13 @@ impl Node {
Temporary::from_rooted(copy.r())
}
- pub fn collect_text_contents<'a, T: Iterator<Item=JSRef<'a, Node>>>(iterator: T) -> String {
+ pub fn collect_text_contents<T: Iterator<Item=Temporary<Node>>>(iterator: T) -> String {
let mut content = String::new();
for node in iterator {
- let text: Option<JSRef<Text>> = TextCast::to_ref(node);
+ let node = node.root();
+ let text = TextCast::to_ref(node.r());
match text {
- Some(text) => content.push_str(text.characterdata().data().as_slice()),
+ Some(text) => content.push_str(&CharacterDataCast::from_ref(text).Data()),
None => (),
}
}
@@ -1864,7 +1908,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> {
NodeTypeId::Text |
NodeTypeId::ProcessingInstruction => {
let characterdata: JSRef<CharacterData> = CharacterDataCast::to_ref(self).unwrap();
- characterdata.set_data(value);
+ characterdata.SetData(value);
// Notify the document that the content of this node is different
let document = self.owner_doc().root();
@@ -1936,7 +1980,9 @@ impl<'a> NodeMethods for JSRef<'a, Node> {
0 => (),
// Step 6.1.2
1 => {
- if self.child_elements().any(|c| NodeCast::from_ref(c) != child) {
+ if self.child_elements()
+ .map(|c| c.root())
+ .any(|c| NodeCast::from_ref(c.r()) != child) {
return Err(HierarchyRequest);
}
if child.following_siblings()
@@ -1952,8 +1998,8 @@ impl<'a> NodeMethods for JSRef<'a, Node> {
// Step 6.2
NodeTypeId::Element(..) => {
if self.child_elements()
- .any(|c| NodeCast::from_ref(c) != child)
- {
+ .map(|c| c.root())
+ .any(|c| NodeCast::from_ref(c.r()) != child) {
return Err(HierarchyRequest);
}
if child.following_siblings()
@@ -2105,7 +2151,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> {
let pi: JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(node).unwrap();
let other_pi: JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(other).unwrap();
(*pi.target() == *other_pi.target()) &&
- (*pi.characterdata().data() == *other_pi.characterdata().data())
+ (*CharacterDataCast::from_ref(pi).data() == *CharacterDataCast::from_ref(other_pi).data())
}
fn is_equal_characterdata(node: JSRef<Node>, other: JSRef<Node>) -> bool {
let characterdata: JSRef<CharacterData> = CharacterDataCast::to_ref(node).unwrap();
@@ -2171,23 +2217,25 @@ impl<'a> NodeMethods for JSRef<'a, Node> {
// step 2.
0
} else {
- let mut lastself = self.clone();
- let mut lastother = other.clone();
+ let mut lastself = Temporary::from_rooted(self.clone());
+ let mut lastother = Temporary::from_rooted(other.clone());
for ancestor in self.ancestors() {
- if ancestor == other {
+ let ancestor = ancestor.root();
+ if ancestor.r() == other {
// step 4.
return NodeConstants::DOCUMENT_POSITION_CONTAINS +
NodeConstants::DOCUMENT_POSITION_PRECEDING;
}
- lastself = ancestor.clone();
+ lastself = Temporary::from_rooted(ancestor.r());
}
for ancestor in other.ancestors() {
- if ancestor == self {
+ let ancestor = ancestor.root();
+ if ancestor.r() == self {
// step 5.
return NodeConstants::DOCUMENT_POSITION_CONTAINED_BY +
NodeConstants::DOCUMENT_POSITION_FOLLOWING;
}
- lastother = ancestor.clone();
+ lastother = Temporary::from_rooted(ancestor.r());
}
if lastself != lastother {
@@ -2205,12 +2253,14 @@ impl<'a> NodeMethods for JSRef<'a, Node> {
NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
}
- for child in lastself.traverse_preorder() {
- if child == other {
+ let lastself = lastself.root();
+ for child in lastself.r().traverse_preorder() {
+ let child = child.root();
+ if child.r() == other {
// step 6.
return NodeConstants::DOCUMENT_POSITION_PRECEDING;
}
- if child == self {
+ if child.r() == self {
// step 7.
return NodeConstants::DOCUMENT_POSITION_FOLLOWING;
}
@@ -2281,7 +2331,7 @@ impl<'a> style::node::TNode<'a> for JSRef<'a, Node> {
fn parent_node(self) -> Option<JSRef<'a, Node>> {
// FIXME(zwarich): Remove this when UFCS lands and there is a better way
// of disambiguating methods.
- fn parent_node<'a, T: NodeHelpers<'a>>(this: T) -> Option<Temporary<Node>> {
+ fn parent_node<'a, T: NodeHelpers>(this: T) -> Option<Temporary<Node>> {
this.parent_node()
}
@@ -2291,7 +2341,7 @@ impl<'a> style::node::TNode<'a> for JSRef<'a, Node> {
fn first_child(self) -> Option<JSRef<'a, Node>> {
// FIXME(zwarich): Remove this when UFCS lands and there is a better way
// of disambiguating methods.
- fn first_child<'a, T: NodeHelpers<'a>>(this: T) -> Option<Temporary<Node>> {
+ fn first_child<'a, T: NodeHelpers>(this: T) -> Option<Temporary<Node>> {
this.first_child()
}
@@ -2301,7 +2351,7 @@ impl<'a> style::node::TNode<'a> for JSRef<'a, Node> {
fn last_child(self) -> Option<JSRef<'a, Node>> {
// FIXME(zwarich): Remove this when UFCS lands and there is a better way
// of disambiguating methods.
- fn last_child<'a, T: NodeHelpers<'a>>(this: T) -> Option<Temporary<Node>> {
+ fn last_child<'a, T: NodeHelpers>(this: T) -> Option<Temporary<Node>> {
this.last_child()
}
@@ -2311,7 +2361,7 @@ impl<'a> style::node::TNode<'a> for JSRef<'a, Node> {
fn prev_sibling(self) -> Option<JSRef<'a, Node>> {
// FIXME(zwarich): Remove this when UFCS lands and there is a better way
// of disambiguating methods.
- fn prev_sibling<'a, T: NodeHelpers<'a>>(this: T) -> Option<Temporary<Node>> {
+ fn prev_sibling<'a, T: NodeHelpers>(this: T) -> Option<Temporary<Node>> {
this.prev_sibling()
}
@@ -2321,7 +2371,7 @@ impl<'a> style::node::TNode<'a> for JSRef<'a, Node> {
fn next_sibling(self) -> Option<JSRef<'a, Node>> {
// FIXME(zwarich): Remove this when UFCS lands and there is a better way
// of disambiguating methods.
- fn next_sibling<'a, T: NodeHelpers<'a>>(this: T) -> Option<Temporary<Node>> {
+ fn next_sibling<'a, T: NodeHelpers>(this: T) -> Option<Temporary<Node>> {
this.next_sibling()
}
@@ -2331,7 +2381,7 @@ impl<'a> style::node::TNode<'a> for JSRef<'a, Node> {
fn is_document(self) -> bool {
// FIXME(zwarich): Remove this when UFCS lands and there is a better way
// of disambiguating methods.
- fn is_document<'a, T: NodeHelpers<'a>>(this: T) -> bool {
+ fn is_document<'a, T: NodeHelpers>(this: T) -> bool {
this.is_document()
}
@@ -2341,7 +2391,7 @@ impl<'a> style::node::TNode<'a> for JSRef<'a, Node> {
fn is_element(self) -> bool {
// FIXME(zwarich): Remove this when UFCS lands and there is a better way
// of disambiguating methods.
- fn is_element<'a, T: NodeHelpers<'a>>(this: T) -> bool {
+ fn is_element<'a, T: NodeHelpers>(this: T) -> bool {
this.is_element()
}
@@ -2415,7 +2465,10 @@ pub trait DisabledStateHelpers {
impl<'a> DisabledStateHelpers for JSRef<'a, Node> {
fn check_ancestors_disabled_state_for_form_control(self) {
if self.get_disabled_state() { return; }
- for ancestor in self.ancestors().filter(|ancestor| ancestor.is_htmlfieldsetelement()) {
+ for ancestor in self.ancestors() {
+ let ancestor = ancestor.root();
+ let ancestor = ancestor.r();
+ if !ancestor.is_htmlfieldsetelement() { continue; }
if !ancestor.get_disabled_state() { continue; }
if ancestor.is_parent_of(self) {
self.set_disabled_state(true);
@@ -2428,7 +2481,7 @@ impl<'a> DisabledStateHelpers for JSRef<'a, Node> {
{
Some(legend) => {
// XXXabinader: should we save previous ancestor to avoid this iteration?
- if self.ancestors().any(|ancestor| ancestor == legend.r()) { continue; }
+ if self.ancestors().any(|ancestor| ancestor.root().r() == legend.r()) { continue; }
},
None => ()
}
diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs
index 84aa5d6e2ea..8826733cfa6 100644
--- a/components/script/dom/nodelist.rs
+++ b/components/script/dom/nodelist.rs
@@ -6,6 +6,7 @@ use dom::bindings::codegen::Bindings::NodeListBinding;
use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, JSRef, Temporary};
+use dom::bindings::trace::RootedVec;
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::node::{Node, NodeHelpers};
use dom::window::Window;
@@ -38,8 +39,8 @@ impl NodeList {
GlobalRef::Window(window), NodeListBinding::Wrap)
}
- pub fn new_simple_list(window: JSRef<Window>, elements: Vec<JSRef<Node>>) -> Temporary<NodeList> {
- NodeList::new(window, NodeListType::Simple(elements.iter().map(|element| JS::from_rooted(*element)).collect()))
+ pub fn new_simple_list(window: JSRef<Window>, elements: &RootedVec<JS<Node>>) -> Temporary<NodeList> {
+ NodeList::new(window, NodeListType::Simple((**elements).clone()))
}
pub fn new_child_list(window: JSRef<Window>, node: JSRef<Node>) -> Temporary<NodeList> {
diff --git a/components/script/dom/processinginstruction.rs b/components/script/dom/processinginstruction.rs
index 528c4cdd64a..7eeb1a7e460 100644
--- a/components/script/dom/processinginstruction.rs
+++ b/components/script/dom/processinginstruction.rs
@@ -37,17 +37,20 @@ impl ProcessingInstruction {
Node::reflect_node(box ProcessingInstruction::new_inherited(target, data, document),
document, ProcessingInstructionBinding::Wrap)
}
+}
- pub fn characterdata<'a>(&'a self) -> &'a CharacterData {
- &self.characterdata
- }
+pub trait ProcessingInstructionHelpers<'a> {
+ fn target(self) -> &'a DOMString;
+}
- pub fn target<'a>(&'a self) -> &'a DOMString {
- &self.target
+impl<'a> ProcessingInstructionHelpers<'a> for JSRef<'a, ProcessingInstruction> {
+ fn target(self) -> &'a DOMString {
+ &self.extended_deref().target
}
}
impl<'a> ProcessingInstructionMethods for JSRef<'a, ProcessingInstruction> {
+ // https://dom.spec.whatwg.org/#dom-processinginstruction-target
fn Target(self) -> DOMString {
self.target.clone()
}
diff --git a/components/script/dom/storage.rs b/components/script/dom/storage.rs
index 7868e53f21c..9ec49555c9c 100644
--- a/components/script/dom/storage.rs
+++ b/components/script/dom/storage.rs
@@ -63,7 +63,7 @@ impl<'a> StorageMethods for JSRef<'a, Storage> {
let (sender, receiver) = channel();
self.get_storage_task().send(StorageTaskMsg::Length(sender, self.get_url(), self.storage_type)).unwrap();
- receiver.recv().unwrap()
+ receiver.recv().unwrap() as u32
}
fn Key(self, index: u32) -> Option<DOMString> {
diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs
index 2b42f26f294..a4bda4eb817 100644
--- a/components/script/dom/testbinding.rs
+++ b/components/script/dom/testbinding.rs
@@ -24,6 +24,7 @@ use js::jsapi::{JSContext, JSObject};
use js::jsval::{JSVal, NullValue};
use std::borrow::ToOwned;
+use std::ptr;
#[dom_struct]
pub struct TestBinding {
@@ -78,6 +79,8 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
fn ArrayAttribute(self, _: *mut JSContext) -> *mut JSObject { NullValue().to_object_or_null() }
fn AnyAttribute(self, _: *mut JSContext) -> JSVal { NullValue() }
fn SetAnyAttribute(self, _: *mut JSContext, _: JSVal) {}
+ fn ObjectAttribute(self, _: *mut JSContext) -> *mut JSObject { panic!() }
+ fn SetObjectAttribute(self, _: *mut JSContext, _: *mut JSObject) {}
fn GetBooleanAttributeNullable(self) -> Option<bool> { Some(false) }
fn SetBooleanAttributeNullable(self, _: Option<bool>) {}
@@ -119,6 +122,8 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
Some(Blob::new(global.r(), None, ""))
}
fn SetInterfaceAttributeNullable(self, _: Option<JSRef<Blob>>) {}
+ fn GetObjectAttributeNullable(self, _: *mut JSContext) -> *mut JSObject { ptr::null_mut() }
+ fn SetObjectAttributeNullable(self, _: *mut JSContext, _: *mut JSObject) {}
fn GetUnionAttributeNullable(self) -> Option<HTMLElementOrLong> { Some(eLong(0)) }
fn SetUnionAttributeNullable(self, _: Option<HTMLElementOrLong>) {}
fn GetUnion2AttributeNullable(self) -> Option<EventOrString> { Some(eString("".to_owned())) }
@@ -147,6 +152,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
Blob::new(global.r(), None, "")
}
fn ReceiveAny(self, _: *mut JSContext) -> JSVal { NullValue() }
+ fn ReceiveObject(self, _: *mut JSContext) -> *mut JSObject { panic!() }
fn ReceiveUnion(self) -> HTMLElementOrLong { eLong(0) }
fn ReceiveUnion2(self) -> EventOrString { eString("".to_owned()) }
@@ -171,6 +177,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
let global = self.global.root();
Some(Blob::new(global.r(), None, ""))
}
+ fn ReceiveNullableObject(self, _: *mut JSContext) -> *mut JSObject { ptr::null_mut() }
fn ReceiveNullableUnion(self) -> Option<HTMLElementOrLong> { Some(eLong(0)) }
fn ReceiveNullableUnion2(self) -> Option<EventOrString> { Some(eString("".to_owned())) }
@@ -196,6 +203,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
fn PassUnion2(self, _: EventOrString) {}
fn PassUnion3(self, _: BlobOrString) {}
fn PassAny(self, _: *mut JSContext, _: JSVal) {}
+ fn PassObject(self, _: *mut JSContext, _: *mut JSObject) {}
fn PassCallbackFunction(self, _: Function) {}
fn PassCallbackInterface(self, _: EventListener) {}
@@ -217,6 +225,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
fn PassNullableByteString(self, _: Option<ByteString>) {}
// fn PassNullableEnum(self, _: Option<TestEnum>) {}
fn PassNullableInterface(self, _: Option<JSRef<Blob>>) {}
+ fn PassNullableObject(self, _: *mut JSContext, _: *mut JSObject) {}
fn PassNullableUnion(self, _: Option<HTMLElementOrLong>) {}
fn PassNullableUnion2(self, _: Option<EventOrString>) {}
fn PassNullableCallbackFunction(self, _: Option<Function>) {}
@@ -243,6 +252,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
fn PassOptionalUnion(self, _: Option<HTMLElementOrLong>) {}
fn PassOptionalUnion2(self, _: Option<EventOrString>) {}
fn PassOptionalAny(self, _: *mut JSContext, _: JSVal) {}
+ fn PassOptionalObject(self, _: *mut JSContext, _: Option<*mut JSObject>) {}
fn PassOptionalCallbackFunction(self, _: Option<Function>) {}
fn PassOptionalCallbackInterface(self, _: Option<EventListener>) {}
@@ -264,6 +274,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
fn PassOptionalNullableByteString(self, _: Option<Option<ByteString>>) {}
// fn PassOptionalNullableEnum(self, _: Option<Option<TestEnum>>) {}
fn PassOptionalNullableInterface(self, _: Option<Option<JSRef<Blob>>>) {}
+ fn PassOptionalNullableObject(self, _: *mut JSContext, _: Option<*mut JSObject>) {}
fn PassOptionalNullableUnion(self, _: Option<Option<HTMLElementOrLong>>) {}
fn PassOptionalNullableUnion2(self, _: Option<Option<EventOrString>>) {}
fn PassOptionalNullableCallbackFunction(self, _: Option<Option<Function>>) {}
@@ -300,6 +311,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
fn PassOptionalNullableByteStringWithDefault(self, _: Option<ByteString>) {}
// fn PassOptionalNullableEnumWithDefault(self, _: Option<TestEnum>) {}
fn PassOptionalNullableInterfaceWithDefault(self, _: Option<JSRef<Blob>>) {}
+ fn PassOptionalNullableObjectWithDefault(self, _: *mut JSContext, _: *mut JSObject) {}
fn PassOptionalNullableUnionWithDefault(self, _: Option<HTMLElementOrLong>) {}
fn PassOptionalNullableUnion2WithDefault(self, _: Option<EventOrString>) {}
// fn PassOptionalNullableCallbackFunctionWithDefault(self, _: Option<Function>) {}
@@ -345,6 +357,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
fn PassVariadicUnion2(self, _: Vec<EventOrString>) {}
fn PassVariadicUnion3(self, _: Vec<BlobOrString>) {}
fn PassVariadicAny(self, _: *mut JSContext, _: Vec<JSVal>) {}
+ fn PassVariadicObject(self, _: *mut JSContext, _: Vec<*mut JSObject>) {}
}
impl TestBinding {
diff --git a/components/script/dom/text.rs b/components/script/dom/text.rs
index ea6c2791e96..5df59274e9e 100644
--- a/components/script/dom/text.rs
+++ b/components/script/dom/text.rs
@@ -2,16 +2,17 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use dom::bindings::codegen::Bindings::TextBinding;
+use dom::bindings::codegen::Bindings::TextBinding::{self, TextMethods};
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
-use dom::bindings::codegen::InheritTypes::TextDerived;
+use dom::bindings::codegen::InheritTypes::{CharacterDataCast, TextDerived};
+use dom::bindings::codegen::InheritTypes::NodeCast;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JSRef, Temporary};
-use dom::characterdata::CharacterData;
+use dom::characterdata::{CharacterData, CharacterDataHelpers};
use dom::document::Document;
use dom::eventtarget::{EventTarget, EventTargetTypeId};
-use dom::node::{Node, NodeTypeId};
+use dom::node::{Node, NodeHelpers, NodeTypeId};
use util::str::DOMString;
/// An HTML text node.
@@ -42,10 +43,23 @@ impl Text {
let document = global.as_window().Document().root();
Ok(Text::new(text, document.r()))
}
+}
- #[inline]
- pub fn characterdata<'a>(&'a self) -> &'a CharacterData {
- &self.characterdata
+impl<'a> TextMethods for JSRef<'a, Text> {
+ // https://dom.spec.whatwg.org/#dom-text-wholetext
+ fn WholeText(self) -> DOMString {
+ let first = NodeCast::from_ref(self).inclusively_preceding_siblings()
+ .map(|node| node.root())
+ .take_while(|node| node.r().is_text())
+ .last().unwrap();
+ let nodes = first.r().inclusively_following_siblings().map(|node| node.root())
+ .take_while(|node| node.r().is_text());
+ let mut text = DOMString::new();
+ for node in nodes {
+ let cdata = CharacterDataCast::to_ref(node.r()).unwrap();
+ text.push_str(&cdata.data());
+ }
+ text
}
}
diff --git a/components/script/dom/textdecoder.rs b/components/script/dom/textdecoder.rs
new file mode 100644
index 00000000000..714200f9a8f
--- /dev/null
+++ b/components/script/dom/textdecoder.rs
@@ -0,0 +1,99 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::TextDecoderBinding;
+use dom::bindings::codegen::Bindings::TextDecoderBinding::TextDecoderMethods;
+use dom::bindings::error::{Error, Fallible};
+use dom::bindings::global::GlobalRef;
+use dom::bindings::js::{JSRef, Temporary};
+use dom::bindings::str::USVString;
+use dom::bindings::trace::JSTraceable;
+use dom::bindings::utils::{Reflector, reflect_dom_object};
+
+use util::str::DOMString;
+
+use encoding::Encoding;
+use encoding::types::{EncodingRef, DecoderTrap};
+use encoding::label::encoding_from_whatwg_label;
+use js::jsapi::{JSContext, JSObject};
+use js::jsfriendapi::bindgen::JS_GetObjectAsArrayBufferView;
+
+use std::borrow::ToOwned;
+use std::ptr;
+use std::slice;
+
+#[dom_struct]
+pub struct TextDecoder {
+ reflector_: Reflector,
+ encoding: EncodingRef,
+ fatal: bool,
+}
+
+impl TextDecoder {
+ fn new_inherited(encoding: EncodingRef, fatal: bool) -> TextDecoder {
+ TextDecoder {
+ reflector_: Reflector::new(),
+ encoding: encoding,
+ fatal: fatal,
+ }
+ }
+
+ pub fn new(global: GlobalRef, encoding: EncodingRef, fatal: bool) -> Temporary<TextDecoder> {
+ reflect_dom_object(box TextDecoder::new_inherited(encoding, fatal),
+ global,
+ TextDecoderBinding::Wrap)
+ }
+
+ /// https://encoding.spec.whatwg.org/#dom-textdecoder
+ pub fn Constructor(global: GlobalRef,
+ label: DOMString,
+ options: &TextDecoderBinding::TextDecoderOptions)
+ -> Fallible<Temporary<TextDecoder>> {
+ let encoding = match encoding_from_whatwg_label(&label) {
+ Some(enc) => enc,
+ // FIXME: Should throw a RangeError as per spec
+ None => return Err(Error::Syntax),
+ };
+ Ok(TextDecoder::new(global, encoding, options.fatal))
+ }
+}
+
+impl<'a> TextDecoderMethods for JSRef<'a, TextDecoder> {
+ fn Encoding(self) -> DOMString {
+ self.encoding.whatwg_name().unwrap().to_owned()
+ }
+
+ fn Fatal(self) -> bool {
+ self.fatal
+ }
+
+ #[allow(unsafe_code)]
+ fn Decode(self, cx: *mut JSContext, input: Option<*mut JSObject>)
+ -> Fallible<USVString> {
+ let input = match input {
+ Some(input) => input,
+ None => return Ok(USVString("".to_owned())),
+ };
+
+ let mut length = 0;
+ let mut data = ptr::null_mut();
+ if unsafe { JS_GetObjectAsArrayBufferView(cx, input, &mut length, &mut data).is_null() } {
+ return Err(Error::Type("Argument to TextDecoder.decode is not an ArrayBufferView".to_owned()));
+ }
+
+ let buffer = unsafe {
+ slice::from_raw_parts(data as *const _, length as usize)
+ };
+ let trap = if self.fatal {
+ DecoderTrap::Strict
+ } else {
+ DecoderTrap::Replace
+ };
+ match self.encoding.decode(buffer, trap) {
+ Ok(s) => Ok(USVString(s)),
+ Err(_) => Err(Error::Type("Decoding failed".to_owned())),
+ }
+ }
+
+}
diff --git a/components/script/dom/treewalker.rs b/components/script/dom/treewalker.rs
index 1957999bcf4..d25dba20d1b 100644
--- a/components/script/dom/treewalker.rs
+++ b/components/script/dom/treewalker.rs
@@ -7,10 +7,7 @@ use dom::bindings::codegen::Bindings::TreeWalkerBinding;
use dom::bindings::codegen::Bindings::TreeWalkerBinding::TreeWalkerMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
-// FIXME: Uncomment when codegen fix allows NodeFilterConstants
-// to move to the NodeFilter binding file (#3149).
-// For now, it is defined in this file.
-// use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilterConstants;
+use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilterConstants;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, JSRef, OptionalRootable, Temporary, MutHeap};
@@ -563,24 +560,3 @@ pub enum Filter {
Native(fn (node: JSRef<Node>) -> u16),
JS(NodeFilter)
}
-
-// FIXME: NodeFilterConstants will be defined in NodeFilterBindings.rs
-// when codegen supports a callback interface with constants (#3149).
-pub mod NodeFilterConstants {
- pub const FILTER_ACCEPT: u16 = 1;
- pub const FILTER_REJECT: u16 = 2;
- pub const FILTER_SKIP: u16 = 3;
- pub const SHOW_ALL: u32 = 4294967295;
- pub const SHOW_ELEMENT: u32 = 1;
- pub const SHOW_ATTRIBUTE: u32 = 2;
- pub const SHOW_TEXT: u32 = 4;
- pub const SHOW_CDATA_SECTION: u32 = 8;
- pub const SHOW_ENTITY_REFERENCE: u32 = 16;
- pub const SHOW_ENTITY: u32 = 32;
- pub const SHOW_PROCESSING_INSTRUCTION: u32 = 64;
- pub const SHOW_COMMENT: u32 = 128;
- pub const SHOW_DOCUMENT: u32 = 256;
- pub const SHOW_DOCUMENT_TYPE: u32 = 512;
- pub const SHOW_DOCUMENT_FRAGMENT: u32 = 1024;
- pub const SHOW_NOTATION: u32 = 2048;
-} // mod NodeFilterConstants
diff --git a/components/script/dom/webidls/CanvasRenderingContext2D.webidl b/components/script/dom/webidls/CanvasRenderingContext2D.webidl
index 511a26ae66b..4c4b94374f2 100644
--- a/components/script/dom/webidls/CanvasRenderingContext2D.webidl
+++ b/components/script/dom/webidls/CanvasRenderingContext2D.webidl
@@ -6,8 +6,8 @@
enum CanvasWindingRule { "nonzero", "evenodd" };
// http://www.whatwg.org/html/#2dcontext
-typedef (/* HTMLImageElement or
- HTMLVideoElement or */
+typedef (HTMLImageElement or
+ /* HTMLVideoElement or */
HTMLCanvasElement or
CanvasRenderingContext2D /* or
ImageBitmap */) CanvasImageSource;
@@ -49,7 +49,7 @@ interface CanvasRenderingContext2D {
//void resetTransform();
// compositing
- // attribute unrestricted double globalAlpha; // (default 1.0)
+ attribute unrestricted double globalAlpha; // (default 1.0)
// attribute DOMString globalCompositeOperation; // (default source-over)
// image smoothing
@@ -131,6 +131,26 @@ interface CanvasRenderingContext2D {
};
[NoInterfaceObject]
+interface CanvasDrawingStyles {
+ // line caps/joins
+ attribute unrestricted double lineWidth; // (default 1)
+ //attribute DOMString lineCap; // "butt", "round", "square" (default "butt")
+ //attribute DOMString lineJoin; // "round", "bevel", "miter" (default "miter")
+ attribute unrestricted double miterLimit; // (default 10)
+
+ // dashed lines
+ //void setLineDash(sequence<unrestricted double> segments); // default empty
+ //sequence<unrestricted double> getLineDash();
+ //attribute unrestricted double lineDashOffset;
+
+ // text
+ //attribute DOMString font; // (default 10px sans-serif)
+ //attribute DOMString textAlign; // "start", "end", "left", "right", "center" (default: "start")
+ //attribute DOMString textBaseline; // "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" (default: "alphabetic")
+ //attribute DOMString direction; // "ltr", "rtl", "inherit" (default: "inherit")
+};
+
+[NoInterfaceObject]
interface CanvasPathMethods {
// shared path API methods
void closePath();
@@ -159,5 +179,6 @@ interface CanvasPathMethods {
};
+CanvasRenderingContext2D implements CanvasDrawingStyles;
CanvasRenderingContext2D implements CanvasPathMethods;
diff --git a/components/script/dom/webidls/CharacterData.webidl b/components/script/dom/webidls/CharacterData.webidl
index d1b222bc168..e758adb4d30 100644
--- a/components/script/dom/webidls/CharacterData.webidl
+++ b/components/script/dom/webidls/CharacterData.webidl
@@ -11,11 +11,10 @@
*/
interface CharacterData : Node {
- [TreatNullAs=EmptyString,SetterThrows] attribute DOMString data;
+ [TreatNullAs=EmptyString] attribute DOMString data;
readonly attribute unsigned long length;
[Throws]
DOMString substringData(unsigned long offset, unsigned long count);
- [Throws]
void appendData(DOMString data);
[Throws]
void insertData(unsigned long offset, DOMString data);
@@ -26,3 +25,4 @@ interface CharacterData : Node {
};
CharacterData implements ChildNode;
+CharacterData implements NonDocumentTypeChildNode;
diff --git a/components/script/dom/webidls/ChildNode.webidl b/components/script/dom/webidls/ChildNode.webidl
index 16562fbafbf..fbcf8ea1208 100644
--- a/components/script/dom/webidls/ChildNode.webidl
+++ b/components/script/dom/webidls/ChildNode.webidl
@@ -16,10 +16,10 @@ interface ChildNode {
void remove();
};
-// [NoInterfaceObject]
-// interface NonDocumentTypeChildNode {
-// [Pure]
-// readonly attribute Element? previousElementSibling;
-// [Pure]
-// readonly attribute Element? nextElementSibling;
-// };
+[NoInterfaceObject]
+interface NonDocumentTypeChildNode {
+ [Pure]
+ readonly attribute Element? previousElementSibling;
+ [Pure]
+ readonly attribute Element? nextElementSibling;
+};
diff --git a/components/script/dom/webidls/Document.webidl b/components/script/dom/webidls/Document.webidl
index 984f79eb528..c57b09fff1b 100644
--- a/components/script/dom/webidls/Document.webidl
+++ b/components/script/dom/webidls/Document.webidl
@@ -27,27 +27,33 @@ interface Document : Node {
HTMLCollection getElementsByClassName(DOMString classNames);
Element? getElementById(DOMString elementId);
- [Throws]
+ [NewObject, Throws]
Element createElement(DOMString localName);
- [Throws]
+ [NewObject, Throws]
Element createElementNS(DOMString? namespace, DOMString qualifiedName);
+ [NewObject]
DocumentFragment createDocumentFragment();
+ [NewObject]
Text createTextNode(DOMString data);
+ [NewObject]
Comment createComment(DOMString data);
- [Throws]
+ [NewObject, Throws]
ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);
- [Throws]
- Attr createAttribute(DOMString localName);
-
- [Throws]
+ [NewObject, Throws]
Node importNode(Node node, optional boolean deep = false);
[Throws]
Node adoptNode(Node node);
- [Throws]
+ [NewObject, Throws]
+ Attr createAttribute(DOMString localName);
+ [NewObject, Throws]
+ Attr createAttributeNS(DOMString? namespace, DOMString localName);
+
+ [NewObject, Throws]
Event createEvent(DOMString interface_);
+ [NewObject]
Range createRange();
// NodeFilter.SHOW_ALL = 0xFFFFFFFF
diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl
index 8d08717cd31..1090ad91588 100644
--- a/components/script/dom/webidls/Element.webidl
+++ b/components/script/dom/webidls/Element.webidl
@@ -70,4 +70,5 @@ partial interface Element {
};
Element implements ChildNode;
+Element implements NonDocumentTypeChildNode;
Element implements ParentNode;
diff --git a/components/script/dom/webidls/NamedNodeMap.webidl b/components/script/dom/webidls/NamedNodeMap.webidl
index 636c4a2782f..5b85e755804 100644
--- a/components/script/dom/webidls/NamedNodeMap.webidl
+++ b/components/script/dom/webidls/NamedNodeMap.webidl
@@ -5,4 +5,14 @@
interface NamedNodeMap {
readonly attribute unsigned long length;
getter Attr? item(unsigned long index);
+ getter Attr? getNamedItem(DOMString name);
+ Attr? getNamedItemNS(DOMString? namespace, DOMString localName);
+ //[Throws]
+ //Attr? setNamedItem(Attr attr);
+ //[Throws]
+ //Attr? setNamedItemNS(Attr attr);
+ [Throws]
+ Attr removeNamedItem(DOMString name);
+ [Throws]
+ Attr removeNamedItemNS(DOMString? namespace, DOMString name);
};
diff --git a/components/script/dom/webidls/NodeFilter.webidl b/components/script/dom/webidls/NodeFilter.webidl
index b84b369829e..2b0b1763c89 100644
--- a/components/script/dom/webidls/NodeFilter.webidl
+++ b/components/script/dom/webidls/NodeFilter.webidl
@@ -6,28 +6,28 @@
* The origin of this IDL file is
* http://dom.spec.whatwg.org/#interface-nodefilter
*/
-// Import form http://hg.mozilla.org/mozilla-central/file/a5a720259d79/dom/webidl/NodeFilter.webidl
+// Import from http://hg.mozilla.org/mozilla-central/file/a5a720259d79/dom/webidl/NodeFilter.webidl
callback interface NodeFilter {
// Constants for acceptNode()
- // const unsigned short FILTER_ACCEPT = 1;
- // const unsigned short FILTER_REJECT = 2;
- // const unsigned short FILTER_SKIP = 3;
+ const unsigned short FILTER_ACCEPT = 1;
+ const unsigned short FILTER_REJECT = 2;
+ const unsigned short FILTER_SKIP = 3;
// Constants for whatToShow
- // const unsigned long SHOW_ALL = 0xFFFFFFFF;
- // const unsigned long SHOW_ELEMENT = 0x1;
- // const unsigned long SHOW_ATTRIBUTE = 0x2; // historical
- // const unsigned long SHOW_TEXT = 0x4;
- // const unsigned long SHOW_CDATA_SECTION = 0x8; // historical
- // const unsigned long SHOW_ENTITY_REFERENCE = 0x10; // historical
- // const unsigned long SHOW_ENTITY = 0x20; // historical
- // const unsigned long SHOW_PROCESSING_INSTRUCTION = 0x40;
- // const unsigned long SHOW_COMMENT = 0x80;
- // const unsigned long SHOW_DOCUMENT = 0x100;
- // const unsigned long SHOW_DOCUMENT_TYPE = 0x200;
- // const unsigned long SHOW_DOCUMENT_FRAGMENT = 0x400;
- // const unsigned long SHOW_NOTATION = 0x800; // historical
+ const unsigned long SHOW_ALL = 0xFFFFFFFF;
+ const unsigned long SHOW_ELEMENT = 0x1;
+ const unsigned long SHOW_ATTRIBUTE = 0x2; // historical
+ const unsigned long SHOW_TEXT = 0x4;
+ const unsigned long SHOW_CDATA_SECTION = 0x8; // historical
+ const unsigned long SHOW_ENTITY_REFERENCE = 0x10; // historical
+ const unsigned long SHOW_ENTITY = 0x20; // historical
+ const unsigned long SHOW_PROCESSING_INSTRUCTION = 0x40;
+ const unsigned long SHOW_COMMENT = 0x80;
+ const unsigned long SHOW_DOCUMENT = 0x100;
+ const unsigned long SHOW_DOCUMENT_TYPE = 0x200;
+ const unsigned long SHOW_DOCUMENT_FRAGMENT = 0x400;
+ const unsigned long SHOW_NOTATION = 0x800; // historical
unsigned short acceptNode(Node node);
};
diff --git a/components/script/dom/webidls/ParentNode.webidl b/components/script/dom/webidls/ParentNode.webidl
index daa4339611f..9df1143f625 100644
--- a/components/script/dom/webidls/ParentNode.webidl
+++ b/components/script/dom/webidls/ParentNode.webidl
@@ -11,14 +11,13 @@
interface ParentNode {
[Constant]
readonly attribute HTMLCollection children;
- /*
[Pure]
readonly attribute Element? firstElementChild;
[Pure]
readonly attribute Element? lastElementChild;
[Pure]
readonly attribute unsigned long childElementCount;
- */
+
// Not implemented yet
// void prepend((Node or DOMString)... nodes);
// void append((Node or DOMString)... nodes);
diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl
index e5c5aa1772c..9906de4e696 100644
--- a/components/script/dom/webidls/TestBinding.webidl
+++ b/components/script/dom/webidls/TestBinding.webidl
@@ -23,6 +23,7 @@ dictionary TestDictionary {
TestEnum enumValue;
Blob interfaceValue;
any anyValue;
+ object objectValue;
};
dictionary TestDictionaryDefaults {
@@ -60,6 +61,7 @@ dictionary TestDictionaryDefaults {
DOMString? nullableStringValue = "foo";
USVString? nullableUsvstringValue = "foo";
// TestEnum? nullableEnumValue = "bar";
+ object? nullableObjectValue = null;
};
interface TestBinding {
@@ -85,6 +87,7 @@ interface TestBinding {
attribute (Event or DOMString) union2Attribute;
readonly attribute Uint8ClampedArray arrayAttribute;
attribute any anyAttribute;
+ attribute object objectAttribute;
attribute boolean? booleanAttributeNullable;
attribute byte? byteAttributeNullable;
@@ -104,6 +107,7 @@ interface TestBinding {
attribute ByteString? byteStringAttributeNullable;
readonly attribute TestEnum? enumAttributeNullable;
attribute Blob? interfaceAttributeNullable;
+ attribute object? objectAttributeNullable;
attribute (HTMLElement or long)? unionAttributeNullable;
attribute (Event or DOMString)? union2AttributeNullable;
[BinaryName="BinaryRenamedAttribute"] attribute DOMString attrToBinaryRename;
@@ -129,6 +133,7 @@ interface TestBinding {
TestEnum receiveEnum();
Blob receiveInterface();
any receiveAny();
+ object receiveObject();
(HTMLElement or long) receiveUnion();
(Event or DOMString) receiveUnion2();
@@ -150,6 +155,7 @@ interface TestBinding {
ByteString? receiveNullableByteString();
TestEnum? receiveNullableEnum();
Blob? receiveNullableInterface();
+ object? receiveNullableObject();
(HTMLElement or long)? receiveNullableUnion();
(Event or DOMString)? receiveNullableUnion2();
@@ -175,6 +181,7 @@ interface TestBinding {
void passUnion2((Event or DOMString) data);
void passUnion3((Blob or DOMString) data);
void passAny(any arg);
+ void passObject(object arg);
void passCallbackFunction(Function fun);
void passCallbackInterface(EventListener listener);
@@ -196,6 +203,7 @@ interface TestBinding {
void passNullableByteString(ByteString? arg);
// void passNullableEnum(TestEnum? arg);
void passNullableInterface(Blob? arg);
+ void passNullableObject(object? arg);
void passNullableUnion((HTMLElement or long)? arg);
void passNullableUnion2((Event or DOMString)? data);
void passNullableCallbackFunction(Function? fun);
@@ -222,6 +230,7 @@ interface TestBinding {
void passOptionalUnion(optional (HTMLElement or long) arg);
void passOptionalUnion2(optional (Event or DOMString) data);
void passOptionalAny(optional any arg);
+ void passOptionalObject(optional object arg);
void passOptionalCallbackFunction(optional Function fun);
void passOptionalCallbackInterface(optional EventListener listener);
@@ -243,6 +252,7 @@ interface TestBinding {
void passOptionalNullableByteString(optional ByteString? arg);
// void passOptionalNullableEnum(optional TestEnum? arg);
void passOptionalNullableInterface(optional Blob? arg);
+ void passOptionalNullableObject(optional object? arg);
void passOptionalNullableUnion(optional (HTMLElement or long)? arg);
void passOptionalNullableUnion2(optional (Event or DOMString)? data);
void passOptionalNullableCallbackFunction(optional Function? fun);
@@ -277,6 +287,7 @@ interface TestBinding {
void passOptionalNullableByteStringWithDefault(optional ByteString? arg = null);
// void passOptionalNullableEnumWithDefault(optional TestEnum? arg = null);
void passOptionalNullableInterfaceWithDefault(optional Blob? arg = null);
+ void passOptionalNullableObjectWithDefault(optional object? arg = null);
void passOptionalNullableUnionWithDefault(optional (HTMLElement or long)? arg = null);
void passOptionalNullableUnion2WithDefault(optional (Event or DOMString)? data = null);
// void passOptionalNullableCallbackFunctionWithDefault(optional Function? fun = null);
@@ -324,6 +335,7 @@ interface TestBinding {
void passVariadicUnion2((Event or DOMString)... args);
void passVariadicUnion3((Blob or DOMString)... args);
void passVariadicAny(any... args);
+ void passVariadicObject(object... args);
static attribute boolean booleanAttributeStatic;
static void receiveVoidStatic();
diff --git a/components/script/dom/webidls/Text.webidl b/components/script/dom/webidls/Text.webidl
index 972797c73c8..e46d4e24d9c 100644
--- a/components/script/dom/webidls/Text.webidl
+++ b/components/script/dom/webidls/Text.webidl
@@ -14,5 +14,5 @@
[Constructor(optional DOMString data = "")]
interface Text : CharacterData {
//[NewObject] Text splitText(unsigned long offset);
- //readonly attribute DOMString wholeText;
+ readonly attribute DOMString wholeText;
};
diff --git a/components/script/dom/webidls/TextDecoder.webidl b/components/script/dom/webidls/TextDecoder.webidl
new file mode 100644
index 00000000000..4ec66f07d7a
--- /dev/null
+++ b/components/script/dom/webidls/TextDecoder.webidl
@@ -0,0 +1,21 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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://encoding.spec.whatwg.org/#interface-textdecoder
+dictionary TextDecoderOptions {
+ boolean fatal = false;
+ //boolean ignoreBOM = false;
+};
+
+[Constructor(optional DOMString label = "utf-8", optional TextDecoderOptions options)/*,
+ Exposed=Window,Worker*/]
+interface TextDecoder {
+ readonly attribute DOMString encoding;
+ readonly attribute boolean fatal;
+ //readonly attribute boolean ignoreBOM;
+ //USVString decode(optional BufferSource input, optional TextDecodeOptions options);
+ [Throws]
+ USVString decode(optional object input);
+};
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 9fe135eb53f..408f4da4ea7 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -34,7 +34,7 @@ use script_task::ScriptMsg;
use script_traits::ScriptControlChan;
use timers::{IsInterval, TimerId, TimerManager, TimerCallback};
-use devtools_traits::DevtoolsControlChan;
+use devtools_traits::{DevtoolsControlChan, TimelineMarker, TimelineMarkerType, TracingMetadata};
use msg::compositor_msg::ScriptListener;
use msg::constellation_msg::{LoadData, PipelineId, SubpageId, ConstellationChan, WindowSizeData, WorkerId};
use net_traits::ResourceTask;
@@ -54,13 +54,15 @@ use url::{Url, UrlParser};
use libc;
use rustc_serialize::base64::{FromBase64, ToBase64, STANDARD};
-use std::cell::{Cell, Ref, RefMut};
+use std::borrow::ToOwned;
+use std::cell::{Cell, Ref, RefMut, RefCell};
+use std::collections::HashSet;
use std::default::Default;
use std::ffi::CString;
use std::mem;
use std::num::Float;
use std::rc::Rc;
-use std::sync::mpsc::{channel, Receiver};
+use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::mpsc::TryRecvError::{Empty, Disconnected};
use time;
@@ -102,6 +104,10 @@ pub struct Window {
/// For providing instructions to an optional devtools server.
devtools_chan: Option<DevtoolsControlChan>,
+ /// For sending timeline markers. Will be ignored if
+ /// no devtools server
+ devtools_markers: RefCell<HashSet<TimelineMarkerType>>,
+ devtools_marker_sender: RefCell<Option<Sender<TimelineMarker>>>,
/// A flag to indicate whether the developer tools have requested live updates of
/// page changes.
@@ -477,6 +483,10 @@ pub trait WindowHelpers {
fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>>;
fn thaw(self);
fn freeze(self);
+ fn need_emit_timeline_marker(self, timeline_type: TimelineMarkerType) -> bool;
+ fn emit_timeline_marker(self, marker: TimelineMarker);
+ fn set_devtools_timeline_marker(self, marker: TimelineMarkerType, reply: Sender<TimelineMarker>);
+ fn drop_devtools_timeline_markers(self);
}
pub trait ScriptHelpers {
@@ -547,6 +557,11 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
None => return,
};
+ if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) {
+ let marker = TimelineMarker::new("Reflow".to_owned(), TracingMetadata::IntervalStart);
+ self.emit_timeline_marker(marker);
+ }
+
// Layout will let us know when it's done.
let (join_chan, join_port) = channel();
@@ -583,6 +598,11 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
debug!("script: layout forked");
self.join_layout();
+
+ if self.need_emit_timeline_marker(TimelineMarkerType::Reflow) {
+ let marker = TimelineMarker::new("Reflow".to_owned(), TracingMetadata::IntervalEnd);
+ self.emit_timeline_marker(marker);
+ }
}
// FIXME(cgaebel): join_layout is racey. What if the compositor triggers a
@@ -776,6 +796,27 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
fn freeze(self) {
self.timers.suspend();
}
+
+ fn need_emit_timeline_marker(self, timeline_type: TimelineMarkerType) -> bool {
+ let markers = self.devtools_markers.borrow();
+ markers.contains(&timeline_type)
+ }
+
+ fn emit_timeline_marker(self, marker: TimelineMarker) {
+ let sender = self.devtools_marker_sender.borrow();
+ let sender = sender.as_ref().expect("There is no marker sender");
+ sender.send(marker).unwrap();
+ }
+
+ fn set_devtools_timeline_marker(self, marker: TimelineMarkerType, reply: Sender<TimelineMarker>) {
+ *self.devtools_marker_sender.borrow_mut() = Some(reply);
+ self.devtools_markers.borrow_mut().insert(marker);
+ }
+
+ fn drop_devtools_timeline_markers(self) {
+ self.devtools_markers.borrow_mut().clear();
+ *self.devtools_marker_sender.borrow_mut() = None;
+ }
}
impl Window {
@@ -836,6 +877,9 @@ impl Window {
layout_rpc: layout_rpc,
layout_join_port: DOMRefCell::new(None),
window_size: Cell::new(window_size),
+
+ devtools_marker_sender: RefCell::new(None),
+ devtools_markers: RefCell::new(HashSet::new()),
devtools_wants_updates: Cell::new(false),
};
diff --git a/components/script/lib.rs b/components/script/lib.rs
index 75699c419cc..4a9fcdbd09c 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -39,6 +39,7 @@ extern crate js;
extern crate libc;
extern crate msg;
extern crate net_traits;
+extern crate png;
extern crate "rustc-serialize" as rustc_serialize;
extern crate time;
extern crate canvas;
@@ -65,6 +66,3 @@ pub mod script_task;
mod timers;
pub mod textinput;
mod devtools;
-
-#[cfg(all(test, target_pointer_width = "64"))]
-mod tests;
diff --git a/components/script/page.rs b/components/script/page.rs
index cea2ddefc27..0a226211016 100644
--- a/components/script/page.rs
+++ b/components/script/page.rs
@@ -76,7 +76,7 @@ impl Page {
Temporary::new(self.frame.borrow().as_ref().unwrap().window.clone())
}
- pub fn window_for_script_dealloation(&self) -> Unrooted<Window> {
+ pub fn window_for_script_deallocation(&self) -> Unrooted<Window> {
Unrooted::from_js(self.frame.borrow().as_ref().unwrap().window)
}
diff --git a/components/script/parse/html.rs b/components/script/parse/html.rs
index 2aecd9264e9..7509fd5d447 100644
--- a/components/script/parse/html.rs
+++ b/components/script/parse/html.rs
@@ -7,12 +7,14 @@
use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
-use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast, HTMLScriptElementCast};
-use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, TextCast, CommentCast};
+use dom::bindings::codegen::InheritTypes::{CharacterDataCast, DocumentTypeCast};
+use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLScriptElementCast};
+use dom::bindings::codegen::InheritTypes::{HTMLFormElementDerived, NodeCast};
use dom::bindings::codegen::InheritTypes::ProcessingInstructionCast;
-use dom::bindings::codegen::InheritTypes::HTMLFormElementDerived;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, Root};
+use dom::bindings::js::RootedReference;
use dom::bindings::trace::RootedVec;
+use dom::characterdata::CharacterDataHelpers;
use dom::comment::Comment;
use dom::document::{Document, DocumentHelpers};
use dom::document::{DocumentSource, IsHTMLDocument};
@@ -23,6 +25,7 @@ use dom::htmlscriptelement::HTMLScriptElementHelpers;
use dom::node::{Node, NodeHelpers, NodeTypeId};
use dom::node::{document_from_node, window_from_node};
use dom::processinginstruction::ProcessingInstruction;
+use dom::processinginstruction::ProcessingInstructionHelpers;
use dom::servohtmlparser;
use dom::servohtmlparser::{ServoHTMLParser, FragmentContext};
use dom::text::Text;
@@ -233,22 +236,19 @@ impl<'a> Serializable for JSRef<'a, Node> {
},
(IncludeNode, NodeTypeId::Text) => {
- let text: JSRef<Text> = TextCast::to_ref(node).unwrap();
- let data = text.characterdata().data();
- serializer.write_text(data.as_slice())
+ let cdata = CharacterDataCast::to_ref(node).unwrap();
+ serializer.write_text(&cdata.data())
},
(IncludeNode, NodeTypeId::Comment) => {
- let comment: JSRef<Comment> = CommentCast::to_ref(node).unwrap();
- let data = comment.characterdata().data();
- serializer.write_comment(data.as_slice())
+ let cdata = CharacterDataCast::to_ref(node).unwrap();
+ serializer.write_comment(&cdata.data())
},
(IncludeNode, NodeTypeId::ProcessingInstruction) => {
let pi: JSRef<ProcessingInstruction> = ProcessingInstructionCast::to_ref(node).unwrap();
- let data = pi.characterdata().data();
- serializer.write_processing_instruction(pi.target().as_slice(),
- data.as_slice())
+ let data = CharacterDataCast::from_ref(pi).data();
+ serializer.write_processing_instruction(&pi.target(), &data)
},
(IncludeNode, NodeTypeId::DocumentFragment) => Ok(()),
@@ -350,10 +350,11 @@ pub fn parse_html_fragment(context_node: JSRef<Node>,
// Step 11.
let form = context_node.inclusive_ancestors()
- .find(|element| element.is_htmlformelement());
+ .map(|element| element.root())
+ .find(|element| element.r().is_htmlformelement());
let fragment_context = FragmentContext {
context_elem: context_node,
- form_elem: form,
+ form_elem: form.r(),
};
parse_html(document.r(), HTMLInput::InputString(input), &url, Some(fragment_context));
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index dbb73117857..62f7381f77e 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -24,8 +24,8 @@ use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, Documen
use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, HTMLIFrameElementCast, NodeCast, EventCast};
use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::conversions::StringificationBehavior;
-use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, RootedReference};
-use dom::bindings::js::{RootCollection, RootCollectionPtr, Unrooted};
+use dom::bindings::js::{JS, JSRef, OptionalRootable, RootedReference};
+use dom::bindings::js::{RootCollection, RootCollectionPtr};
use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference};
use dom::bindings::structuredclone::StructuredCloneData;
use dom::bindings::trace::{JSTraceable, trace_collections};
@@ -47,6 +47,7 @@ use devtools;
use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, DevtoolsPageInfo};
use devtools_traits::{DevtoolsControlMsg, DevtoolScriptControlMsg};
+use devtools_traits::{TimelineMarker, TimelineMarkerType, TracingMetadata};
use script_traits::CompositorEvent;
use script_traits::CompositorEvent::{ResizeEvent, ReflowEvent, ClickEvent};
use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
@@ -74,11 +75,10 @@ use util::task_state;
use geom::Rect;
use geom::point::Point2D;
use hyper::header::{LastModified, Headers};
-use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_SetExtraGCRootsTracer, JS_DEFAULT_ZEAL_FREQ};
+use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetExtraGCRootsTracer};
use js::jsapi::{JSContext, JSRuntime, JSObject, JSTracer};
-use js::jsapi::{JS_SetGCParameter, JSGC_MAX_BYTES};
use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSGC_BEGIN, JSGC_END};
-use js::rust::{Cx, RtUtils};
+use js::rust::{Runtime, Cx, RtUtils};
use js;
use url::Url;
@@ -87,24 +87,26 @@ use std::ascii::AsciiExt;
use std::any::Any;
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
+use std::collections::HashSet;
use std::num::ToPrimitive;
use std::option::Option;
use std::ptr;
use std::rc::Rc;
use std::result::Result;
use std::sync::mpsc::{channel, Sender, Receiver, Select};
-use std::u32;
use time::Tm;
thread_local!(pub static STACK_ROOTS: Cell<Option<RootCollectionPtr>> = Cell::new(None));
thread_local!(static SCRIPT_TASK_ROOT: RefCell<Option<*const ScriptTask>> = RefCell::new(None));
-unsafe extern fn trace_script_tasks(tr: *mut JSTracer, _data: *mut libc::c_void) {
+unsafe extern fn trace_rust_roots(tr: *mut JSTracer, _data: *mut libc::c_void) {
SCRIPT_TASK_ROOT.with(|root| {
if let Some(script_task) = *root.borrow() {
(*script_task).trace(tr);
}
});
+
+ trace_collections(tr);
}
/// A document load that is in the process of fetching the requested resource. Contains
@@ -237,6 +239,7 @@ impl Drop for StackRootTLS {
}
}
+
/// Information for an entire page. Pages are top-level browsing contexts and can contain multiple
/// frames.
///
@@ -278,6 +281,10 @@ pub struct ScriptTask {
/// no such server exists.
devtools_port: DevtoolsControlPort,
devtools_sender: Sender<DevtoolScriptControlMsg>,
+ /// For sending timeline markers. Will be ignored if
+ /// no devtools server
+ devtools_markers: RefCell<HashSet<TimelineMarkerType>>,
+ devtools_marker_sender: RefCell<Option<Sender<TimelineMarker>>>,
/// The JavaScript runtime.
js_runtime: js::rust::rt,
@@ -316,7 +323,7 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> {
unsafe {
let page = owner.page.borrow_for_script_deallocation();
for page in page.iter() {
- let window = Unrooted::from_temporary(page.window());
+ let window = page.window_for_script_deallocation();
(*window.unsafe_get()).clear_js_context_for_script_deallocation();
}
*owner.js_context.borrow_for_script_deallocation() = None;
@@ -446,6 +453,8 @@ impl ScriptTask {
devtools_chan: devtools_chan,
devtools_port: devtools_receiver,
devtools_sender: devtools_sender,
+ devtools_markers: RefCell::new(HashSet::new()),
+ devtools_marker_sender: RefCell::new(None),
js_runtime: js_runtime,
js_context: DOMRefCell::new(Some(js_context)),
@@ -455,46 +464,21 @@ impl ScriptTask {
pub fn new_rt_and_cx() -> (js::rust::rt, Rc<Cx>) {
LiveDOMReferences::initialize();
- let js_runtime = js::rust::rt();
- assert!({
- let ptr: *mut JSRuntime = (*js_runtime).ptr;
- !ptr.is_null()
- });
-
-
- unsafe {
- JS_SetExtraGCRootsTracer((*js_runtime).ptr, Some(trace_collections), ptr::null_mut());
- }
- // Unconstrain the runtime's threshold on nominal heap size, to avoid
- // triggering GC too often if operating continuously near an arbitrary
- // finite threshold. This leaves the maximum-JS_malloc-bytes threshold
- // still in effect to cause periodical, and we hope hygienic,
- // last-ditch GCs from within the GC's allocator.
- unsafe {
- JS_SetGCParameter(js_runtime.ptr, JSGC_MAX_BYTES, u32::MAX);
- }
+ let runtime = Runtime::new();
- let js_context = js_runtime.cx();
- assert!({
- let ptr: *mut JSContext = (*js_context).ptr;
- !ptr.is_null()
- });
- js_context.set_default_options_and_version();
- js_context.set_logging_error_reporter();
unsafe {
- JS_SetGCZeal((*js_context).ptr, 0, JS_DEFAULT_ZEAL_FREQ);
- JS_SetExtraGCRootsTracer((*js_runtime).ptr, Some(trace_script_tasks), ptr::null_mut());
+ JS_SetExtraGCRootsTracer(runtime.rt(), Some(trace_rust_roots), ptr::null_mut());
}
// Needed for debug assertions about whether GC is running.
if !cfg!(ndebug) {
unsafe {
- JS_SetGCCallback(js_runtime.ptr,
+ JS_SetGCCallback(runtime.rt(),
Some(debug_gc_callback as unsafe extern "C" fn(*mut JSRuntime, JSGCStatus)));
}
}
- (js_runtime, js_context)
+ (runtime.rt, runtime.cx)
}
// Return the root page in the frame tree. Panics if it doesn't exist.
@@ -724,6 +708,10 @@ impl ScriptTask {
devtools::handle_modify_attribute(&page, id, node_id, modifications),
DevtoolScriptControlMsg::WantsLiveNotifications(pipeline_id, to_send) =>
devtools::handle_wants_live_notifications(&page, pipeline_id, to_send),
+ DevtoolScriptControlMsg::SetTimelineMarkers(_pipeline_id, marker_types, reply) =>
+ devtools::handle_set_timeline_markers(&page, self, marker_types, reply),
+ DevtoolScriptControlMsg::DropTimelineMarkers(_pipeline_id, marker_types) =>
+ devtools::handle_drop_timeline_markers(&page, self, marker_types),
}
}
@@ -835,9 +823,8 @@ impl ScriptTask {
let doc: JSRef<Node> = NodeCast::from_ref(doc.r());
doc.traverse_preorder()
- .filter_map(HTMLIFrameElementCast::to_ref)
- .find(|node| node.subpage_id() == Some(subpage_id))
- .map(Temporary::from_rooted)
+ .filter_map(HTMLIFrameElementCast::to_temporary)
+ .find(|node| node.root().r().subpage_id() == Some(subpage_id))
}).root();
if let Some(frame_element) = frame_element {
@@ -856,9 +843,8 @@ impl ScriptTask {
let doc: JSRef<Node> = NodeCast::from_ref(doc.r());
doc.traverse_preorder()
- .filter_map(HTMLIFrameElementCast::to_ref)
- .find(|node| node.subpage_id() == Some(old_subpage_id))
- .map(Temporary::from_rooted)
+ .filter_map(HTMLIFrameElementCast::to_temporary)
+ .find(|node| node.root().r().subpage_id() == Some(old_subpage_id))
}).root();
frame_element.unwrap().r().update_subpage_id(new_subpage_id);
@@ -976,10 +962,9 @@ impl ScriptTask {
let doc: JSRef<Node> = NodeCast::from_ref(doc.r());
doc.traverse_preorder()
- .filter_map(HTMLIFrameElementCast::to_ref)
- .find(|node| node.subpage_id() == Some(subpage_id))
- .map(ElementCast::from_ref)
- .map(Temporary::from_rooted)
+ .filter_map(HTMLIFrameElementCast::to_temporary)
+ .find(|node| node.root().r().subpage_id() == Some(subpage_id))
+ .map(ElementCast::from_temporary)
})
})
}).root();
@@ -1032,7 +1017,7 @@ impl ScriptTask {
match self.page {
PageToRemove::Root => *self.script_task.page.borrow_mut() = None,
PageToRemove::Child(id) => {
- let _ = self.script_task.root_page().remove(id);
+ self.script_task.root_page().remove(id).unwrap();
}
}
}
@@ -1177,8 +1162,14 @@ impl ScriptTask {
///
/// TODO: Actually perform DOM event dispatch.
fn handle_event(&self, pipeline_id: PipelineId, event: CompositorEvent) {
+
match event {
ResizeEvent(new_size) => {
+ let _marker;
+ if self.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
+ _marker = AutoDOMEventMarker::new(self);
+ }
+
self.handle_resize_event(pipeline_id, new_size);
}
@@ -1206,6 +1197,10 @@ impl ScriptTask {
}
ClickEvent(button, point) => {
+ let _marker;
+ if self.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
+ _marker = AutoDOMEventMarker::new(self);
+ }
let page = get_page(&self.root_page(), pipeline_id);
let document = page.document().root();
document.r().handle_click_event(self.js_runtime.ptr, button, point);
@@ -1214,6 +1209,10 @@ impl ScriptTask {
MouseDownEvent(..) => {}
MouseUpEvent(..) => {}
MouseMoveEvent(point) => {
+ let _marker;
+ if self.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
+ _marker = AutoDOMEventMarker::new(self);
+ }
let page = get_page(&self.root_page(), pipeline_id);
let document = page.document().root();
let mouse_over_targets = &mut *self.mouse_over_targets.borrow_mut();
@@ -1222,6 +1221,10 @@ impl ScriptTask {
}
KeyEvent(key, state, modifiers) => {
+ let _marker;
+ if self.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) {
+ _marker = AutoDOMEventMarker::new(self);
+ }
let page = get_page(&self.root_page(), pipeline_id);
let document = page.document().root();
document.r().dispatch_key_event(
@@ -1242,9 +1245,8 @@ impl ScriptTask {
let doc: JSRef<Node> = NodeCast::from_ref(doc.r());
doc.traverse_preorder()
- .filter_map(HTMLIFrameElementCast::to_ref)
- .find(|node| node.subpage_id() == Some(subpage_id))
- .map(Temporary::from_rooted)
+ .filter_map(HTMLIFrameElementCast::to_temporary)
+ .find(|node| node.root().r().subpage_id() == Some(subpage_id))
}).root();
if let Some(iframe) = iframe.r() {
iframe.navigate_child_browsing_context(load_data.url);
@@ -1339,6 +1341,26 @@ impl ScriptTask {
self.incomplete_loads.borrow_mut().push(incomplete);
}
+
+ fn need_emit_timeline_marker(&self, timeline_type: TimelineMarkerType) -> bool {
+ self.devtools_markers.borrow().contains(&timeline_type)
+ }
+
+ fn emit_timeline_marker(&self, marker: TimelineMarker) {
+ let sender = self.devtools_marker_sender.borrow();
+ let sender = sender.as_ref().expect("There is no marker sender");
+ sender.send(marker).unwrap();
+ }
+
+ pub fn set_devtools_timeline_marker(&self, marker: TimelineMarkerType, reply: Sender<TimelineMarker>) {
+ *self.devtools_marker_sender.borrow_mut() = Some(reply);
+ self.devtools_markers.borrow_mut().insert(marker);
+ }
+
+ pub fn drop_devtools_timeline_markers(&self) {
+ self.devtools_markers.borrow_mut().clear();
+ *self.devtools_marker_sender.borrow_mut() = None;
+ }
}
impl Drop for ScriptTask {
@@ -1349,6 +1371,28 @@ impl Drop for ScriptTask {
}
}
+struct AutoDOMEventMarker<'a> {
+ script_task: &'a ScriptTask
+}
+
+impl<'a> AutoDOMEventMarker<'a> {
+ fn new(script_task: &'a ScriptTask) -> AutoDOMEventMarker<'a> {
+ let marker = TimelineMarker::new("DOMEvent".to_owned(), TracingMetadata::IntervalStart);
+ script_task.emit_timeline_marker(marker);
+ AutoDOMEventMarker {
+ script_task: script_task
+ }
+ }
+}
+
+#[unsafe_destructor]
+impl<'a> Drop for AutoDOMEventMarker<'a> {
+ fn drop(&mut self) {
+ let marker = TimelineMarker::new("DOMEvent".to_owned(), TracingMetadata::IntervalEnd);
+ self.script_task.emit_timeline_marker(marker);
+ }
+}
+
/// Shuts down layout for the given page tree.
fn shut_down_layout(page_tree: &Rc<Page>, exit_type: PipelineExitType) {
let mut channels = vec!();
diff --git a/components/script/tests.rs b/components/script/tests.rs
deleted file mode 100644
index a0f82628cd6..00000000000
--- a/components/script/tests.rs
+++ /dev/null
@@ -1,48 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-use dom::characterdata::CharacterData;
-use dom::element::Element;
-use dom::eventtarget::EventTarget;
-use dom::htmldivelement::HTMLDivElement;
-use dom::htmlelement::HTMLElement;
-use dom::htmlspanelement::HTMLSpanElement;
-use dom::node::Node;
-use dom::text::Text;
-
-use std::mem::size_of;
-
-// Macro so that we can stringify type names
-// I'd really prefer the tests themselves to be run at plugin time,
-// however rustc::middle doesn't have access to the full type data
-macro_rules! sizeof_checker (
- ($testname: ident, $t:ty, $known_size:expr) => (
- #[test]
- fn $testname() {
- let new = size_of::<$t>();
- let old = $known_size;
- if new < old {
- panic!("Your changes have decreased the stack size of commonly used DOM struct {} from {} to {}. \
- Good work! Please update the size in script/tests.rs",
- stringify!($t), old, new)
- } else if new > old {
- panic!("Your changes have increased the stack size of commonly used DOM struct {} from {} to {}. \
- These structs are present in large quantities in the DOM, and increasing the size \
- may dramatically affect our memory footprint. Please consider choosing a design which \
- avoids this increase. If you feel that the increase is necessary, \
- update to the new size in script/tests.rs.",
- stringify!($t), old, new)
- }
- });
-);
-
-// Update the sizes here
-sizeof_checker!(size_event_target, EventTarget, 48);
-sizeof_checker!(size_node, Node, 216);
-sizeof_checker!(size_element, Element, 328);
-sizeof_checker!(size_htmlelement, HTMLElement, 344);
-sizeof_checker!(size_div, HTMLDivElement, 344);
-sizeof_checker!(size_span, HTMLSpanElement, 344);
-sizeof_checker!(size_text, Text, 248);
-sizeof_checker!(size_characterdata, CharacterData, 248);
diff --git a/components/script/textinput.rs b/components/script/textinput.rs
index 8239c08c58f..c26549bff73 100644
--- a/components/script/textinput.rs
+++ b/components/script/textinput.rs
@@ -15,18 +15,18 @@ use std::default::Default;
use std::num::SignedInt;
#[derive(Copy, PartialEq)]
-enum Selection {
+pub enum Selection {
Selected,
NotSelected
}
#[jstraceable]
#[derive(Copy)]
-struct TextPoint {
+pub struct TextPoint {
/// 0-based line number
- line: usize,
+ pub line: usize,
/// 0-based column number
- index: usize,
+ pub index: usize,
}
/// Encapsulated state for handling keyboard input in a single or multiline text input control.
@@ -35,7 +35,7 @@ pub struct TextInput {
/// Current text input content, split across lines without trailing '\n'
lines: Vec<DOMString>,
/// Current cursor input point
- edit_point: TextPoint,
+ pub edit_point: TextPoint,
/// Beginning of selection range with edit_point as end that can span multiple lines.
selection_begin: Option<TextPoint>,
/// Is this a multiline input?
@@ -67,7 +67,7 @@ pub enum Lines {
/// The direction in which to delete a character.
#[derive(PartialEq)]
-enum DeleteDir {
+pub enum DeleteDir {
Forward,
Backward
}
@@ -99,7 +99,7 @@ impl TextInput {
}
/// Remove a character at the current editing point
- fn delete_char(&mut self, dir: DeleteDir) {
+ pub fn delete_char(&mut self, dir: DeleteDir) {
if self.selection_begin.is_none() {
self.adjust_horizontal(if dir == DeleteDir::Forward {
1
@@ -111,14 +111,14 @@ impl TextInput {
}
/// Insert a character at the current editing point
- fn insert_char(&mut self, ch: char) {
+ pub fn insert_char(&mut self, ch: char) {
if self.selection_begin.is_none() {
self.selection_begin = Some(self.edit_point);
}
self.replace_selection(ch.to_string());
}
- fn get_sorted_selection(&self) -> (TextPoint, TextPoint) {
+ pub fn get_sorted_selection(&self) -> (TextPoint, TextPoint) {
let begin = self.selection_begin.unwrap();
let end = self.edit_point;
@@ -129,7 +129,7 @@ impl TextInput {
}
}
- fn replace_selection(&mut self, insert: String) {
+ pub fn replace_selection(&mut self, insert: String) {
let (begin, end) = self.get_sorted_selection();
self.clear_selection();
@@ -166,13 +166,13 @@ impl TextInput {
}
/// Return the length of the current line under the editing point.
- fn current_line_length(&self) -> usize {
+ pub fn current_line_length(&self) -> usize {
self.lines[self.edit_point.line].chars().count()
}
/// Adjust the editing point position by a given of lines. The resulting column is
/// as close to the original column position as possible.
- fn adjust_vertical(&mut self, adjust: isize, select: Selection) {
+ pub fn adjust_vertical(&mut self, adjust: isize, select: Selection) {
if !self.multiline {
return;
}
@@ -206,7 +206,7 @@ impl TextInput {
/// Adjust the editing point position by a given number of columns. If the adjustment
/// requested is larger than is available in the current line, the editing point is
/// adjusted vertically and the process repeats with the remaining adjustment requested.
- fn adjust_horizontal(&mut self, adjust: isize, select: Selection) {
+ pub fn adjust_horizontal(&mut self, adjust: isize, select: Selection) {
if select == Selection::Selected {
if self.selection_begin.is_none() {
self.selection_begin = Some(self.edit_point);
@@ -244,7 +244,7 @@ impl TextInput {
}
/// Deal with a newline input.
- fn handle_return(&mut self) -> KeyReaction {
+ pub fn handle_return(&mut self) -> KeyReaction {
if !self.multiline {
return KeyReaction::TriggerDefaultAction;
}
@@ -253,7 +253,7 @@ impl TextInput {
}
/// Select all text in the input control.
- fn select_all(&mut self) {
+ pub fn select_all(&mut self) {
self.selection_begin = Some(TextPoint {
line: 0,
index: 0,
@@ -264,7 +264,7 @@ impl TextInput {
}
/// Remove the current selection.
- fn clear_selection(&mut self) {
+ pub fn clear_selection(&mut self) {
self.selection_begin = None;
}
@@ -361,159 +361,3 @@ impl TextInput {
self.edit_point.index = min(self.edit_point.index, self.current_line_length());
}
}
-
-#[test]
-fn test_textinput_delete_char() {
- let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned());
- textinput.adjust_horizontal(2, Selection::NotSelected);
- textinput.delete_char(DeleteDir::Backward);
- assert_eq!(textinput.get_content(), "acdefg");
-
- textinput.delete_char(DeleteDir::Forward);
- assert_eq!(textinput.get_content(), "adefg");
-
- textinput.adjust_horizontal(2, Selection::Selected);
- textinput.delete_char(DeleteDir::Forward);
- assert_eq!(textinput.get_content(), "afg");
-}
-
-#[test]
-fn test_textinput_insert_char() {
- let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned());
- textinput.adjust_horizontal(2, Selection::NotSelected);
- textinput.insert_char('a');
- assert_eq!(textinput.get_content(), "abacdefg");
-
- textinput.adjust_horizontal(2, Selection::Selected);
- textinput.insert_char('b');
- assert_eq!(textinput.get_content(), "ababefg");
-}
-
-#[test]
-fn test_textinput_get_sorted_selection() {
- let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned());
- textinput.adjust_horizontal(2, Selection::NotSelected);
- textinput.adjust_horizontal(2, Selection::Selected);
- let (begin, end) = textinput.get_sorted_selection();
- assert_eq!(begin.index, 2);
- assert_eq!(end.index, 4);
-
- textinput.clear_selection();
-
- textinput.adjust_horizontal(-2, Selection::Selected);
- let (begin, end) = textinput.get_sorted_selection();
- assert_eq!(begin.index, 2);
- assert_eq!(end.index, 4);
-}
-
-#[test]
-fn test_textinput_replace_selection() {
- let mut textinput = TextInput::new(Lines::Single, "abcdefg".to_owned());
- textinput.adjust_horizontal(2, Selection::NotSelected);
- textinput.adjust_horizontal(2, Selection::Selected);
-
- textinput.replace_selection("xyz".to_owned());
- assert_eq!(textinput.get_content(), "abxyzefg");
-}
-
-#[test]
-fn test_textinput_current_line_length() {
- let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
- assert_eq!(textinput.current_line_length(), 3);
-
- textinput.adjust_vertical(1, Selection::NotSelected);
- assert_eq!(textinput.current_line_length(), 2);
-
- textinput.adjust_vertical(1, Selection::NotSelected);
- assert_eq!(textinput.current_line_length(), 1);
-}
-
-#[test]
-fn test_textinput_adjust_vertical() {
- let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
- textinput.adjust_horizontal(3, Selection::NotSelected);
- textinput.adjust_vertical(1, Selection::NotSelected);
- assert_eq!(textinput.edit_point.line, 1);
- assert_eq!(textinput.edit_point.index, 2);
-
- textinput.adjust_vertical(-1, Selection::NotSelected);
- assert_eq!(textinput.edit_point.line, 0);
- assert_eq!(textinput.edit_point.index, 2);
-
- textinput.adjust_vertical(2, Selection::NotSelected);
- assert_eq!(textinput.edit_point.line, 2);
- assert_eq!(textinput.edit_point.index, 1);
-}
-
-#[test]
-fn test_textinput_adjust_horizontal() {
- let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
- textinput.adjust_horizontal(4, Selection::NotSelected);
- assert_eq!(textinput.edit_point.line, 1);
- assert_eq!(textinput.edit_point.index, 0);
-
- textinput.adjust_horizontal(1, Selection::NotSelected);
- assert_eq!(textinput.edit_point.line, 1);
- assert_eq!(textinput.edit_point.index, 1);
-
- textinput.adjust_horizontal(2, Selection::NotSelected);
- assert_eq!(textinput.edit_point.line, 2);
- assert_eq!(textinput.edit_point.index, 0);
-
- textinput.adjust_horizontal(-1, Selection::NotSelected);
- assert_eq!(textinput.edit_point.line, 1);
- assert_eq!(textinput.edit_point.index, 2);
-}
-
-#[test]
-fn test_textinput_handle_return() {
- let mut single_line_textinput = TextInput::new(Lines::Single, "abcdef".to_owned());
- single_line_textinput.adjust_horizontal(3, Selection::NotSelected);
- single_line_textinput.handle_return();
- assert_eq!(single_line_textinput.get_content(), "abcdef");
-
- let mut multi_line_textinput = TextInput::new(Lines::Multiple, "abcdef".to_owned());
- multi_line_textinput.adjust_horizontal(3, Selection::NotSelected);
- multi_line_textinput.handle_return();
- assert_eq!(multi_line_textinput.get_content(), "abc\ndef");
-}
-
-#[test]
-fn test_textinput_select_all() {
- let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
- assert_eq!(textinput.edit_point.line, 0);
- assert_eq!(textinput.edit_point.index, 0);
-
- textinput.select_all();
- assert_eq!(textinput.edit_point.line, 2);
- assert_eq!(textinput.edit_point.index, 1);
-}
-
-#[test]
-fn test_textinput_get_content() {
- let single_line_textinput = TextInput::new(Lines::Single, "abcdefg".to_owned());
- assert_eq!(single_line_textinput.get_content(), "abcdefg");
-
- let multi_line_textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
- assert_eq!(multi_line_textinput.get_content(), "abc\nde\nf");
-}
-
-#[test]
-fn test_textinput_set_content() {
- let mut textinput = TextInput::new(Lines::Multiple, "abc\nde\nf".to_owned());
- assert_eq!(textinput.get_content(), "abc\nde\nf");
-
- textinput.set_content("abc\nf".to_owned());
- assert_eq!(textinput.get_content(), "abc\nf");
-
- assert_eq!(textinput.edit_point.line, 0);
- assert_eq!(textinput.edit_point.index, 0);
- textinput.adjust_horizontal(3, Selection::Selected);
- assert_eq!(textinput.edit_point.line, 0);
- assert_eq!(textinput.edit_point.index, 3);
- textinput.set_content("de".to_owned());
- assert_eq!(textinput.get_content(), "de");
- assert_eq!(textinput.edit_point.line, 0);
- assert_eq!(textinput.edit_point.index, 2);
-}
-