mirror of
https://github.com/servo/servo.git
synced 2025-08-02 04:00:32 +01:00
Introduce trait WeakReferenceable
This allows to take weak references of JS-managed DOM objects.
This commit is contained in:
parent
12f6ba29a7
commit
72c67efe96
10 changed files with 268 additions and 5 deletions
|
@ -21,4 +21,8 @@ DOMInterfaces = {
|
||||||
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
|
#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
|
||||||
'TestBinding': {},
|
'TestBinding': {},
|
||||||
|
|
||||||
|
'URL': {
|
||||||
|
'weakReferenceable': True,
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1765,12 +1765,16 @@ class CGDOMJSClass(CGThing):
|
||||||
def define(self):
|
def define(self):
|
||||||
traceHook = 'Some(%s)' % TRACE_HOOK_NAME
|
traceHook = 'Some(%s)' % TRACE_HOOK_NAME
|
||||||
if self.descriptor.isGlobal():
|
if self.descriptor.isGlobal():
|
||||||
|
assert not self.descriptor.weakReferenceable
|
||||||
traceHook = "Some(js::jsapi::JS_GlobalObjectTraceHook)"
|
traceHook = "Some(js::jsapi::JS_GlobalObjectTraceHook)"
|
||||||
flags = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL"
|
flags = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL"
|
||||||
slots = "JSCLASS_GLOBAL_SLOT_COUNT + 1"
|
slots = "JSCLASS_GLOBAL_SLOT_COUNT + 1"
|
||||||
else:
|
else:
|
||||||
flags = "0"
|
flags = "0"
|
||||||
slots = "1"
|
if self.descriptor.weakReferenceable:
|
||||||
|
slots = "2"
|
||||||
|
else:
|
||||||
|
slots = "1"
|
||||||
return """\
|
return """\
|
||||||
static Class: DOMJSClass = DOMJSClass {
|
static Class: DOMJSClass = DOMJSClass {
|
||||||
base: js::jsapi::Class {
|
base: js::jsapi::Class {
|
||||||
|
@ -2175,6 +2179,9 @@ let obj = RootedObject::new(cx, obj);\
|
||||||
"\n"
|
"\n"
|
||||||
"JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT,\n"
|
"JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT,\n"
|
||||||
" PrivateValue(raw as *const libc::c_void));")
|
" PrivateValue(raw as *const libc::c_void));")
|
||||||
|
if descriptor.weakReferenceable:
|
||||||
|
create += """
|
||||||
|
JS_SetReservedSlot(obj.ptr, DOM_WEAK_SLOT, PrivateValue(ptr::null()));"""
|
||||||
return create
|
return create
|
||||||
|
|
||||||
|
|
||||||
|
@ -4521,8 +4528,8 @@ class CGAbstractClassHook(CGAbstractExternMethod):
|
||||||
args)
|
args)
|
||||||
|
|
||||||
def definition_body_prologue(self):
|
def definition_body_prologue(self):
|
||||||
return CGGeneric("""\
|
return CGGeneric("""
|
||||||
let this = private_from_object(obj) as *const %s;
|
let this = native_from_object::<%s>(obj).unwrap();
|
||||||
""" % self.descriptor.concreteType)
|
""" % self.descriptor.concreteType)
|
||||||
|
|
||||||
def definition_body(self):
|
def definition_body(self):
|
||||||
|
@ -4541,6 +4548,24 @@ def finalizeHook(descriptor, hookName, context):
|
||||||
release += """\
|
release += """\
|
||||||
finalize_global(obj);
|
finalize_global(obj);
|
||||||
"""
|
"""
|
||||||
|
elif descriptor.weakReferenceable:
|
||||||
|
release += """\
|
||||||
|
let weak_box_ptr = JS_GetReservedSlot(obj, DOM_WEAK_SLOT).to_private() as *mut WeakBox<%s>;
|
||||||
|
if !weak_box_ptr.is_null() {
|
||||||
|
let count = {
|
||||||
|
let weak_box = &*weak_box_ptr;
|
||||||
|
assert!(weak_box.value.get().is_some());
|
||||||
|
assert!(weak_box.count.get() > 0);
|
||||||
|
weak_box.value.set(None);
|
||||||
|
let count = weak_box.count.get() - 1;
|
||||||
|
weak_box.count.set(count);
|
||||||
|
count
|
||||||
|
};
|
||||||
|
if count == 0 {
|
||||||
|
mem::drop(Box::from_raw(weak_box_ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""" % descriptor.concreteType
|
||||||
release += """\
|
release += """\
|
||||||
let _ = Box::from_raw(this as *mut %s);
|
let _ = Box::from_raw(this as *mut %s);
|
||||||
debug!("%s finalize: {:p}", this);\
|
debug!("%s finalize: {:p}", this);\
|
||||||
|
@ -4718,6 +4743,16 @@ class CGInterfaceTrait(CGThing):
|
||||||
return self.cgRoot.define()
|
return self.cgRoot.define()
|
||||||
|
|
||||||
|
|
||||||
|
class CGWeakReferenceableTrait(CGThing):
|
||||||
|
def __init__(self, descriptor):
|
||||||
|
CGThing.__init__(self)
|
||||||
|
assert descriptor.weakReferenceable
|
||||||
|
self.code = "impl WeakReferenceable for %s {}" % descriptor.interface.identifier.name
|
||||||
|
|
||||||
|
def define(self):
|
||||||
|
return self.code
|
||||||
|
|
||||||
|
|
||||||
class CGDescriptor(CGThing):
|
class CGDescriptor(CGThing):
|
||||||
def __init__(self, descriptor):
|
def __init__(self, descriptor):
|
||||||
CGThing.__init__(self)
|
CGThing.__init__(self)
|
||||||
|
@ -4826,6 +4861,8 @@ class CGDescriptor(CGThing):
|
||||||
if not descriptor.interface.isCallback():
|
if not descriptor.interface.isCallback():
|
||||||
cgThings.append(CGIDLInterface(descriptor))
|
cgThings.append(CGIDLInterface(descriptor))
|
||||||
cgThings.append(CGInterfaceTrait(descriptor))
|
cgThings.append(CGInterfaceTrait(descriptor))
|
||||||
|
if descriptor.weakReferenceable:
|
||||||
|
cgThings.append(CGWeakReferenceableTrait(descriptor))
|
||||||
|
|
||||||
cgThings = CGList(cgThings, "\n")
|
cgThings = CGList(cgThings, "\n")
|
||||||
# self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
|
# self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
|
||||||
|
@ -5215,7 +5252,7 @@ class CGBindingRoot(CGThing):
|
||||||
'dom::bindings::conversions::{ConversionBehavior, DOM_OBJECT_SLOT, IDLInterface}',
|
'dom::bindings::conversions::{ConversionBehavior, DOM_OBJECT_SLOT, IDLInterface}',
|
||||||
'dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}',
|
'dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}',
|
||||||
'dom::bindings::conversions::{ToJSValConvertible, jsid_to_str, native_from_handlevalue}',
|
'dom::bindings::conversions::{ToJSValConvertible, jsid_to_str, native_from_handlevalue}',
|
||||||
'dom::bindings::conversions::{private_from_object, root_from_object}',
|
'dom::bindings::conversions::{native_from_object, private_from_object, root_from_object}',
|
||||||
'dom::bindings::conversions::{root_from_handleobject, root_from_handlevalue}',
|
'dom::bindings::conversions::{root_from_handleobject, root_from_handlevalue}',
|
||||||
'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}',
|
'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}',
|
||||||
'dom::bindings::codegen::Bindings::*',
|
'dom::bindings::codegen::Bindings::*',
|
||||||
|
@ -5230,6 +5267,7 @@ class CGBindingRoot(CGThing):
|
||||||
'dom::bindings::str::ByteString',
|
'dom::bindings::str::ByteString',
|
||||||
'dom::bindings::str::USVString',
|
'dom::bindings::str::USVString',
|
||||||
'dom::bindings::trace::RootedVec',
|
'dom::bindings::trace::RootedVec',
|
||||||
|
'dom::bindings::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable}',
|
||||||
'mem::heap_size_of_raw_self_and_children',
|
'mem::heap_size_of_raw_self_and_children',
|
||||||
'libc',
|
'libc',
|
||||||
'util::str::DOMString',
|
'util::str::DOMString',
|
||||||
|
|
|
@ -168,6 +168,7 @@ class Descriptor(DescriptorProvider):
|
||||||
self.register = desc.get('register', True)
|
self.register = desc.get('register', True)
|
||||||
self.outerObjectHook = desc.get('outerObjectHook', 'None')
|
self.outerObjectHook = desc.get('outerObjectHook', 'None')
|
||||||
self.proxy = False
|
self.proxy = False
|
||||||
|
self.weakReferenceable = desc.get('weakReferenceable', False)
|
||||||
|
|
||||||
# If we're concrete, we need to crawl our ancestor interfaces and mark
|
# If we're concrete, we need to crawl our ancestor interfaces and mark
|
||||||
# them as having a concrete descendant.
|
# them as having a concrete descendant.
|
||||||
|
|
|
@ -729,7 +729,8 @@ pub unsafe fn private_from_proto_check<F>(mut obj: *mut JSObject, proto_check: F
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn native_from_object<T>(obj: *mut JSObject) -> Result<*const T, ()>
|
/// Get a `*const T` for a DOM object accessible from a `JSObject`.
|
||||||
|
pub fn native_from_object<T>(obj: *mut JSObject) -> Result<*const T, ()>
|
||||||
where T: Reflectable + IDLInterface
|
where T: Reflectable + IDLInterface
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -147,6 +147,7 @@ pub mod str;
|
||||||
pub mod structuredclone;
|
pub mod structuredclone;
|
||||||
pub mod trace;
|
pub mod trace;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
pub mod weakref;
|
||||||
pub mod xmlname;
|
pub mod xmlname;
|
||||||
|
|
||||||
/// Generated JS-Rust bindings.
|
/// Generated JS-Rust bindings.
|
||||||
|
|
175
components/script/dom/bindings/weakref.rs
Normal file
175
components/script/dom/bindings/weakref.rs
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
//! Weak-referenceable JS-managed DOM objects.
|
||||||
|
//!
|
||||||
|
//! IDL interfaces marked as `weakReferenceable` in `Bindings.conf`
|
||||||
|
//! automatically implement the `WeakReferenceable` trait in codegen.
|
||||||
|
//! The instance object is responsible for setting `None` in its own
|
||||||
|
//! own `WeakBox` when it is collected, through the `DOM_WEAK_SLOT`
|
||||||
|
//! slot. When all associated `WeakRef` values are dropped, the
|
||||||
|
//! `WeakBox` itself is dropped too.
|
||||||
|
|
||||||
|
use core::nonzero::NonZero;
|
||||||
|
use dom::bindings::js::Root;
|
||||||
|
use dom::bindings::reflector::Reflectable;
|
||||||
|
use dom::bindings::trace::JSTraceable;
|
||||||
|
use js::jsapi::{JSTracer, JS_GetReservedSlot, JS_SetReservedSlot};
|
||||||
|
use js::jsval::PrivateValue;
|
||||||
|
use libc::c_void;
|
||||||
|
use std::cell::{Cell, UnsafeCell};
|
||||||
|
use std::mem;
|
||||||
|
use util::mem::HeapSizeOf;
|
||||||
|
|
||||||
|
/// The index of the slot wherein a pointer to the weak holder cell is
|
||||||
|
/// stored for weak-referenceable bindings. We use slot 1 for holding it,
|
||||||
|
/// this is unsafe for globals, we disallow weak-referenceable globals
|
||||||
|
/// directly in codegen.
|
||||||
|
pub const DOM_WEAK_SLOT: u32 = 1;
|
||||||
|
|
||||||
|
/// A weak reference to a JS-managed DOM object.
|
||||||
|
#[allow_unrooted_interior]
|
||||||
|
pub struct WeakRef<T: WeakReferenceable> {
|
||||||
|
ptr: NonZero<*mut WeakBox<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The inner box of weak references, public for the finalization in codegen.
|
||||||
|
#[must_root]
|
||||||
|
pub struct WeakBox<T: WeakReferenceable> {
|
||||||
|
/// The reference count. When it reaches zero, the `value` field should
|
||||||
|
/// have already been set to `None`. The pointee contributes one to the count.
|
||||||
|
pub count: Cell<usize>,
|
||||||
|
/// The pointer to the JS-managed object, set to None when it is collected.
|
||||||
|
pub value: Cell<Option<NonZero<*const T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait implemented by weak-referenceable interfaces.
|
||||||
|
pub trait WeakReferenceable: Reflectable + Sized {
|
||||||
|
/// Downgrade a DOM object reference to a weak one.
|
||||||
|
fn downgrade(&self) -> WeakRef<Self> {
|
||||||
|
unsafe {
|
||||||
|
let object = self.reflector().get_jsobject().get();
|
||||||
|
let mut ptr =
|
||||||
|
JS_GetReservedSlot(object, DOM_WEAK_SLOT).to_private() as *mut WeakBox<Self>;
|
||||||
|
if ptr.is_null() {
|
||||||
|
debug!("Creating new WeakBox holder for {:p}.", self);
|
||||||
|
ptr = Box::into_raw(box WeakBox {
|
||||||
|
count: Cell::new(1),
|
||||||
|
value: Cell::new(Some(NonZero::new(self))),
|
||||||
|
});
|
||||||
|
JS_SetReservedSlot(object, DOM_WEAK_SLOT, PrivateValue(ptr as *const c_void));
|
||||||
|
}
|
||||||
|
let box_ = &*ptr;
|
||||||
|
assert!(box_.value.get().is_some());
|
||||||
|
let new_count = box_.count.get() + 1;
|
||||||
|
debug!("Incrementing WeakBox refcount for {:p} to {}.", self, new_count);
|
||||||
|
box_.count.set(new_count);
|
||||||
|
WeakRef { ptr: NonZero::new(ptr) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WeakReferenceable> WeakRef<T> {
|
||||||
|
/// Create a new weak reference from a `WeakReferenceable` interface instance.
|
||||||
|
/// This is just a convenience wrapper around `<T as WeakReferenceable>::downgrade`
|
||||||
|
/// to not have to import `WeakReferenceable`.
|
||||||
|
pub fn new(value: &T) -> Self {
|
||||||
|
value.downgrade()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Root a weak reference. Returns `None` if the object was already collected.
|
||||||
|
pub fn root(&self) -> Option<Root<T>> {
|
||||||
|
unsafe { &**self.ptr }.value.get().map(Root::new)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return whether the weakly-referenced object is still alive.
|
||||||
|
pub fn is_alive(&self) -> bool {
|
||||||
|
unsafe { &**self.ptr }.value.get().is_some()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WeakReferenceable> Clone for WeakRef<T> {
|
||||||
|
fn clone(&self) -> WeakRef<T> {
|
||||||
|
unsafe {
|
||||||
|
let box_ = &**self.ptr;
|
||||||
|
let new_count = box_.count.get() + 1;
|
||||||
|
box_.count.set(new_count);
|
||||||
|
WeakRef { ptr: self.ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WeakReferenceable> HeapSizeOf for WeakRef<T> {
|
||||||
|
fn heap_size_of_children(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
no_jsmanaged_fields!(WeakRef<T: WeakReferenceable>);
|
||||||
|
|
||||||
|
impl<T: WeakReferenceable> Drop for WeakRef<T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
let (count, value) = {
|
||||||
|
let weak_box = &**self.ptr;
|
||||||
|
assert!(weak_box.count.get() > 0);
|
||||||
|
let count = weak_box.count.get() - 1;
|
||||||
|
weak_box.count.set(count);
|
||||||
|
(count, weak_box.value.get())
|
||||||
|
};
|
||||||
|
if count == 0 {
|
||||||
|
assert!(value.is_none());
|
||||||
|
mem::drop(Box::from_raw(*self.ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A mutable weak reference to a JS-managed DOM object. On tracing,
|
||||||
|
/// the contained weak reference is dropped if the pointee was already
|
||||||
|
/// collected.
|
||||||
|
pub struct MutableWeakRef<T: WeakReferenceable> {
|
||||||
|
cell: UnsafeCell<Option<WeakRef<T>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WeakReferenceable> MutableWeakRef<T> {
|
||||||
|
/// Create a new mutable weak reference.
|
||||||
|
pub fn new(value: Option<&T>) -> MutableWeakRef<T> {
|
||||||
|
MutableWeakRef {
|
||||||
|
cell: UnsafeCell::new(value.map(WeakRef::new)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the pointee of a mutable weak reference.
|
||||||
|
pub fn set(&self, value: Option<&T>) {
|
||||||
|
unsafe { *self.cell.get() = value.map(WeakRef::new); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Root a mutable weak reference. Returns `None` if the object
|
||||||
|
/// was already collected.
|
||||||
|
pub fn root(&self) -> Option<Root<T>> {
|
||||||
|
unsafe { &*self.cell.get() }.as_ref().and_then(WeakRef::root)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WeakReferenceable> HeapSizeOf for MutableWeakRef<T> {
|
||||||
|
fn heap_size_of_children(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: WeakReferenceable> JSTraceable for MutableWeakRef<T> {
|
||||||
|
fn trace(&self, _: *mut JSTracer) {
|
||||||
|
let ptr = self.cell.get();
|
||||||
|
unsafe {
|
||||||
|
let should_drop = match *ptr {
|
||||||
|
Some(ref value) => !value.is_alive(),
|
||||||
|
None => false,
|
||||||
|
};
|
||||||
|
if should_drop {
|
||||||
|
mem::drop((*ptr).take().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,7 +15,9 @@ use dom::bindings::js::Root;
|
||||||
use dom::bindings::num::Finite;
|
use dom::bindings::num::Finite;
|
||||||
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
use dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||||||
use dom::bindings::str::{ByteString, USVString};
|
use dom::bindings::str::{ByteString, USVString};
|
||||||
|
use dom::bindings::weakref::MutableWeakRef;
|
||||||
use dom::blob::Blob;
|
use dom::blob::Blob;
|
||||||
|
use dom::url::URL;
|
||||||
use js::jsapi::{HandleValue, JSContext, JSObject};
|
use js::jsapi::{HandleValue, JSContext, JSObject};
|
||||||
use js::jsval::{JSVal, NullValue};
|
use js::jsval::{JSVal, NullValue};
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
@ -26,12 +28,14 @@ use util::str::DOMString;
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct TestBinding {
|
pub struct TestBinding {
|
||||||
reflector_: Reflector,
|
reflector_: Reflector,
|
||||||
|
url: MutableWeakRef<URL>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestBinding {
|
impl TestBinding {
|
||||||
fn new_inherited() -> TestBinding {
|
fn new_inherited() -> TestBinding {
|
||||||
TestBinding {
|
TestBinding {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
|
url: MutableWeakRef::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,6 +146,12 @@ impl TestBindingMethods for TestBinding {
|
||||||
Some(Blob::new(global_root_from_reflector(self).r(), None, ""))
|
Some(Blob::new(global_root_from_reflector(self).r(), None, ""))
|
||||||
}
|
}
|
||||||
fn SetInterfaceAttributeNullable(&self, _: Option<&Blob>) {}
|
fn SetInterfaceAttributeNullable(&self, _: Option<&Blob>) {}
|
||||||
|
fn GetInterfaceAttributeWeak(&self) -> Option<Root<URL>> {
|
||||||
|
self.url.root()
|
||||||
|
}
|
||||||
|
fn SetInterfaceAttributeWeak(&self, url: Option<&URL>) {
|
||||||
|
self.url.set(url);
|
||||||
|
}
|
||||||
fn GetObjectAttributeNullable(&self, _: *mut JSContext) -> *mut JSObject { ptr::null_mut() }
|
fn GetObjectAttributeNullable(&self, _: *mut JSContext) -> *mut JSObject { ptr::null_mut() }
|
||||||
fn SetObjectAttributeNullable(&self, _: *mut JSContext, _: *mut JSObject) {}
|
fn SetObjectAttributeNullable(&self, _: *mut JSContext, _: *mut JSObject) {}
|
||||||
fn GetUnionAttributeNullable(&self) -> Option<HTMLElementOrLong> {
|
fn GetUnionAttributeNullable(&self) -> Option<HTMLElementOrLong> {
|
||||||
|
|
|
@ -113,6 +113,7 @@ interface TestBinding {
|
||||||
attribute ByteString? byteStringAttributeNullable;
|
attribute ByteString? byteStringAttributeNullable;
|
||||||
readonly attribute TestEnum? enumAttributeNullable;
|
readonly attribute TestEnum? enumAttributeNullable;
|
||||||
attribute Blob? interfaceAttributeNullable;
|
attribute Blob? interfaceAttributeNullable;
|
||||||
|
attribute URL? interfaceAttributeWeak;
|
||||||
attribute object? objectAttributeNullable;
|
attribute object? objectAttributeNullable;
|
||||||
attribute (HTMLElement or long)? unionAttributeNullable;
|
attribute (HTMLElement or long)? unionAttributeNullable;
|
||||||
attribute (Event or DOMString)? union2AttributeNullable;
|
attribute (Event or DOMString)? union2AttributeNullable;
|
||||||
|
|
|
@ -7,6 +7,10 @@
|
||||||
{
|
{
|
||||||
"path": "mozilla/prototypes.html",
|
"path": "mozilla/prototypes.html",
|
||||||
"url": "/_mozilla/mozilla/prototypes.html"
|
"url": "/_mozilla/mozilla/prototypes.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "mozilla/weakref.html",
|
||||||
|
"url": "/_mozilla/mozilla/weakref.html"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"wdspec": []
|
"wdspec": []
|
||||||
|
|
28
tests/wpt/mozilla/tests/mozilla/weakref.html
Normal file
28
tests/wpt/mozilla/tests/mozilla/weakref.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
</head>
|
||||||
|
<script>
|
||||||
|
test(function() {
|
||||||
|
// We don't use assert_equals() etc here because it somehow
|
||||||
|
// keeps hold of its passed arguments, and thus the weak
|
||||||
|
// reference is never freed before the end of the test.
|
||||||
|
|
||||||
|
var t = new TestBinding;
|
||||||
|
assert_true(t.interfaceAttributeWeak === null);
|
||||||
|
|
||||||
|
var url = new URL("http://blog.servo.org/");
|
||||||
|
t.interfaceAttributeWeak = url;
|
||||||
|
assert_true(t.interfaceAttributeWeak !== null);
|
||||||
|
|
||||||
|
gc();
|
||||||
|
assert_true(t.interfaceAttributeWeak !== null);
|
||||||
|
|
||||||
|
url = null;
|
||||||
|
gc();
|
||||||
|
|
||||||
|
assert_true(t.interfaceAttributeWeak === null);
|
||||||
|
}, "Weak references work");
|
||||||
|
</script>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue