diff --git a/src/servo/content/content_task.rs b/src/servo/content/content_task.rs index d3c8d5c846b..64c9a2ec0ff 100644 --- a/src/servo/content/content_task.rs +++ b/src/servo/content/content_task.rs @@ -4,6 +4,8 @@ tasks. */ use dom::bindings::utils::rust_box; +use dom::bindings::utils::CacheableWrapper; +use dom::bindings::utils::GlobalStaticData; use dom::document::Document; use dom::node::define_bindings; use dom::event::{Event, ResizeEvent, ReflowEvent}; @@ -21,7 +23,6 @@ use core::task::{SingleThreaded, spawn, task}; use core::io::{println, read_whole_file}; use core::ptr::null; use core::util::replace; -use core::hashmap::linear; use geom::size::Size2D; use gfx::resource::image_cache_task::ImageCacheTask; use gfx::resource::resource_task::ResourceTask; @@ -93,7 +94,7 @@ pub struct Content { jsrt: jsrt, cx: @Cx, - mut proxy_handlers: linear::LinearMap, + dom_static: GlobalStaticData, document: Option<@Document>, window: Option<@Window>, @@ -138,7 +139,7 @@ pub fn Content(layout_task: LayoutTask, jsrt : jsrt, cx : cx, - proxy_handlers: linear::LinearMap::new(), + dom_static: GlobalStaticData(), document : None, window : None, @@ -158,7 +159,7 @@ pub fn Content(layout_task: LayoutTask, pub fn task_from_context(cx: *JSContext) -> *mut Content { unsafe { - cast::reinterpret_cast(&JS_GetContextPrivate(cx)) + JS_GetContextPrivate(cx) as *Content } } @@ -212,21 +213,19 @@ pub impl Content { let js_scripts = result.js_port.recv(); debug!("js_scripts: %?", js_scripts); - let document = Document(root); - let window = Window(self.control_chan.clone()); + let document = @Document(root); + let window = @Window(self.control_chan.clone()); self.damage.add(MatchSelectorsDamage); - self.relayout(&document, &url); + self.relayout(document, &url); - self.document = Some(@document); - self.window = Some(@window); + self.document = Some(document); + self.window = Some(window); self.doc_url = Some(url); let compartment = option::expect(self.compartment, ~"TODO error checking"); compartment.define_functions(debug_fns); - define_bindings(compartment, - option::get(self.document), - option::get(self.window)); + define_bindings(compartment, document, window); do vec::consume(js_scripts) |_i, bytes| { self.cx.evaluate_script(compartment.global_obj, bytes, ~"???", 1u); diff --git a/src/servo/dom/bindings/clientrect.rs b/src/servo/dom/bindings/clientrect.rs index 039cf993c46..3caa87c25db 100644 --- a/src/servo/dom/bindings/clientrect.rs +++ b/src/servo/dom/bindings/clientrect.rs @@ -1,4 +1,5 @@ -use dom::bindings::utils::{CacheableWrapper, WrapperCache}; +use content::content_task::task_from_context; +use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, OpaqueBindingReference}; use dom::bindings::ClientRectBinding; use js::jsapi::{JSObject, JSContext}; @@ -12,7 +13,7 @@ pub trait ClientRect { } pub struct ClientRectImpl { - mut wrapper: ~WrapperCache, + wrapper: WrapperCache, top: f32, bottom: f32, left: f32, @@ -45,17 +46,31 @@ pub impl ClientRect for ClientRectImpl { } } +pub fn ClientRect(top: f32, bottom: f32, left: f32, right: f32) -> ClientRectImpl { + ClientRectImpl { + top: top, bottom: bottom, left: left, right: right, + wrapper: WrapperCache::new() + } +} + pub impl CacheableWrapper for ClientRectImpl { - fn get_wrapper(@self) -> *JSObject { - unsafe { cast::transmute(self.wrapper.wrapper) } + fn get_wrappercache(&self) -> &WrapperCache { + unsafe { cast::transmute(&self.wrapper) } } - fn set_wrapper(@self, wrapper: *JSObject) { - unsafe { self.wrapper.wrapper = cast::transmute(wrapper); } - } - - fn wrap_object(@self, cx: *JSContext, scope: *JSObject) -> *JSObject { + fn wrap_object_unique(~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 ClientRectImpl { + fn GetParentObject(&self, cx: *JSContext) -> OpaqueBindingReference { + let content = task_from_context(cx); + unsafe { OpaqueBindingReference(Right((*content).window.get() as @CacheableWrapper)) } + } } \ No newline at end of file diff --git a/src/servo/dom/bindings/clientrectlist.rs b/src/servo/dom/bindings/clientrectlist.rs index c560fdbda51..c7366523880 100644 --- a/src/servo/dom/bindings/clientrectlist.rs +++ b/src/servo/dom/bindings/clientrectlist.rs @@ -1,46 +1,69 @@ use content::content_task::task_from_context; -use dom::bindings::clientrect::ClientRectImpl; +use dom::bindings::clientrect::{ClientRect, ClientRectImpl}; use dom::bindings::ClientRectListBinding; -use dom::bindings::utils::{WrapperCache, CacheableWrapper, BindingObject}; +use dom::bindings::utils::{WrapperCache, CacheableWrapper, BindingObject, OpaqueBindingReference}; +use dom::window::Window; +use dom::bindings::window::Window; use js::jsapi::{JSObject, JSContext}; pub trait ClientRectList { fn Length(&self) -> u32; - fn Item(&self, index: u32) -> Option<@ClientRectImpl>; + fn Item(&self, index: u32) -> Option<~ClientRectImpl>; + fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<~ClientRectImpl>; } pub struct ClientRectListImpl { - mut wrapper: ~WrapperCache + wrapper: WrapperCache, + rects: ~[(f32, f32, f32, f32)] } impl ClientRectList for ClientRectListImpl { fn Length(&self) -> u32 { - 0 + self.rects.len() as u32 } - fn Item(&self, index: u32) -> Option<@ClientRectImpl> { - None + 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) + } +} + +impl ClientRectListImpl { + static fn new() -> ClientRectListImpl { + ClientRectListImpl { + wrapper: WrapperCache::new(), + rects: ~[(5.6, 80.2, 3.7, 4.8), (800.1, 8001.1, -50.000001, -45.01)] + } } } pub impl CacheableWrapper for ClientRectListImpl { - fn get_wrapper(@self) -> *JSObject { - unsafe { cast::transmute(self.wrapper.wrapper) } + fn get_wrappercache(&self) -> &WrapperCache { + unsafe { cast::transmute(&self.wrapper) } } - fn set_wrapper(@self, wrapper: *JSObject) { - unsafe { self.wrapper.wrapper = cast::transmute(wrapper); } - } - - fn wrap_object(@self, cx: *JSContext, scope: *JSObject) -> *JSObject { + fn wrap_object_unique(~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") + } } -pub impl BindingObject for ClientRectListImpl { - fn GetParentObject(@self, cx: *JSContext) -> @CacheableWrapper { +impl BindingObject for ClientRectListImpl { + fn GetParentObject(&self, cx: *JSContext) -> OpaqueBindingReference { let content = task_from_context(cx); - unsafe { (*content).window.get() as @CacheableWrapper } + unsafe { OpaqueBindingReference(Right((*content).window.get() as @CacheableWrapper)) } } } \ No newline at end of file diff --git a/src/servo/dom/bindings/codegen/CodegenRust.py b/src/servo/dom/bindings/codegen/CodegenRust.py index 48b38643e15..9b0294521fb 100644 --- a/src/servo/dom/bindings/codegen/CodegenRust.py +++ b/src/servo/dom/bindings/codegen/CodegenRust.py @@ -80,7 +80,7 @@ class CastableObjectUnwrapper(): def __init__(self, descriptor, source, target, codeOnFailure): assert descriptor.castable - self.substitution = { "type" : descriptor.pointerType + descriptor.nativeType, + self.substitution = { "type" : descriptor.nativeType, "protoID" : "prototypes::id::" + descriptor.name + " as uint", "source" : source, "target" : target, @@ -105,7 +105,7 @@ class CastableObjectUnwrapper(): def __str__(self): return string.Template( -"""${target} = unwrap::<${type}>(${source}); +"""${target} = unwrap(${source}); """).substitute(self.substitution) #"""{ # nsresult rv = UnwrapObject<${protoID}, ${type}>(cx, ${source}, ${target}); @@ -1346,7 +1346,7 @@ class CGArgumentConverter(CGThing): "holderName" : ("arg%d" % index) + "_holder" } self.replacementVariables["val"] = string.Template( - "${argv}[${index}]" + "(*${argv}.offset(${index}))" ).substitute(replacer) self.replacementVariables["valPtr"] = ( "&" + self.replacementVariables["val"]) @@ -1653,7 +1653,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider, result = CGGeneric(descriptorProvider.getDescriptor( returnType.unroll().inner.identifier.name).nativeType) if resultAlreadyAddRefed: - result = CGWrapper(result, pre="Option<@", post=">") + result = CGWrapper(result, pre="Option<~", post=">") else: result = CGWrapper(result, post="*") return result, False @@ -1814,8 +1814,8 @@ class PropertyDefiner: #"static Prefable<%s> %s[] = [\n" + #',\n'.join(prefableSpecs) + "\n" + #"];\n\n") - #if doIdArrays: - # arrays += ("const %s_ids: [jsid * %i] = [" % (name, len(specs))) + ", ".join(["JSID_VOID"] * len(specs)) + "];\n\n" + if doIdArrays: + arrays += ("const %s_ids: [jsid * %i] = [" % (name, len(specs))) + ", ".join(["JSID_VOID"] * len(specs)) + "];\n\n" return arrays # The length of a method is the maximum of the lengths of the @@ -2011,7 +2011,7 @@ class CGNativePropertyHooks(CGThing): parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::NativeHooks" if parent else '0 as *NativePropertyHooks') return """ -const NativeHooks: NativePropertyHooks = NativePropertyHooks { resolve_own_property: /*%s*/ 0 as *u8, resolve_property: /*ResolveProperty*/ 0 as *u8, enumerate_own_properties: /*%s*/ 0 as *u8, enumerate_properties: /*EnumerateProperties*/ 0 as *u8, proto_hooks: %s }; +const NativeHooks: NativePropertyHooks = NativePropertyHooks { resolve_own_property: /*%s*/ 0 as *u8, resolve_property: ResolveProperty, enumerate_own_properties: /*%s*/ 0 as *u8, enumerate_properties: /*EnumerateProperties*/ 0 as *u8, proto_hooks: %s }; """ % (resolveOwnProperty, enumerateOwnProperties, parentHooks) # We'll want to insert the indent at the beginnings of lines, but we @@ -2097,6 +2097,13 @@ class CGImports(CGWrapper): CGWrapper.__init__(self, child, definePre=_useString(sorted(defineImports))) +class CGIfWrapper(CGWrapper): + def __init__(self, child, condition): + pre = CGWrapper(CGGeneric(condition), pre="if ", post=" {\n", + reindent=True) + CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(), + post="\n}") + class CGNamespace(CGWrapper): def __init__(self, namespace, child, declareOnly=False): pre = "mod %s {\n" % namespace @@ -2377,9 +2384,13 @@ class CGAbstractMethod(CGThing): def CreateBindingJSObject(descriptor, parent): if descriptor.proxy: - handler = " let content = task_from_context(aCx);\n let handler = (*content).proxy_handlers.get(&(prototypes::id::%s as uint));\n" % descriptor.name + handler = """ let cache = ptr::to_unsafe_ptr(aObject.get_wrappercache()); + + let content = task_from_context(aCx); + 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(aObject) as *libc::c_void)), + ptr::addr_of(&RUST_PRIVATE_TO_JSVAL(squirrel_away_ref(aObject) as *libc::c_void)), proto, %s, ptr::null(), ptr::null()); if obj.is_null() { @@ -2388,12 +2399,13 @@ def CreateBindingJSObject(descriptor, parent): """ else: - create = """ let obj = JS_NewObject(aCx, &Class.mBase, proto, %s); + create = """ let obj = JS_NewObject(aCx, &Class.base, proto, %s); if obj.is_null() { return ptr::null(); } - JS_SetReservedSlot(obj, DOM_OBJECT_SLOT, RUST_PRIVATE_TO_JSVAL(squirrel_away(aObject))); + JS_SetReservedSlot(obj, DOM_OBJECT_SLOT as u32, + RUST_PRIVATE_TO_JSVAL(squirrel_away_ref(aObject) as *libc::c_void)); """ return create % parent @@ -2401,8 +2413,7 @@ class CGWrapWithCacheMethod(CGAbstractMethod): def __init__(self, descriptor): assert descriptor.interface.hasInterfacePrototypeObject() args = [Argument('*JSContext', 'aCx'), Argument('*JSObject', 'aScope'), - Argument('@' + descriptor.nativeType, 'aObject'), - Argument('@CacheableWrapper', 'aCache'), + Argument('BindingReference<' + descriptor.nativeType + '>', 'aObject'), Argument('*mut bool', 'aTriedToWrap')] CGAbstractMethod.__init__(self, descriptor, 'Wrap_', '*JSObject', args) @@ -2411,7 +2422,7 @@ class CGWrapWithCacheMethod(CGAbstractMethod): return """ *aTriedToWrap = true; return aObject->GetJSObject();""" - return """/* *aTriedToWrap = true; + return """ *aTriedToWrap = true; let parent = WrapNativeParent(aCx, aScope, aObject.GetParentObject(aCx)); if parent.is_null() { @@ -2426,12 +2437,14 @@ class CGWrapWithCacheMethod(CGAbstractMethod): return ptr::null(); } + let cache = ptr::to_unsafe_ptr(aObject.get_wrappercache()); + %s //NS_ADDREF(aObject); - aCache.set_wrapper(obj); + (*cache).set_wrapper(obj); - return obj;*/return ptr::null();""" % (CheckPref(self.descriptor, "global", "*aTriedToWrap", "NULL", "aCache"), + return obj;""" % (CheckPref(self.descriptor, "global", "*aTriedToWrap", "NULL", "aCache"), CreateBindingJSObject(self.descriptor, "parent")) class CGWrapMethod(CGAbstractMethod): @@ -2439,12 +2452,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')] - #CGAbstractMethod.__init__(self, descriptor, 'Wrap', '*JSObject', args, inline=True, templateArgs=["T: &static + BindingObject + CacheableWrapper + " + descriptor.name], pub=True) + Argument('~' + descriptor.nativeType, 'aObject'), Argument('*mut bool', 'aTriedToWrap')] CGAbstractMethod.__init__(self, descriptor, 'Wrap', '*JSObject', args, inline=True, pub=True) def definition_body(self): - return " return Wrap_(aCx, aScope, aObject, aObject as @CacheableWrapper, aTriedToWrap);" + return " return Wrap_(aCx, aScope, BindingReference(Left(aObject)), aTriedToWrap);" class CGWrapNonWrapperCacheMethod(CGAbstractMethod): def __init__(self, descriptor): @@ -2538,7 +2550,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): idsToInit = [] # There is no need to init any IDs in workers, because worker bindings # don't have Xrays. - if False and not self.descriptor.workers: #XXXjdm punt on the interned string optimization + if not self.descriptor.workers: for var in self.properties.xrayRelevantArrayNames(): props = getattr(self.properties, var) # We only have non-chrome ids to init if we have no chrome ids. @@ -2548,17 +2560,19 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): idsToInit.append(props.variableName(False)) if len(idsToInit) > 0: initIds = CGList( - [CGGeneric("!InitIds(aCx, %s, %s_ids)" % (varname, varname)) for + [CGGeneric("!InitIds(aCx, %s, *%s_ids_mut)" % (varname, varname)) for varname in idsToInit], ' ||\n') if len(idsToInit) > 1: initIds = CGWrapper(initIds, pre="(", post=")", reindent=True) initIds = CGList( - [CGGeneric("%s_ids[0] == JSID_VOID &&" % idsToInit[0]), initIds], + [CGGeneric("%s_ids_mut[0] == JSID_VOID &&" % idsToInit[0]), initIds], "\n") initIds = CGWrapper(initIds, pre="if ", post=" {", reindent=True) initIds = CGList( - [initIds, - CGGeneric((" %s_ids[0] = JSID_VOID;\n" + [CGGeneric("let content = task_from_context(aCx);\n" + +"let sAttributes_ids_mut = (*content).dom_static.attribute_ids.get(&(prototypes::id::%s as uint));" % self.descriptor.name), + initIds, + CGGeneric((" %s_ids_mut[0] = JSID_VOID;\n" " return ptr::null();") % idsToInit[0]), CGGeneric("}")], "\n") @@ -2762,11 +2776,11 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): else: getter = "GetConstructorObject" - body = "" + body = " let content = task_from_context(aCx);\n" if self.descriptor.proxy: - body = """ let traps = ProxyTraps { - getPropertyDescriptor: ptr::null(), - getOwnPropertyDescriptor: ptr::null(), + body += """ let traps = ProxyTraps { + getPropertyDescriptor: getPropertyDescriptor, + getOwnPropertyDescriptor: getOwnPropertyDescriptor, defineProperty: ptr::null(), getOwnPropertyNames: ptr::null(), delete_: ptr::null(), @@ -2774,7 +2788,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): has: ptr::null(), hasOwn: ptr::null(), - get: ptr::null(), + get: get, set: ptr::null(), keys: ptr::null(), iterate: ptr::null(), @@ -2785,7 +2799,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): hasInstance: ptr::null(), typeOf: ptr::null(), objectClassIs: ptr::null(), - obj_toString: ptr::null(), + obj_toString: obj_toString, fun_toString: ptr::null(), //regexp_toShared: ptr::null(), defaultValue: ptr::null(), @@ -2794,10 +2808,13 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): getElementIfPresent: ptr::null(), getPrototypeOf: ptr::null() }; - let content = task_from_context(aCx); - (*content).proxy_handlers.insert(prototypes::id::%s as uint, - CreateProxyHandler(ptr::addr_of(&traps))); + (*content).dom_static.proxy_handlers.insert(prototypes::id::%s as uint, + CreateProxyHandler(ptr::addr_of(&traps))); +""" % self.descriptor.name + else: + body += """ (*content).dom_static.attribute_ids.insert(prototypes::id::%s as uint, + vec::cast_to_mut(vec::from_slice(sAttributes_ids))); """ % self.descriptor.name return (body + " let global: *JSObject = JS_GetGlobalForObject(aCx, aReceiver);\n" + @@ -2938,7 +2955,7 @@ class CGPerSignatureCall(CGThing): def getArgv(self): return "argv" if self.argCount > 0 else "" def getArgvDecl(self): - return "\nlet argv = JS_ARGV(cx, vp);\n" + return "\nlet argv = JS_ARGV(cx, cast::transmute(vp));\n" def getArgc(self): return "argc" def getArguments(self): @@ -2989,6 +3006,21 @@ class CGGetterCall(CGPerSignatureCall): nativeMethodName, False, descriptor, attr, getter=True) +class FakeArgument(): + """ + A class that quacks like an IDLArgument. This is used to make + setters look like method calls or for special operations. + """ + def __init__(self, type, interfaceMember): + self.type = type + self.optional = False + self.variadic = False + self.defaultValue = None + self.treatNullAs = interfaceMember.treatNullAs + self.treatUndefinedAs = interfaceMember.treatUndefinedAs + self.enforceRange = False + self.clamp = False + class CGAbstractBindingMethod(CGAbstractExternMethod): """ Common class to generate the JSNatives for all our methods, getters, and @@ -3025,7 +3057,7 @@ class CGAbstractBindingMethod(CGAbstractExternMethod): " return false as JSBool;\n" "}\n" "\n" - "let self: %s;" % (self.descriptor.pointerType + self.descriptor.nativeType))) + "let self: *rust_box<%s>;" % self.descriptor.nativeType)) def generate_code(self): assert(False) # Override me @@ -3042,8 +3074,7 @@ class CGGenericMethod(CGAbstractBindingMethod): def generate_code(self): return CGIndenter(CGGeneric( "let _info: *JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n" - "let tmp: *rust_box<%s> = cast::reinterpret_cast(&self);\n" - "return CallJitMethodOp(_info, cx, obj, ptr::addr_of(&(*tmp).payload) as *libc::c_void, argc, vp);" % self.descriptor.nativeType)) + "return CallJitMethodOp(_info, cx, obj, ptr::to_unsafe_ptr(&(*self).payload) as *libc::c_void, argc, vp);")) class CGAbstractStaticMethod(CGAbstractMethod): """ @@ -3065,16 +3096,17 @@ class CGSpecializedMethod(CGAbstractExternMethod): def __init__(self, descriptor, method): self.method = method name = method.identifier.name - args = [Argument('*JSContext', 'cx'), Argument('*JSHandleObject', 'obj'), + args = [Argument('*JSContext', 'cx'), Argument('JSHandleObject', '++obj'), Argument('*%s' % descriptor.nativeType, 'self'), - Argument('libc::c_uint', 'argc'), Argument('*JSVal', 'vp')] + Argument('libc::c_uint', 'argc'), Argument('*mut JSVal', 'vp')] CGAbstractExternMethod.__init__(self, descriptor, name, 'JSBool', args) def definition_body(self): name = self.method.identifier.name nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name)) return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(), - self.descriptor, self.method), pre="/*", post="*/return 1;").define() + self.descriptor, self.method), + pre=" let obj = (*obj.unnamed);\n").define() class CGGenericGetter(CGAbstractBindingMethod): """ @@ -3098,8 +3130,7 @@ class CGGenericGetter(CGAbstractBindingMethod): def generate_code(self): return CGIndenter(CGGeneric( "let _info: *JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n" - "let tmp: *rust_box<%s> = cast::reinterpret_cast(&self);\n" - "return CallJitPropertyOp(_info, cx, obj, ptr::addr_of(&(*tmp).payload) as *libc::c_void, vp);" % self.descriptor.nativeType)) + "return CallJitPropertyOp(_info, cx, obj, ptr::to_unsafe_ptr(&(*self).payload) as *libc::c_void, vp);")) class CGSpecializedGetter(CGAbstractExternMethod): """ @@ -3110,7 +3141,7 @@ class CGSpecializedGetter(CGAbstractExternMethod): self.attr = attr name = 'get_' + attr.identifier.name args = [ Argument('*JSContext', 'cx'), - Argument('*JSObject', 'obj'), + Argument('JSHandleObject', '++obj'), Argument('*%s' % descriptor.nativeType, 'self'), Argument('*mut JSVal', 'vp') ] CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args) @@ -3204,6 +3235,252 @@ class CGMemberJITInfo(CGThing): return result raise TypeError("Illegal member type to CGPropertyJITInfo") +class CGXrayHelper(CGAbstractExternMethod): + def __init__(self, descriptor, name, args, properties): + CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) + self.properties = properties + + def definition_body(self): + varNames = self.properties.variableNames(True) + + setup = "let content = task_from_context(cx);\n" + + methods = self.properties.methods + if methods.hasNonChromeOnly() or methods.hasChromeOnly(): + methodArgs = """// %(methods)s has an end-of-list marker at the end that we ignore +%(methods)s, %(methods)s_ids, %(methods)s_specs, ArrayLength(%(methods)s) - 1""" % varNames + else: + methodArgs = "None" + methodArgs = CGGeneric(methodArgs) + + attrs = self.properties.attrs + if attrs.hasNonChromeOnly() or attrs.hasChromeOnly(): + attrArgs = "Some(vec::zip_slice(%(attrs)s, *attr_ids))" % varNames + setup += "let attr_ids = (*content).dom_static.attribute_ids.get(&(prototypes::id::ClientRect as uint));\n" + else: + attrArgs = "None" + attrArgs = CGGeneric(attrArgs) + + consts = self.properties.consts + if consts.hasNonChromeOnly() or consts.hasChromeOnly(): + constArgs = """// %(consts)s has an end-of-list marker at the end that we ignore +%(consts)s, %(consts)s_ids, %(consts)s_specs, ArrayLength(%(consts)s) - 1""" % varNames + else: + constArgs = "None" + constArgs = CGGeneric(constArgs) + + prefixArgs = CGGeneric(self.getPrefixArgs()) + + return CGIndenter( + CGWrapper(CGList([prefixArgs, methodArgs, attrArgs, constArgs], ", "), + pre=(setup + "return Xray%s(" % self.name), + post=");", + reindent=True)).define() + +class CGResolveProperty(CGXrayHelper): + def __init__(self, descriptor, properties): + args = [Argument('*JSContext', 'cx'), Argument('*JSObject', 'wrapper'), + Argument('jsid', 'id'), Argument('bool', 'set'), + Argument('*mut JSPropertyDescriptor', 'desc')] + CGXrayHelper.__init__(self, descriptor, "ResolveProperty", args, + properties) + + def getPrefixArgs(self): + return "cx, wrapper, id, desc" + + +class CGEnumerateProperties(CGXrayHelper): + def __init__(self, descriptor, properties): + args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'), + Argument('JS::AutoIdVector&', 'props')] + CGXrayHelper.__init__(self, descriptor, "EnumerateProperties", args, + properties) + + def getPrefixArgs(self): + return "props" + +class CGProxySpecialOperation(CGPerSignatureCall): + """ + Base class for classes for calling an indexed or named special operation + (don't use this directly, use the derived classes below). + """ + def __init__(self, descriptor, operation): + nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation)) + operation = descriptor.operations[operation] + assert len(operation.signatures()) == 1 + signature = operation.signatures()[0] + extendedAttributes = descriptor.getExtendedAttributes(operation) + + (returnType, arguments) = signature + + # We pass len(arguments) as the final argument so that the + # CGPerSignatureCall won't do any argument conversion of its own. + CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName, + False, descriptor, operation, + len(arguments)) + + if operation.isSetter() or operation.isCreator(): + # arguments[0] is the index or name of the item that we're setting. + argument = arguments[1] + template = getJSToNativeConversionTemplate(argument.type, descriptor, + treatNullAs=argument.treatNullAs, + treatUndefinedAs=argument.treatUndefinedAs) + templateValues = { + "declName": argument.identifier.name, + "holderName": argument.identifier.name + "_holder", + "val": "desc->value", + "valPtr": "&desc->value" + } + self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues)) + elif operation.isGetter(): + self.cgRoot.prepend(CGGeneric("let mut found = false;")) + + def getArguments(self): + args = [(a, a.identifier.name) for a in self.arguments] + if self.idlNode.isGetter(): + args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean], + self.idlNode), + "&mut found")) + return args + + def wrap_return_value(self): + if not self.idlNode.isGetter() or self.templateValues is None: + return "" + + wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues)) + wrap = CGIfWrapper(wrap, "found") + return "\n" + wrap.define() + +class CGProxyIndexedGetter(CGProxySpecialOperation): + """ + Class to generate a call to an indexed getter. If templateValues is not None + the returned value will be wrapped with wrapForType using templateValues. + """ + def __init__(self, descriptor, templateValues=None): + self.templateValues = templateValues + CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter') + +class CGProxyUnwrap(CGAbstractMethod): + def __init__(self, descriptor): + args = [Argument('*JSObject', 'obj')] + CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", '*' + descriptor.nativeType, args, alwaysInline=True) + def declare(self): + return "" + def definition_body(self): + return """ /*if (xpc::WrapperFactory::IsXrayWrapper(obj)) { + obj = js::UnwrapObject(obj); + }*/ + //MOZ_ASSERT(IsProxy(obj)); + let box: *rust_box<%s> = cast::transmute(RUST_JSVAL_TO_PRIVATE(GetProxyPrivate(obj))); + return ptr::to_unsafe_ptr(&(*box).payload);""" % (self.descriptor.nativeType) + +class CGDOMJSProxyHandler_get(CGAbstractExternMethod): + def __init__(self, descriptor): + args = [Argument('*JSContext', 'cx'), Argument('*JSObject', 'proxy'), + Argument('*JSObject', 'receiver'), Argument('jsid', 'id'), + Argument('*mut JSVal', 'vp')] + CGAbstractExternMethod.__init__(self, descriptor, "get", "JSBool", args) + self.descriptor = descriptor + def getBody(self): + getFromExpando = """let expando = GetExpandoObject(proxy); +if expando.is_not_null() { + let hasProp = 0; + if JS_HasPropertyById(cx, expando, id, ptr::to_unsafe_ptr(&hasProp)) == 0 { + return 0; + } + + if hasProp != 0 { + return JS_GetPropertyById(cx, expando, id, cast::transmute(vp)); + } +}""" + + templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp', 'obj': 'proxy'} + + indexedGetter = self.descriptor.operations['IndexedGetter'] + if indexedGetter: + getIndexedOrExpando = ("let index = GetArrayIndexFromId(cx, id);\n" + + "if index.is_some() {\n" + + " let index = index.get();\n" + + " let self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) + getIndexedOrExpando += """ + // Even if we don't have this index, we don't forward the + // get on to our expando object. +} else { + %s +} +""" % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n '))) + else: + getIndexedOrExpando = getFromExpando + "\n" + + namedGetter = self.descriptor.operations['NamedGetter'] + if namedGetter: + getNamed = ("if (JSID_IS_STRING(id)) {\n" + + " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" + + " FakeDependentString name;\n" + " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" + + " eStringify, eStringify, name)) {\n" + + " return false;\n" + + " }\n" + + "\n" + + " let self = UnwrapProxy(proxy);\n" + + CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + + "}\n") % (self.descriptor.nativeType) + else: + getNamed = "" + + return """//MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), + //"Should not have a XrayWrapper here"); + +%s +let mut found = false; +if !GetPropertyOnPrototype(cx, proxy, id, &mut found, cast::transmute(vp)) { + return 0; +} + +if (found) { + return 1; +} +%s +*vp = JSVAL_VOID; +return 1;""" % (getIndexedOrExpando, getNamed) + + def definition_body(self): + return self.getBody() + +class CGDOMJSProxyHandler_obj_toString(CGAbstractExternMethod): + def __init__(self, descriptor): + args = [Argument('*JSContext', 'cx'), Argument('*JSObject', 'proxy')] + CGAbstractExternMethod.__init__(self, descriptor, "obj_toString", "*JSString", args) + self.descriptor = descriptor + def getBody(self): + stringifier = self.descriptor.operations['Stringifier'] + if stringifier: + name = stringifier.identifier.name + nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name)) + signature = stringifier.signatures()[0] + returnType = signature[0] + extendedAttributes = self.descriptor.getExtendedAttributes(stringifier) + infallible = 'infallible' in extendedAttributes + if not infallible: + error = CGGeneric( + ('ThrowMethodFailedWithDetails(cx, rv, "%s", "toString");\n' + + "return NULL;") % self.descriptor.interface.identifier.name) + else: + error = None + call = CGCallGenerator(error, [], "", returnType, extendedAttributes, self.descriptor, nativeName, False, object="UnwrapProxy(proxy)") + return call.define() + """ + +JSString* jsresult; +return xpc_qsStringToJsstring(cx, result, &jsresult) ? jsresult : NULL;""" + + return """ do str::as_c_str("%s") |s| { + _obj_toString(cx, s) + }""" % self.descriptor.name + + def definition_body(self): + return self.getBody() + class CGAbstractClassHook(CGAbstractExternMethod): """ Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw @@ -3442,7 +3719,7 @@ class CGDescriptor(CGThing): #cgThings.append(CGResolveOwnProperty(descriptor)) #cgThings.append(CGEnumerateOwnProperties(descriptor)) pass - #cgThings.append(CGResolveProperty(descriptor, properties)) + cgThings.append(CGResolveProperty(descriptor, properties)) #cgThings.append(CGEnumerateProperties(descriptor, properties)) if descriptor.interface.hasInterfaceObject(): @@ -3461,8 +3738,10 @@ class CGDescriptor(CGThing): if descriptor.concrete: if descriptor.proxy: #cgThings.append(CGProxyIsProxy(descriptor)) - #cgThings.append(CGProxyUnwrap(descriptor)) + cgThings.append(CGProxyUnwrap(descriptor)) cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor)) + cgThings.append(CGDOMJSProxyHandler_obj_toString(descriptor)) + cgThings.append(CGDOMJSProxyHandler_get(descriptor)) #cgThings.append(CGDOMJSProxyHandler(descriptor)) #cgThings.append(CGIsMethod(descriptor)) pass @@ -3525,7 +3804,8 @@ class CGBindingRoot(CGThing): 'dom::bindings::utils::*', 'dom::bindings::conversions::*', 'dom::bindings::clientrect::*', #XXXjdm - 'dom::bindings::clientrectlist::*' #XXXjdm + 'dom::bindings::clientrectlist::*', #XXXjdm + 'dom::bindings::proxyhandler::*', ], curr) diff --git a/src/servo/dom/bindings/document.rs b/src/servo/dom/bindings/document.rs index 5903934a751..53668365bef 100644 --- a/src/servo/dom/bindings/document.rs +++ b/src/servo/dom/bindings/document.rs @@ -72,8 +72,8 @@ extern fn getDocumentElement(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> J let doc = &(*unwrap(obj)).payload; *vp = RUST_OBJECT_TO_JSVAL(node::create(cx, doc.root).ptr); + return 1; } - return 1; } unsafe fn unwrap(obj: *JSObject) -> *rust_box { diff --git a/src/servo/dom/bindings/element.rs b/src/servo/dom/bindings/element.rs index 3118388880c..072a4a47fe5 100644 --- a/src/servo/dom/bindings/element.rs +++ b/src/servo/dom/bindings/element.rs @@ -2,7 +2,9 @@ use content::content_task::{Content, task_from_context}; use dom::bindings::node::unwrap; use dom::bindings::utils::{rust_box, squirrel_away_unique, get_compartment}; use dom::bindings::utils::{domstring_to_jsval, WrapNewBindingObject}; -use dom::bindings::utils::{str}; +use dom::bindings::utils::{str, CacheableWrapper, DOM_OBJECT_SLOT}; +use dom::bindings::utils; +use dom::bindings::clientrectlist::ClientRectListImpl; use dom::element::*; use dom::node::{AbstractNode, Node, Element, ElementNodeTypeId}; use layout::layout_task; @@ -14,7 +16,8 @@ use js::crust::{JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_Con use js::glue::bindgen::*; use js::jsapi::bindgen::*; use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSPropertySpec}; -use js::jsapi::{JSPropertyOpWrapper, JSStrictPropertyOpWrapper}; +use js::jsapi::{JSPropertyOpWrapper, JSStrictPropertyOpWrapper, JSFunctionSpec}; +use js::jsapi::JSNativeWrapper; use js::rust::{Compartment, jsobj}; use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL}; use js::{JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS}; @@ -22,7 +25,7 @@ use js::{JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS}; extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) { debug!("element finalize!"); unsafe { - let val = JS_GetReservedSlot(obj, 0); + let val = JS_GetReservedSlot(obj, DOM_OBJECT_SLOT as u32); let _node: ~AbstractNode = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)); } } @@ -51,6 +54,11 @@ pub fn init(compartment: @mut Compartment) { call: JSNativeWrapper {op: getClientRects, info: null()}, nargs: 0, flags: 0, + selfHostedName: null()}, + JSFunctionSpec {name: null(), + call: JSNativeWrapper {op: null(), info: null()}, + nargs: 0, + flags: 0, selfHostedName: null()}]; vec::as_imm_buf(*methods, |fns, _len| { JS_DefineFunctions(compartment.cx.ptr, obj.ptr, fns); @@ -82,24 +90,25 @@ pub fn init(compartment: @mut Compartment) { }); } -/*trait Element: utils::CacheableWrapper { - fn getClientRects() -> Option<@ClientRectListImpl>; -}*/ - -/*extern fn getClientRects(cx: *JSContext, argc: c_uint, vp: *JSVal) -> JSBool { +extern fn getClientRects(cx: *JSContext, argc: c_uint, vp: *JSVal) -> JSBool { unsafe { - let self: @Element = - cast::reinterpret_cast(&utils::unwrap::(JS_THIS_OBJECT(cx, vp))); - let rval = self.getClientRects(); - if rval.is_none() { - JS_SET_RVAL(cx, vp, JSVAL_NULL); - } else { - assert WrapNewBindingObject(cx, (self as utils::CacheableWrapper).get_wrapper(), rval.get(), cast::transmute(vp)); - } - cast::forget(self); - return 1; + let obj = JS_THIS_OBJECT(cx, vp); + let box = utils::unwrap::<*rust_box>(obj); + let node = &(*box).payload; + let rval = do node.with_imm_element |elem| { + elem.getClientRects() + }; + if rval.is_none() { + JS_SET_RVAL(cx, vp, JSVAL_NULL); + } else { + let cache = node.get_wrappercache(); + assert WrapNewBindingObject(cx, cache.get_wrapper(), + rval.get(), + cast::transmute(vp)); + } + return 1; } -}*/ +} #[allow(non_implicitly_copyable_typarams)] extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { @@ -149,6 +158,7 @@ extern fn HTMLImageElement_setWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa ElementNodeTypeId(_) => fail!(~"why is this not an image element?"), _ => fail!(~"why is this not an element?") }; + return 1; } } @@ -187,12 +197,13 @@ pub fn create(cx: *JSContext, node: AbstractNode) -> jsobj { let obj = result::unwrap(compartment.new_object_with_proto(~"GenericElementInstance", proto, compartment.global_obj.ptr)); - + + node.get_wrappercache().set_wrapper(obj.ptr); + unsafe { - let raw_ptr: *libc::c_void = - cast::reinterpret_cast(&squirrel_away_unique(~node)); - JS_SetReservedSlot(obj.ptr, 0, RUST_PRIVATE_TO_JSVAL(raw_ptr)); + let raw_ptr = squirrel_away_unique(~node) 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/node.rs b/src/servo/dom/bindings/node.rs index be1aba2a87d..e5c5fd2181b 100644 --- a/src/servo/dom/bindings/node.rs +++ b/src/servo/dom/bindings/node.rs @@ -1,5 +1,6 @@ use dom::bindings::utils::{rust_box, squirrel_away_unique, get_compartment}; -use dom::bindings::utils::{str, domstring_to_jsval}; +use dom::bindings::utils::{str, domstring_to_jsval, CacheableWrapper, WrapperCache}; +use dom::bindings::utils::{DOM_OBJECT_SLOT}; use dom::node::{AbstractNode, Node, ElementNodeTypeId, TextNodeTypeId, CommentNodeTypeId}; use dom::node::{DoctypeNodeTypeId}; use super::element; @@ -69,8 +70,8 @@ pub fn create(cx: *JSContext, node: AbstractNode) -> jsobj { } pub unsafe fn unwrap(obj: *JSObject) -> *rust_box { - let val = js::GetReservedSlot(obj, 0); - cast::reinterpret_cast(&JSVAL_TO_PRIVATE(val)) + let val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT as u64); + cast::transmute(JSVAL_TO_PRIVATE(val)) } #[allow(non_implicitly_copyable_typarams)] @@ -107,6 +108,7 @@ extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBoo let node = &(*unwrap(obj)).payload; let rval = do node.with_imm_node |node| { node.getNextSibling() +<<<<<<< HEAD }; match rval { Some(n) => { @@ -115,6 +117,16 @@ extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBoo } None => *vp = JSVAL_NULL } +======= + }; + match rval { + Some(n) => { + let obj = create(cx, n).ptr; + *vp = RUST_OBJECT_TO_JSVAL(obj) + } + None => *vp = JSVAL_NULL + }; +>>>>>>> Generate working ClientRectList and ClientRect bindings that can wrap, call methods, and access properties. } return 1; } @@ -131,6 +143,13 @@ impl Node { fn getNextSibling(&self) -> Option { self.next_sibling +<<<<<<< HEAD +======= + } + + fn getFirstChild(&self) -> Option { + self.first_child +>>>>>>> Generate working ClientRectList and ClientRect bindings that can wrap, call methods, and access properties. } fn getFirstChild(&self) -> Option { @@ -153,3 +172,19 @@ extern fn getNodeType(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool { } return 1; } + +impl CacheableWrapper for AbstractNode { + fn get_wrappercache(&self) -> &WrapperCache { + do self.with_imm_node |n| { + unsafe { cast::transmute(&n.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"); + } +} diff --git a/src/servo/dom/bindings/proxyhandler.rs b/src/servo/dom/bindings/proxyhandler.rs new file mode 100644 index 00000000000..03ccbd747f4 --- /dev/null +++ b/src/servo/dom/bindings/proxyhandler.rs @@ -0,0 +1,82 @@ +use js::jsapi::{JSContext, jsid, JSPropertyDescriptor, JSObject, JSString, jschar}; +use js::jsapi::bindgen::{JS_GetPropertyDescriptorById, JS_GetPrototype}; +use js::jsapi::bindgen::{JS_NewUCString, JS_malloc, JS_free}; +use js::glue::bindgen::{RUST_JSVAL_IS_VOID, RUST_JSVAL_TO_OBJECT, GetProxyExtra}; +use js::glue::bindgen::{GetObjectProto}; + +use core::sys::size_of; + +type c_bool = libc::c_int; + +extern fn getPropertyDescriptor(cx: *JSContext, proxy: *JSObject, id: jsid, + set: c_bool, desc: *mut JSPropertyDescriptor) -> c_bool { + unsafe { + if _getOwnPropertyDescriptor(cx, proxy, id, set, desc) == 0 { + return 0; + } + if (*desc).obj.is_not_null() { + return 1; + } + + //let proto = JS_GetPrototype(proxy); + let proto = GetObjectProto(proxy); + if proto.is_null() { + (*desc).obj = ptr::null(); + return 1; + } + + JS_GetPropertyDescriptorById(cx, proto, id, 0x01 /*JSRESOLVE_QUALIFIED*/, + cast::transmute(desc)) + } +} + +fn _getOwnPropertyDescriptor(cx: *JSContext, proxy: *JSObject, id: jsid, + set: c_bool, desc: *mut JSPropertyDescriptor) -> c_bool { + unsafe { + let v = GetProxyExtra(proxy, 0 /*JSPROXYSLOT_EXPANDO*/); + if RUST_JSVAL_IS_VOID(v) == 0 { + let expando = RUST_JSVAL_TO_OBJECT(v); + if JS_GetPropertyDescriptorById(cx, expando, id, 0x01 /*JSRESOLVE_QUALIFIED*/, + cast::transmute(desc)) == 0 { + return 0; + } + if (*desc).obj.is_not_null() { + (*desc).obj = proxy; + return 1; + } + } + (*desc).obj = ptr::null(); + 1 + } +} + +extern fn getOwnPropertyDescriptor(cx: *JSContext, proxy: *JSObject, id: jsid, + set: c_bool, desc: *mut JSPropertyDescriptor) -> c_bool { + _getOwnPropertyDescriptor(cx, proxy, id, set, desc) +} + +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))); + if chars.is_null() { + return ptr::null(); + } + + let result = ~"[object " + name + ~"]"; + for result.each_chari |i, c| { + *chars.offset(i) = c as jschar; + } + *chars.offset(nchars) = 0; + let jsstr = JS_NewUCString(cx, cast::transmute(chars), nchars as u64); + if jsstr.is_null() { + JS_free(cx, cast::transmute(chars)); + } + jsstr + } +} + +pub fn GetExpandoObject(proxy: *JSObject) -> *JSObject { + ptr::null() +} \ No newline at end of file diff --git a/src/servo/dom/bindings/utils.rs b/src/servo/dom/bindings/utils.rs index 62ee6406dc9..68fc35f6ca5 100644 --- a/src/servo/dom/bindings/utils.rs +++ b/src/servo/dom/bindings/utils.rs @@ -2,9 +2,9 @@ use js; use js::rust::Compartment; use js::{JS_ARGV, JSCLASS_HAS_RESERVED_SLOTS, JSPROP_ENUMERATE, JSPROP_SHARED, JSVAL_NULL, JS_THIS_OBJECT, JS_SET_RVAL, JSFUN_CONSTRUCTOR, JS_CALLEE, JSPROP_READONLY, - JSPROP_PERMANENT}; + JSPROP_PERMANENT, JSID_VOID, JSPROP_NATIVE_ACCESSORS, JSPROP_GETTER, JSPROP_SETTER}; use js::jsapi::{JSContext, JSVal, JSObject, JSBool, jsid, JSClass, JSFreeOp, JSNative, - JSFunctionSpec, JSPropertySpec, JSVal, JSString}; + JSFunctionSpec, JSPropertySpec, JSVal, JSString, JSPropertyDescriptor}; use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_ReportError, JS_GetReservedSlot, JS_SetReservedSlot, JS_NewStringCopyN, JS_DefineFunctions, JS_DefineProperty, JS_GetContextPrivate, @@ -12,7 +12,8 @@ use js::jsapi::bindgen::{JS_ValueToString, JS_GetStringCharsZAndLength, JS_Repor JS_AlreadyHasOwnProperty, JS_NewObject, JS_NewFunction, JS_GetFunctionPrototype, JS_InternString, JS_GetFunctionObject, JS_GetInternedStringCharsAndLength, JS_DefineProperties, - JS_WrapValue}; + JS_WrapValue, JS_GetObjectPrototype, JS_ForwardGetPropertyTo, + JS_HasPropertyById, JS_GetPrototype, JS_GetGlobalForObject}; use js::jsfriendapi::bindgen::{DefineFunctionWithReserved, GetObjectJSClass, JS_NewObjectWithUniqueType}; use js::glue::{PROPERTY_STUB, STRICT_PROPERTY_STUB, ENUMERATE_STUB, CONVERT_STUB, @@ -22,9 +23,23 @@ use core::ptr::null; use core::cast; use content::content_task::{Content, task_from_context}; +use core::hashmap::linear; + const TOSTRING_CLASS_RESERVED_SLOT: u64 = 0; const TOSTRING_NAME_RESERVED_SLOT: u64 = 1; +struct GlobalStaticData { + mut proxy_handlers: linear::LinearMap, + mut attribute_ids: linear::LinearMap +} + +pub fn GlobalStaticData() -> GlobalStaticData { + GlobalStaticData { + proxy_handlers: linear::LinearMap::new(), + attribute_ids: linear::LinearMap::new() + } +} + extern fn InterfaceObjectToString(cx: *JSContext, argc: uint, vp: *mut JSVal) -> JSBool { unsafe { let callee = RUST_JSVAL_TO_OBJECT(*JS_CALLEE(cx, cast::transmute(&vp))); @@ -72,9 +87,20 @@ pub struct rust_box { payload: T } +fn is_dom_class(clasp: *JSClass) -> bool { + unsafe { + ((*clasp).flags & js::JSCLASS_IS_DOMJSCLASS) != 0 + } +} + pub unsafe fn unwrap(obj: *JSObject) -> T { - let val = JS_GetReservedSlot(obj, 0); - cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val)) + let slot = if is_dom_class(JS_GetClass(obj)) { + DOM_OBJECT_SLOT + } else { + DOM_PROXY_OBJECT_SLOT + } as u32; + let val = JS_GetReservedSlot(obj, slot); + cast::transmute(RUST_JSVAL_TO_PRIVATE(val)) } pub unsafe fn squirrel_away(x: @T) -> *rust_box { @@ -189,7 +215,7 @@ pub fn instance_jsclass(name: ~str, finalize: *u8) let f: @fn(@mut Compartment) -> JSClass = |compartment: @mut Compartment| { JSClass { name: compartment.add_name(copy name), - flags: JSCLASS_HAS_RESERVED_SLOTS(1), + flags: JSCLASS_HAS_RESERVED_SLOTS(1) | js::JSCLASS_IS_DOMJSCLASS, addProperty: GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, delProperty: GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, getProperty: GetJSClassHookStubPointer(PROPERTY_STUB) as *u8, @@ -240,7 +266,8 @@ pub fn define_empty_prototype(name: ~str, proto: Option<~str>, compartment: @mut // We use slot 0 for holding the raw object. This is safe for both // globals and non-globals. -const DOM_OBJECT_SLOT: uint = 0; +pub const DOM_OBJECT_SLOT: uint = 0; +const DOM_PROXY_OBJECT_SLOT: uint = js::JSSLOT_PROXY_PRIVATE as uint; // 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 @@ -511,27 +538,45 @@ pub extern fn ThrowingConstructor(cx: *JSContext, argc: uint, vp: *JSVal) -> JSB pub fn initialize_global(global: *JSObject) { let protoArray = @[0 as *JSObject, ..2]; //XXXjdm number of constructors unsafe { + //XXXjdm we should be storing the box pointer instead of the inner let box = squirrel_away(protoArray); let inner = ptr::to_unsafe_ptr(&(*box).payload); - JS_SetReservedSlot(global, DOM_PROTOTYPE_SLOT, + JS_SetReservedSlot(global, + DOM_PROTOTYPE_SLOT, RUST_PRIVATE_TO_JSVAL(inner as *libc::c_void)); } } pub trait CacheableWrapper { - fn get_wrapper(@self) -> *JSObject; - fn set_wrapper(@self, wrapper: *JSObject); - fn wrap_object(@self, cx: *JSContext, scope: *JSObject) -> *JSObject; + fn get_wrappercache(&self) -> &WrapperCache; + fn wrap_object_unique(~self, cx: *JSContext, scope: *JSObject) -> *JSObject; + fn wrap_object_shared(@self, cx: *JSContext, scope: *JSObject) -> *JSObject; } pub struct WrapperCache { - wrapper: *mut JSObject + mut wrapper: *JSObject +} + +impl WrapperCache { + fn get_wrapper(&self) -> *JSObject { + unsafe { cast::transmute(self.wrapper) } + } + + fn set_wrapper(&self, wrapper: *JSObject) { + unsafe { self.wrapper = wrapper; } + } + + static fn new() -> WrapperCache { + WrapperCache { + wrapper: ptr::null() + } + } } pub fn WrapNewBindingObject(cx: *JSContext, scope: *JSObject, - value: @T, vp: *mut JSVal) -> bool { + value: ~T, vp: *mut JSVal) -> bool { unsafe { - let obj = value.get_wrapper(); + let obj = value.get_wrappercache().get_wrapper(); if obj.is_not_null() /*&& js::GetObjectCompartment(obj) == js::GetObjectCompartment(scope)*/ { *vp = RUST_OBJECT_TO_JSVAL(obj); return true; @@ -540,7 +585,7 @@ pub fn WrapNewBindingObject(cx: *JSContext, scope: *JSObjec let obj = if obj.is_not_null() { obj } else { - value.wrap_object(cx, scope) + value.wrap_object_unique(cx, scope) }; if obj.is_null() { @@ -553,16 +598,173 @@ pub fn WrapNewBindingObject(cx: *JSContext, scope: *JSObjec } } -pub fn WrapNativeParent(cx: *JSContext, scope: *JSObject, p: @CacheableWrapper) - -> *JSObject { - let obj = p.get_wrapper(); - if obj.is_not_null() { - return obj; - } +pub struct OpaqueBindingReference(Either<~CacheableWrapper, @CacheableWrapper>); - return ptr::null(); +pub fn WrapNativeParent(cx: *JSContext, scope: *JSObject, p: OpaqueBindingReference) -> *JSObject { + match p { + OpaqueBindingReference(Left(p)) => { + let obj = p.get_wrappercache().get_wrapper(); + if obj.is_not_null() { + return obj; + } + p.wrap_object_unique(cx, scope) + } + OpaqueBindingReference(Right(p)) => { + let obj = p.get_wrappercache().get_wrapper(); + if obj.is_not_null() { + return obj; + } + p.wrap_object_shared(cx, scope) + } + } } +pub struct BindingReference(Either<~T, @T>); + pub trait BindingObject { - fn GetParentObject(@self, cx: *JSContext) -> @CacheableWrapper; + 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(&self) -> &self/WrapperCache { + match **self { + Left(ref obj) => obj.get_wrappercache(), + Right(ref obj) => obj.get_wrappercache() + } + } +} + +pub fn squirrel_away_ref(obj: BindingReference) -> *rust_box { + unsafe { + match obj { + BindingReference(Left(obj)) => squirrel_away_unique(obj), + BindingReference(Right(obj)) => squirrel_away(obj) + } + } +} + +pub fn GetPropertyOnPrototype(cx: *JSContext, proxy: *JSObject, id: jsid, found: *mut bool, + vp: *JSVal) -> bool { + unsafe { + //let proto = GetObjectProto(proxy); + let proto = JS_GetPrototype(proxy); + if proto.is_null() { + *found = false; + return true; + } + let hasProp = 0; + if JS_HasPropertyById(cx, proto, id, ptr::to_unsafe_ptr(&hasProp)) == 0 { + return false; + } + *found = hasProp != 0; + let no_output = vp.is_null(); + if hasProp == 0 || no_output { + return true; + } + + JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp) != 0 + } +} + +pub fn GetArrayIndexFromId(cx: *JSContext, id: jsid) -> Option { + if RUST_JSID_IS_INT(id) != 0 { + return Some(RUST_JSID_TO_INT(id) as u32); + } + return None; + // if id is length atom, -1, otherwise + /*return if JSID_IS_ATOM(id) { + let atom = JSID_TO_ATOM(id); + //let s = *GetAtomChars(id); + if s > 'a' && s < 'z' { + return -1; + } + + let i = 0; + let str = AtomToLinearString(JSID_TO_ATOM(id)); + return if StringIsArray(str, &mut i) != 0 { i } else { -1 } + } else { + IdToInt32(cx, id); + }*/ +} + +pub fn XrayResolveProperty(cx: *JSContext, + wrapper: *JSObject, + id: jsid, + desc: *mut JSPropertyDescriptor, + methods: Option<~[(JSFunctionSpec, jsid)]>, + attributes: Option<~[(JSPropertySpec, jsid)]>, + constants: Option<~[(ConstantSpec, jsid)]>) -> bool +{ + unsafe { + match attributes { + Some(attrs) => { + for attrs.each |&elem| { + let (attr, attr_id) = elem; + if attr_id == JSID_VOID || attr_id != id { + loop; + } + + (*desc).attrs = (attr.flags & !(JSPROP_NATIVE_ACCESSORS as u8)) as u32; + let global = JS_GetGlobalForObject(cx, wrapper); + let fun = JS_NewFunction(cx, attr.getter.op, 0, 0, global, ptr::null()); + if fun.is_null() { + return false; + } + + RUST_SET_JITINFO(fun, attr.getter.info); + let funobj = JS_GetFunctionObject(fun); + (*desc).getter = funobj as *u8; + (*desc).attrs |= JSPROP_GETTER; + if attr.setter.op.is_not_null() { + let fun = JS_NewFunction(cx, attr.setter.op, 1, 0, global, ptr::null()); + if fun.is_null() { + return false + } + + RUST_SET_JITINFO(fun, attr.setter.info); + let funobj = JS_GetFunctionObject(fun); + (*desc).setter = funobj as *u8; + (*desc).attrs |= JSPROP_SETTER; + } else { + (*desc).setter = ptr::null(); + } + } + } + None => () + } + return true; + } +} + +fn InternJSString(cx: *JSContext, chars: *libc::c_char) -> Option { + let s = JS_InternString(cx, chars); + if s.is_not_null() { + Some(RUST_INTERNED_STRING_TO_JSID(cx, s)) + } else { + None + } +} + +pub fn InitIds(cx: *JSContext, specs: &[JSPropertySpec], ids: &[mut jsid]) -> bool { + let mut rval = true; + for specs.eachi |i, spec| { + if spec.name.is_null() == true { + break; + } + match InternJSString(cx, spec.name) { + Some(id) => ids[i] = id, + None => { + rval = false; + return false; + } + } + } + rval } \ No newline at end of file diff --git a/src/servo/dom/bindings/window.rs b/src/servo/dom/bindings/window.rs index 57291968e96..469cf10c681 100644 --- a/src/servo/dom/bindings/window.rs +++ b/src/servo/dom/bindings/window.rs @@ -2,6 +2,7 @@ use dom::bindings::node::create; use dom::bindings::utils::{rust_box, squirrel_away, jsval_to_str, CacheableWrapper}; +use dom::bindings::utils::{WrapperCache}; use dom::node::Node; use dom::window::{Window, TimerMessage_Fire}; use super::utils; @@ -115,6 +116,8 @@ pub fn init(compartment: @mut Compartment, win: @Window) { } ]; + win.get_wrappercache().set_wrapper(obj.ptr); + unsafe { JS_DefineFunctions(compartment.cx.ptr, proto.ptr, &methods[0]); @@ -127,20 +130,18 @@ pub fn init(compartment: @mut Compartment, win: @Window) { compartment.define_property(~"window", RUST_OBJECT_TO_JSVAL(obj.ptr), JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE); - - win.set_wrapper(obj.ptr); } pub impl CacheableWrapper for Window { - fn get_wrapper(@self) -> *JSObject { - self.wrapper + fn get_wrappercache(&self) -> &WrapperCache { + unsafe { cast::transmute(&self.wrapper) } } - fn set_wrapper(@self, wrapper: *JSObject) { - self.wrapper = wrapper; + fn wrap_object_unique(~self, cx: *JSContext, scope: *JSObject) -> *JSObject { + fail!(~"should this be called?"); } - fn wrap_object(@self, cx: *JSContext, scope: *JSObject) -> *JSObject { + fn wrap_object_shared(@self, cx: *JSContext, scope: *JSObject) -> *JSObject { fail!(~"should this be called?"); } } diff --git a/src/servo/dom/element.rs b/src/servo/dom/element.rs index be35e837c90..39fad8ea99d 100644 --- a/src/servo/dom/element.rs +++ b/src/servo/dom/element.rs @@ -3,6 +3,7 @@ // use dom::node::{ElementNodeTypeId, Node}; +use dom::bindings::clientrectlist::ClientRectListImpl; use core::str::eq_slice; use core::cell::Cell; @@ -138,6 +139,10 @@ pub impl Element { } self.attrs.push(Attr::new(name.to_str(), value_cell.take())); } + + fn getClientRects(&self) -> Option<~ClientRectListImpl> { + Some(~ClientRectListImpl::new()) + } } pub struct Attr { diff --git a/src/servo/dom/node.rs b/src/servo/dom/node.rs index 4a7b792b8d8..7f163e73cdb 100644 --- a/src/servo/dom/node.rs +++ b/src/servo/dom/node.rs @@ -3,6 +3,7 @@ // use dom::bindings; +use dom::bindings::utils::WrapperCache; use dom::document::Document; use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId}; use dom::element::{HTMLStyleElementTypeId}; @@ -30,7 +31,8 @@ use std::arc::ARC; /// /// FIXME: This should be replaced with a trait once they can inherit from structs. pub struct AbstractNode { - priv obj: *mut Node + priv obj: *mut Node, + //wrapper: WrapperCache } impl Eq for AbstractNode { @@ -39,6 +41,7 @@ impl Eq for AbstractNode { } pub struct Node { + wrapper: WrapperCache, type_id: NodeTypeId, parent_node: Option, @@ -350,12 +353,14 @@ impl Node { static pub unsafe fn as_abstract_node(node: ~N) -> AbstractNode { // This surrenders memory management of the node! AbstractNode { - obj: transmute(node) + obj: transmute(node), + //wrapper: WrapperCache::new() } } static pub fn new(type_id: NodeTypeId) -> Node { Node { + wrapper: WrapperCache::new(), type_id: type_id, parent_node: None, diff --git a/src/servo/dom/window.rs b/src/servo/dom/window.rs index 36951d52ac5..cc82730c2c0 100644 --- a/src/servo/dom/window.rs +++ b/src/servo/dom/window.rs @@ -1,4 +1,5 @@ use content::content_task::{ControlMsg, Timer, ExitMsg}; +use dom::bindings::utils::WrapperCache; use js::jsapi::{JSVal, JSObject}; use util::task::spawn_listener; @@ -14,7 +15,7 @@ pub enum TimerControlMsg { pub struct Window { timer_chan: Chan, - mut wrapper: *JSObject + wrapper: WrapperCache } impl Drop for Window { @@ -75,7 +76,7 @@ pub impl Window { pub fn Window(content_chan: comm::SharedChan) -> Window { Window { - wrapper: ptr::null(), + wrapper: WrapperCache::new(), timer_chan: do spawn_listener |timer_port: Port| { loop { match timer_port.recv() { diff --git a/src/servo/servo.rc b/src/servo/servo.rc index 2e205e2e0c3..5668f9c1f70 100755 --- a/src/servo/servo.rc +++ b/src/servo/servo.rc @@ -46,6 +46,7 @@ pub mod dom { pub mod utils; pub mod conversions; pub mod window; + pub mod proxyhandler; pub mod clientrect; pub mod clientrectlist; pub mod ClientRectBinding; diff --git a/src/test/test_bindings.js b/src/test/test_bindings.js index 7f8cd9b6de7..1823cc4ba9f 100644 --- a/src/test/test_bindings.js +++ b/src/test/test_bindings.js @@ -1,3 +1,25 @@ -window.alert(ClientRect); -window.alert(ClientRectList); +//window.alert(ClientRect); +//window.alert(ClientRectList); + +window.alert("1"); +let elem = document.documentElement; +window.alert(elem); +window.alert("2"); +var rects = elem.getClientRects(); +window.alert("3"); +window.alert(rects); +window.alert(rects.length); +window.alert("4"); +let rect = rects[0]; +window.alert(rect); +/*window.alert(Object.prototype.toString.call(rect.__proto__)); +window.alert(rect.__proto__ === Object.getPrototypeOf(rect)); +window.alert(rect.__proto__.top); +window.alert(Object.getPrototypeOf(rect).top);*/ +window.alert(rect.top); +window.alert(rect.bottom); +window.alert(rect.left); +window.alert(rect.right); +window.alert(rect.width); +window.alert(rect.height);