Bug 820846 - Modify the ListBase IC to work with [OverrideBuiltins] bindings, codegen changes and make HTMLDocument OverrideBuiltins. r=bz.

--HG--
extra : rebase_source : 12dbaff8dd2acfeee87bdfa738a013344cb1977a
This commit is contained in:
Peter Van der Beken 2013-04-20 18:04:09 +02:00
parent ddd94e0693
commit 97c5edb4da
10 changed files with 184 additions and 79 deletions

View File

@ -437,6 +437,14 @@ nsIdentifierMapEntry::RemoveNameElement(Element* aElement)
}
}
bool
nsIdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty()
{
Element* idElement = GetIdElement();
return idElement &&
nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
}
// static
size_t
nsIdentifierMapEntry::SizeOfExcludingThis(nsIdentifierMapEntry* aEntry,
@ -1784,6 +1792,9 @@ CustomPrototypeTrace(const nsAString& aName, JSObject* aObject, void *aArg)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
CustomPrototypeTraceArgs customPrototypeArgs = { aCallback, aClosure };
tmp->mCustomPrototypes.EnumerateRead(CustomPrototypeTrace, &customPrototypeArgs);
if (tmp->PreservingWrapper()) {
NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
}
nsINode::Trace(tmp, aCallback, aClosure);
NS_IMPL_CYCLE_COLLECTION_TRACE_END
@ -1848,6 +1859,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
// else, and not unlink an awful lot here.
tmp->mIdentifierMap.Clear();
++tmp->mExpandoAndGeneration.generation;
tmp->mExpandoAndGeneration.expando = JS::UndefinedValue();
tmp->mCustomPrototypes.Clear();
@ -2725,11 +2738,19 @@ nsIDocument::GetLastModified(nsAString& aLastModified) const
void
nsDocument::AddToNameTable(Element *aElement, nsIAtom* aName)
{
MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
"Only put elements that need to be exposed as document['name'] in "
"the named table.");
nsIdentifierMapEntry *entry =
mIdentifierMap.PutEntry(nsDependentAtomString(aName));
// Null for out-of-memory
if (entry) {
if (!entry->HasNameElement() &&
!entry->HasIdElementExposedAsHTMLDocumentProperty()) {
++mExpandoAndGeneration.generation;
}
entry->AddNameElement(this, aElement);
}
}
@ -2747,6 +2768,10 @@ nsDocument::RemoveFromNameTable(Element *aElement, nsIAtom* aName)
return;
entry->RemoveNameElement(aElement);
if (!entry->HasNameElement() &&
!entry->HasIdElementExposedAsHTMLDocumentProperty()) {
++mExpandoAndGeneration.generation;
}
}
void
@ -2756,6 +2781,11 @@ nsDocument::AddToIdTable(Element *aElement, nsIAtom* aId)
mIdentifierMap.PutEntry(nsDependentAtomString(aId));
if (entry) { /* True except on OOM */
if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
!entry->HasNameElement() &&
!entry->HasIdElementExposedAsHTMLDocumentProperty()) {
++mExpandoAndGeneration.generation;
}
entry->AddIdElement(aElement);
}
}
@ -2776,6 +2806,11 @@ nsDocument::RemoveFromIdTable(Element *aElement, nsIAtom* aId)
return;
entry->RemoveIdElement(aElement);
if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
!entry->HasNameElement() &&
!entry->HasIdElementExposedAsHTMLDocumentProperty()) {
++mExpandoAndGeneration.generation;
}
if (entry->IsEmpty()) {
mIdentifierMap.RawRemoveEntry(entry);
}
@ -8122,6 +8157,7 @@ nsDocument::DestroyElementMaps()
#endif
mStyledLinks.Clear();
mIdentifierMap.Clear();
++mExpandoAndGeneration.generation;
}
static

View File

@ -178,6 +178,7 @@ public:
* GetIdElement(true) if non-null.
*/
void SetImageElement(Element* aElement);
bool HasIdElementExposedAsHTMLDocumentProperty();
bool HasContentChangeCallback() { return mChangeCallbacks != nullptr; }
void AddContentChangeCallback(nsIDocument::IDTargetObserver aCallback,
@ -1139,6 +1140,9 @@ public:
virtual void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv);
static void XPCOMShutdown();
js::ExpandoAndGeneration mExpandoAndGeneration;
protected:
already_AddRefed<nsIPresShell> doCreateShell(nsPresContext* aContext,
nsViewManager* aViewManager,

View File

@ -1047,16 +1047,6 @@ nsGenericHTMLElement::GetBaseTarget(nsAString& aBaseTarget) const
//----------------------------------------------------------------------
static bool
CanHaveName(nsIAtom* aTag)
{
return aTag == nsGkAtoms::img ||
aTag == nsGkAtoms::form ||
aTag == nsGkAtoms::applet ||
aTag == nsGkAtoms::embed ||
aTag == nsGkAtoms::object;
}
bool
nsGenericHTMLElement::ParseAttribute(int32_t aNamespaceID,
nsIAtom* aAttribute,

View File

@ -717,6 +717,20 @@ public:
static bool TouchEventsEnabled(JSContext* /* unused */, JSObject* /* unused */);
static inline bool
CanHaveName(nsIAtom* aTag)
{
return aTag == nsGkAtoms::img ||
aTag == nsGkAtoms::form ||
aTag == nsGkAtoms::applet ||
aTag == nsGkAtoms::embed ||
aTag == nsGkAtoms::object;
}
static inline bool
ShouldExposeNameAsHTMLDocumentProperty(Element* aElement)
{
return aElement->IsHTML() && CanHaveName(aElement->Tag());
}
static inline bool
ShouldExposeIdAsHTMLDocumentProperty(Element* aElement)
{

View File

@ -2385,10 +2385,8 @@ static PLDHashOperator
IdentifierMapEntryAddNames(nsIdentifierMapEntry* aEntry, void* aArg)
{
nsTArray<nsString>* aNames = static_cast<nsTArray<nsString>*>(aArg);
Element* idElement;
if (aEntry->HasNameElement() ||
((idElement = aEntry->GetIdElement()) &&
nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement))) {
aEntry->HasIdElementExposedAsHTMLDocumentProperty()) {
aNames->AppendElement(aEntry->GetKey());
}
return PL_DHASH_NEXT;

View File

@ -995,7 +995,10 @@ def finalizeHook(descriptor, hookName, context):
finalize = "self->%s(%s);" % (hookName, context)
else:
finalize = "JSBindingFinalized<%s>::Finalized(self);\n" % descriptor.nativeType
finalize += "ClearWrapper(self, self);\n" if descriptor.wrapperCache else ""
if descriptor.wrapperCache:
finalize += "ClearWrapper(self, self);\n"
if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n"
if descriptor.workers:
finalize += "self->Release();"
elif descriptor.nativeOwnership == 'nsisupports':
@ -2006,6 +2009,11 @@ def CreateBindingJSObject(descriptor, properties, parent):
return NULL;
}
"""
if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
create += """ js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO,
JS::PrivateValue(&aObject->mExpandoAndGeneration));
"""
else:
create = """ obj = JS_NewObject(aCx, &Class.mBase, proto, %s);
@ -6234,9 +6242,7 @@ class CGResolveOwnProperty(CGAbstractMethod):
]
CGAbstractMethod.__init__(self, descriptor, "ResolveOwnProperty", "bool", args)
def definition_body(self):
return """ // We rely on getOwnPropertyDescriptor not shadowing prototype properties by named
// properties. If that changes we'll need to filter here.
return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc, flags);
return """ return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc, flags);
"""
class CGEnumerateOwnProperties(CGAbstractMethod):
@ -6247,9 +6253,7 @@ class CGEnumerateOwnProperties(CGAbstractMethod):
Argument('JS::AutoIdVector&', 'props')]
CGAbstractMethod.__init__(self, descriptor, "EnumerateOwnProperties", "bool", args)
def definition_body(self):
return """ // We rely on getOwnPropertyNames not shadowing prototype properties by named
// properties. If that changes we'll need to filter here.
return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);
return """ return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);
"""
class CGPrototypeTraitsClass(CGClass):
@ -6584,10 +6588,10 @@ MOZ_ASSERT_IF(desc->obj, desc->obj == ${holder});"""
fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
'obj': 'proxy', 'successCode': fillDescriptor}
# Once we start supporting OverrideBuiltins we need to make
# ResolveOwnProperty or EnumerateOwnProperties filter out named
# properties that shadow prototype properties.
condition = "!(flags & JSRESOLVE_ASSIGNING) && !HasPropertyOnPrototype(cx, proxy, this, id)"
condition = "!HasPropertyOnPrototype(cx, proxy, this, id)"
if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
condition = "(!isXray || %s)" % condition
condition = "!(flags & JSRESOLVE_ASSIGNING) && " + condition
if self.descriptor.supportsIndexedProperties():
condition = "!IsArrayIndex(index) && " + condition
namedGet = ("\n" +
@ -6745,14 +6749,16 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
# We always return above for an index id in the case when we support
# indexed properties, so we can just treat the id as a name
# unconditionally here.
delete += ("if (!HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
CGIndenter(CGGeneric(namedBody)).define() + "\n"
" if (found) {\n"
" return true;\n"
" }\n"
delete += (CGGeneric(namedBody).define() + "\n"
"if (found) {\n"
" return true;\n"
"}\n")
if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
delete = CGIfWrapper(CGGeneric(delete),
"!HasPropertyOnPrototype(cx, proxy, this, id)").define()
delete += """
delete += "return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);";
return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);"""
return delete
@ -6779,13 +6785,17 @@ for (int32_t i = 0; i < int32_t(length); ++i) {
addIndices = ""
if self.descriptor.supportsNamedProperties():
if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
shadow = "!isXray"
else:
shadow = "false"
addNames = """
nsTArray<nsString> names;
UnwrapProxy(proxy)->GetSupportedNames(names);
if (!AppendNamedPropertyIds(cx, proxy, names, props)) {
if (!AppendNamedPropertyIds(cx, proxy, names, %s, props)) {
return false;
}
"""
""" % shadow
else:
addNames = ""
@ -6842,14 +6852,15 @@ class CGDOMJSProxyHandler_hasOwn(ClassMethod):
if self.descriptor.supportsNamedProperties():
# If we support indexed properties we always return above for index
# property names, so no need to check for those here.
named = ("if (!HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
CGIndenter(CGProxyNamedPresenceChecker(self.descriptor)).define() + "\n" +
" *bp = found;\n"
" return true;\n"
"}\n" +
"\n")
named = (CGProxyNamedPresenceChecker(self.descriptor).define() + "\n" +
"*bp = found;\n")
if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
named = CGIfWrapper(CGGeneric(named + "return true;\n"),
"!HasPropertyOnPrototype(cx, proxy, this, id)").define()
named += ("\n"
"*bp = false;")
else:
named = ""
named = "*bp = false;"
return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
"Should not have a XrayWrapper here");
@ -6865,7 +6876,7 @@ if (expando) {
}
}
""" + named + """*bp = false;
""" + named + """
return true;"""
class CGDOMJSProxyHandler_get(ClassMethod):
@ -6915,35 +6926,39 @@ if (expando) {
} else {
%s
}
""" % (stripTrailingWhitespace(getUnforgeableOrExpando.replace('\n', '\n ')))
else:
getIndexedOrExpando = getUnforgeableOrExpando + "\n"
getIndexedOrExpando = getUnforgeableOrExpando + "\n\n"
if self.descriptor.supportsNamedProperties():
getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
if self.descriptor.supportsIndexedProperties():
getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
getNamed = getNamed.define() + "\n"
getNamed = getNamed.define() + "\n\n"
else:
getNamed = ""
getOnPrototype = """bool foundOnPrototype;
if (!GetPropertyOnPrototype(cx, proxy, id, &foundOnPrototype, vp.address())) {
return false;
}
if (foundOnPrototype) {
return true;
}
"""
if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
getNamed = getNamed + getOnPrototype
else:
getNamed = getOnPrototype + getNamed
return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
"Should not have a XrayWrapper here");
%s
{ // Scope for this "found" so it doesn't leak to things below
bool found;
if (!GetPropertyOnPrototype(cx, proxy, id, &found, vp.address())) {
return false;
}
if (found) {
return true;
}
}
%s
vp.setUndefined();
return true;""" % (getIndexedOrExpando, getNamed)
""" + getIndexedOrExpando + getNamed + """vp.setUndefined();
return true;"""
class CGDOMJSProxyHandler_className(ClassMethod):
def __init__(self, descriptor):

View File

@ -75,11 +75,25 @@ SetListBaseInformation gSetListBaseInformation;
JSObject*
DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
{
JSObject* expando = GetExpandoObject(obj);
XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
scope->RemoveDOMExpandoObject(obj);
js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
return expando;
MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
if (v.isUndefined()) {
return nullptr;
}
if (v.isObject()) {
js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, UndefinedValue());
return &v.toObject();
}
js::ExpandoAndGeneration* expandoAndGeneration =
static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
v = expandoAndGeneration->expando;
if (v.isUndefined()) {
return nullptr;
}
expandoAndGeneration->expando = UndefinedValue();
return &v.toObject();
}
// static
@ -87,25 +101,42 @@ JSObject*
DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
{
NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object");
JS::Rooted<JSObject*> expando(cx, GetExpandoObject(obj));
JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
if (v.isObject()) {
return &v.toObject();
}
js::ExpandoAndGeneration* expandoAndGeneration;
if (!v.isUndefined()) {
expandoAndGeneration = static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
if (expandoAndGeneration->expando.isObject()) {
return &expandoAndGeneration->expando.toObject();
}
} else {
expandoAndGeneration = nullptr;
}
JSObject* expando = JS_NewObjectWithGivenProto(cx, nullptr, nullptr,
js::GetObjectParent(obj));
if (!expando) {
expando = JS_NewObjectWithGivenProto(cx, nullptr, nullptr,
js::GetObjectParent(obj));
if (!expando) {
return NULL;
}
return nullptr;
}
XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
if (!scope->RegisterDOMExpandoObject(obj)) {
return NULL;
}
XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
if (!scope->RegisterDOMExpandoObject(obj)) {
return nullptr;
}
nsWrapperCache* cache;
CallQueryInterface(UnwrapDOMObject<nsISupports>(obj), &cache);
cache->SetPreservingWrapper(true);
nsWrapperCache* cache;
CallQueryInterface(UnwrapDOMObject<nsISupports>(obj), &cache);
cache->SetPreservingWrapper(true);
if (expandoAndGeneration) {
expandoAndGeneration->expando.setObject(*expando);
} else {
js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
}
return expando;
}
@ -234,6 +265,7 @@ bool
DOMProxyHandler::AppendNamedPropertyIds(JSContext* cx,
JS::Handle<JSObject*> proxy,
nsTArray<nsString>& names,
bool shadowPrototypeProperties,
JS::AutoIdVector& props)
{
for (uint32_t i = 0; i < names.Length(); ++i) {
@ -247,7 +279,8 @@ DOMProxyHandler::AppendNamedPropertyIds(JSContext* cx,
return false;
}
if (!HasPropertyOnPrototype(cx, proxy, this, id)) {
if (shadowPrototypeProperties ||
!HasPropertyOnPrototype(cx, proxy, this, id)) {
if (!props.append(id)) {
return false;
}

View File

@ -51,7 +51,18 @@ public:
{
MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
return v.isUndefined() ? NULL : v.toObjectOrNull();
if (v.isObject()) {
return &v.toObject();
}
if (v.isUndefined()) {
return nullptr;
}
js::ExpandoAndGeneration* expandoAndGeneration =
static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
v = expandoAndGeneration->expando;
return v.isUndefined() ? nullptr : &v.toObject();
}
static JSObject* GetAndClearExpandoObject(JSObject* obj);
static JSObject* EnsureExpandoObject(JSContext* cx,
@ -60,10 +71,12 @@ public:
const DOMClass& mClass;
protected:
// Append the property names in "names" that don't live on our proto
// chain to "props"
// Append the property names in "names" to "props". If
// shadowPrototypeProperties is false then skip properties that are also
// present on our proto chain.
bool AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
nsTArray<nsString>& names,
bool shadowPrototypeProperties,
JS::AutoIdVector& props);
};

View File

@ -903,7 +903,8 @@ class IDLInterface(IDLObjectWithScope):
identifier == "NeedNewResolve" or
identifier == "JSImplementation" or
identifier == "HeaderFile" or
identifier == "NavigatorProperty"):
identifier == "NavigatorProperty" or
identifier == "OverrideBuiltins"):
# Known attributes that we don't need to do anything with here
pass
else:

View File

@ -6,6 +6,7 @@
interface Selection;
[OverrideBuiltins]
interface HTMLDocument : Document {
[Throws]
attribute DOMString? domain;