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/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..5f82df03dde --- /dev/null +++ b/src/patches/mozjs-stack-bounds.diff @@ -0,0 +1,77 @@ +diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp +index 5571fc0..7e1e30d 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,18 @@ JS_SetRuntimeThread(JSRuntime *rt) + #endif + } + ++extern JS_PUBLIC_API(void) ++JS_SetNativeStackBounds(JSRuntime *rt, uintptr_t minValue, uintptr_t maxValue) ++{ ++#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..9ac582e 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 minValue, uintptr_t maxValue); ++ + #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/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/content/content_task.rs b/src/servo/content/content_task.rs index b7518cadafa..84f9da7be04 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,14 @@ 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(), + ptr::to_mut_unsafe_ptr(&mut *self)); //FIXME store this safely + 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 +245,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); @@ -322,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/clientrect.rs b/src/servo/dom/bindings/clientrect.rs index 90e81f03e9b..06c5be0cf5b 100644 --- a/src/servo/dom/bindings/clientrect.rs +++ b/src/servo/dom/bindings/clientrect.rs @@ -2,79 +2,54 @@ * 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::utils::{CacheableWrapper, WrapperCache, BindingObject, OpaqueBindingReference}; +use content::content_task::{task_from_context, global_content}; +use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper}; use dom::bindings::codegen::ClientRectBinding; -use js::jsapi::{JSObject, JSContext}; +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) } } - fn wrap_object_unique(~self, cx: *JSContext, scope: *JSObject) -> *JSObject { + fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { let mut unused = false; ClientRectBinding::Wrap(cx, scope, self, &mut unused) } +} - fn wrap_object_shared(@self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"nyi") +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 BindingObject for ClientRectImpl { - fn GetParentObject(&self, cx: *JSContext) -> OpaqueBindingReference { - let content = task_from_context(cx); - unsafe { OpaqueBindingReference(Right((*content).window.get() as @CacheableWrapper)) } +impl DerivedWrapper for ClientRect { + fn wrap(&mut self, _cx: *JSContext, _scope: *JSObject, _vp: *mut JSVal) -> i32 { + fail!(~"nyi") } -} \ No newline at end of file + + 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..bc684b8c8b7 100644 --- a/src/servo/dom/bindings/clientrectlist.rs +++ b/src/servo/dom/bindings/clientrectlist.rs @@ -2,70 +2,37 @@ * 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, OpaqueBindingReference}; +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<~ClientRectImpl>; - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<~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<~ClientRectImpl> { - if index < self.rects.len() as u32 { - let (top, bottom, left, right) = self.rects[index]; - Some(~ClientRect(top, bottom, left, right)) - } else { - None - } - } - - fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<~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) } } - fn wrap_object_unique(~self, cx: *JSContext, scope: *JSObject) -> *JSObject { + fn wrap_object_shared(@mut self, cx: *JSContext, scope: *JSObject) -> *JSObject { let mut unused = false; ClientRectListBinding::Wrap(cx, scope, self, &mut unused) } - - fn wrap_object_shared(@self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - fail!(~"nyi") - } } -impl BindingObject for ClientRectListImpl { - fn GetParentObject(&self, cx: *JSContext) -> OpaqueBindingReference { +impl BindingObject for ClientRectList { + 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..8c2b3cb29a5 100644 --- a/src/servo/dom/bindings/codegen/Bindings.conf +++ b/src/servo/dom/bindings/codegen/Bindings.conf @@ -115,15 +115,14 @@ DOMInterfaces = { 'ClientRect': [ { - 'nativeType': 'ClientRectImpl', + 'nativeType': 'ClientRect', + 'pointerType': '@mut ' }], 'ClientRectList': [ { - 'nativeType': 'ClientRectListImpl', - #'headerFile': 'nsClientRect.h', - #'prefable': True, - #'resultNotAddRefed': [ 'item' ] + 'nativeType': 'ClientRectList', + '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..55694abf746 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] @@ -2440,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() { @@ -2455,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 @@ -2463,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) @@ -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,12 +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 Wrap_(aCx, aScope, &mut binding, aTriedToWrap);" + return "return Wrap_(aCx, aScope, aObject, aTriedToWrap);" class CGWrapNonWrapperCacheMethod(CGAbstractMethod): def __init__(self, descriptor): @@ -3167,7 +3165,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" @@ -3194,8 +3192,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) @@ -3289,6 +3287,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) @@ -3334,7 +3372,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) @@ -3431,7 +3469,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 @@ -3504,7 +3542,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): @@ -3574,42 +3612,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"] @@ -3675,7 +3709,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) @@ -3835,6 +3869,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 +3891,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 +3901,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::clientrect::*', #XXXjdm + 'dom::clientrectlist::*', #XXXjdm + 'dom::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..807b84c78a5 100644 --- a/src/servo/dom/bindings/document.rs +++ b/src/servo/dom/bindings/document.rs @@ -14,13 +14,13 @@ 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; +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 { @@ -31,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; } } @@ -50,15 +52,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; } @@ -78,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 = @~[ @@ -113,8 +116,12 @@ 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)); + compartment.register_class(utils::instance_jsclass(~"DocumentInstance", + finalize, + ptr::null())); +} +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)); @@ -129,6 +136,8 @@ pub fn init(compartment: @mut Compartment, doc: @mut Document) { GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, GetJSClassHookStubPointer(STRICT_PROPERTY_STUB) as *u8, JSPROP_ENUMERATE); + + instance.ptr } impl CacheableWrapper for Document { @@ -136,11 +145,8 @@ 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(@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..bada542d91b --- /dev/null +++ b/src/servo/dom/bindings/domparser.rs @@ -0,0 +1,40 @@ +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_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..0fc0728e8a6 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,49 @@ 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: *mut JSTracer, obj: *JSObject) { + let node = unsafe { unwrap(obj) }; + + fn trace_node(tracer: *mut 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 { + (*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); + 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 +86,16 @@ 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, + flags: 0, + selfHostedName: null()}, JSFunctionSpec {name: null(), call: JSNativeWrapper {op: null(), info: null()}, nargs: 0, @@ -65,7 +106,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 +134,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() }; @@ -102,14 +142,68 @@ 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; } } +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))); + } + return 1; + } +} + +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 { @@ -118,13 +212,17 @@ 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)) { - Ok(rect) => rect.width, + match (*content).query_layout(layout_task::ContentBox(node)) { + 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 @@ -147,13 +245,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?"), @@ -164,7 +262,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)); @@ -172,8 +269,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); @@ -182,7 +278,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", @@ -200,9 +295,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 c8d110c8c1b..175f0524b4c 100644 --- a/src/servo/dom/bindings/htmlcollection.rs +++ b/src/servo/dom/bindings/htmlcollection.rs @@ -2,53 +2,27 @@ * 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, OpaqueBindingReference}; 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); } } 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 +31,8 @@ impl CacheableWrapper for HTMLCollection { unsafe { cast::transmute(&self.wrapper) } } - fn wrap_object_unique(~self, cx: *JSContext, scope: *JSObject) -> *JSObject { + 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..fa0e2c6f3d3 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() }; @@ -171,11 +167,7 @@ impl CacheableWrapper for AbstractNode { } } - fn wrap_object_unique(~self, _cx: *JSContext, _scope: *JSObject) -> *JSObject { - 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/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(); } diff --git a/src/servo/dom/bindings/text.rs b/src/servo/dom/bindings/text.rs new file mode 100644 index 00000000000..d5f8fe7581d --- /dev/null +++ b/src/servo/dom/bindings/text.rs @@ -0,0 +1,86 @@ +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_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 node: AbstractNode = unwrap(obj); + let _elem: ~Text = cast::transmute(node.raw_object()); + } +} + +extern fn finalize_comment(_fop: *JSFreeOp, obj: *JSObject) { + debug!("comment finalize: %?!", obj as uint); + unsafe { + let node: AbstractNode = unwrap(obj); + let _elem: ~Comment = cast::transmute(node.raw_object()); + } +} + +extern fn finalize_doctype(_fop: *JSFreeOp, obj: *JSObject) { + debug!("doctype finalize: %?!", obj as uint); + unsafe { + let node: AbstractNode = unwrap(obj); + 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); + + 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/utils.rs b/src/servo/dom/bindings/utils.rs index c2cbd671483..94620eed5f2 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, @@ -91,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, @@ -121,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; @@ -217,7 +220,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 { @@ -235,7 +238,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 @@ -281,10 +284,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 @@ -356,6 +357,7 @@ pub mod prototypes { pub enum Prototype { ClientRect, ClientRectList, + DOMParser, HTMLCollection, _ID_Count } @@ -540,7 +542,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); @@ -553,8 +555,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 { @@ -570,6 +571,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() @@ -577,87 +582,42 @@ 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; -} - -pub impl BindingReference { - fn GetParentObject(&self, cx: *JSContext) -> OpaqueBindingReference { - 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) - } - } + fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper; } pub fn GetPropertyOnPrototype(cx: *JSContext, proxy: *JSObject, id: jsid, found: *mut bool, @@ -781,6 +741,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 +755,53 @@ 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") + } } 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..9ff133e6f31 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,12 @@ extern fn close(cx: *JSContext, _argc: c_uint, vp: *JSVal) -> JSBool { } } +extern fn gc(cx: *JSContext, _argc: c_uint, _vp: *JSVal) -> JSBool { + 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)) @@ -76,13 +85,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 = [ @@ -103,7 +108,14 @@ pub fn init(compartment: @mut Compartment, win: @mut Window) { 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() }, @@ -116,11 +128,17 @@ pub fn init(compartment: @mut Compartment, win: @mut Window) { } ]; + 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)); } @@ -137,11 +155,7 @@ 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(@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/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..cd543f1d6ce --- /dev/null +++ b/src/servo/dom/clientrectlist.rs @@ -0,0 +1,35 @@ +use dom::clientrect::ClientRect; +use dom::bindings::utils::WrapperCache; + +pub struct ClientRectList { + wrapper: WrapperCache, + rects: ~[@mut ClientRect] +} + +pub impl ClientRectList { + fn new(rects: ~[@mut ClientRect]) -> @mut ClientRectList { + let list = @mut ClientRectList { + wrapper: WrapperCache::new(), + rects: rects + }; + 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 { + Some(self.rects[index]) + } else { + None + } + } + + fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<@mut ClientRect> { + *found = index < self.rects.len() as u32; + self.Item(index) + } +} diff --git a/src/servo/dom/document.rs b/src/servo/dom/document.rs index 16a178be051..11fc5de0457 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| { + assert!(node.wrapper.get_wrapper().is_not_null()); + let rootable = node.wrapper.get_rootable(); + JS_AddObjectRoot(compartment.cx.ptr, rootable); + } + 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| { + assert!(node.wrapper.get_wrapper().is_not_null()); + let rootable = node.wrapper.get_rootable(); + JS_RemoveObjectRoot(compartment.cx.ptr, rootable); + } } } 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, - _ => ~"" - }; + 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(~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 new file mode 100644 index 00000000000..2b08540e40f --- /dev/null +++ b/src/servo/dom/domparser.rs @@ -0,0 +1,36 @@ +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}; +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) -> @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 { + 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) }; + Document(root, None) + } +} \ No newline at end of file diff --git a/src/servo/dom/element.rs b/src/servo/dom/element.rs index d366320a701..f6c94bb3347 100644 --- a/src/servo/dom/element.rs +++ b/src/servo/dom/element.rs @@ -7,7 +7,11 @@ // use dom::node::{ElementNodeTypeId, Node}; -use dom::bindings::clientrectlist::ClientRectListImpl; +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; @@ -132,20 +136,104 @@ 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<~ClientRectListImpl> { - Some(~ClientRectListImpl::new()) + fn getClientRects(&self) -> Option<@mut ClientRectList> { + 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/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 f1903892e0a..446dc036d62 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; @@ -41,12 +43,16 @@ pub struct Node { wrapper: WrapperCache, type_id: NodeTypeId, + abstract: Option, + parent_node: Option, first_child: Option, last_child: Option, 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 +112,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) } } } @@ -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 } } @@ -316,6 +330,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 +368,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(); } } @@ -358,22 +395,27 @@ impl Node { wrapper: WrapperCache::new(), type_id: type_id, + abstract: None, + parent_node: None, first_child: None, last_child: None, 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, @@ -385,4 +427,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/dom/window.rs b/src/servo/dom/window.rs index 2c90fa77c29..7943f8b6bf8 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, 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; @@ -17,8 +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 } @@ -77,10 +83,13 @@ pub impl Window { } } -pub fn Window(content_chan: comm::SharedChan) -> Window { +pub fn Window(content_chan: comm::SharedChan, + dom_event_chan: comm::SharedChan, + content_task: *mut Content) -> @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() { @@ -91,6 +100,10 @@ pub fn Window(content_chan: comm::SharedChan) -> Window { TimerMessage_TriggerExit => content_chan.send(ExitMsg) } } - } - } + }, + content_task: content_task + }; + let compartment = global_content().compartment.get(); + window::create(compartment, win); + win } 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)); } diff --git a/src/servo/layout/box_builder.rs b/src/servo/layout/box_builder.rs index a9c1c03fbdb..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()), @@ -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/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/servo/servo.rc b/src/servo/servo.rc index 30db1ec3c81..b2d6424cdae 100755 --- a/src/servo/servo.rc +++ b/src/servo/servo.rc @@ -62,22 +62,30 @@ pub mod dom { pub mod document; pub mod element; pub mod node; + pub mod text; pub mod utils; pub mod conversions; pub mod window; 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 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.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 1f48599d08e..5019616de62 100644 --- a/src/test/test_bindings.js +++ b/src/test/test_bindings.js @@ -1,15 +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__)); @@ -24,11 +33,19 @@ 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]); + +window.alert("DOMParser:"); +window.alert(DOMParser); +let parser = new DOMParser(); +window.alert(parser); +//window.alert(parser.parseFromString("", "text/html")); diff --git a/src/test/test_hammer_layout.css b/src/test/test_hammer_layout.css new file mode 100644 index 00000000000..8218b9cab7d --- /dev/null +++ b/src/test/test_hammer_layout.css @@ -0,0 +1,3 @@ +#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..119d67a1796 --- /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..9b28847e9aa --- /dev/null +++ b/src/test/test_hammer_layout.js @@ -0,0 +1,12 @@ +var divs = document.getElementsByTagName("div"); +var div = divs[0]; + +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);