From 886eb35dfde369cbba6b98e210a5b09c045990ec Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 1 Apr 2013 15:14:31 -0400 Subject: [PATCH 01/10] Generate DOMParser bindings. --- src/rust-mozjs | 2 +- src/servo/dom/bindings/clientrect.rs | 37 +++-- src/servo/dom/bindings/clientrectlist.rs | 28 ++-- src/servo/dom/bindings/codegen/Bindings.conf | 19 +-- src/servo/dom/bindings/codegen/CodegenRust.py | 139 ++++++++++++------ .../dom/bindings/codegen/DOMParser.webidl | 48 ++++++ .../dom/bindings/codegen/RegisterBindings.cpp | 2 + src/servo/dom/bindings/document.rs | 28 ++-- src/servo/dom/bindings/domparser.rs | 44 ++++++ src/servo/dom/bindings/element.rs | 5 +- src/servo/dom/bindings/htmlcollection.rs | 16 +- src/servo/dom/bindings/node.rs | 2 +- src/servo/dom/bindings/utils.rs | 139 +++++++++++++----- src/servo/dom/bindings/window.rs | 2 +- src/servo/dom/document.rs | 4 +- src/servo/dom/domparser.rs | 30 ++++ src/servo/dom/element.rs | 4 +- src/servo/dom/node.rs | 3 + src/servo/servo.rc | 3 + src/test/test_bindings.js | 6 + 20 files changed, 417 insertions(+), 144 deletions(-) create mode 100644 src/servo/dom/bindings/codegen/DOMParser.webidl create mode 100644 src/servo/dom/bindings/domparser.rs create mode 100644 src/servo/dom/domparser.rs diff --git a/src/rust-mozjs b/src/rust-mozjs index aefcf146400..fe2f31f7f33 160000 --- a/src/rust-mozjs +++ b/src/rust-mozjs @@ -1 +1 @@ -Subproject commit aefcf146400a42e7302243db2844f4022f938fc0 +Subproject commit fe2f31f7f33150615e0cc5385cf869053e64a65a diff --git a/src/servo/dom/bindings/clientrect.rs b/src/servo/dom/bindings/clientrect.rs index 90e81f03e9b..5746733a53e 100644 --- a/src/servo/dom/bindings/clientrect.rs +++ b/src/servo/dom/bindings/clientrect.rs @@ -3,9 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use content::content_task::task_from_context; -use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, OpaqueBindingReference}; +use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper}; use dom::bindings::codegen::ClientRectBinding; -use js::jsapi::{JSObject, JSContext}; +use js::jsapi::{JSObject, JSContext, JSVal}; +use js::glue::bindgen::RUST_OBJECT_TO_JSVAL; pub trait ClientRect { fn Top(&self) -> f32; @@ -62,19 +63,35 @@ impl CacheableWrapper for ClientRectImpl { unsafe { cast::transmute(&self.wrapper) } } - fn wrap_object_unique(~self, cx: *JSContext, scope: *JSObject) -> *JSObject { - let mut unused = false; - ClientRectBinding::Wrap(cx, scope, self, &mut unused) + fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { + fail!(~"nyi") } - fn wrap_object_shared(@self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"nyi") + fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { + let mut unused = false; + ClientRectBinding::Wrap(cx, scope, self, &mut unused) } } impl BindingObject for ClientRectImpl { - fn GetParentObject(&self, cx: *JSContext) -> OpaqueBindingReference { + fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { let content = task_from_context(cx); - unsafe { OpaqueBindingReference(Right((*content).window.get() as @CacheableWrapper)) } + unsafe { (*content).window.get() as @mut CacheableWrapper } } -} \ No newline at end of file +} + +impl DerivedWrapper for ClientRectImpl { + fn wrap(&mut self, _cx: *JSContext, _scope: *JSObject, _vp: *mut JSVal) -> i32 { + fail!(~"nyi") + } + + fn wrap_shared(@mut self, cx: *JSContext, scope: *JSObject, vp: *mut JSVal) -> i32 { + let obj = self.wrap_object_shared(cx, scope); + if obj.is_null() { + return 0; + } else { + unsafe { *vp = RUST_OBJECT_TO_JSVAL(obj) }; + return 1; + } + } +} diff --git a/src/servo/dom/bindings/clientrectlist.rs b/src/servo/dom/bindings/clientrectlist.rs index f4660b5ae44..86d6c0fc788 100644 --- a/src/servo/dom/bindings/clientrectlist.rs +++ b/src/servo/dom/bindings/clientrectlist.rs @@ -5,13 +5,13 @@ use content::content_task::task_from_context; use dom::bindings::clientrect::{ClientRect, ClientRectImpl}; use dom::bindings::codegen::ClientRectListBinding; -use dom::bindings::utils::{WrapperCache, CacheableWrapper, BindingObject, OpaqueBindingReference}; +use dom::bindings::utils::{WrapperCache, CacheableWrapper, BindingObject}; use js::jsapi::{JSObject, JSContext}; pub trait ClientRectList { fn Length(&self) -> u32; - fn Item(&self, index: u32) -> Option<~ClientRectImpl>; - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<~ClientRectImpl>; + fn Item(&self, index: u32) -> Option<@mut ClientRectImpl>; + fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<@mut ClientRectImpl>; } pub struct ClientRectListImpl { @@ -24,16 +24,16 @@ impl ClientRectList for ClientRectListImpl { self.rects.len() as u32 } - fn Item(&self, index: u32) -> Option<~ClientRectImpl> { + fn Item(&self, index: u32) -> Option<@mut ClientRectImpl> { if index < self.rects.len() as u32 { let (top, bottom, left, right) = self.rects[index]; - Some(~ClientRect(top, bottom, left, right)) + Some(@mut ClientRect(top, bottom, left, right)) } else { None } } - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<~ClientRectImpl> { + fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<@mut ClientRectImpl> { *found = index < self.rects.len() as u32; self.Item(index) } @@ -53,19 +53,19 @@ impl CacheableWrapper for ClientRectListImpl { unsafe { cast::transmute(&self.wrapper) } } - fn wrap_object_unique(~self, cx: *JSContext, scope: *JSObject) -> *JSObject { - let mut unused = false; - ClientRectListBinding::Wrap(cx, scope, self, &mut unused) + fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { + fail!(~"nyi") } - fn wrap_object_shared(@self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"nyi") + fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { + let mut unused = false; + ClientRectListBinding::Wrap(cx, scope, self, &mut unused) } } impl BindingObject for ClientRectListImpl { - fn GetParentObject(&self, cx: *JSContext) -> OpaqueBindingReference { + fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { let content = task_from_context(cx); - unsafe { OpaqueBindingReference(Right((*content).window.get() as @CacheableWrapper)) } + unsafe { (*content).window.get() as @mut CacheableWrapper } } -} \ No newline at end of file +} diff --git a/src/servo/dom/bindings/codegen/Bindings.conf b/src/servo/dom/bindings/codegen/Bindings.conf index 8bfc300ca49..bc5a19cf172 100644 --- a/src/servo/dom/bindings/codegen/Bindings.conf +++ b/src/servo/dom/bindings/codegen/Bindings.conf @@ -116,14 +116,13 @@ DOMInterfaces = { 'ClientRect': [ { 'nativeType': 'ClientRectImpl', + 'pointerType': '@mut ' }], 'ClientRectList': [ { 'nativeType': 'ClientRectListImpl', - #'headerFile': 'nsClientRect.h', - #'prefable': True, - #'resultNotAddRefed': [ 'item' ] + 'pointerType': '@mut ' }], 'CSS2Properties': { @@ -136,13 +135,10 @@ DOMInterfaces = { 'prefable': True }, -'Document': [ -{ - 'nativeType': 'nsIDocument', +'DOMParser': { + 'nativeType': 'DOMParser', + 'pointerType': '@mut ' }, -{ - 'workers': True, -}], 'DOMSettableTokenList': [ { @@ -213,8 +209,7 @@ DOMInterfaces = { 'HTMLCollection': [ { 'nativeType': 'HTMLCollection', - #'prefable': True, - #'resultNotAddRefed': [ 'item' ] + 'pointerType': '@mut ' }], 'HTMLOptionsCollection': [ @@ -516,9 +511,9 @@ addExternalHTMLElement('HTMLOptGroupElement') addExternalHTMLElement('HTMLVideoElement') addExternalIface('CanvasGradient', headerFile='nsIDOMCanvasRenderingContext2D.h') addExternalIface('CanvasPattern', headerFile='nsIDOMCanvasRenderingContext2D.h') -#addExternalIface('ClientRect') addExternalIface('CSSRule') addExternalIface('CSSValue') +addExternalIface('Document', nativeType='Document', pointerType='@mut ') addExternalIface('DOMStringList', nativeType='nsDOMStringList', headerFile='nsDOMLists.h') addExternalIface('Element', nativeType='AbstractNode', pointerType='') diff --git a/src/servo/dom/bindings/codegen/CodegenRust.py b/src/servo/dom/bindings/codegen/CodegenRust.py index 71c22234ec5..50dd7f6aa24 100644 --- a/src/servo/dom/bindings/codegen/CodegenRust.py +++ b/src/servo/dom/bindings/codegen/CodegenRust.py @@ -1098,22 +1098,19 @@ for (uint32_t i = 0; i < length; ++i) { "yet") enum = type.inner.identifier.name if invalidEnumValueFatal: - handleInvalidEnumValueCode = " MOZ_ASSERT(index >= 0);\n" + handleInvalidEnumValueCode = " return 0;\n" else: - handleInvalidEnumValueCode = ( - " if (index < 0) {\n" - " return true;\n" - " }\n") + handleInvalidEnumValueCode = " return 1;\n" template = ( "{\n" - " bool ok;\n" - " int index = FindEnumStringIndex<%(invalidEnumValueFatal)s>(cx, ${val}, %(values)s, \"%(enumtype)s\", &ok);\n" - " if (!ok) {\n" - " return false;\n" - " }\n" + #" int index = FindEnumStringIndex<%(invalidEnumValueFatal)s>(cx, ${val}, %(values)s, \"%(enumtype)s\", &ok);\n" + " let result = FindEnumStringIndex(cx, ${val}, %(values)s);\n" + " if result.is_err() {\n" "%(handleInvalidEnumValueCode)s" - " ${declName} = static_cast<%(enumtype)s>(index);\n" + " }\n" + " let index = result.get();\n" + " ${declName} = cast::transmute(index); //XXXjdm need some range checks up in here\n" "}" % { "enumtype" : enum, "values" : enum + "Values::strings", "invalidEnumValueFatal" : toStringBool(invalidEnumValueFatal), @@ -1529,7 +1526,7 @@ for (uint32_t i = 0; i < length; ++i) { if not isCreator: raise MethodNotCreatorError(descriptor.interface.identifier.name) wrapMethod = "WrapNewBindingNonWrapperCachedObject" - wrap = "%s(cx, ${obj}, %s, ${jsvalPtr})" % (wrapMethod, result) + wrap = "%s(cx, ${obj}, %s as @mut CacheableWrapper, ${jsvalPtr})" % (wrapMethod, result) # We don't support prefable stuff in workers. assert(not descriptor.prefable or not descriptor.workers) if not descriptor.prefable: @@ -1547,12 +1544,11 @@ for (uint32_t i = 0; i < length; ++i) { failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalPtr})" % result) wrappingCode += wrapAndSetPtr(wrap, failed) else: - if descriptor.notflattened: - getIID = "&NS_GET_IID(%s), " % descriptor.nativeType - else: - getIID = "" #wrap = "WrapObject(cx, ${obj}, %s, %s${jsvalPtr})" % (result, getIID) - wrap = "%s.wrap(cx, ${obj}, %s${jsvalPtr})" % (result, getIID) + if descriptor.pointerType == '': + wrap = "%s.wrap(cx, ${obj}, ${jsvalPtr})" % result + else: + wrap = "if WrapNewBindingObject(cx, ${obj}, %s as @mut CacheableWrapper, ${jsvalPtr}) { 1 } else { 0 };" % result wrappingCode += wrapAndSetPtr(wrap) return (wrappingCode, False) @@ -1701,10 +1697,10 @@ def getRetvalDeclarationForType(returnType, descriptorProvider, descriptor = descriptorProvider.getDescriptor( returnType.unroll().inner.identifier.name) result = CGGeneric(descriptor.nativeType) - if resultAlreadyAddRefed: + if returnType.nullable(): result = CGWrapper(result, pre=("Option<" + descriptor.pointerType), post=">") else: - result = CGWrapper(result, post="*") + result = CGWrapper(result, pre=descriptor.pointerType) return result, False if returnType.isCallback(): # XXXbz we're going to assume that callback types are always @@ -2154,20 +2150,23 @@ class CGIfWrapper(CGWrapper): post="\n}") class CGNamespace(CGWrapper): - def __init__(self, namespace, child, declareOnly=False): - pre = "mod %s {\n" % namespace + def __init__(self, namespace, child, declareOnly=False, public=False): + pre = "%smod %s {\n" % ("pub " if public else "", namespace) post = "} // mod %s\n" % namespace CGWrapper.__init__(self, child, pre=pre, post=post, declareOnly=declareOnly) @staticmethod - def build(namespaces, child, declareOnly=False): + def build(namespaces, child, declareOnly=False, public=False): """ Static helper method to build multiple wrapped namespaces. """ if not namespaces: return CGWrapper(child, declareOnly=declareOnly) - inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly) - return CGNamespace(namespaces[0], inner, declareOnly=declareOnly) + inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly, public=public) + return CGNamespace(namespaces[0], inner, declareOnly=declareOnly, public=public) + + def declare(self): + return "" def DOMClass(descriptor): protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeChain] @@ -2474,7 +2473,7 @@ class CGWrapWithCacheMethod(CGAbstractMethod): return """ *aTriedToWrap = true; let mut parent = aObject.GetParentObject(aCx); - let parent = WrapNativeParent(aCx, aScope, &mut parent); + let parent = WrapNativeParent(aCx, aScope, parent); if parent.is_null() { return ptr::null(); } @@ -2502,11 +2501,11 @@ class CGWrapMethod(CGAbstractMethod): # XXX can we wrap if we don't have an interface prototype object? assert descriptor.interface.hasInterfacePrototypeObject() args = [Argument('*JSContext', 'aCx'), Argument('*JSObject', 'aScope'), - Argument('~' + descriptor.nativeType, 'aObject'), Argument('*mut bool', 'aTriedToWrap')] + Argument(descriptor.pointerType + descriptor.nativeType, 'aObject'), Argument('*mut bool', 'aTriedToWrap')] CGAbstractMethod.__init__(self, descriptor, 'Wrap', '*JSObject', args, inline=True, pub=True) def definition_body(self): - return " let mut binding = BindingReference(Left(aObject)); \ + return " let mut binding = BindingReference(Right(aObject)); \ return Wrap_(aCx, aScope, &mut binding, aTriedToWrap);" class CGWrapNonWrapperCacheMethod(CGAbstractMethod): @@ -3289,6 +3288,46 @@ class CGMemberJITInfo(CGThing): return result raise TypeError("Illegal member type to CGPropertyJITInfo") +def getEnumValueName(value): + # Some enum values can be empty strings. Others might have weird + # characters in them. Deal with the former by returning "_empty", + # deal with possible name collisions from that by throwing if the + # enum value is actually "_empty", and throw on any value + # containing non-ASCII chars for now. Replace all chars other than + # [0-9A-Za-z_] with '_'. + if re.match("[^\x20-\x7E]", value): + raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters') + if re.match("^[0-9]", value): + raise SyntaxError('Enum value "' + value + '" starts with a digit') + value = re.sub(r'[^0-9A-Za-z_]', '_', value) + if re.match("^_[A-Z]|__", value): + raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec') + if value == "_empty": + raise SyntaxError('"_empty" is not an IDL enum value we support yet') + if value == "": + return "_empty" + return MakeNativeName(value) + +class CGEnum(CGThing): + def __init__(self, enum): + CGThing.__init__(self) + self.enum = enum + + def declare(self): + return "" + + def define(self): + return """ + pub enum valuelist { + %s + } + + pub static strings: &'static [EnumEntry] = &[ + %s, + ]; +""" % (",\n ".join(map(getEnumValueName, self.enum.values())), + ",\n ".join(['EnumEntry {value: &"' + val + '", length: ' + str(len(val)) + '}' for val in self.enum.values()])) + class CGXrayHelper(CGAbstractExternMethod): def __init__(self, descriptor, name, args, properties): CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) @@ -3574,42 +3613,38 @@ let _: %s = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); #return clearWrapper + release return release -class CGClassConstructHook(CGAbstractStaticMethod): +class CGClassConstructHook(CGAbstractExternMethod): """ JS-visible constructor for our objects """ def __init__(self, descriptor): - args = [Argument('*JSContext', 'cx'), Argument('unsigned', 'argc'), Argument('*jsval', 'vp')] - CGAbstractStaticMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME, + args = [Argument('*JSContext', 'cx'), Argument('u32', 'argc'), Argument('*mut JSVal', 'vp')] + CGAbstractExternMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME, 'JSBool', args) self._ctor = self.descriptor.interface.ctor() def define(self): if not self._ctor: return "" - return CGAbstractStaticMethod.define(self) + return CGAbstractExternMethod.define(self) def definition_body(self): return self.generate_code() def generate_code(self): preamble = """ - JSObject* obj = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp))); + //JSObject* obj = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp))); """ if self.descriptor.workers: preArgs = ["cx", "obj"] else: preamble += """ - nsISupports* global; - xpc_qsSelfRef globalRef; - { - nsresult rv; - JS::Value val = OBJECT_TO_JSVAL(obj); - rv = xpc_qsUnwrapArg(cx, val, &global, &globalRef.ptr, &val); - if (NS_FAILED(rv)) { - return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS); - } - } + //XXXjdm Gecko obtains a GlobalObject from the global (maybe from the private value, + // or through unwrapping a slot or something). We'll punt and get the Window + // from the context for now. + let content = task_from_context(cx); + let global = (*content).window.get(); + let obj = global.get_wrappercache().get_wrapper(); """ preArgs = ["global"] @@ -3835,6 +3870,17 @@ class CGBindingRoot(CGThing): cgthings = [] + # Do codegen for all the enums + def makeEnum(e): + return CGNamespace.build([e.identifier.name + "Values"], + CGList([CGGeneric(" use dom::bindings::utils::EnumEntry;"), + CGEnum(e)]), public=True) + def makeEnumTypedef(e): + return CGGeneric(declare=("pub type %s = self::%sValues::valuelist;\n" % + (e.identifier.name, e.identifier.name))) + cgthings = [ fun(e) for e in config.getEnums(webIDLFile) + for fun in [makeEnum, makeEnumTypedef] ] + # Do codegen for all the descriptors cgthings.extend([CGDescriptor(x) for x in descriptors]) @@ -3846,6 +3892,8 @@ 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(descriptors, dictionaries, ['js::*', @@ -3854,14 +3902,17 @@ class CGBindingRoot(CGThing): 'js::jsfriendapi::bindgen::*', 'js::glue::bindgen::*', 'js::glue::*', - 'dom::node::AbstractNode', + 'dom::node::AbstractNode', #XXXjdm + 'dom::document::Document', #XXXjdm 'dom::bindings::utils::*', 'dom::bindings::conversions::*', 'dom::bindings::clientrect::*', #XXXjdm 'dom::bindings::clientrectlist::*', #XXXjdm 'dom::bindings::htmlcollection::*', #XXXjdm 'dom::bindings::proxyhandler::*', - 'content::content_task::task_from_context' + 'dom::domparser::*', #XXXjdm + 'content::content_task::task_from_context', + 'dom::bindings::utils::EnumEntry', ], [], curr) diff --git a/src/servo/dom/bindings/codegen/DOMParser.webidl b/src/servo/dom/bindings/codegen/DOMParser.webidl new file mode 100644 index 00000000000..435cd8dda92 --- /dev/null +++ b/src/servo/dom/bindings/codegen/DOMParser.webidl @@ -0,0 +1,48 @@ +/* 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/. + * + * The origin of this IDL file is + * http://domparsing.spec.whatwg.org/#the-domparser-interface + */ + +/*interface Principal; +interface URI; +interface InputStream;*/ +interface Document; + +enum SupportedType { + "text/html", + "text/xml", + "application/xml", + "application/xhtml+xml", + "image/svg+xml" +}; + +// the latter is Mozilla-specific +/*[Constructor, + Constructor(Principal? prin, optional URI? documentURI = null, + optional URI? baseURI = null)]*/ +[Constructor] +interface DOMParser { + [Creator, Throws] + Document parseFromString(DOMString str, SupportedType type); + + /* // Mozilla-specific stuff + // Throws if the passed-in length is greater than the actual sequence length + [Creator, Throws, ChromeOnly] + Document parseFromBuffer(sequence buf, unsigned long bufLen, + SupportedType type); + // Throws if the passed-in length is greater than the actual typed array length + [Creator, Throws, ChromeOnly] + Document parseFromBuffer(Uint8Array buf, unsigned long bufLen, + SupportedType type); + [Creator, Throws, ChromeOnly] + Document parseFromStream(InputStream stream, DOMString? charset, + long contentLength, SupportedType type); + [Throws, ChromeOnly] + void init(optional Principal? principal = null, + optional URI? documentURI = null, + optional URI? baseURI = null);*/ +}; + diff --git a/src/servo/dom/bindings/codegen/RegisterBindings.cpp b/src/servo/dom/bindings/codegen/RegisterBindings.cpp index 0cc45d1ddc4..c1fb6c3350b 100644 --- a/src/servo/dom/bindings/codegen/RegisterBindings.cpp +++ b/src/servo/dom/bindings/codegen/RegisterBindings.cpp @@ -1,5 +1,6 @@ #include "ClientRectBinding.h" #include "ClientRectListBinding.h" +#include "DOMParserBinding.h" #include "HTMLCollectionBinding.h" #include "nsScriptNameSpaceManager.h" @@ -14,6 +15,7 @@ Register(nsScriptNameSpaceManager* aNameSpaceManager) REGISTER_PROTO(ClientRect, nullptr); REGISTER_PROTO(ClientRectList, nullptr); +REGISTER_PROTO(DOMParser, nullptr); REGISTER_PROTO(HTMLCollection, nullptr); #undef REGISTER_PROTO diff --git a/src/servo/dom/bindings/document.rs b/src/servo/dom/bindings/document.rs index 17f35735f52..5c55b960f80 100644 --- a/src/servo/dom/bindings/document.rs +++ b/src/servo/dom/bindings/document.rs @@ -14,6 +14,7 @@ use js::glue::bindgen::*; use js::glue::{PROPERTY_STUB, STRICT_PROPERTY_STUB}; use core::ptr::null; use core::libc::c_uint; +use content::content_task::task_from_context; use dom::bindings::utils::{DOMString, rust_box, squirrel_away, str}; use dom::bindings::utils::{jsval_to_str, WrapNewBindingObject, CacheableWrapper}; use dom::bindings::utils::WrapperCache; @@ -50,15 +51,16 @@ extern fn getElementsByTagName(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSB arg0 = str(strval.get()); let doc = &mut (*unwrap(obj)).payload; - let rval: Option<~HTMLCollection>; + let rval: Option<@mut HTMLCollection>; rval = doc.getElementsByTagName(arg0); if rval.is_none() { JS_SET_RVAL(cx, vp, JSVAL_NULL); } else { let cache = doc.get_wrappercache(); + let rval = rval.get() as @mut CacheableWrapper; assert!(WrapNewBindingObject(cx, cache.get_wrapper(), - rval.get(), - cast::transmute(vp))); + rval, + cast::transmute(vp))); } return 1; } @@ -115,6 +117,15 @@ pub fn init(compartment: @mut Compartment, doc: @mut Document) { compartment.register_class(utils::instance_jsclass(~"DocumentInstance", finalize)); + let ptr = create(compartment, doc); + + compartment.define_property(~"document", RUST_OBJECT_TO_JSVAL(ptr), + GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, + GetJSClassHookStubPointer(STRICT_PROPERTY_STUB) as *u8, + JSPROP_ENUMERATE); +} + +pub fn create(compartment: @mut Compartment, doc: @mut Document) -> *JSObject { let instance : jsobj = result::unwrap( compartment.new_object_with_proto(~"DocumentInstance", ~"Document", compartment.global_obj.ptr)); @@ -124,11 +135,7 @@ pub fn init(compartment: @mut Compartment, doc: @mut Document) { let raw_ptr: *libc::c_void = cast::reinterpret_cast(&squirrel_away(doc)); JS_SetReservedSlot(instance.ptr, 0, RUST_PRIVATE_TO_JSVAL(raw_ptr)); } - - compartment.define_property(~"document", RUST_OBJECT_TO_JSVAL(instance.ptr), - GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, - GetJSClassHookStubPointer(STRICT_PROPERTY_STUB) as *u8, - JSPROP_ENUMERATE); + instance.ptr } impl CacheableWrapper for Document { @@ -140,7 +147,8 @@ impl CacheableWrapper for Document { fail!(~"need to implement wrapping"); } - fn wrap_object_shared(@self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"need to implement wrapping"); + fn wrap_object_shared(@mut self, cx: *JSContext, _scope: *JSObject) -> *JSObject { + let content = task_from_context(cx); + unsafe { create((*content).compartment.get(), self) } } } diff --git a/src/servo/dom/bindings/domparser.rs b/src/servo/dom/bindings/domparser.rs new file mode 100644 index 00000000000..61c80044871 --- /dev/null +++ b/src/servo/dom/bindings/domparser.rs @@ -0,0 +1,44 @@ +use dom::bindings::codegen::DOMParserBinding; +use dom::bindings::utils::{CacheableWrapper, WrapperCache}; +use dom::bindings::utils::{BindingObject, DerivedWrapper}; +use dom::domparser::DOMParser; + +use js::jsapi::{JSContext, JSObject, JSVal}; +use js::glue::bindgen::{RUST_OBJECT_TO_JSVAL}; + +impl CacheableWrapper for DOMParser { + fn get_wrappercache(&mut self) -> &mut WrapperCache { + unsafe { cast::transmute(&self.wrapper) } + } + + fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { + fail!(~"need to implement wrapping"); + } + + fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { + let mut unused = false; + DOMParserBinding::Wrap(cx, scope, self, &mut unused) + } +} + +impl BindingObject for DOMParser { + fn GetParentObject(&self, _cx: *JSContext) -> @mut CacheableWrapper { + return self.owner as @mut CacheableWrapper; + } +} + +impl DerivedWrapper for DOMParser { + fn wrap(&mut self, _cx: *JSContext, _scope: *JSObject, _vp: *mut JSVal) -> i32 { + fail!(~"nyi") + } + + fn wrap_shared(@mut self, cx: *JSContext, scope: *JSObject, vp: *mut JSVal) -> i32 { + let obj = self.wrap_object_shared(cx, scope); + if obj.is_null() { + return 0; + } else { + unsafe { *vp = RUST_OBJECT_TO_JSVAL(obj) }; + return 1; + } + } +} diff --git a/src/servo/dom/bindings/element.rs b/src/servo/dom/bindings/element.rs index 9270375030d..cfb02df48b4 100644 --- a/src/servo/dom/bindings/element.rs +++ b/src/servo/dom/bindings/element.rs @@ -102,9 +102,10 @@ extern fn getClientRects(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool { JS_SET_RVAL(cx, vp, JSVAL_NULL); } else { let cache = node.get_wrappercache(); + let rval = rval.get() as @mut CacheableWrapper; assert!(WrapNewBindingObject(cx, cache.get_wrapper(), - rval.get(), - cast::transmute(vp))); + rval, + cast::transmute(vp))); } return 1; } diff --git a/src/servo/dom/bindings/htmlcollection.rs b/src/servo/dom/bindings/htmlcollection.rs index c8d110c8c1b..d09057cc9a3 100644 --- a/src/servo/dom/bindings/htmlcollection.rs +++ b/src/servo/dom/bindings/htmlcollection.rs @@ -5,7 +5,7 @@ use content::content_task::task_from_context; use dom::node::AbstractNode; use dom::bindings::codegen::HTMLCollectionBinding; -use dom::bindings::utils::{DOMString, ErrorResult, OpaqueBindingReference}; +use dom::bindings::utils::{DOMString, ErrorResult}; use dom::bindings::utils::{CacheableWrapper, BindingObject, WrapperCache}; use js::jsapi::{JSObject, JSContext}; @@ -46,9 +46,9 @@ pub impl HTMLCollection { } impl BindingObject for HTMLCollection { - fn GetParentObject(&self, cx: *JSContext) -> OpaqueBindingReference { + fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { let content = task_from_context(cx); - unsafe { OpaqueBindingReference(Right((*content).window.get() as @CacheableWrapper)) } + unsafe { (*content).window.get() as @mut CacheableWrapper } } } @@ -57,12 +57,12 @@ impl CacheableWrapper for HTMLCollection { unsafe { cast::transmute(&self.wrapper) } } - fn wrap_object_unique(~self, cx: *JSContext, scope: *JSObject) -> *JSObject { + fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { + fail!(~"nyi") + } + + fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { let mut unused = false; HTMLCollectionBinding::Wrap(cx, scope, self, &mut unused) } - - fn wrap_object_shared(@self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"nyi") - } } diff --git a/src/servo/dom/bindings/node.rs b/src/servo/dom/bindings/node.rs index 90d70e72445..d9fc8225012 100644 --- a/src/servo/dom/bindings/node.rs +++ b/src/servo/dom/bindings/node.rs @@ -175,7 +175,7 @@ impl CacheableWrapper for AbstractNode { fail!(~"need to implement wrapping"); } - fn wrap_object_shared(@self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { + fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { fail!(~"need to implement wrapping"); } } diff --git a/src/servo/dom/bindings/utils.rs b/src/servo/dom/bindings/utils.rs index c2cbd671483..47dafe5e6db 100644 --- a/src/servo/dom/bindings/utils.rs +++ b/src/servo/dom/bindings/utils.rs @@ -18,7 +18,7 @@ use js::jsapi::bindgen::{JS_ValueToString, JS_DefineProperties, JS_WrapValue, JS_ForwardGetPropertyTo, JS_HasPropertyById, JS_GetPrototype, JS_GetGlobalForObject, - JS_EncodeString, JS_free}; + JS_EncodeString, JS_free, JS_GetStringCharsAndLength}; use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType; use js::glue::bindgen::{DefineFunctionWithReserved, GetObjectJSClass, RUST_OBJECT_TO_JSVAL}; use js::glue::{PROPERTY_STUB, STRICT_PROPERTY_STUB, ENUMERATE_STUB, CONVERT_STUB, @@ -30,7 +30,9 @@ use content::content_task::task_from_context; use core::hashmap::HashMap; +use dom::bindings::document; use dom::bindings::node; +use dom::document::Document; use dom::node::AbstractNode; static TOSTRING_CLASS_RESERVED_SLOT: u64 = 0; @@ -356,6 +358,7 @@ pub mod prototypes { pub enum Prototype { ClientRect, ClientRectList, + DOMParser, HTMLCollection, _ID_Count } @@ -540,7 +543,7 @@ pub extern fn ThrowingConstructor(_cx: *JSContext, _argc: uint, _vp: *JSVal) -> } pub fn initialize_global(global: *JSObject) { - let protoArray = @mut ([0 as *JSObject, ..3]); //XXXjdm prototypes::_ID_COUNT + let protoArray = @mut ([0 as *JSObject, ..4]); //XXXjdm prototypes::_ID_COUNT unsafe { //XXXjdm we should be storing the box pointer instead of the inner let box = squirrel_away(protoArray); @@ -554,7 +557,7 @@ pub fn initialize_global(global: *JSObject) { pub trait CacheableWrapper { fn get_wrappercache(&mut self) -> &mut WrapperCache; fn wrap_object_unique(~self, cx: *JSContext, scope: *JSObject) -> *JSObject; - fn wrap_object_shared(@self, cx: *JSContext, scope: *JSObject) -> *JSObject; + fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject; } pub struct WrapperCache { @@ -577,64 +580,48 @@ pub impl WrapperCache { } } -pub fn WrapNewBindingObject(cx: *JSContext, scope: *JSObject, - mut value: ~T, vp: *mut JSVal) -> bool { +pub fn WrapNewBindingObject(cx: *JSContext, scope: *JSObject, + mut value: @mut CacheableWrapper, + vp: *mut JSVal) -> bool { unsafe { - let obj = value.get_wrappercache().get_wrapper(); + let mut cache = value.get_wrappercache(); + let mut obj = cache.get_wrapper(); if obj.is_not_null() /*&& js::GetObjectCompartment(obj) == js::GetObjectCompartment(scope)*/ { *vp = RUST_OBJECT_TO_JSVAL(obj); return true; } - let obj = if obj.is_not_null() { - obj - } else { - value.wrap_object_unique(cx, scope) - }; - + let obj = value.wrap_object_shared(cx, scope); if obj.is_null() { return false; } // MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx)); + cache.set_wrapper(obj); *vp = RUST_OBJECT_TO_JSVAL(obj); return JS_WrapValue(cx, cast::transmute(vp)) != 0; } } -pub struct OpaqueBindingReference(Either<~CacheableWrapper, @CacheableWrapper>); - -pub fn WrapNativeParent(cx: *JSContext, scope: *JSObject, p: &mut OpaqueBindingReference) -> *JSObject { - match p { - &OpaqueBindingReference(Left(ref mut p)) => { - let cache = p.get_wrappercache(); - let obj = cache.get_wrapper(); - if obj.is_not_null() { - return obj; - } - let mut tmp: ~CacheableWrapper = unstable::intrinsics::init(); - tmp <-> *p; - tmp.wrap_object_unique(cx, scope) - } - &OpaqueBindingReference(Right(ref mut p)) => { - let cache = p.get_wrappercache(); - let obj = cache.get_wrapper(); - if obj.is_not_null() { - return obj; - } - p.wrap_object_shared(cx, scope) - } +pub fn WrapNativeParent(cx: *JSContext, scope: *JSObject, mut p: @mut CacheableWrapper) -> *JSObject { + let cache = p.get_wrappercache(); + let wrapper = cache.get_wrapper(); + if wrapper.is_not_null() { + return wrapper; } + let wrapper = p.wrap_object_shared(cx, scope); + cache.set_wrapper(wrapper); + wrapper } pub struct BindingReference(Either<~T, @mut T>); pub trait BindingObject { - fn GetParentObject(&self, cx: *JSContext) -> OpaqueBindingReference; + fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper; } pub impl BindingReference { - fn GetParentObject(&self, cx: *JSContext) -> OpaqueBindingReference { + fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { match **self { Left(ref obj) => obj.GetParentObject(cx), Right(ref obj) => obj.GetParentObject(cx) @@ -781,6 +768,7 @@ pub fn InitIds(cx: *JSContext, specs: &[JSPropertySpec], ids: &mut [jsid]) -> bo pub trait DerivedWrapper { fn wrap(&mut self, cx: *JSContext, scope: *JSObject, vp: *mut JSVal) -> i32; + fn wrap_shared(@mut self, cx: *JSContext, scope: *JSObject, vp: *mut JSVal) -> i32; } impl DerivedWrapper for AbstractNode { @@ -794,10 +782,87 @@ impl DerivedWrapper for AbstractNode { unsafe { *vp = RUST_OBJECT_TO_JSVAL(node::create(cx, self).ptr) }; return 1; } + + fn wrap_shared(@mut self, _cx: *JSContext, _scope: *JSObject, _vp: *mut JSVal) -> i32 { + fail!(~"nyi") + } +} + +/*impl DerivedWrapper for Document { + fn wrap(&mut self, cx: *JSContext, scope: *JSObject, vp: *mut JSVal) -> i32 { + let cache = self.get_wrappercache(); + let wrapper = cache.get_wrapper(); + if wrapper.is_not_null() { + unsafe { *vp = RUST_OBJECT_TO_JSVAL(wrapper) }; + return 1; + } + let content = task_from_context(cx); + unsafe { + let compartment = (*content).compartment.get(); + *vp = RUST_OBJECT_TO_JSVAL(document::create(compartment, self)); + } + return 1; + } +}*/ + +pub impl Document { + fn wrap(@mut self, cx: *JSContext, _scope: *JSObject, vp: *mut JSVal) -> i32 { + let cache = self.get_wrappercache(); + let wrapper = cache.get_wrapper(); + if wrapper.is_not_null() { + unsafe { *vp = RUST_OBJECT_TO_JSVAL(wrapper) }; + return 1; + } + let content = task_from_context(cx); + unsafe { + let compartment = (*content).compartment.get(); + *vp = RUST_OBJECT_TO_JSVAL(document::create(compartment, self)); + } + return 1; + } } pub enum Error { FailureUnknown } -pub type ErrorResult = Result<(), Error>; \ No newline at end of file +pub type ErrorResult = Result<(), Error>; + +pub struct EnumEntry { + value: &'static str, + length: uint +} + +pub fn FindEnumStringIndex(cx: *JSContext, + v: JSVal, + values: &[EnumEntry]) -> Result { + unsafe { + let jsstr = JS_ValueToString(cx, v); + if jsstr.is_null() { + return Err(()); + } + let length = 0; + let chars = JS_GetStringCharsAndLength(cx, jsstr, ptr::to_unsafe_ptr(&length)); + if chars.is_null() { + return Err(()); + } + for values.eachi |i, value| { + if value.length != length as uint { + loop; + } + let mut equal = true; + for uint::iterate(0, length as uint) |j| { + if value.value[j] as u16 != *chars.offset(j) { + equal = false; + break; + } + }; + + if equal { + return Ok(i); + } + } + + return Err(()); //XXX pass in behaviour for value not found + } +} diff --git a/src/servo/dom/bindings/window.rs b/src/servo/dom/bindings/window.rs index e764674a4c9..da24c5e3757 100644 --- a/src/servo/dom/bindings/window.rs +++ b/src/servo/dom/bindings/window.rs @@ -141,7 +141,7 @@ impl CacheableWrapper for Window { fail!(~"should this be called?"); } - fn wrap_object_shared(@self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { + fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { fail!(~"should this be called?"); } } diff --git a/src/servo/dom/document.rs b/src/servo/dom/document.rs index 16a178be051..1085a6120b4 100644 --- a/src/servo/dom/document.rs +++ b/src/servo/dom/document.rs @@ -20,7 +20,7 @@ pub fn Document(root: AbstractNode) -> Document { } pub impl Document { - fn getElementsByTagName(&self, tag: DOMString) -> Option<~HTMLCollection> { + fn getElementsByTagName(&self, tag: DOMString) -> Option<@mut HTMLCollection> { let mut elements = ~[]; let tag = match tag { str(s) => s, @@ -35,6 +35,6 @@ pub impl Document { } } }; - Some(~HTMLCollection::new(elements)) + Some(@mut HTMLCollection::new(elements)) } } \ No newline at end of file diff --git a/src/servo/dom/domparser.rs b/src/servo/dom/domparser.rs new file mode 100644 index 00000000000..e10e37e5ff4 --- /dev/null +++ b/src/servo/dom/domparser.rs @@ -0,0 +1,30 @@ +use dom::bindings::utils::{DOMString, ErrorResult, WrapperCache}; +use dom::bindings::codegen::DOMParserBinding; +use dom::document::Document; +use dom::element::{Element, HTMLHtmlElement, HTMLHtmlElementTypeId}; +use dom::node::Node; +use dom::window::Window; + +pub struct DOMParser { + owner: @mut Window, //XXXjdm Document instead? + wrapper: WrapperCache +} + +pub impl DOMParser { + fn new(owner: @mut Window) -> DOMParser { + DOMParser { + owner: owner, + wrapper: WrapperCache::new() + } + } + + fn Constructor(owner: @mut Window, _rv: &mut ErrorResult) -> @mut DOMParser { + @mut DOMParser::new(owner) + } + + fn ParseFromString(&self, _s: DOMString, _type_: DOMParserBinding::SupportedType, _rv: &mut ErrorResult) -> @mut Document { + let root = ~HTMLHtmlElement { parent: Element::new(HTMLHtmlElementTypeId, ~"html") }; + let root = unsafe { Node::as_abstract_node(root) }; + @mut Document(root) + } +} \ No newline at end of file diff --git a/src/servo/dom/element.rs b/src/servo/dom/element.rs index d366320a701..4c86d413f1c 100644 --- a/src/servo/dom/element.rs +++ b/src/servo/dom/element.rs @@ -144,8 +144,8 @@ pub impl<'self> Element { self.attrs.push(Attr::new(name.to_str(), value_cell.take())); } - fn getClientRects(&self) -> Option<~ClientRectListImpl> { - Some(~ClientRectListImpl::new()) + fn getClientRects(&self) -> Option<@mut ClientRectListImpl> { + Some(@mut ClientRectListImpl::new()) } } diff --git a/src/servo/dom/node.rs b/src/servo/dom/node.rs index f1903892e0a..d5e38bda35a 100644 --- a/src/servo/dom/node.rs +++ b/src/servo/dom/node.rs @@ -385,4 +385,7 @@ pub fn define_bindings(compartment: @mut Compartment, doc: @mut Document, win: @ assert!(codegen::HTMLCollectionBinding::DefineDOMInterface(compartment.cx.ptr, compartment.global_obj.ptr, &mut unused)); + assert!(codegen::DOMParserBinding::DefineDOMInterface(compartment.cx.ptr, + compartment.global_obj.ptr, + &mut unused)); } diff --git a/src/servo/servo.rc b/src/servo/servo.rc index 30db1ec3c81..b6c25bd881f 100755 --- a/src/servo/servo.rc +++ b/src/servo/servo.rc @@ -68,14 +68,17 @@ pub mod dom { pub mod proxyhandler; pub mod clientrect; pub mod clientrectlist; + pub mod domparser; pub mod htmlcollection; pub mod codegen { pub mod ClientRectBinding; pub mod ClientRectListBinding; + pub mod DOMParserBinding; pub mod HTMLCollectionBinding; } } pub mod document; + pub mod domparser; pub mod element; pub mod event; pub mod node; diff --git a/src/test/test_bindings.js b/src/test/test_bindings.js index 1f48599d08e..5f9409b36be 100644 --- a/src/test/test_bindings.js +++ b/src/test/test_bindings.js @@ -32,3 +32,9 @@ window.alert(tags[0]); window.alert(tags[1]); window.alert(tags[2]); window.alert(tags[3]); + +window.alert("DOMParser:"); +window.alert(DOMParser); +let parser = new DOMParser(); +window.alert(parser); +window.alert(parser.parseFromString("", "text/html")); \ No newline at end of file From 4887fc7c9e08fa2c546983c6334261baa1a86004 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 5 Apr 2013 13:50:26 -0400 Subject: [PATCH 02/10] Hide most unused variable warnings in generated bindings. --- src/servo/dom/bindings/codegen/CodegenRust.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/servo/dom/bindings/codegen/CodegenRust.py b/src/servo/dom/bindings/codegen/CodegenRust.py index 50dd7f6aa24..7dec994c0af 100644 --- a/src/servo/dom/bindings/codegen/CodegenRust.py +++ b/src/servo/dom/bindings/codegen/CodegenRust.py @@ -3166,7 +3166,7 @@ class CGGenericGetter(CGAbstractBindingMethod): A class for generating the C++ code for an IDL attribute getter. """ def __init__(self, descriptor, lenientThis=False): - args = [Argument('*JSContext', 'cx'), Argument('uint', 'argc'), + args = [Argument('*JSContext', 'cx'), Argument('uint', '_argc'), Argument('*JSVal', 'vp')] if lenientThis: name = "genericLenientGetter" @@ -3193,8 +3193,8 @@ class CGSpecializedGetter(CGAbstractExternMethod): def __init__(self, descriptor, attr): self.attr = attr name = 'get_' + attr.identifier.name - args = [ Argument('*JSContext', 'cx'), - Argument('JSHandleObject', 'obj'), + args = [ Argument('*JSContext', '_cx'), + Argument('JSHandleObject', '_obj'), Argument('*%s' % descriptor.nativeType, 'self'), Argument('*mut JSVal', 'vp') ] CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args) @@ -3373,7 +3373,7 @@ class CGXrayHelper(CGAbstractExternMethod): class CGResolveProperty(CGXrayHelper): def __init__(self, descriptor, properties): args = [Argument('*JSContext', 'cx'), Argument('*JSObject', 'wrapper'), - Argument('jsid', 'id'), Argument('bool', 'set'), + Argument('jsid', 'id'), Argument('bool', '_set'), Argument('*mut JSPropertyDescriptor', 'desc')] CGXrayHelper.__init__(self, descriptor, "ResolveProperty", args, properties) @@ -3470,7 +3470,7 @@ class CGProxyUnwrap(CGAbstractMethod): class CGDOMJSProxyHandler_get(CGAbstractExternMethod): def __init__(self, descriptor): args = [Argument('*JSContext', 'cx'), Argument('*JSObject', 'proxy'), - Argument('*JSObject', 'receiver'), Argument('jsid', 'id'), + Argument('*JSObject', '_receiver'), Argument('jsid', 'id'), Argument('*mut JSVal', 'vp')] CGAbstractExternMethod.__init__(self, descriptor, "get", "JSBool", args) self.descriptor = descriptor @@ -3543,7 +3543,7 @@ return 1;""" % (getIndexedOrExpando, getNamed) class CGDOMJSProxyHandler_obj_toString(CGAbstractExternMethod): def __init__(self, descriptor): - args = [Argument('*JSContext', 'cx'), Argument('*JSObject', 'proxy')] + args = [Argument('*JSContext', 'cx'), Argument('*JSObject', '_proxy')] CGAbstractExternMethod.__init__(self, descriptor, "obj_toString", "*JSString", args) self.descriptor = descriptor def getBody(self): @@ -3618,7 +3618,7 @@ class CGClassConstructHook(CGAbstractExternMethod): JS-visible constructor for our objects """ def __init__(self, descriptor): - args = [Argument('*JSContext', 'cx'), Argument('u32', 'argc'), Argument('*mut JSVal', 'vp')] + args = [Argument('*JSContext', 'cx'), Argument('u32', '_argc'), Argument('*mut JSVal', 'vp')] CGAbstractExternMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME, 'JSBool', args) self._ctor = self.descriptor.interface.ctor() @@ -3710,7 +3710,7 @@ class CGClassFinalizeHook(CGAbstractClassHook): A hook for finalize, used to release our native object. """ def __init__(self, descriptor): - args = [Argument('*JSFreeOp', 'fop'), Argument('*JSObject', 'obj')] + args = [Argument('*JSFreeOp', '_fop'), Argument('*JSObject', 'obj')] CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, 'void', args) From 6c6d070dab43d96ba5724d3e5007025c56ff290e Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 8 Apr 2013 10:19:25 -0400 Subject: [PATCH 03/10] Fix write past buffer length for proxy toString operation. --- src/servo/dom/bindings/proxyhandler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/servo/dom/bindings/proxyhandler.rs b/src/servo/dom/bindings/proxyhandler.rs index c77e5ec952e..f073f54d8c1 100644 --- a/src/servo/dom/bindings/proxyhandler.rs +++ b/src/servo/dom/bindings/proxyhandler.rs @@ -62,7 +62,7 @@ pub fn _obj_toString(cx: *JSContext, className: *libc::c_char) -> *JSString { unsafe { let name = str::raw::from_buf(className as *u8); let nchars = "[object ]".len() + name.len(); - let chars: *mut jschar = cast::transmute(JS_malloc(cx, nchars as u64 * (size_of::() as u64))); + let chars: *mut jschar = cast::transmute(JS_malloc(cx, (nchars + 1) as u64 * (size_of::() as u64))); if chars.is_null() { return ptr::null(); } From 56c4efde7c61279f0589204942b123b3556318e8 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 8 Apr 2013 10:22:48 -0400 Subject: [PATCH 04/10] Several orthogonal changes that all got tangled up: * Split ClientRect, ClientRectList, and HTMLCollection blobs into separate DOM implementation and binding-related files. * Enforce wrapper initialization at creation time for all DOM objects * Set up the basis for triggering reflow on DOM changes, such as Element.setAttribute * Fix crashes stemming from storing pointers to stack-local AbstractNode objects in DOM node wrappers * Add untested trace hooks for DOM nodes * Implement proper CharacterData inheritance for Text and Comment nodes --- src/servo/content/content_task.rs | 28 ++++- src/servo/dom/bindings/clientrect.rs | 64 +++-------- src/servo/dom/bindings/clientrectlist.rs | 53 +++------ src/servo/dom/bindings/codegen/Bindings.conf | 4 +- src/servo/dom/bindings/codegen/CodegenRust.py | 6 +- src/servo/dom/bindings/document.rs | 28 ++--- src/servo/dom/bindings/element.rs | 101 ++++++++++++++---- src/servo/dom/bindings/htmlcollection.rs | 44 ++------ src/servo/dom/bindings/node.rs | 36 +++---- src/servo/dom/bindings/text.rs | 90 ++++++++++++++++ src/servo/dom/bindings/utils.rs | 49 ++------- src/servo/dom/bindings/window.rs | 20 ++-- src/servo/dom/characterdata.rs | 57 ++++++++++ src/servo/dom/clientrect.rs | 45 ++++++++ src/servo/dom/clientrectlist.rs | 36 +++++++ src/servo/dom/document.rs | 54 ++++++++-- src/servo/dom/domparser.rs | 18 ++-- src/servo/dom/element.rs | 33 ++++-- src/servo/dom/htmlcollection.rs | 43 ++++++++ src/servo/dom/node.rs | 52 ++++++--- src/servo/dom/window.rs | 18 +++- src/servo/layout/box_builder.rs | 2 +- src/servo/servo.rc | 5 + src/test/test_bindings.js | 5 +- src/test/test_hammer_layout.css | 3 + src/test/test_hammer_layout.html | 9 ++ src/test/test_hammer_layout.js | 17 +++ 27 files changed, 642 insertions(+), 278 deletions(-) create mode 100644 src/servo/dom/bindings/text.rs create mode 100644 src/servo/dom/characterdata.rs create mode 100644 src/servo/dom/clientrect.rs create mode 100644 src/servo/dom/clientrectlist.rs create mode 100644 src/servo/dom/htmlcollection.rs create mode 100644 src/test/test_hammer_layout.css create mode 100644 src/test/test_hammer_layout.html create mode 100644 src/test/test_hammer_layout.js diff --git a/src/servo/content/content_task.rs b/src/servo/content/content_task.rs index b7518cadafa..ee1466a8233 100644 --- a/src/servo/content/content_task.rs +++ b/src/servo/content/content_task.rs @@ -151,14 +151,30 @@ pub fn Content(layout_task: LayoutTask, }; cx.set_cx_private(ptr::to_unsafe_ptr(&*content) as *()); + unsafe { task::local_data::local_data_set(global_content_key, cast::transmute(content)); } content } +fn global_content_key(_: @Content) {} + +pub fn global_content() -> @Content { + unsafe { + return task::local_data::local_data_get(global_content_key).get(); + } +} + pub fn task_from_context(cx: *JSContext) -> *mut Content { JS_GetContextPrivate(cx) as *mut Content } +#[unsafe_destructor] +impl Drop for Content { + fn finalize(&self) { + unsafe { task::local_data::local_data_pop(global_content_key) }; + } +} + #[allow(non_implicitly_copyable_typarams)] pub impl Content { fn start(&mut self) { @@ -185,6 +201,8 @@ pub impl Content { ParseMsg(url) => { debug!("content: Received url `%s` to parse", url_to_str(&url)); + define_bindings(self.compartment.get()); + // Note: we can parse the next document in parallel // with any previous documents. @@ -209,8 +227,13 @@ pub impl Content { let js_scripts = result.js_port.recv(); debug!("js_scripts: %?", js_scripts); - let document = @mut Document(root); - let window = @mut Window(self.control_chan.clone()); + let window = Window(self.control_chan.clone(), + self.event_chan.clone()); + let document = Document(root, Some(window)); + + do root.with_mut_node |node| { + node.add_to_doc(document); + } self.damage.add(MatchSelectorsDamage); self.relayout(document, &url); @@ -221,7 +244,6 @@ pub impl Content { let compartment = self.compartment.expect(~"TODO error checking"); compartment.define_functions(debug_fns); - define_bindings(compartment, document, window); do vec::consume(js_scripts) |_i, bytes| { self.cx.evaluate_script(compartment.global_obj, bytes, ~"???", 1u); diff --git a/src/servo/dom/bindings/clientrect.rs b/src/servo/dom/bindings/clientrect.rs index 5746733a53e..af65ca60c27 100644 --- a/src/servo/dom/bindings/clientrect.rs +++ b/src/servo/dom/bindings/clientrect.rs @@ -2,63 +2,25 @@ * 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 content::content_task::task_from_context; +use content::content_task::{task_from_context, global_content}; use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper}; use dom::bindings::codegen::ClientRectBinding; +use dom::clientrect::ClientRect; use js::jsapi::{JSObject, JSContext, JSVal}; use js::glue::bindgen::RUST_OBJECT_TO_JSVAL; -pub trait ClientRect { - fn Top(&self) -> f32; - fn Bottom(&self) -> f32; - fn Left(&self) -> f32; - fn Right(&self) -> f32; - fn Width(&self) -> f32; - fn Height(&self) -> f32; -} - -pub struct ClientRectImpl { - wrapper: WrapperCache, - top: f32, - bottom: f32, - left: f32, - right: f32, -} - -impl ClientRect for ClientRectImpl { - fn Top(&self) -> f32 { - self.top - } - - fn Bottom(&self) -> f32 { - self.bottom - } - - fn Left(&self) -> f32 { - self.left - } - - fn Right(&self) -> f32 { - self.right - } - - fn Width(&self) -> f32 { - f32::abs(self.right - self.left) - } - - fn Height(&self) -> f32 { - f32::abs(self.bottom - self.top) +pub impl ClientRect { + pub fn init_wrapper(@mut self) { + let content = global_content(); + let cx = content.compartment.get().cx.ptr; + let owner = content.window.get(); + let cache = owner.get_wrappercache(); + let scope = cache.get_wrapper(); + self.wrap_object_shared(cx, scope); } } -pub fn ClientRect(top: f32, bottom: f32, left: f32, right: f32) -> ClientRectImpl { - ClientRectImpl { - top: top, bottom: bottom, left: left, right: right, - wrapper: WrapperCache::new() - } -} - -impl CacheableWrapper for ClientRectImpl { +impl CacheableWrapper for ClientRect { fn get_wrappercache(&mut self) -> &mut WrapperCache { unsafe { cast::transmute(&self.wrapper) } } @@ -73,14 +35,14 @@ impl CacheableWrapper for ClientRectImpl { } } -impl BindingObject for ClientRectImpl { +impl BindingObject for ClientRect { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { let content = task_from_context(cx); unsafe { (*content).window.get() as @mut CacheableWrapper } } } -impl DerivedWrapper for ClientRectImpl { +impl DerivedWrapper for ClientRect { fn wrap(&mut self, _cx: *JSContext, _scope: *JSObject, _vp: *mut JSVal) -> i32 { fail!(~"nyi") } diff --git a/src/servo/dom/bindings/clientrectlist.rs b/src/servo/dom/bindings/clientrectlist.rs index 86d6c0fc788..a04eadf4165 100644 --- a/src/servo/dom/bindings/clientrectlist.rs +++ b/src/servo/dom/bindings/clientrectlist.rs @@ -2,53 +2,24 @@ * 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 content::content_task::task_from_context; -use dom::bindings::clientrect::{ClientRect, ClientRectImpl}; +use content::content_task::{task_from_context, global_content}; use dom::bindings::codegen::ClientRectListBinding; use dom::bindings::utils::{WrapperCache, CacheableWrapper, BindingObject}; +use dom::clientrectlist::ClientRectList; use js::jsapi::{JSObject, JSContext}; -pub trait ClientRectList { - fn Length(&self) -> u32; - fn Item(&self, index: u32) -> Option<@mut ClientRectImpl>; - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<@mut ClientRectImpl>; -} - -pub struct ClientRectListImpl { - wrapper: WrapperCache, - rects: ~[(f32, f32, f32, f32)] -} - -impl ClientRectList for ClientRectListImpl { - fn Length(&self) -> u32 { - self.rects.len() as u32 - } - - fn Item(&self, index: u32) -> Option<@mut ClientRectImpl> { - if index < self.rects.len() as u32 { - let (top, bottom, left, right) = self.rects[index]; - Some(@mut ClientRect(top, bottom, left, right)) - } else { - None - } - } - - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<@mut ClientRectImpl> { - *found = index < self.rects.len() as u32; - self.Item(index) +pub impl ClientRectList { + fn init_wrapper(@mut self) { + let content = global_content(); + let cx = content.compartment.get().cx.ptr; + let owner = content.window.get(); + let cache = owner.get_wrappercache(); + let scope = cache.get_wrapper(); + self.wrap_object_shared(cx, scope); } } -pub impl ClientRectListImpl { - fn new() -> ClientRectListImpl { - ClientRectListImpl { - wrapper: WrapperCache::new(), - rects: ~[(5.6, 80.2, 3.7, 4.8), (800.1, 8001.1, -50.000001, -45.01)] - } - } -} - -impl CacheableWrapper for ClientRectListImpl { +impl CacheableWrapper for ClientRectList { fn get_wrappercache(&mut self) -> &mut WrapperCache { unsafe { cast::transmute(&self.wrapper) } } @@ -63,7 +34,7 @@ impl CacheableWrapper for ClientRectListImpl { } } -impl BindingObject for ClientRectListImpl { +impl BindingObject for ClientRectList { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { let content = task_from_context(cx); unsafe { (*content).window.get() as @mut CacheableWrapper } diff --git a/src/servo/dom/bindings/codegen/Bindings.conf b/src/servo/dom/bindings/codegen/Bindings.conf index bc5a19cf172..8c2b3cb29a5 100644 --- a/src/servo/dom/bindings/codegen/Bindings.conf +++ b/src/servo/dom/bindings/codegen/Bindings.conf @@ -115,13 +115,13 @@ DOMInterfaces = { 'ClientRect': [ { - 'nativeType': 'ClientRectImpl', + 'nativeType': 'ClientRect', 'pointerType': '@mut ' }], 'ClientRectList': [ { - 'nativeType': 'ClientRectListImpl', + 'nativeType': 'ClientRectList', 'pointerType': '@mut ' }], diff --git a/src/servo/dom/bindings/codegen/CodegenRust.py b/src/servo/dom/bindings/codegen/CodegenRust.py index 7dec994c0af..b828b80a0bc 100644 --- a/src/servo/dom/bindings/codegen/CodegenRust.py +++ b/src/servo/dom/bindings/codegen/CodegenRust.py @@ -3906,9 +3906,9 @@ class CGBindingRoot(CGThing): 'dom::document::Document', #XXXjdm 'dom::bindings::utils::*', 'dom::bindings::conversions::*', - 'dom::bindings::clientrect::*', #XXXjdm - 'dom::bindings::clientrectlist::*', #XXXjdm - 'dom::bindings::htmlcollection::*', #XXXjdm + 'dom::clientrect::*', #XXXjdm + 'dom::clientrectlist::*', #XXXjdm + 'dom::htmlcollection::*', #XXXjdm 'dom::bindings::proxyhandler::*', 'dom::domparser::*', #XXXjdm 'content::content_task::task_from_context', diff --git a/src/servo/dom/bindings/document.rs b/src/servo/dom/bindings/document.rs index 5c55b960f80..992b574ed95 100644 --- a/src/servo/dom/bindings/document.rs +++ b/src/servo/dom/bindings/document.rs @@ -17,11 +17,10 @@ use core::libc::c_uint; use content::content_task::task_from_context; use dom::bindings::utils::{DOMString, rust_box, squirrel_away, str}; use dom::bindings::utils::{jsval_to_str, WrapNewBindingObject, CacheableWrapper}; -use dom::bindings::utils::WrapperCache; +use dom::bindings::utils::{WrapperCache, DerivedWrapper}; use dom::document::Document; -use dom::bindings::htmlcollection::HTMLCollection; -use dom::bindings::node; +use dom::htmlcollection::HTMLCollection; use dom::bindings::utils; extern fn getDocumentElement(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { @@ -32,7 +31,9 @@ extern fn getDocumentElement(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> J } let doc = &mut (*unwrap(obj)).payload; - *vp = RUST_OBJECT_TO_JSVAL(node::create(cx, &mut doc.root).ptr); + let root = &mut doc.root; + assert!(root.is_element()); + root.wrap(cx, ptr::null(), vp); //XXXjdm proper scope at some point return 1; } } @@ -80,7 +81,7 @@ extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) { } } -pub fn init(compartment: @mut Compartment, doc: @mut Document) { +pub fn init(compartment: @mut Compartment) { let obj = utils::define_empty_prototype(~"Document", None, compartment); let attrs = @~[ @@ -115,14 +116,9 @@ pub fn init(compartment: @mut Compartment, doc: @mut Document) { JS_DefineFunctions(compartment.cx.ptr, obj.ptr, fns); }); - compartment.register_class(utils::instance_jsclass(~"DocumentInstance", finalize)); - - let ptr = create(compartment, doc); - - compartment.define_property(~"document", RUST_OBJECT_TO_JSVAL(ptr), - GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, - GetJSClassHookStubPointer(STRICT_PROPERTY_STUB) as *u8, - JSPROP_ENUMERATE); + compartment.register_class(utils::instance_jsclass(~"DocumentInstance", + finalize, + ptr::null())); } pub fn create(compartment: @mut Compartment, doc: @mut Document) -> *JSObject { @@ -135,6 +131,12 @@ pub fn create(compartment: @mut Compartment, doc: @mut Document) -> *JSObject { let raw_ptr: *libc::c_void = cast::reinterpret_cast(&squirrel_away(doc)); JS_SetReservedSlot(instance.ptr, 0, RUST_PRIVATE_TO_JSVAL(raw_ptr)); } + + compartment.define_property(~"document", RUST_OBJECT_TO_JSVAL(instance.ptr), + GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, + GetJSClassHookStubPointer(STRICT_PROPERTY_STUB) as *u8, + JSPROP_ENUMERATE); + instance.ptr } diff --git a/src/servo/dom/bindings/element.rs b/src/servo/dom/bindings/element.rs index cfb02df48b4..52af49b497f 100644 --- a/src/servo/dom/bindings/element.rs +++ b/src/servo/dom/bindings/element.rs @@ -3,8 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use content::content_task::task_from_context; +use dom::bindings::node::unwrap; use dom::bindings::utils::{domstring_to_jsval, WrapNewBindingObject}; -use dom::bindings::utils::{str, CacheableWrapper, DOM_OBJECT_SLOT}; +use dom::bindings::utils::{str, CacheableWrapper, DOM_OBJECT_SLOT, DOMString}; +use dom::bindings::utils::jsval_to_str; use dom::element::*; use dom::node::{AbstractNode, Element, ElementNodeTypeId}; use layout::layout_task; @@ -16,20 +18,44 @@ use js::glue::bindgen::*; use js::jsapi::bindgen::*; use js::jsapi::{JSContext, JSVal, JSObject, JSBool, JSFreeOp, JSPropertySpec}; use js::jsapi::{JSPropertyOpWrapper, JSStrictPropertyOpWrapper, JSFunctionSpec}; -use js::jsapi::JSNativeWrapper; +use js::jsapi::{JSNativeWrapper, JSTracer, JSTRACE_OBJECT}; use js::rust::{Compartment, jsobj}; use js::{JS_ARGV, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL}; use js::{JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS}; extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) { - debug!("element finalize: %?!", obj as uint); + debug!("element finalize: %x!", obj as uint); unsafe { - let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32); - let node: AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); + let node: AbstractNode = unwrap(obj); + //XXXjdm We need separate finalizers for each specialty element type like headings let _elem: ~Element = cast::transmute(node.raw_object()); } } +pub extern fn trace(tracer: *JSTracer, obj: *JSObject) { + let node = unsafe { unwrap(obj) }; + + fn trace_node(tracer: *JSTracer, node: Option, name: &str) { + if node.is_none() { + return; + } + error!("tracing %s", name); + let mut node = node.get(); + let cache = node.get_wrappercache(); + let wrapper = cache.get_wrapper(); + assert!(wrapper.is_not_null()); + unsafe { + JS_CallTracer(tracer, wrapper, JSTRACE_OBJECT as u32); + } + } + error!("tracing %?:", obj as uint); + trace_node(tracer, node.parent_node(), "parent"); + trace_node(tracer, node.first_child(), "first child"); + trace_node(tracer, node.last_child(), "last child"); + trace_node(tracer, node.next_sibling(), "next sibling"); + trace_node(tracer, node.prev_sibling(), "prev sibling"); +} + pub fn init(compartment: @mut Compartment) { let obj = utils::define_empty_prototype(~"Element", Some(~"Node"), compartment); let attrs = @~[ @@ -55,6 +81,11 @@ pub fn init(compartment: @mut Compartment) { nargs: 0, flags: 0, selfHostedName: null()}, + JSFunctionSpec {name: compartment.add_name(~"setAttribute"), + call: JSNativeWrapper {op: setAttribute, info: null()}, + nargs: 0, + flags: 0, + selfHostedName: null()}, JSFunctionSpec {name: null(), call: JSNativeWrapper {op: null(), info: null()}, nargs: 0, @@ -65,7 +96,7 @@ pub fn init(compartment: @mut Compartment) { }); compartment.register_class(utils::instance_jsclass(~"GenericElementInstance", - finalize)); + finalize, trace)); let _ = utils::define_empty_prototype(~"HTMLElement", Some(~"Element"), compartment); let _ = utils::define_empty_prototype(~"HTMLDivElement", Some(~"HTMLElement"), compartment); @@ -93,8 +124,7 @@ pub fn init(compartment: @mut Compartment) { extern fn getClientRects(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool { unsafe { let obj = JS_THIS_OBJECT(cx, vp); - let mut box = utils::unwrap::<*mut AbstractNode>(obj); - let node = &mut *box; + let mut node = unwrap(obj); let rval = do node.with_imm_element |elem| { elem.getClientRects() }; @@ -111,6 +141,39 @@ extern fn getClientRects(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool { } } +extern fn setAttribute(cx: *JSContext, argc: c_uint, vp: *JSVal) -> JSBool { + unsafe { + let obj = JS_THIS_OBJECT(cx, vp); + let mut node = unwrap(obj); + + if (argc < 2) { + return 0; //XXXjdm throw exception + } + + let argv = JS_ARGV(cx, cast::transmute(vp)); + + let arg0: DOMString; + let strval = jsval_to_str(cx, (*argv.offset(0))); + if strval.is_err() { + return 0; + } + arg0 = str(strval.get()); + + let arg1: DOMString; + let strval = jsval_to_str(cx, (*argv.offset(1))); + if strval.is_err() { + return 0; + } + arg1 = str(strval.get()); + + do node.as_mut_element |elem| { + elem.set_attr(&arg0, &arg1); + }; + + return 1; + } +} + #[allow(non_implicitly_copyable_typarams)] extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { unsafe { @@ -119,12 +182,11 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa return 0; } - let mut box = utils::unwrap::<*mut AbstractNode>(obj); - let node = &mut *box; + let mut node = unwrap(obj); let width = match node.type_id() { ElementNodeTypeId(HTMLImageElementTypeId) => { let content = task_from_context(cx); - match (*content).query_layout(layout_task::ContentBox(*node)) { + match (*content).query_layout(layout_task::ContentBox(node)) { Ok(rect) => rect.width, Err(()) => 0 } @@ -148,13 +210,13 @@ extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa return 0; } - let mut box = utils::unwrap::<*mut AbstractNode>(obj); - let node = &mut *box; + let mut node = unwrap(obj); match node.type_id() { ElementNodeTypeId(HTMLImageElementTypeId) => { do node.as_mut_element |elem| { let arg = ptr::offset(JS_ARGV(cx, cast::reinterpret_cast(&vp)), 0); - elem.set_attr(~"width", (RUST_JSVAL_TO_INT(*arg) as int).to_str()) + elem.set_attr(&str(~"width"), + &str((RUST_JSVAL_TO_INT(*arg) as int).to_str())) } } ElementNodeTypeId(_) => fail!(~"why is this not an image element?"), @@ -165,7 +227,6 @@ extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa } } -#[allow(non_implicitly_copyable_typarams)] extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { unsafe { let obj = JS_THIS_OBJECT(cx, cast::reinterpret_cast(&vp)); @@ -173,8 +234,7 @@ extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { return 0; } - let mut box = utils::unwrap::<*mut AbstractNode>(obj); - let node = &mut *box; + let mut node = unwrap(obj); do node.with_imm_element |elem| { let s = str(copy elem.tag_name); *vp = domstring_to_jsval(cx, &s); @@ -183,7 +243,6 @@ extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { return 1; } -#[allow(non_implicitly_copyable_typarams)] pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj { let proto = match node.type_id() { ElementNodeTypeId(HTMLDivElementTypeId) => ~"HTMLDivElement", @@ -201,9 +260,11 @@ pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj { proto, compartment.global_obj.ptr)); - node.get_wrappercache().set_wrapper(obj.ptr); + let cache = node.get_wrappercache(); + assert!(cache.get_wrapper().is_null()); + cache.set_wrapper(obj.ptr); - let raw_ptr = ptr::addr_of(node) as *libc::c_void; + let raw_ptr = node.raw_object() as *libc::c_void; JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT as u32, RUST_PRIVATE_TO_JSVAL(raw_ptr)); return obj; diff --git a/src/servo/dom/bindings/htmlcollection.rs b/src/servo/dom/bindings/htmlcollection.rs index d09057cc9a3..4e2ac722b5f 100644 --- a/src/servo/dom/bindings/htmlcollection.rs +++ b/src/servo/dom/bindings/htmlcollection.rs @@ -2,46 +2,20 @@ * 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 content::content_task::task_from_context; -use dom::node::AbstractNode; +use content::content_task::{task_from_context, global_content}; use dom::bindings::codegen::HTMLCollectionBinding; -use dom::bindings::utils::{DOMString, ErrorResult}; use dom::bindings::utils::{CacheableWrapper, BindingObject, WrapperCache}; +use dom::htmlcollection::HTMLCollection; use js::jsapi::{JSObject, JSContext}; -pub struct HTMLCollection { - elements: ~[AbstractNode], - wrapper: WrapperCache -} - pub impl HTMLCollection { - fn new(elements: ~[AbstractNode]) -> HTMLCollection { - HTMLCollection { - elements: elements, - wrapper: WrapperCache::new() - } - } - - fn Length(&self) -> u32 { - self.elements.len() as u32 - } - - fn Item(&self, index: u32) -> Option { - if index < self.Length() { - Some(self.elements[index]) - } else { - None - } - } - - fn NamedItem(&self, _cx: *JSContext, _name: DOMString, rv: &mut ErrorResult) -> *JSObject { - *rv = Ok(()); - ptr::null() - } - - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option { - *found = true; - self.Item(index) + fn init_wrapper(@mut self) { + let content = global_content(); + let cx = content.compartment.get().cx.ptr; + let owner = content.window.get(); + let cache = owner.get_wrappercache(); + let scope = cache.get_wrapper(); + self.wrap_object_shared(cx, scope); } } diff --git a/src/servo/dom/bindings/node.rs b/src/servo/dom/bindings/node.rs index d9fc8225012..41a14b93507 100644 --- a/src/servo/dom/bindings/node.rs +++ b/src/servo/dom/bindings/node.rs @@ -2,24 +2,22 @@ * 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::utils::{CacheableWrapper, WrapperCache}; -use dom::bindings::utils::{DOM_OBJECT_SLOT}; +use dom::bindings::element; +use dom::bindings::text; +use dom::bindings::utils; +use dom::bindings::utils::{CacheableWrapper, WrapperCache, DerivedWrapper}; use dom::node::{AbstractNode, Node, ElementNodeTypeId, TextNodeTypeId, CommentNodeTypeId}; use dom::node::{DoctypeNodeTypeId}; -use super::element; -use super::utils; use core::libc::c_uint; use core::ptr::null; -use js::glue::bindgen::*; use js::jsapi::bindgen::*; use js::jsapi::{JSContext, JSVal, JSObject, JSBool, JSPropertySpec}; use js::jsapi::{JSPropertyOpWrapper, JSStrictPropertyOpWrapper}; -use js::jsval::{INT_TO_JSVAL, JSVAL_TO_PRIVATE}; +use js::jsval::{INT_TO_JSVAL}; use js::rust::{Compartment, jsobj}; use js::{JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL}; use js::{JS_THIS_OBJECT, JSPROP_NATIVE_ACCESSORS}; -use js; pub fn init(compartment: @mut Compartment) { let obj = utils::define_empty_prototype(~"Node", None, compartment); @@ -62,15 +60,15 @@ pub fn init(compartment: @mut Compartment) { pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj { match node.type_id() { ElementNodeTypeId(_) => element::create(cx, node), - TextNodeTypeId => fail!(~"no text node bindings yet"), - CommentNodeTypeId => fail!(~"no comment node bindings yet"), - DoctypeNodeTypeId => fail!(~"no doctype node bindings yet") + TextNodeTypeId | + CommentNodeTypeId | + DoctypeNodeTypeId => text::create(cx, node), } } -pub unsafe fn unwrap(obj: *JSObject) -> *AbstractNode { - let val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT as u64); - cast::transmute(JSVAL_TO_PRIVATE(val)) +pub unsafe fn unwrap(obj: *JSObject) -> AbstractNode { + let raw = unsafe { utils::unwrap::<*mut Node>(obj) }; + AbstractNode::from_raw(raw) } #[allow(non_implicitly_copyable_typarams)] @@ -81,14 +79,13 @@ extern fn getFirstChild(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool return 0; } - let node = *unwrap(obj); + let node = unwrap(obj); let rval = do node.with_mut_node |node| { node.getFirstChild() }; match rval { Some(n) => { - let obj = create(cx, n).ptr; - *vp = RUST_OBJECT_TO_JSVAL(obj) + n.wrap(cx, ptr::null(), vp); //XXXjdm pass a real scope } None => *vp = JSVAL_NULL }; @@ -104,14 +101,13 @@ extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBoo return 0; } - let node = *unwrap(obj); + let node = unwrap(obj); let rval = do node.with_mut_node |node| { node.getNextSibling() }; match rval { Some(n) => { - let obj = create(cx, n).ptr; - *vp = RUST_OBJECT_TO_JSVAL(obj) + n.wrap(cx, ptr::null(), vp); //XXXjdm pass a real scope } None => *vp = JSVAL_NULL }; @@ -155,7 +151,7 @@ extern fn getNodeType(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { return 0; } - let node = *unwrap(obj); + let node = unwrap(obj); let rval = do node.with_imm_node |node| { node.getNodeType() }; diff --git a/src/servo/dom/bindings/text.rs b/src/servo/dom/bindings/text.rs new file mode 100644 index 00000000000..001b8647f61 --- /dev/null +++ b/src/servo/dom/bindings/text.rs @@ -0,0 +1,90 @@ +use dom::bindings::element; +use dom::bindings::utils; +use dom::bindings::utils::{DOM_OBJECT_SLOT, CacheableWrapper}; +use dom::node::{AbstractNode, Text, Comment, Doctype, TextNodeTypeId, CommentNodeTypeId}; +use dom::node::{DoctypeNodeTypeId}; + +use js::jsapi::{JSFreeOp, JSObject, JSContext}; +use js::jsapi::bindgen::{JS_GetReservedSlot, JS_SetReservedSlot}; +use js::glue::bindgen::{RUST_JSVAL_TO_PRIVATE, RUST_PRIVATE_TO_JSVAL}; +use js::rust::{Compartment, jsobj}; + +extern fn finalize_text(_fop: *JSFreeOp, obj: *JSObject) { + debug!("text finalize: %?!", obj as uint); + unsafe { + let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32); + let node: AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); + let _elem: ~Text = cast::transmute(node.raw_object()); + } +} + +extern fn finalize_comment(_fop: *JSFreeOp, obj: *JSObject) { + debug!("comment finalize: %?!", obj as uint); + unsafe { + let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32); + let node: AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); + let _elem: ~Comment = cast::transmute(node.raw_object()); + } +} + +extern fn finalize_doctype(_fop: *JSFreeOp, obj: *JSObject) { + debug!("doctype finalize: %?!", obj as uint); + unsafe { + let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32); + let node: AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); + let _elem: ~Doctype = cast::transmute(node.raw_object()); + } +} + +pub fn init(compartment: @mut Compartment) { + let _ = utils::define_empty_prototype(~"CharacterData", Some(~"Node"), compartment); + + let _ = utils::define_empty_prototype(~"TextPrototype", + Some(~"CharacterData"), + compartment); + let _ = utils::define_empty_prototype(~"CommentPrototype", + Some(~"CharacterData"), + compartment); + let _ = utils::define_empty_prototype(~"DocumentTypePrototype", + Some(~"Node"), + compartment); + + compartment.register_class(utils::instance_jsclass(~"Text", + finalize_text, + element::trace)); + compartment.register_class(utils::instance_jsclass(~"Comment", + finalize_comment, + element::trace)); + compartment.register_class(utils::instance_jsclass(~"DocumentType", + finalize_doctype, + element::trace)); + + +} + +pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj { + let (proto, instance) = match node.type_id() { + TextNodeTypeId => (~"TextPrototype", ~"Text"), + CommentNodeTypeId => (~"CommentPrototype", ~"Comment"), + DoctypeNodeTypeId => (~"DocumentTypePrototype", ~"DocumentType"), + _ => fail!(~"text::create only handles textual nodes") + }; + + //XXXjdm the parent should probably be the node parent instead of the global + //TODO error checking + let compartment = utils::get_compartment(cx); + let obj = result::unwrap(compartment.new_object_with_proto(instance, + proto, + compartment.global_obj.ptr)); + + let cache = node.get_wrappercache(); + assert!(cache.get_wrapper().is_null()); + cache.set_wrapper(obj.ptr); + + unsafe { + let raw_ptr = ptr::to_unsafe_ptr(node) as *libc::c_void; + JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT as u32, RUST_PRIVATE_TO_JSVAL(raw_ptr)); + } + + return obj; +} \ No newline at end of file diff --git a/src/servo/dom/bindings/utils.rs b/src/servo/dom/bindings/utils.rs index 47dafe5e6db..3e23170a904 100644 --- a/src/servo/dom/bindings/utils.rs +++ b/src/servo/dom/bindings/utils.rs @@ -30,9 +30,7 @@ use content::content_task::task_from_context; use core::hashmap::HashMap; -use dom::bindings::document; use dom::bindings::node; -use dom::document::Document; use dom::node::AbstractNode; static TOSTRING_CLASS_RESERVED_SLOT: u64 = 0; @@ -93,6 +91,15 @@ pub enum DOMString { null_string } +pub impl DOMString { + fn to_str(&self) -> ~str { + match *self { + str(ref s) => s.clone(), + null_string => ~"" + } + } +} + pub struct rust_box { rc: uint, td: *sys::TypeDesc, @@ -219,7 +226,7 @@ pub fn prototype_jsclass(name: ~str) -> @fn(compartment: @mut Compartment) -> JS return f; } -pub fn instance_jsclass(name: ~str, finalize: *u8) +pub fn instance_jsclass(name: ~str, finalize: *u8, trace: *u8) -> @fn(compartment: @mut Compartment) -> JSClass { let f: @fn(@mut Compartment) -> JSClass = |compartment: @mut Compartment| { JSClass { @@ -237,7 +244,7 @@ pub fn instance_jsclass(name: ~str, finalize: *u8) call: null(), hasInstance: has_instance, construct: null(), - trace: null(), + trace: trace, reserved: (null(), null(), null(), null(), null(), // 05 null(), null(), null(), null(), null(), // 10 null(), null(), null(), null(), null(), // 15 @@ -788,40 +795,6 @@ impl DerivedWrapper for AbstractNode { } } -/*impl DerivedWrapper for Document { - fn wrap(&mut self, cx: *JSContext, scope: *JSObject, vp: *mut JSVal) -> i32 { - let cache = self.get_wrappercache(); - let wrapper = cache.get_wrapper(); - if wrapper.is_not_null() { - unsafe { *vp = RUST_OBJECT_TO_JSVAL(wrapper) }; - return 1; - } - let content = task_from_context(cx); - unsafe { - let compartment = (*content).compartment.get(); - *vp = RUST_OBJECT_TO_JSVAL(document::create(compartment, self)); - } - return 1; - } -}*/ - -pub impl Document { - fn wrap(@mut self, cx: *JSContext, _scope: *JSObject, vp: *mut JSVal) -> i32 { - let cache = self.get_wrappercache(); - let wrapper = cache.get_wrapper(); - if wrapper.is_not_null() { - unsafe { *vp = RUST_OBJECT_TO_JSVAL(wrapper) }; - return 1; - } - let content = task_from_context(cx); - unsafe { - let compartment = (*content).compartment.get(); - *vp = RUST_OBJECT_TO_JSVAL(document::create(compartment, self)); - } - return 1; - } -} - pub enum Error { FailureUnknown } diff --git a/src/servo/dom/bindings/window.rs b/src/servo/dom/bindings/window.rs index da24c5e3757..f50bb1f6c2c 100644 --- a/src/servo/dom/bindings/window.rs +++ b/src/servo/dom/bindings/window.rs @@ -76,13 +76,9 @@ extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) { } } -pub fn init(compartment: @mut Compartment, win: @mut Window) { +pub fn init(compartment: @mut Compartment) { let proto = utils::define_empty_prototype(~"Window", None, compartment); - compartment.register_class(utils::instance_jsclass(~"WindowInstance", finalize)); - - let obj = result::unwrap( - compartment.new_object_with_proto(~"WindowInstance", - ~"Window", null())); + compartment.register_class(utils::instance_jsclass(~"WindowInstance", finalize, null())); /* Define methods on a window */ let methods = [ @@ -116,11 +112,19 @@ pub fn init(compartment: @mut Compartment, win: @mut Window) { } ]; + unsafe { + JS_DefineFunctions(compartment.cx.ptr, proto.ptr, &methods[0]); + } +} + +pub fn create(compartment: @mut Compartment, win: @mut Window) { + let obj = result::unwrap( + compartment.new_object_with_proto(~"WindowInstance", + ~"Window", null())); + win.get_wrappercache().set_wrapper(obj.ptr); unsafe { - JS_DefineFunctions(compartment.cx.ptr, proto.ptr, &methods[0]); - let raw_ptr: *libc::c_void = cast::reinterpret_cast(&squirrel_away(win)); JS_SetReservedSlot(obj.ptr, 0, RUST_PRIVATE_TO_JSVAL(raw_ptr)); } diff --git a/src/servo/dom/characterdata.rs b/src/servo/dom/characterdata.rs new file mode 100644 index 00000000000..b078d6dd03c --- /dev/null +++ b/src/servo/dom/characterdata.rs @@ -0,0 +1,57 @@ +use dom::bindings::utils::{DOMString, null_string, str}; +use dom::node::{Node, NodeTypeId}; + +use core::str; + +pub struct CharacterData { + parent: Node, + data: DOMString +} + +pub impl CharacterData { + fn new(id: NodeTypeId, data: ~str) -> CharacterData { + CharacterData { + parent: Node::new(id), + data: str(data) + } + } + + fn GetData(&self) -> DOMString { + copy self.data + } + + fn SetData(&mut self, arg: DOMString) { + self.data = arg; + } + + fn Length(&self) -> u32 { + match self.data { + str(ref s) => s.len() as u32, + null_string => 0 + } + } + + fn SubstringData(&self, offset: u32, count: u32) -> DOMString { + match self.data { + str(ref s) => str(s.slice(offset as uint, count as uint).to_str()), + null_string => null_string + } + } + + fn AppendData(&mut self, arg: DOMString) { + let s = self.data.to_str(); + self.data = str(str::append(s, arg.to_str())); + } + + fn InsertData(&mut self, _offset: u32, _arg: DOMString) { + fail!(~"nyi") + } + + fn DeleteData(&mut self, _offset: u32, _count: u32) { + fail!(~"nyi") + } + + fn ReplaceData(&mut self, _offset: u32, _count: u32, _arg: DOMString) { + fail!(~"nyi") + } +} diff --git a/src/servo/dom/clientrect.rs b/src/servo/dom/clientrect.rs new file mode 100644 index 00000000000..90856ca5cd3 --- /dev/null +++ b/src/servo/dom/clientrect.rs @@ -0,0 +1,45 @@ +//use dom::bindings::clientrect::ClientRect; +use dom::bindings::utils::WrapperCache; + +pub struct ClientRect { + wrapper: WrapperCache, + top: f32, + bottom: f32, + left: f32, + right: f32, +} + +pub impl ClientRect { + fn new(top: f32, bottom: f32, left: f32, right: f32) -> @mut ClientRect { + let rect = @mut ClientRect { + top: top, bottom: bottom, left: left, right: right, + wrapper: WrapperCache::new() + }; + rect.init_wrapper(); + rect + } + + fn Top(&self) -> f32 { + self.top + } + + fn Bottom(&self) -> f32 { + self.bottom + } + + fn Left(&self) -> f32 { + self.left + } + + fn Right(&self) -> f32 { + self.right + } + + fn Width(&self) -> f32 { + f32::abs(self.right - self.left) + } + + fn Height(&self) -> f32 { + f32::abs(self.bottom - self.top) + } +} diff --git a/src/servo/dom/clientrectlist.rs b/src/servo/dom/clientrectlist.rs new file mode 100644 index 00000000000..ab25d5f4764 --- /dev/null +++ b/src/servo/dom/clientrectlist.rs @@ -0,0 +1,36 @@ +use dom::clientrect::ClientRect; +use dom::bindings::utils::WrapperCache; + +pub struct ClientRectList { + wrapper: WrapperCache, + rects: ~[(f32, f32, f32, f32)] +} + +pub impl ClientRectList { + fn new() -> @mut ClientRectList { + let list = @mut ClientRectList { + wrapper: WrapperCache::new(), + rects: ~[(5.6, 80.2, 3.7, 4.8), (800.1, 8001.1, -50.000001, -45.01)] + }; + list.init_wrapper(); + list + } + + fn Length(&self) -> u32 { + self.rects.len() as u32 + } + + fn Item(&self, index: u32) -> Option<@mut ClientRect> { + if index < self.rects.len() as u32 { + let (top, bottom, left, right) = self.rects[index]; + Some(ClientRect::new(top, bottom, left, right)) + } else { + None + } + } + + fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<@mut ClientRect> { + *found = index < self.rects.len() as u32; + self.Item(index) + } +} \ No newline at end of file diff --git a/src/servo/dom/document.rs b/src/servo/dom/document.rs index 1085a6120b4..2cd3f9b2ade 100644 --- a/src/servo/dom/document.rs +++ b/src/servo/dom/document.rs @@ -2,30 +2,55 @@ * 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::htmlcollection::HTMLCollection; -use dom::bindings::utils::{DOMString, WrapperCache, str}; +use content::content_task::global_content; +use dom::bindings::document; +use dom::bindings::utils::{DOMString, WrapperCache}; +use dom::event::ReflowEvent; +use dom::htmlcollection::HTMLCollection; use dom::node::AbstractNode; +use dom::window::Window; +use js::jsapi::bindgen::{JS_AddObjectRoot, JS_RemoveObjectRoot}; pub struct Document { root: AbstractNode, - wrapper: WrapperCache + wrapper: WrapperCache, + window: Option<@mut Window>, } -pub fn Document(root: AbstractNode) -> Document { - Document { +pub fn Document(root: AbstractNode, + window: Option<@mut Window>) -> @mut Document { + let doc = @mut Document { root: root, - wrapper: WrapperCache::new() + wrapper: WrapperCache::new(), + window: window + }; + let compartment = global_content().compartment.get(); + do root.with_imm_node |node| { + let wrapper = node.wrapper.get_wrapper(); + assert!(wrapper.is_not_null()); + unsafe { JS_AddObjectRoot(compartment.cx.ptr, ptr::addr_of(&wrapper)); } + } + document::create(compartment, doc); + doc +} + +#[unsafe_destructor] +impl Drop for Document { + fn finalize(&self) { + let compartment = global_content().compartment.get(); + do self.root.with_imm_node |node| { + let wrapper = node.wrapper.get_wrapper(); + assert!(wrapper.is_not_null()); + unsafe { JS_RemoveObjectRoot(compartment.cx.ptr, ptr::addr_of(&wrapper)); } + } } } pub impl Document { fn getElementsByTagName(&self, tag: DOMString) -> Option<@mut HTMLCollection> { let mut elements = ~[]; - let tag = match tag { - str(s) => s, - _ => ~"" - }; + let tag = tag.to_str(); let _ = for self.root.traverse_preorder |child| { if child.is_element() { do child.with_imm_element |elem| { @@ -35,6 +60,13 @@ pub impl Document { } } }; - Some(@mut HTMLCollection::new(elements)) + Some(HTMLCollection::new(elements)) + } + + fn content_changed(&self) { + do self.window.map |window| { + let chan = &mut window.dom_event_chan; + chan.send(ReflowEvent) + }; } } \ No newline at end of file diff --git a/src/servo/dom/domparser.rs b/src/servo/dom/domparser.rs index e10e37e5ff4..2b08540e40f 100644 --- a/src/servo/dom/domparser.rs +++ b/src/servo/dom/domparser.rs @@ -1,4 +1,5 @@ -use dom::bindings::utils::{DOMString, ErrorResult, WrapperCache}; +use content::content_task::global_content; +use dom::bindings::utils::{DOMString, ErrorResult, WrapperCache, CacheableWrapper}; use dom::bindings::codegen::DOMParserBinding; use dom::document::Document; use dom::element::{Element, HTMLHtmlElement, HTMLHtmlElementTypeId}; @@ -11,20 +12,25 @@ pub struct DOMParser { } pub impl DOMParser { - fn new(owner: @mut Window) -> DOMParser { - DOMParser { + fn new(owner: @mut Window) -> @mut DOMParser { + let parser = @mut DOMParser { owner: owner, wrapper: WrapperCache::new() - } + }; + let cx = global_content().compartment.get().cx.ptr; + let cache = owner.get_wrappercache(); + let scope = cache.get_wrapper(); + parser.wrap_object_shared(cx, scope); + parser } fn Constructor(owner: @mut Window, _rv: &mut ErrorResult) -> @mut DOMParser { - @mut DOMParser::new(owner) + DOMParser::new(owner) } fn ParseFromString(&self, _s: DOMString, _type_: DOMParserBinding::SupportedType, _rv: &mut ErrorResult) -> @mut Document { let root = ~HTMLHtmlElement { parent: Element::new(HTMLHtmlElementTypeId, ~"html") }; let root = unsafe { Node::as_abstract_node(root) }; - @mut Document(root) + Document(root, None) } } \ No newline at end of file diff --git a/src/servo/dom/element.rs b/src/servo/dom/element.rs index 4c86d413f1c..5512e0c68d5 100644 --- a/src/servo/dom/element.rs +++ b/src/servo/dom/element.rs @@ -7,7 +7,8 @@ // use dom::node::{ElementNodeTypeId, Node}; -use dom::bindings::clientrectlist::ClientRectListImpl; +use dom::clientrectlist::ClientRectList; +use dom::bindings::utils::DOMString; use core::str::eq_slice; use core::cell::Cell; @@ -19,6 +20,13 @@ pub struct Element { attrs: ~[Attr], } +#[unsafe_destructor] +impl Drop for Element { + fn finalize(&self) { + fail!(~"uh oh"); + } +} + #[deriving(Eq)] pub enum ElementTypeId { HTMLAnchorElementTypeId, @@ -132,20 +140,31 @@ pub impl<'self> Element { return None; } - fn set_attr(&mut self, name: &str, value: ~str) { + fn set_attr(&mut self, name: &DOMString, value: &DOMString) { + let name = name.to_str(); + let value = value.to_str(); // FIXME: We need a better each_mut in Rust; this is ugly. let value_cell = Cell(value); + let mut found = false; for uint::range(0, self.attrs.len()) |i| { if eq_slice(self.attrs[i].name, name) { - self.attrs[i].value = value_cell.take(); - return; + self.attrs[i].value = value_cell.take().clone(); + found = true; + break; } } - self.attrs.push(Attr::new(name.to_str(), value_cell.take())); + if !found { + self.attrs.push(Attr::new(name.to_str(), value_cell.take().clone())); + } + + match self.parent.owner_doc { + Some(owner) => owner.content_changed(), + None => {} + } } - fn getClientRects(&self) -> Option<@mut ClientRectListImpl> { - Some(@mut ClientRectListImpl::new()) + fn getClientRects(&self) -> Option<@mut ClientRectList> { + Some(ClientRectList::new()) } } diff --git a/src/servo/dom/htmlcollection.rs b/src/servo/dom/htmlcollection.rs new file mode 100644 index 00000000000..51025cf6c10 --- /dev/null +++ b/src/servo/dom/htmlcollection.rs @@ -0,0 +1,43 @@ +use dom::bindings::utils::WrapperCache; +use dom::bindings::utils::{DOMString, ErrorResult}; +use dom::node::AbstractNode; + +use js::jsapi::{JSObject, JSContext}; + +pub struct HTMLCollection { + elements: ~[AbstractNode], + wrapper: WrapperCache +} + +pub impl HTMLCollection { + fn new(elements: ~[AbstractNode]) -> @mut HTMLCollection { + let collection = @mut HTMLCollection { + elements: elements, + wrapper: WrapperCache::new() + }; + collection.init_wrapper(); + collection + } + + fn Length(&self) -> u32 { + self.elements.len() as u32 + } + + fn Item(&self, index: u32) -> Option { + if index < self.Length() { + Some(self.elements[index]) + } else { + None + } + } + + fn NamedItem(&self, _cx: *JSContext, _name: DOMString, rv: &mut ErrorResult) -> *JSObject { + *rv = Ok(()); + ptr::null() + } + + fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option { + *found = true; + self.Item(index) + } +} diff --git a/src/servo/dom/node.rs b/src/servo/dom/node.rs index d5e38bda35a..050eb85feac 100644 --- a/src/servo/dom/node.rs +++ b/src/servo/dom/node.rs @@ -6,13 +6,15 @@ // The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements. // +use content::content_task::global_content; use dom::bindings; use dom::bindings::codegen; +use dom::bindings::node; use dom::bindings::utils::WrapperCache; +use dom::characterdata::CharacterData; use dom::document::Document; use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId}; use dom::element::{HTMLStyleElementTypeId}; -use dom::window::Window; use layout::debug::DebugMethods; use layout::flow::FlowContext; use newcss::complete::CompleteSelectResults; @@ -47,6 +49,8 @@ pub struct Node { next_sibling: Option, prev_sibling: Option, + owner_doc: Option<@mut Document>, + // You must not touch this if you are not layout. priv layout_data: Option<@mut LayoutData> } @@ -106,29 +110,25 @@ impl Doctype { } pub struct Comment { - parent: Node, - text: ~str, + parent: CharacterData, } impl Comment { pub fn new(text: ~str) -> Comment { Comment { - parent: Node::new(CommentNodeTypeId), - text: text + parent: CharacterData::new(CommentNodeTypeId, text) } } } pub struct Text { - parent: Node, - text: ~str, + parent: CharacterData, } impl Text { pub fn new(text: ~str) -> Text { Text { - parent: Node::new(TextNodeTypeId), - text: text + parent: CharacterData::new(TextNodeTypeId, text) } } } @@ -316,6 +316,12 @@ pub impl AbstractNode { unsafe fn raw_object(self) -> *mut Node { self.obj } + + fn from_raw(raw: *mut Node) -> AbstractNode { + AbstractNode { + obj: raw + } + } } impl DebugMethods for AbstractNode { @@ -348,8 +354,25 @@ impl DebugMethods for AbstractNode { impl Node { pub unsafe fn as_abstract_node(node: ~N) -> AbstractNode { // This surrenders memory management of the node! - AbstractNode { + let mut node = AbstractNode { obj: transmute(node), + }; + let cx = global_content().compartment.get().cx.ptr; + node::create(cx, &mut node); + node + } + + pub fn add_to_doc(&mut self, doc: @mut Document) { + self.owner_doc = Some(doc); + let mut node = self.first_child; + while node.is_some() { + node.get().traverse_preorder(|n| { + do n.with_mut_node |n| { + n.owner_doc = Some(doc); + } + true + }); + node = node.get().next_sibling(); } } @@ -364,16 +387,19 @@ impl Node { next_sibling: None, prev_sibling: None, + owner_doc: None, + layout_data: None, } } } -pub fn define_bindings(compartment: @mut Compartment, doc: @mut Document, win: @mut Window) { - bindings::window::init(compartment, win); - bindings::document::init(compartment, doc); +pub fn define_bindings(compartment: @mut Compartment) { + bindings::window::init(compartment); + bindings::document::init(compartment); bindings::node::init(compartment); bindings::element::init(compartment); + bindings::text::init(compartment); bindings::utils::initialize_global(compartment.global_obj.ptr); let mut unused = false; assert!(codegen::ClientRectBinding::DefineDOMInterface(compartment.cx.ptr, diff --git a/src/servo/dom/window.rs b/src/servo/dom/window.rs index 2c90fa77c29..6775c1a26da 100644 --- a/src/servo/dom/window.rs +++ b/src/servo/dom/window.rs @@ -2,12 +2,14 @@ * 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 content::content_task::{ControlMsg, Timer, ExitMsg}; +use content::content_task::{ControlMsg, Timer, ExitMsg, global_content}; use dom::bindings::utils::WrapperCache; +use dom::bindings::window; +use dom::event::Event; use js::jsapi::JSVal; use util::task::spawn_listener; -use core::comm::{Port, Chan}; +use core::comm::{Port, Chan, SharedChan}; use std::timer; use std::uv_global_loop; @@ -19,6 +21,7 @@ pub enum TimerControlMsg { pub struct Window { timer_chan: Chan, + dom_event_chan: SharedChan, wrapper: WrapperCache } @@ -77,10 +80,12 @@ pub impl Window { } } -pub fn Window(content_chan: comm::SharedChan) -> Window { +pub fn Window(content_chan: comm::SharedChan, + dom_event_chan: comm::SharedChan) -> @mut Window { - Window { + let win = @mut Window { wrapper: WrapperCache::new(), + dom_event_chan: dom_event_chan, timer_chan: do spawn_listener |timer_port: Port| { loop { match timer_port.recv() { @@ -92,5 +97,8 @@ pub fn Window(content_chan: comm::SharedChan) -> Window { } } } - } + }; + let compartment = global_content().compartment.get(); + window::create(compartment, win); + win } diff --git a/src/servo/layout/box_builder.rs b/src/servo/layout/box_builder.rs index a9c1c03fbdb..ff6d35ff4c1 100644 --- a/src/servo/layout/box_builder.rs +++ b/src/servo/layout/box_builder.rs @@ -489,7 +489,7 @@ pub impl LayoutTreeBuilder { // FIXME: Don't copy text. I guess it should be atomically reference counted? do node.with_imm_text |text_node| { - let string = text_node.text.to_str(); + let string = text_node.parent.data.to_str(); @mut UnscannedTextBox(RenderBoxData(node, ctx, self.next_box_id()), string) } } diff --git a/src/servo/servo.rc b/src/servo/servo.rc index b6c25bd881f..b2d6424cdae 100755 --- a/src/servo/servo.rc +++ b/src/servo/servo.rc @@ -62,6 +62,7 @@ pub mod dom { pub mod document; pub mod element; pub mod node; + pub mod text; pub mod utils; pub mod conversions; pub mod window; @@ -77,10 +78,14 @@ pub mod dom { pub mod HTMLCollectionBinding; } } + pub mod characterdata; + pub mod clientrect; + pub mod clientrectlist; pub mod document; pub mod domparser; pub mod element; pub mod event; + pub mod htmlcollection; pub mod node; pub mod window; } diff --git a/src/test/test_bindings.js b/src/test/test_bindings.js index 5f9409b36be..0897476d3b3 100644 --- a/src/test/test_bindings.js +++ b/src/test/test_bindings.js @@ -3,6 +3,7 @@ window.alert("1"); let elem = document.documentElement; +window.alert(elem.nodeType); window.alert(elem); window.alert("2"); var rects = elem.getClientRects(); @@ -24,11 +25,13 @@ window.alert(rect.width); window.alert(rect.height); window.alert("HTMLCollection:"); -let tags = document.getElementsByTagName("head"); +let tags = document.getElementsByTagName("div"); //let tag = tags[0]; window.alert(tags); window.alert(tags.length); window.alert(tags[0]); +window.alert(tags[0].tagName); +window.alert(tags[0].getClientRects()); window.alert(tags[1]); window.alert(tags[2]); window.alert(tags[3]); diff --git a/src/test/test_hammer_layout.css b/src/test/test_hammer_layout.css new file mode 100644 index 00000000000..0e141509285 --- /dev/null +++ b/src/test/test_hammer_layout.css @@ -0,0 +1,3 @@ +div #styled { + color: red; +} \ No newline at end of file diff --git a/src/test/test_hammer_layout.html b/src/test/test_hammer_layout.html new file mode 100644 index 00000000000..b2cb232036a --- /dev/null +++ b/src/test/test_hammer_layout.html @@ -0,0 +1,9 @@ + + + + + + +
This text is unstyled.
+ + diff --git a/src/test/test_hammer_layout.js b/src/test/test_hammer_layout.js new file mode 100644 index 00000000000..89cb0139d8c --- /dev/null +++ b/src/test/test_hammer_layout.js @@ -0,0 +1,17 @@ +window.setTimeout(function() { + //var divs = document.getElementsByTagName("div"); +// divs[0].setAttribute('id', 'styled'); + function print_tree(n) { + window.alert(n.nodeType); + //window.alert(n.tagName); + n = n.firstChild; + while (n) { + print_tree(n); + n = n.nextSibling; + } + } + print_tree(document.documentElement); +//window.alert(document.documentElement.tagName); +//window.alert(document.documentElement.firstChild.nodeType); +//window.alert(document.documentElement.firstChild.firstChild.nodeType); +}, 200); \ No newline at end of file From 535bfb3cbb343b969e46e556f5f20ee77560c1b4 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 10 Apr 2013 10:34:55 -0400 Subject: [PATCH 05/10] Make text nodes use same private slot representation as DOM elements. --- src/servo/dom/bindings/text.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/servo/dom/bindings/text.rs b/src/servo/dom/bindings/text.rs index 001b8647f61..4d89363fc62 100644 --- a/src/servo/dom/bindings/text.rs +++ b/src/servo/dom/bindings/text.rs @@ -1,19 +1,19 @@ use dom::bindings::element; +use dom::bindings::node::unwrap; use dom::bindings::utils; use dom::bindings::utils::{DOM_OBJECT_SLOT, CacheableWrapper}; use dom::node::{AbstractNode, Text, Comment, Doctype, TextNodeTypeId, CommentNodeTypeId}; use dom::node::{DoctypeNodeTypeId}; use js::jsapi::{JSFreeOp, JSObject, JSContext}; -use js::jsapi::bindgen::{JS_GetReservedSlot, JS_SetReservedSlot}; -use js::glue::bindgen::{RUST_JSVAL_TO_PRIVATE, RUST_PRIVATE_TO_JSVAL}; +use js::jsapi::bindgen::{JS_SetReservedSlot}; +use js::glue::bindgen::{RUST_PRIVATE_TO_JSVAL}; use js::rust::{Compartment, jsobj}; extern fn finalize_text(_fop: *JSFreeOp, obj: *JSObject) { debug!("text finalize: %?!", obj as uint); unsafe { - let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32); - let node: AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); + let node: AbstractNode = unwrap(obj); let _elem: ~Text = cast::transmute(node.raw_object()); } } @@ -21,8 +21,7 @@ extern fn finalize_text(_fop: *JSFreeOp, obj: *JSObject) { extern fn finalize_comment(_fop: *JSFreeOp, obj: *JSObject) { debug!("comment finalize: %?!", obj as uint); unsafe { - let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32); - let node: AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); + let node: AbstractNode = unwrap(obj); let _elem: ~Comment = cast::transmute(node.raw_object()); } } @@ -30,8 +29,7 @@ extern fn finalize_comment(_fop: *JSFreeOp, obj: *JSObject) { extern fn finalize_doctype(_fop: *JSFreeOp, obj: *JSObject) { debug!("doctype finalize: %?!", obj as uint); unsafe { - let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32); - let node: AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); + let node: AbstractNode = unwrap(obj); let _elem: ~Doctype = cast::transmute(node.raw_object()); } } @@ -82,7 +80,7 @@ pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj { cache.set_wrapper(obj.ptr); unsafe { - let raw_ptr = ptr::to_unsafe_ptr(node) as *libc::c_void; + let raw_ptr = node.raw_object() as *libc::c_void; JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT as u32, RUST_PRIVATE_TO_JSVAL(raw_ptr)); } From db5eca476491597e9b29a7acce7333784ca1ca93 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 10 Apr 2013 10:37:49 -0400 Subject: [PATCH 06/10] Make GC work. --- src/patches/README | 7 +++ src/patches/mozjs-stack-bounds.diff | 72 +++++++++++++++++++++++++++++ src/servo/dom/bindings/element.rs | 11 +++-- src/servo/dom/bindings/utils.rs | 10 ++-- src/servo/dom/bindings/window.rs | 24 ++++++++-- src/servo/dom/document.rs | 12 ++--- src/servo/dom/element.rs | 7 --- src/test/test_hammer_layout.css | 2 +- src/test/test_hammer_layout.html | 2 +- src/test/test_hammer_layout.js | 23 +++------ 10 files changed, 128 insertions(+), 42 deletions(-) create mode 100644 src/patches/README create mode 100644 src/patches/mozjs-stack-bounds.diff diff --git a/src/patches/README b/src/patches/README new file mode 100644 index 00000000000..63868fe4824 --- /dev/null +++ b/src/patches/README @@ -0,0 +1,7 @@ +Patches live here for submodules that should remain as pristine as possible. +This will allow us to unconditionally update them, then apply necessary +patches as needed. + +* mozjs-stack-bounds.diff: + add a public API to overwrite the engine's computed stack bounds for + GC scanning. diff --git a/src/patches/mozjs-stack-bounds.diff b/src/patches/mozjs-stack-bounds.diff new file mode 100644 index 00000000000..e09e9194100 --- /dev/null +++ b/src/patches/mozjs-stack-bounds.diff @@ -0,0 +1,72 @@ +diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp +index 5571fc0..df2fabd 100644 +--- a/js/src/jsapi.cpp ++++ b/js/src/jsapi.cpp +@@ -735,6 +735,7 @@ JSRuntime::JSRuntime() + #endif + selfHostedGlobal_(NULL), + nativeStackBase(0), ++ nativeStackEnd(0), + nativeStackQuota(0), + interpreterFrames(NULL), + cxCallback(NULL), +@@ -7084,6 +7085,13 @@ JS_SetRuntimeThread(JSRuntime *rt) + #endif + } + ++extern JS_PUBLIC_API(void) ++JS_SetNativeStackBounds(JSRuntime *rt, uintptr_t stackBase, uintptr_t stackEnd) ++{ ++ rt->nativeStackBase = stackBase; ++ rt->nativeStackEnd = stackEnd; ++} ++ + extern JS_NEVER_INLINE JS_PUBLIC_API(void) + JS_AbortIfWrongThread(JSRuntime *rt) + { +diff --git a/js/src/jsapi.h b/js/src/jsapi.h +index c8ab0f0..0edb722 100644 +--- a/js/src/jsapi.h ++++ b/js/src/jsapi.h +@@ -6248,6 +6248,9 @@ JS_ClearRuntimeThread(JSRuntime *rt); + extern JS_PUBLIC_API(void) + JS_SetRuntimeThread(JSRuntime *rt); + ++extern JS_PUBLIC_API(void) ++JS_SetNativeStackBounds(JSRuntime *rt, uintptr_t stackBase, uintptr_t stackEnd); ++ + #ifdef __cplusplus + JS_END_EXTERN_C + +diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h +index 0bb6d1c..32e016e 100644 +--- a/js/src/jscntxt.h ++++ b/js/src/jscntxt.h +@@ -439,6 +439,9 @@ struct JSRuntime : js::RuntimeFriendFields + /* Base address of the native stack for the current thread. */ + uintptr_t nativeStackBase; + ++ /* Base address of the native stack for the current thread. */ ++ uintptr_t nativeStackEnd; ++ + /* The native stack size limit that runtime should not exceed. */ + size_t nativeStackQuota; + +diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp +index f5cbc62..eae29da 100644 +--- a/js/src/jsgc.cpp ++++ b/js/src/jsgc.cpp +@@ -1177,9 +1177,11 @@ MarkConservativeStackRoots(JSTracer *trc, bool useSavedRoots) + uintptr_t *stackMin, *stackEnd; + #if JS_STACK_GROWTH_DIRECTION > 0 + stackMin = rt->nativeStackBase; +- stackEnd = cgcd->nativeStackTop; ++ stackEnd = rt->nativeStackEnd ? reinterpret_cast(rt->nativeStackEnd) ++ : cgcd->nativeStackTop; + #else +- stackMin = cgcd->nativeStackTop + 1; ++ stackMin = rt->nativeStackEnd ? reinterpret_cast(rt->nativeStackEnd) ++ : cgcd->nativeStackTop + 1; + stackEnd = reinterpret_cast(rt->nativeStackBase); + #endif + diff --git a/src/servo/dom/bindings/element.rs b/src/servo/dom/bindings/element.rs index 52af49b497f..ae0599afa65 100644 --- a/src/servo/dom/bindings/element.rs +++ b/src/servo/dom/bindings/element.rs @@ -32,10 +32,10 @@ extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) { } } -pub extern fn trace(tracer: *JSTracer, obj: *JSObject) { +pub extern fn trace(tracer: *mut JSTracer, obj: *JSObject) { let node = unsafe { unwrap(obj) }; - fn trace_node(tracer: *JSTracer, node: Option, name: &str) { + fn trace_node(tracer: *mut JSTracer, node: Option, name: &str) { if node.is_none() { return; } @@ -45,7 +45,12 @@ pub extern fn trace(tracer: *JSTracer, obj: *JSObject) { let wrapper = cache.get_wrapper(); assert!(wrapper.is_not_null()); unsafe { - JS_CallTracer(tracer, wrapper, JSTRACE_OBJECT as u32); + (*tracer).debugPrinter = ptr::null(); + (*tracer).debugPrintIndex = -1; + do str::as_c_str(name) |name| { + (*tracer).debugPrintArg = name as *libc::c_void; + JS_CallTracer(cast::transmute(tracer), wrapper, JSTRACE_OBJECT as u32); + } } } error!("tracing %?:", obj as uint); diff --git a/src/servo/dom/bindings/utils.rs b/src/servo/dom/bindings/utils.rs index 3e23170a904..85ac2bf23c0 100644 --- a/src/servo/dom/bindings/utils.rs +++ b/src/servo/dom/bindings/utils.rs @@ -290,10 +290,8 @@ static DOM_PROXY_OBJECT_SLOT: uint = js::JSSLOT_PROXY_PRIVATE as uint; // changes. static DOM_PROTO_INSTANCE_CLASS_SLOT: u32 = 0; -// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT. We have to -// start at 1 past JSCLASS_GLOBAL_SLOT_COUNT because XPConnect uses -// that one. -pub static DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT + 1; +// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT. +pub static DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT; // NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and // LSetDOMProperty. Those constants need to be changed accordingly if this value @@ -580,6 +578,10 @@ pub impl WrapperCache { self.wrapper = wrapper; } + fn get_rootable(&self) -> **JSObject { + return ptr::addr_of(&self.wrapper); + } + fn new() -> WrapperCache { WrapperCache { wrapper: ptr::null() diff --git a/src/servo/dom/bindings/window.rs b/src/servo/dom/bindings/window.rs index f50bb1f6c2c..0bd710516e1 100644 --- a/src/servo/dom/bindings/window.rs +++ b/src/servo/dom/bindings/window.rs @@ -16,7 +16,7 @@ use js::crust::{JS_PropertyStub, JS_StrictPropertyStub}; use js::global::jsval_to_rust_str; use js::glue::bindgen::*; use js::glue::bindgen::RUST_JSVAL_TO_INT; -use js::jsapi::bindgen::{JS_DefineFunctions}; +use js::jsapi::bindgen::{JS_DefineFunctions, JS_GC, JS_GetRuntime}; use js::jsapi::bindgen::{JS_GetReservedSlot, JS_SetReservedSlot}; use js::jsapi::bindgen::{JS_ValueToString}; use js::jsapi::{JSContext, JSVal, JSObject, JSBool, JSFreeOp, JSFunctionSpec}; @@ -31,12 +31,15 @@ extern fn alert(cx: *JSContext, argc: c_uint, vp: *JSVal) -> JSBool { assert!(argc == 1); // Abstract this pattern and use it in debug, too? let jsstr = JS_ValueToString(cx, *ptr::offset(argv, 0)); + if jsstr.is_null() { + return 0; + } (*unwrap(JS_THIS_OBJECT(cx, vp))).payload.alert(jsval_to_rust_str(cx, jsstr)); JS_SET_RVAL(cx, vp, JSVAL_NULL); + return 1; } - 1_i32 } extern fn setTimeout(cx: *JSContext, argc: c_uint, vp: *JSVal) -> JSBool { @@ -63,6 +66,14 @@ extern fn close(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool { } } +extern fn gc(cx: *JSContext, _argc: c_uint, _vp: *JSVal) -> JSBool { + unsafe { + let runtime = JS_GetRuntime(cx); + JS_GC(runtime); + return 1; + } +} + unsafe fn unwrap(obj: *JSObject) -> *rust_box { let val = JS_GetReservedSlot(obj, 0); cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)) @@ -99,7 +110,14 @@ pub fn init(compartment: @mut Compartment) { JSFunctionSpec { name: compartment.add_name(~"close"), call: JSNativeWrapper { op: close, info: null() }, - nargs: 2, + nargs: 0, + flags: 0, + selfHostedName: null() + }, + JSFunctionSpec { + name: compartment.add_name(~"_trigger_gc"), + call: JSNativeWrapper { op: gc, info: null() }, + nargs: 0, flags: 0, selfHostedName: null() }, diff --git a/src/servo/dom/document.rs b/src/servo/dom/document.rs index 2cd3f9b2ade..26bb27ac26f 100644 --- a/src/servo/dom/document.rs +++ b/src/servo/dom/document.rs @@ -27,9 +27,9 @@ pub fn Document(root: AbstractNode, }; let compartment = global_content().compartment.get(); do root.with_imm_node |node| { - let wrapper = node.wrapper.get_wrapper(); - assert!(wrapper.is_not_null()); - unsafe { JS_AddObjectRoot(compartment.cx.ptr, ptr::addr_of(&wrapper)); } + assert!(node.wrapper.get_wrapper().is_not_null()); + let rootable = node.wrapper.get_rootable(); + unsafe { JS_AddObjectRoot(compartment.cx.ptr, rootable); } } document::create(compartment, doc); doc @@ -40,9 +40,9 @@ impl Drop for Document { fn finalize(&self) { let compartment = global_content().compartment.get(); do self.root.with_imm_node |node| { - let wrapper = node.wrapper.get_wrapper(); - assert!(wrapper.is_not_null()); - unsafe { JS_RemoveObjectRoot(compartment.cx.ptr, ptr::addr_of(&wrapper)); } + assert!(node.wrapper.get_wrapper().is_not_null()); + let rootable = node.wrapper.get_rootable(); + unsafe { JS_RemoveObjectRoot(compartment.cx.ptr, rootable); } } } } diff --git a/src/servo/dom/element.rs b/src/servo/dom/element.rs index 5512e0c68d5..489e9bd7a6b 100644 --- a/src/servo/dom/element.rs +++ b/src/servo/dom/element.rs @@ -20,13 +20,6 @@ pub struct Element { attrs: ~[Attr], } -#[unsafe_destructor] -impl Drop for Element { - fn finalize(&self) { - fail!(~"uh oh"); - } -} - #[deriving(Eq)] pub enum ElementTypeId { HTMLAnchorElementTypeId, diff --git a/src/test/test_hammer_layout.css b/src/test/test_hammer_layout.css index 0e141509285..8218b9cab7d 100644 --- a/src/test/test_hammer_layout.css +++ b/src/test/test_hammer_layout.css @@ -1,3 +1,3 @@ -div #styled { +#styled { color: red; } \ No newline at end of file diff --git a/src/test/test_hammer_layout.html b/src/test/test_hammer_layout.html index b2cb232036a..119d67a1796 100644 --- a/src/test/test_hammer_layout.html +++ b/src/test/test_hammer_layout.html @@ -4,6 +4,6 @@ -
This text is unstyled.
+
This text is unstyled.
diff --git a/src/test/test_hammer_layout.js b/src/test/test_hammer_layout.js index 89cb0139d8c..201b41e752e 100644 --- a/src/test/test_hammer_layout.js +++ b/src/test/test_hammer_layout.js @@ -1,17 +1,6 @@ -window.setTimeout(function() { - //var divs = document.getElementsByTagName("div"); -// divs[0].setAttribute('id', 'styled'); - function print_tree(n) { - window.alert(n.nodeType); - //window.alert(n.tagName); - n = n.firstChild; - while (n) { - print_tree(n); - n = n.nextSibling; - } - } - print_tree(document.documentElement); -//window.alert(document.documentElement.tagName); -//window.alert(document.documentElement.firstChild.nodeType); -//window.alert(document.documentElement.firstChild.firstChild.nodeType); -}, 200); \ No newline at end of file +var divs = document.getElementsByTagName("div"); +var div = divs[0]; +for (var i = 0; i < 1000000; i++) { + div.setAttribute('id', 'styled'); +} + From 6fbf40988faf757a3bfa9b60be0dd131379c27c6 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 10 Apr 2013 10:53:26 -0400 Subject: [PATCH 07/10] Remove all traces of owned DOM objects. This DOM is all about sharing. --- src/servo/dom/bindings/clientrect.rs | 4 --- src/servo/dom/bindings/clientrectlist.rs | 4 --- src/servo/dom/bindings/codegen/CodegenRust.py | 9 +++-- src/servo/dom/bindings/document.rs | 4 --- src/servo/dom/bindings/domparser.rs | 4 --- src/servo/dom/bindings/htmlcollection.rs | 4 --- src/servo/dom/bindings/node.rs | 4 --- src/servo/dom/bindings/utils.rs | 36 ------------------- src/servo/dom/bindings/window.rs | 4 --- 9 files changed, 4 insertions(+), 69 deletions(-) diff --git a/src/servo/dom/bindings/clientrect.rs b/src/servo/dom/bindings/clientrect.rs index af65ca60c27..06c5be0cf5b 100644 --- a/src/servo/dom/bindings/clientrect.rs +++ b/src/servo/dom/bindings/clientrect.rs @@ -25,10 +25,6 @@ impl CacheableWrapper for ClientRect { unsafe { cast::transmute(&self.wrapper) } } - fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"nyi") - } - fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { let mut unused = false; ClientRectBinding::Wrap(cx, scope, self, &mut unused) diff --git a/src/servo/dom/bindings/clientrectlist.rs b/src/servo/dom/bindings/clientrectlist.rs index a04eadf4165..bc684b8c8b7 100644 --- a/src/servo/dom/bindings/clientrectlist.rs +++ b/src/servo/dom/bindings/clientrectlist.rs @@ -24,10 +24,6 @@ impl CacheableWrapper for ClientRectList { unsafe { cast::transmute(&self.wrapper) } } - fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"nyi") - } - fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { let mut unused = false; ClientRectListBinding::Wrap(cx, scope, self, &mut unused) diff --git a/src/servo/dom/bindings/codegen/CodegenRust.py b/src/servo/dom/bindings/codegen/CodegenRust.py index b828b80a0bc..55694abf746 100644 --- a/src/servo/dom/bindings/codegen/CodegenRust.py +++ b/src/servo/dom/bindings/codegen/CodegenRust.py @@ -2439,7 +2439,7 @@ def CreateBindingJSObject(descriptor, parent): let handler = (*content).dom_static.proxy_handlers.get(&(prototypes::id::%s as uint)); """ % descriptor.name create = handler + """ let obj = NewProxyObject(aCx, *handler, - ptr::addr_of(&RUST_PRIVATE_TO_JSVAL(squirrel_away_ref(aObject) as *libc::c_void)), + ptr::addr_of(&RUST_PRIVATE_TO_JSVAL(squirrel_away(aObject) as *libc::c_void)), proto, %s, ptr::null(), ptr::null()); if obj.is_null() { @@ -2454,7 +2454,7 @@ def CreateBindingJSObject(descriptor, parent): } JS_SetReservedSlot(obj, DOM_OBJECT_SLOT as u32, - RUST_PRIVATE_TO_JSVAL(squirrel_away_ref(aObject) as *libc::c_void)); + RUST_PRIVATE_TO_JSVAL(squirrel_away(aObject) as *libc::c_void)); """ return create % parent @@ -2462,7 +2462,7 @@ class CGWrapWithCacheMethod(CGAbstractMethod): def __init__(self, descriptor): assert descriptor.interface.hasInterfacePrototypeObject() args = [Argument('*JSContext', 'aCx'), Argument('*JSObject', 'aScope'), - Argument('&mut BindingReference<' + descriptor.nativeType + '>', 'aObject'), + Argument('@mut ' + descriptor.nativeType, 'aObject'), Argument('*mut bool', 'aTriedToWrap')] CGAbstractMethod.__init__(self, descriptor, 'Wrap_', '*JSObject', args) @@ -2505,8 +2505,7 @@ class CGWrapMethod(CGAbstractMethod): CGAbstractMethod.__init__(self, descriptor, 'Wrap', '*JSObject', args, inline=True, pub=True) def definition_body(self): - return " let mut binding = BindingReference(Right(aObject)); \ - return Wrap_(aCx, aScope, &mut binding, aTriedToWrap);" + return "return Wrap_(aCx, aScope, aObject, aTriedToWrap);" class CGWrapNonWrapperCacheMethod(CGAbstractMethod): def __init__(self, descriptor): diff --git a/src/servo/dom/bindings/document.rs b/src/servo/dom/bindings/document.rs index 992b574ed95..807b84c78a5 100644 --- a/src/servo/dom/bindings/document.rs +++ b/src/servo/dom/bindings/document.rs @@ -145,10 +145,6 @@ impl CacheableWrapper for Document { unsafe { cast::transmute(&self.wrapper) } } - fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"need to implement wrapping"); - } - fn wrap_object_shared(@mut self, cx: *JSContext, _scope: *JSObject) -> *JSObject { let content = task_from_context(cx); unsafe { create((*content).compartment.get(), self) } diff --git a/src/servo/dom/bindings/domparser.rs b/src/servo/dom/bindings/domparser.rs index 61c80044871..bada542d91b 100644 --- a/src/servo/dom/bindings/domparser.rs +++ b/src/servo/dom/bindings/domparser.rs @@ -11,10 +11,6 @@ impl CacheableWrapper for DOMParser { unsafe { cast::transmute(&self.wrapper) } } - fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"need to implement wrapping"); - } - fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { let mut unused = false; DOMParserBinding::Wrap(cx, scope, self, &mut unused) diff --git a/src/servo/dom/bindings/htmlcollection.rs b/src/servo/dom/bindings/htmlcollection.rs index 4e2ac722b5f..175f0524b4c 100644 --- a/src/servo/dom/bindings/htmlcollection.rs +++ b/src/servo/dom/bindings/htmlcollection.rs @@ -31,10 +31,6 @@ impl CacheableWrapper for HTMLCollection { unsafe { cast::transmute(&self.wrapper) } } - fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"nyi") - } - fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { let mut unused = false; HTMLCollectionBinding::Wrap(cx, scope, self, &mut unused) diff --git a/src/servo/dom/bindings/node.rs b/src/servo/dom/bindings/node.rs index 41a14b93507..fa0e2c6f3d3 100644 --- a/src/servo/dom/bindings/node.rs +++ b/src/servo/dom/bindings/node.rs @@ -167,10 +167,6 @@ impl CacheableWrapper for AbstractNode { } } - fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"need to implement wrapping"); - } - fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { fail!(~"need to implement wrapping"); } diff --git a/src/servo/dom/bindings/utils.rs b/src/servo/dom/bindings/utils.rs index 85ac2bf23c0..94620eed5f2 100644 --- a/src/servo/dom/bindings/utils.rs +++ b/src/servo/dom/bindings/utils.rs @@ -130,12 +130,6 @@ pub unsafe fn squirrel_away(x: @mut T) -> *rust_box { y } -pub unsafe fn squirrel_away_unique(x: ~T) -> *rust_box { - let y: *rust_box = cast::reinterpret_cast(&x); - cast::forget(x); - y -} - //XXX very incomplete pub fn jsval_to_str(cx: *JSContext, v: JSVal) -> Result<~str, ()> { let jsstr; @@ -561,7 +555,6 @@ pub fn initialize_global(global: *JSObject) { pub trait CacheableWrapper { fn get_wrappercache(&mut self) -> &mut WrapperCache; - fn wrap_object_unique(~self, cx: *JSContext, scope: *JSObject) -> *JSObject; fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject; } @@ -623,39 +616,10 @@ pub fn WrapNativeParent(cx: *JSContext, scope: *JSObject, mut p: @mut CacheableW wrapper } -pub struct BindingReference(Either<~T, @mut T>); - pub trait BindingObject { fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper; } -pub impl BindingReference { - fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper { - match **self { - Left(ref obj) => obj.GetParentObject(cx), - Right(ref obj) => obj.GetParentObject(cx) - } - } - - fn get_wrappercache(&mut self) -> &mut WrapperCache { - match **self { - Left(ref mut obj) => obj.get_wrappercache(), - Right(ref mut obj) => obj.get_wrappercache() - } - } -} - -pub fn squirrel_away_ref(obj: &mut BindingReference) -> *rust_box { - let mut tmp: BindingReference = unstable::intrinsics::init(); - tmp <-> *obj; - unsafe { - match tmp { - BindingReference(Left(obj)) => squirrel_away_unique(obj), - BindingReference(Right(obj)) => squirrel_away(obj) - } - } -} - pub fn GetPropertyOnPrototype(cx: *JSContext, proxy: *JSObject, id: jsid, found: *mut bool, vp: *JSVal) -> bool { unsafe { diff --git a/src/servo/dom/bindings/window.rs b/src/servo/dom/bindings/window.rs index 0bd710516e1..92910a029f6 100644 --- a/src/servo/dom/bindings/window.rs +++ b/src/servo/dom/bindings/window.rs @@ -159,10 +159,6 @@ impl CacheableWrapper for Window { unsafe { cast::transmute(&self.wrapper) } } - fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"should this be called?"); - } - fn wrap_object_shared(@mut self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { fail!(~"should this be called?"); } From 71df18a8394711ecb46e9704bfbcc4787934c242 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 15 Apr 2013 08:45:25 +0200 Subject: [PATCH 08/10] Unbreak GC some more. --- src/mozjs | 2 +- src/patches/mozjs-stack-bounds.diff | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/mozjs b/src/mozjs index 19a4939e13f..bfe6b9544ff 160000 --- a/src/mozjs +++ b/src/mozjs @@ -1 +1 @@ -Subproject commit 19a4939e13f2387cb7831623480d91301092c202 +Subproject commit bfe6b9544ff0df852bdca16a68770b9381e5f35c diff --git a/src/patches/mozjs-stack-bounds.diff b/src/patches/mozjs-stack-bounds.diff index e09e9194100..5f82df03dde 100644 --- a/src/patches/mozjs-stack-bounds.diff +++ b/src/patches/mozjs-stack-bounds.diff @@ -1,5 +1,5 @@ diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp -index 5571fc0..df2fabd 100644 +index 5571fc0..7e1e30d 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -735,6 +735,7 @@ JSRuntime::JSRuntime() @@ -10,22 +10,27 @@ index 5571fc0..df2fabd 100644 nativeStackQuota(0), interpreterFrames(NULL), cxCallback(NULL), -@@ -7084,6 +7085,13 @@ JS_SetRuntimeThread(JSRuntime *rt) +@@ -7084,6 +7085,18 @@ JS_SetRuntimeThread(JSRuntime *rt) #endif } +extern JS_PUBLIC_API(void) -+JS_SetNativeStackBounds(JSRuntime *rt, uintptr_t stackBase, uintptr_t stackEnd) ++JS_SetNativeStackBounds(JSRuntime *rt, uintptr_t minValue, uintptr_t maxValue) +{ -+ rt->nativeStackBase = stackBase; -+ rt->nativeStackEnd = stackEnd; ++#if JS_STACK_GROWTH_DIRECTION < 0 ++ rt->nativeStackBase = maxValue; ++ rt->nativeStackEnd = minValue; ++#else ++ rt->nativeStackBase = minValue; ++ rt->nativeStackEnd = maxValue; ++#endif +} + extern JS_NEVER_INLINE JS_PUBLIC_API(void) JS_AbortIfWrongThread(JSRuntime *rt) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h -index c8ab0f0..0edb722 100644 +index c8ab0f0..9ac582e 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -6248,6 +6248,9 @@ JS_ClearRuntimeThread(JSRuntime *rt); @@ -33,7 +38,7 @@ index c8ab0f0..0edb722 100644 JS_SetRuntimeThread(JSRuntime *rt); +extern JS_PUBLIC_API(void) -+JS_SetNativeStackBounds(JSRuntime *rt, uintptr_t stackBase, uintptr_t stackEnd); ++JS_SetNativeStackBounds(JSRuntime *rt, uintptr_t minValue, uintptr_t maxValue); + #ifdef __cplusplus JS_END_EXTERN_C From 5bade7b0fb093c7eac7473f41fc95e78e000b325 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 15 Apr 2013 15:54:06 +0200 Subject: [PATCH 09/10] Add getBoundingClientRect, and make it and getClientRects synchronously query layout. Associate flows with DOM nodes to allow this querying to occur. Alleviate the problem of Element objects not having access to the original AbstractNode by adding a transient field to Node that is non-null while a node downcast is taking place. --- src/servo/content/content_task.rs | 5 +- src/servo/dom/bindings/element.rs | 34 +++++++++++++- src/servo/dom/clientrectlist.rs | 11 ++--- src/servo/dom/element.rs | 78 ++++++++++++++++++++++++++++++- src/servo/dom/node.rs | 20 +++++++- src/servo/dom/window.rs | 11 +++-- src/servo/layout/box_builder.rs | 34 +++++++------- src/servo/layout/flow.rs | 6 +-- src/servo/layout/layout_task.rs | 37 +++++++++++---- src/test/test_bindings.html | 8 ++-- src/test/test_bindings.js | 22 ++++++--- src/test/test_hammer_layout.js | 12 +++-- 12 files changed, 219 insertions(+), 59 deletions(-) diff --git a/src/servo/content/content_task.rs b/src/servo/content/content_task.rs index ee1466a8233..84f9da7be04 100644 --- a/src/servo/content/content_task.rs +++ b/src/servo/content/content_task.rs @@ -228,7 +228,8 @@ pub impl Content { debug!("js_scripts: %?", js_scripts); let window = Window(self.control_chan.clone(), - self.event_chan.clone()); + self.event_chan.clone(), + ptr::to_mut_unsafe_ptr(&mut *self)); //FIXME store this safely let document = Document(root, Some(window)); do root.with_mut_node |node| { @@ -344,7 +345,7 @@ pub impl Content { } fn query_layout(&mut self, query: layout_task::LayoutQuery) -> layout_task::LayoutQueryResponse { - self.relayout(self.document.get(), &(copy self.doc_url).get()); + //self.relayout(self.document.get(), &(copy self.doc_url).get()); self.join_layout(); let (response_port, response_chan) = comm::stream(); diff --git a/src/servo/dom/bindings/element.rs b/src/servo/dom/bindings/element.rs index ae0599afa65..0fc0728e8a6 100644 --- a/src/servo/dom/bindings/element.rs +++ b/src/servo/dom/bindings/element.rs @@ -86,6 +86,11 @@ pub fn init(compartment: @mut Compartment) { nargs: 0, flags: 0, selfHostedName: null()}, + JSFunctionSpec {name: compartment.add_name(~"getBoundingClientRect"), + call: JSNativeWrapper {op: getBoundingClientRect, info: null()}, + nargs: 0, + flags: 0, + selfHostedName: null()}, JSFunctionSpec {name: compartment.add_name(~"setAttribute"), call: JSNativeWrapper {op: setAttribute, info: null()}, nargs: 0, @@ -137,7 +142,27 @@ extern fn getClientRects(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool { JS_SET_RVAL(cx, vp, JSVAL_NULL); } else { let cache = node.get_wrappercache(); - let rval = rval.get() as @mut CacheableWrapper; + let rval = rval.get() as @mut CacheableWrapper; + assert!(WrapNewBindingObject(cx, cache.get_wrapper(), + rval, + cast::transmute(vp))); + } + return 1; + } +} + +extern fn getBoundingClientRect(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool { + unsafe { + let obj = JS_THIS_OBJECT(cx, vp); + let mut node = unwrap(obj); + let rval = do node.with_imm_element |elem| { + elem.getBoundingClientRect() + }; + if rval.is_none() { + JS_SET_RVAL(cx, vp, JSVAL_NULL); + } else { + let cache = node.get_wrappercache(); + let rval = rval.get() as @mut CacheableWrapper; assert!(WrapNewBindingObject(cx, cache.get_wrapper(), rval, cast::transmute(vp))); @@ -192,7 +217,12 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa ElementNodeTypeId(HTMLImageElementTypeId) => { let content = task_from_context(cx); match (*content).query_layout(layout_task::ContentBox(node)) { - Ok(rect) => rect.width, + Ok(rect) => { + match rect { + layout_task::ContentRect(rect) => rect.size.width.to_px(), + _ => fail!(~"unexpected layout reply") + } + } Err(()) => 0 } // TODO: if nothing is being rendered(?), return zero dimensions diff --git a/src/servo/dom/clientrectlist.rs b/src/servo/dom/clientrectlist.rs index ab25d5f4764..cd543f1d6ce 100644 --- a/src/servo/dom/clientrectlist.rs +++ b/src/servo/dom/clientrectlist.rs @@ -3,14 +3,14 @@ use dom::bindings::utils::WrapperCache; pub struct ClientRectList { wrapper: WrapperCache, - rects: ~[(f32, f32, f32, f32)] + rects: ~[@mut ClientRect] } pub impl ClientRectList { - fn new() -> @mut ClientRectList { + fn new(rects: ~[@mut ClientRect]) -> @mut ClientRectList { let list = @mut ClientRectList { wrapper: WrapperCache::new(), - rects: ~[(5.6, 80.2, 3.7, 4.8), (800.1, 8001.1, -50.000001, -45.01)] + rects: rects }; list.init_wrapper(); list @@ -22,8 +22,7 @@ pub impl ClientRectList { fn Item(&self, index: u32) -> Option<@mut ClientRect> { if index < self.rects.len() as u32 { - let (top, bottom, left, right) = self.rects[index]; - Some(ClientRect::new(top, bottom, left, right)) + Some(self.rects[index]) } else { None } @@ -33,4 +32,4 @@ pub impl ClientRectList { *found = index < self.rects.len() as u32; self.Item(index) } -} \ No newline at end of file +} diff --git a/src/servo/dom/element.rs b/src/servo/dom/element.rs index 489e9bd7a6b..f6c94bb3347 100644 --- a/src/servo/dom/element.rs +++ b/src/servo/dom/element.rs @@ -7,9 +7,12 @@ // use dom::node::{ElementNodeTypeId, Node}; +use dom::clientrect::ClientRect; use dom::clientrectlist::ClientRectList; use dom::bindings::utils::DOMString; +use layout::layout_task; + use core::str::eq_slice; use core::cell::Cell; use std::net::url::Url; @@ -157,7 +160,80 @@ pub impl<'self> Element { } fn getClientRects(&self) -> Option<@mut ClientRectList> { - Some(ClientRectList::new()) + let rects = match self.parent.owner_doc { + Some(doc) => { + match doc.window { + Some(win) => { + let node = self.parent.abstract.get(); + assert!(node.is_element()); + let content = unsafe { &mut *win.content_task }; + match content.query_layout(layout_task::ContentBoxes(node)) { + Ok(rects) => match rects { + layout_task::ContentRects(rects) => + do rects.map |r| { + ClientRect::new( + r.origin.y.to_f32(), + (r.origin.y + r.size.height).to_f32(), + r.origin.x.to_f32(), + (r.origin.x + r.size.width).to_f32()) + }, + _ => fail!(~"unexpected layout reply") + }, + Err(()) => { + debug!("layout query error"); + ~[] + } + } + } + None => { + debug!("no window"); + ~[] + } + } + } + None => { + debug!("no document"); + ~[] + } + }; + Some(ClientRectList::new(rects)) + } + + fn getBoundingClientRect(&self) -> Option<@mut ClientRect> { + match self.parent.owner_doc { + Some(doc) => { + match doc.window { + Some(win) => { + let node = self.parent.abstract.get(); + assert!(node.is_element()); + let content = unsafe { &mut *win.content_task }; + match content.query_layout(layout_task::ContentBox(node)) { + Ok(rect) => match rect { + layout_task::ContentRect(rect) => + Some(ClientRect::new( + rect.origin.y.to_f32(), + (rect.origin.y + rect.size.height).to_f32(), + rect.origin.x.to_f32(), + (rect.origin.x + rect.size.width).to_f32())), + _ => fail!(~"unexpected layout result") + }, + Err(()) => { + debug!("error querying layout"); + None + } + } + } + None => { + debug!("no window"); + None + } + } + } + None => { + debug!("no document"); + None + } + } } } diff --git a/src/servo/dom/node.rs b/src/servo/dom/node.rs index 050eb85feac..446dc036d62 100644 --- a/src/servo/dom/node.rs +++ b/src/servo/dom/node.rs @@ -43,6 +43,8 @@ pub struct Node { wrapper: WrapperCache, type_id: NodeTypeId, + abstract: Option, + parent_node: Option, first_child: Option, last_child: Option, @@ -238,15 +240,27 @@ pub impl AbstractNode { fn transmute(self, f: &fn(&T) -> R) -> R { unsafe { + let node_box: *mut bindings::utils::rust_box = transmute(self.obj); + let node = &mut (*node_box).payload; + let old = node.abstract; + node.abstract = Some(self); let box: *bindings::utils::rust_box = transmute(self.obj); - f(&(*box).payload) + let rv = f(&(*box).payload); + node.abstract = old; + rv } } fn transmute_mut(self, f: &fn(&mut T) -> R) -> R { unsafe { + let node_box: *mut bindings::utils::rust_box = transmute(self.obj); + let node = &mut (*node_box).payload; + let old = node.abstract; + node.abstract = Some(self); let box: *bindings::utils::rust_box = transmute(self.obj); - f(cast::transmute(&(*box).payload)) + let rv = f(cast::transmute(&(*box).payload)); + node.abstract = old; + rv } } @@ -381,6 +395,8 @@ impl Node { wrapper: WrapperCache::new(), type_id: type_id, + abstract: None, + parent_node: None, first_child: None, last_child: None, diff --git a/src/servo/dom/window.rs b/src/servo/dom/window.rs index 6775c1a26da..7943f8b6bf8 100644 --- a/src/servo/dom/window.rs +++ b/src/servo/dom/window.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 content::content_task::{ControlMsg, Timer, ExitMsg, global_content}; +use content::content_task::{ControlMsg, Timer, ExitMsg, global_content, Content}; use dom::bindings::utils::WrapperCache; use dom::bindings::window; use dom::event::Event; @@ -19,9 +19,12 @@ pub enum TimerControlMsg { TimerMessage_TriggerExit //XXXjdm this is just a quick hack to talk to the content task } +//FIXME If we're going to store the content task, find a way to do so safely. Currently it's +// only used for querying layout from arbitrary content. pub struct Window { timer_chan: Chan, dom_event_chan: SharedChan, + content_task: *mut Content, wrapper: WrapperCache } @@ -81,7 +84,8 @@ pub impl Window { } pub fn Window(content_chan: comm::SharedChan, - dom_event_chan: comm::SharedChan) -> @mut Window { + dom_event_chan: comm::SharedChan, + content_task: *mut Content) -> @mut Window { let win = @mut Window { wrapper: WrapperCache::new(), @@ -96,7 +100,8 @@ pub fn Window(content_chan: comm::SharedChan, TimerMessage_TriggerExit => content_chan.send(ExitMsg) } } - } + }, + content_task: content_task }; let compartment = global_content().compartment.get(); window::create(compartment, win); diff --git a/src/servo/layout/box_builder.rs b/src/servo/layout/box_builder.rs index ff6d35ff4c1..edaeaf5c00e 100644 --- a/src/servo/layout/box_builder.rs +++ b/src/servo/layout/box_builder.rs @@ -230,16 +230,17 @@ impl BuilderContext { priv fn create_child_flow_of_type(&self, flow_type: FlowContextType, - builder: &mut LayoutTreeBuilder) -> BuilderContext { - let new_flow = builder.make_flow(flow_type); + builder: &mut LayoutTreeBuilder, + node: AbstractNode) -> BuilderContext { + let new_flow = builder.make_flow(flow_type, node); self.attach_child_flow(new_flow); BuilderContext::new(@mut BoxGenerator::new(new_flow)) } - priv fn make_inline_collector(&mut self, builder: &mut LayoutTreeBuilder) -> BuilderContext { + priv fn make_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode) -> BuilderContext { debug!("BuilderContext: making new inline collector flow"); - let new_flow = builder.make_flow(Flow_Inline); + let new_flow = builder.make_flow(Flow_Inline, node); let new_generator = @mut BoxGenerator::new(new_flow); self.inline_collector = Some(new_generator); @@ -248,10 +249,10 @@ impl BuilderContext { BuilderContext::new(new_generator) } - priv fn get_inline_collector(&mut self, builder: &mut LayoutTreeBuilder) -> BuilderContext { + priv fn get_inline_collector(&mut self, builder: &mut LayoutTreeBuilder, node: AbstractNode) -> BuilderContext { match copy self.inline_collector { Some(collector) => BuilderContext::new(collector), - None => self.make_inline_collector(builder) + None => self.make_inline_collector(builder, node) } } @@ -278,18 +279,18 @@ impl BuilderContext { // If this is the root node, then use the root flow's // context. Otherwise, make a child block context. match node.parent_node() { - Some(_) => { self.create_child_flow_of_type(Flow_Block, builder) } + Some(_) => { self.create_child_flow_of_type(Flow_Block, builder, node) } None => { self.clone() }, } }, (CSSDisplayBlock, @BlockFlow(*)) => { self.clear_inline_collector(); - self.create_child_flow_of_type(Flow_Block, builder) + self.create_child_flow_of_type(Flow_Block, builder, node) }, (CSSDisplayInline, @InlineFlow(*)) => self.clone(), (CSSDisplayInlineBlock, @InlineFlow(*)) => self.clone(), - (CSSDisplayInline, @BlockFlow(*)) => self.get_inline_collector(builder), - (CSSDisplayInlineBlock, @BlockFlow(*)) => self.get_inline_collector(builder), + (CSSDisplayInline, @BlockFlow(*)) => self.get_inline_collector(builder, node), + (CSSDisplayInlineBlock, @BlockFlow(*)) => self.get_inline_collector(builder, node), _ => self.clone() }; @@ -332,10 +333,9 @@ pub impl LayoutTreeBuilder { // nodes and FlowContexts should not change during layout. let flow = &mut this_ctx.default_collector.flow; for tree::each_child(&FlowTree, flow) |child_flow: &@mut FlowContext| { - for (copy child_flow.d().node).each |node| { - assert!(node.has_layout_data()); - node.layout_data().flow = Some(*child_flow); - } + let node = child_flow.d().node; + assert!(node.has_layout_data()); + node.layout_data().flow = Some(*child_flow); } } @@ -406,7 +406,7 @@ pub impl LayoutTreeBuilder { called on root DOM element. */ fn construct_trees(&mut self, layout_ctx: &LayoutContext, root: AbstractNode) -> Result<@mut FlowContext, ()> { - let new_flow = self.make_flow(Flow_Root); + let new_flow = self.make_flow(Flow_Root, root); let new_generator = @mut BoxGenerator::new(new_flow); let mut root_ctx = BuilderContext::new(new_generator); @@ -415,8 +415,8 @@ pub impl LayoutTreeBuilder { return Ok(new_flow) } - fn make_flow(&mut self, ty: FlowContextType) -> @mut FlowContext { - let data = FlowData(self.next_flow_id()); + fn make_flow(&mut self, ty: FlowContextType, node: AbstractNode) -> @mut FlowContext { + let data = FlowData(self.next_flow_id(), node); let ret = match ty { Flow_Absolute => @mut AbsoluteFlow(data), Flow_Block => @mut BlockFlow(data, BlockFlowData()), diff --git a/src/servo/layout/flow.rs b/src/servo/layout/flow.rs index 29f59ec0770..a4ea3965a7b 100644 --- a/src/servo/layout/flow.rs +++ b/src/servo/layout/flow.rs @@ -70,7 +70,7 @@ pub enum FlowContextType { /* A particular kind of layout context. It manages the positioning of render boxes within the context. */ pub struct FlowData { - node: Option, + node: AbstractNode, /* reference to parent, children flow contexts */ tree: tree::Tree<@mut FlowContext>, /* TODO (Issue #87): debug only */ @@ -84,9 +84,9 @@ pub struct FlowData { position: Rect, } -pub fn FlowData(id: int) -> FlowData { +pub fn FlowData(id: int, node: AbstractNode) -> FlowData { FlowData { - node: None, + node: node, tree: tree::empty(), id: id, diff --git a/src/servo/layout/layout_task.rs b/src/servo/layout/layout_task.rs index 5045760283e..3a3b106324d 100644 --- a/src/servo/layout/layout_task.rs +++ b/src/servo/layout/layout_task.rs @@ -40,13 +40,15 @@ use std::net::url::Url; pub type LayoutTask = SharedChan; pub enum LayoutQuery { - ContentBox(AbstractNode) + ContentBox(AbstractNode), + ContentBoxes(AbstractNode) } pub type LayoutQueryResponse = Result; -enum LayoutQueryResponse_ { - ContentSize(Size2D) +pub enum LayoutQueryResponse_ { + ContentRect(Rect), + ContentRects(~[Rect]) } pub enum Msg { @@ -256,7 +258,10 @@ impl Layout { match query { ContentBox(node) => { let response = match node.layout_data().flow { - None => Err(()), + None => { + error!("no flow present"); + Err(()) + } Some(flow) => { let start_val: Option> = None; let rect = do flow.foldl_boxes_for_node(node, start_val) |acc, box| { @@ -267,16 +272,30 @@ impl Layout { }; match rect { - None => Err(()), - Some(rect) => { - let size = Size2D(rect.size.width.to_px(), - rect.size.height.to_px()); - Ok(ContentSize(size)) + None => { + error!("no boxes for node"); + Err(()) } + Some(rect) => Ok(ContentRect(rect)) } } }; + reply_chan.send(response) + } + ContentBoxes(node) => { + let response = match node.layout_data().flow { + None => Err(()), + Some(flow) => { + let mut boxes = ~[]; + for flow.iter_boxes_for_node(node) |box| { + boxes.push(box.content_box()); + } + + Ok(ContentRects(boxes)) + } + }; + reply_chan.send(response) } } diff --git a/src/test/test_bindings.html b/src/test/test_bindings.html index 7b906908e1c..1d9fd6de5a0 100644 --- a/src/test/test_bindings.html +++ b/src/test/test_bindings.html @@ -3,9 +3,9 @@ -
-
- -
+
fffff



fffffffffffffffff
+
ggg
+ hhhhhhhh +
iiiiiiiiiiiiiiiiiii
diff --git a/src/test/test_bindings.js b/src/test/test_bindings.js index 0897476d3b3..5019616de62 100644 --- a/src/test/test_bindings.js +++ b/src/test/test_bindings.js @@ -1,16 +1,24 @@ //window.alert(ClientRect); //window.alert(ClientRectList); - -window.alert("1"); -let elem = document.documentElement; +window.alert("==1=="); +let elem = document.getElementsByTagName('div')[0]; window.alert(elem.nodeType); window.alert(elem); -window.alert("2"); +window.alert("==1.5=="); +var rect = elem.getBoundingClientRect(); +window.alert(rect); +window.alert(rect.top); +window.alert(rect.bottom); +window.alert(rect.left); +window.alert(rect.right); +window.alert(rect.width); +window.alert(rect.height); +window.alert("==2=="); var rects = elem.getClientRects(); -window.alert("3"); +window.alert("==3=="); window.alert(rects); window.alert(rects.length); -window.alert("4"); +window.alert("==4=="); let rect = rects[0]; window.alert(rect); /*window.alert(Object.prototype.toString.call(rect.__proto__)); @@ -40,4 +48,4 @@ window.alert("DOMParser:"); window.alert(DOMParser); let parser = new DOMParser(); window.alert(parser); -window.alert(parser.parseFromString("", "text/html")); \ No newline at end of file +//window.alert(parser.parseFromString("", "text/html")); diff --git a/src/test/test_hammer_layout.js b/src/test/test_hammer_layout.js index 201b41e752e..9b28847e9aa 100644 --- a/src/test/test_hammer_layout.js +++ b/src/test/test_hammer_layout.js @@ -1,6 +1,12 @@ var divs = document.getElementsByTagName("div"); var div = divs[0]; -for (var i = 0; i < 1000000; i++) { - div.setAttribute('id', 'styled'); -} +var count = 1000000; +var start = new Date(); +for (var i = 0; i < count; i++) { + div.setAttribute('id', 'styled'); + div.getBoundingClientRect(); + window.alert(i); +} +var stop = new Date(); +window.alert((stop - start) / count * 1e6); From bc26b4658847f2830c2b9359d9e1dd32f8d2dd54 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Tue, 23 Apr 2013 23:52:55 +0200 Subject: [PATCH 10/10] Fix build errors after rebase. --- src/servo/dom/bindings/text.rs | 6 ++---- src/servo/dom/bindings/window.rs | 12 ++++-------- src/servo/dom/document.rs | 4 ++-- src/servo/html/hubbub_html_parser.rs | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/servo/dom/bindings/text.rs b/src/servo/dom/bindings/text.rs index 4d89363fc62..d5f8fe7581d 100644 --- a/src/servo/dom/bindings/text.rs +++ b/src/servo/dom/bindings/text.rs @@ -79,10 +79,8 @@ pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj { assert!(cache.get_wrapper().is_null()); cache.set_wrapper(obj.ptr); - unsafe { - let raw_ptr = node.raw_object() as *libc::c_void; - JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT as u32, RUST_PRIVATE_TO_JSVAL(raw_ptr)); - } + let raw_ptr = node.raw_object() as *libc::c_void; + JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT as u32, RUST_PRIVATE_TO_JSVAL(raw_ptr)); return obj; } \ No newline at end of file diff --git a/src/servo/dom/bindings/window.rs b/src/servo/dom/bindings/window.rs index 92910a029f6..9ff133e6f31 100644 --- a/src/servo/dom/bindings/window.rs +++ b/src/servo/dom/bindings/window.rs @@ -67,11 +67,9 @@ extern fn close(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool { } extern fn gc(cx: *JSContext, _argc: c_uint, _vp: *JSVal) -> JSBool { - unsafe { - let runtime = JS_GetRuntime(cx); - JS_GC(runtime); - return 1; - } + let runtime = JS_GetRuntime(cx); + JS_GC(runtime); + return 1; } unsafe fn unwrap(obj: *JSObject) -> *rust_box { @@ -130,9 +128,7 @@ pub fn init(compartment: @mut Compartment) { } ]; - unsafe { - JS_DefineFunctions(compartment.cx.ptr, proto.ptr, &methods[0]); - } + JS_DefineFunctions(compartment.cx.ptr, proto.ptr, &methods[0]); } pub fn create(compartment: @mut Compartment, win: @mut Window) { diff --git a/src/servo/dom/document.rs b/src/servo/dom/document.rs index 26bb27ac26f..11fc5de0457 100644 --- a/src/servo/dom/document.rs +++ b/src/servo/dom/document.rs @@ -29,7 +29,7 @@ pub fn Document(root: AbstractNode, do root.with_imm_node |node| { assert!(node.wrapper.get_wrapper().is_not_null()); let rootable = node.wrapper.get_rootable(); - unsafe { JS_AddObjectRoot(compartment.cx.ptr, rootable); } + JS_AddObjectRoot(compartment.cx.ptr, rootable); } document::create(compartment, doc); doc @@ -42,7 +42,7 @@ impl Drop for Document { do self.root.with_imm_node |node| { assert!(node.wrapper.get_wrapper().is_not_null()); let rootable = node.wrapper.get_rootable(); - unsafe { JS_RemoveObjectRoot(compartment.cx.ptr, rootable); } + JS_RemoveObjectRoot(compartment.cx.ptr, rootable); } } } diff --git a/src/servo/html/hubbub_html_parser.rs b/src/servo/html/hubbub_html_parser.rs index 27146f49d67..ab9d6c789f8 100644 --- a/src/servo/html/hubbub_html_parser.rs +++ b/src/servo/html/hubbub_html_parser.rs @@ -243,7 +243,7 @@ pub fn parse_html(url: Url, let url = url::from_str("http://example.com/"); // FIXME let url_cell = Cell(url); do child_node.with_imm_text |text_node| { - let data = text_node.text.to_str(); // FIXME: Bad copy. + let data = text_node.parent.data.to_str(); // FIXME: Bad copy. let provenance = InlineProvenance(result::unwrap(url_cell.take()), data); css_chan2.send(CSSTaskNewFile(provenance)); }