Bug 895758. Make the global scope polluter a proxy. r=bzbarsky

This commit is contained in:
Peter Van der Beken 2013-07-09 10:45:13 -04:00
parent 6595b4e5da
commit ac70c03afa
17 changed files with 376 additions and 320 deletions

View File

@ -0,0 +1,181 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et tw=78: */
/* 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/. */
#include "WindowNamedPropertiesHandler.h"
#include "nsDOMClassInfo.h"
#include "nsGlobalWindow.h"
#include "nsHTMLDocument.h"
#include "nsJSUtils.h"
#include "xpcprivate.h"
namespace mozilla {
namespace dom {
bool
WindowNamedPropertiesHandler::getOwnPropertyDescriptor(JSContext* aCx,
JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId,
JS::MutableHandle<JSPropertyDescriptor> aDesc,
unsigned aFlags)
{
if (!JSID_IS_STRING(aId)) {
// Nothing to do if we're resolving a non-string property.
return true;
}
JSObject* global = JS_GetGlobalForObject(aCx, aProxy);
nsresult rv =
nsDOMClassInfo::ScriptSecurityManager()->CheckPropertyAccess(aCx, global,
"Window", aId,
nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
if (NS_FAILED(rv)) {
// The security check failed. The security manager set a JS exception for
// us.
return false;
}
if (HasPropertyOnPrototype(aCx, aProxy, aId)) {
return true;
}
nsDependentJSString str(aId);
// Grab the DOM window.
XPCWrappedNative* wrapper = XPCWrappedNative::Get(global);
nsCOMPtr<nsPIDOMWindow> piWin = do_QueryWrappedNative(wrapper);
MOZ_ASSERT(piWin);
nsGlobalWindow* win = static_cast<nsGlobalWindow*>(piWin.get());
if (win->GetLength() > 0) {
nsCOMPtr<nsIDOMWindow> childWin = win->GetChildWindow(str);
if (childWin) {
// We found a subframe of the right name. Shadowing via |var foo| in
// global scope is still allowed, since |var| only looks up |own|
// properties. But unqualified shadowing will fail, per-spec.
JS::Rooted<JS::Value> v(aCx);
if (!WrapObject(aCx, aProxy, childWin, &v)) {
return false;
}
aDesc.object().set(aProxy);
aDesc.value().set(v);
aDesc.setAttributes(JSPROP_ENUMERATE);
return true;
}
}
// The rest of this function is for HTML documents only.
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(win->GetExtantDoc());
if (!htmlDoc) {
return true;
}
nsHTMLDocument *document = static_cast<nsHTMLDocument*>(htmlDoc.get());
Element *element = document->GetElementById(str);
if (element) {
JS::Rooted<JS::Value> v(aCx);
if (!WrapObject(aCx, aProxy, element, &v)) {
return false;
}
aDesc.object().set(aProxy);
aDesc.value().set(v);
aDesc.setAttributes(JSPROP_ENUMERATE);
return true;
}
nsWrapperCache* cache;
nsISupports* result = document->ResolveName(str, &cache);
if (!result) {
return true;
}
JS::Rooted<JS::Value> v(aCx);
if (!WrapObject(aCx, aProxy, result, cache, nullptr, &v)) {
return false;
}
aDesc.object().set(aProxy);
aDesc.value().set(v);
aDesc.setAttributes(JSPROP_ENUMERATE);
return true;
}
bool
WindowNamedPropertiesHandler::defineProperty(JSContext* aCx,
JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId,
JS::MutableHandle<JSPropertyDescriptor> aDesc)
{
ErrorResult rv;
rv.ThrowTypeError(MSG_DEFINEPROPERTY_ON_GSP);
rv.ReportTypeError(aCx);
return false;
}
bool
WindowNamedPropertiesHandler::getOwnPropertyNames(JSContext *aCx,
JS::Handle<JSObject*> aProxy,
JS::AutoIdVector& aProps)
{
// Grab the DOM window.
JSObject* global = JS_GetGlobalForObject(aCx, aProxy);
XPCWrappedNative* wrapper = XPCWrappedNative::Get(global);
nsCOMPtr<nsPIDOMWindow> piWin = do_QueryWrappedNative(wrapper);
MOZ_ASSERT(piWin);
nsGlobalWindow* win = static_cast<nsGlobalWindow*>(piWin.get());
nsTArray<nsString> names;
win->GetSupportedNames(names);
if (!AppendNamedPropertyIds(aCx, aProxy, names, false, aProps)) {
return false;
}
names.Clear();
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(win->GetExtantDoc());
if (!htmlDoc) {
return true;
}
nsHTMLDocument *document = static_cast<nsHTMLDocument*>(htmlDoc.get());
document->GetSupportedNames(names);
JS::AutoIdVector docProps(aCx);
if (!AppendNamedPropertyIds(aCx, aProxy, names, false, docProps)) {
return false;
}
return js::AppendUnique(aCx, aProps, docProps);
}
bool
WindowNamedPropertiesHandler::delete_(JSContext* aCx,
JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId, bool* aBp)
{
*aBp = false;
return true;
}
// static
void
WindowNamedPropertiesHandler::Install(JSContext *aCx,
JS::Handle<JSObject*> aProto)
{
JS::Rooted<JSObject*> protoProto(aCx);
if (!::JS_GetPrototype(aCx, aProto, &protoProto)) {
return;
}
JS::Rooted<JSObject*> gsp(aCx);
gsp = js::NewProxyObject(aCx, WindowNamedPropertiesHandler::getInstance(),
JS::NullHandleValue, protoProto,
js::GetGlobalForObjectCrossCompartment(aProto));
if (!gsp) {
return;
}
// And then set the prototype of the interface prototype object to be the
// global scope polluter.
::JS_SplicePrototype(aCx, aProto, gsp);
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,74 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et tw=78: */
/* 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/. */
#ifndef mozilla_dom_WindowNamedPropertiesHandler_h
#define mozilla_dom_WindowNamedPropertiesHandler_h__
#include "jsproxy.h"
#include "mozilla/dom/DOMJSProxyHandler.h"
namespace mozilla {
namespace dom {
class WindowNamedPropertiesHandler : public BaseDOMProxyHandler
{
public:
WindowNamedPropertiesHandler() : BaseDOMProxyHandler(nullptr)
{
setHasPrototype(true);
}
virtual bool
preventExtensions(JSContext* aCx, JS::Handle<JSObject*> aProxy) MOZ_OVERRIDE
{
// Throw a TypeError, per WebIDL.
JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
JSMSG_CANT_CHANGE_EXTENSIBILITY);
return false;
}
virtual bool
getOwnPropertyDescriptor(JSContext* aCx, JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId,
JS::MutableHandle<JSPropertyDescriptor> aDesc,
unsigned aFlags) MOZ_OVERRIDE;
virtual bool
defineProperty(JSContext* aCx, JS::Handle<JSObject*> aProxy,
JS::Handle<jsid> aId,
JS::MutableHandle<JSPropertyDescriptor> aDesc) MOZ_OVERRIDE;
virtual bool
getOwnPropertyNames(JSContext* aCx, JS::Handle<JSObject*> aProxy,
JS::AutoIdVector& aProps) MOZ_OVERRIDE;
virtual bool
delete_(JSContext* aCx, JS::Handle<JSObject*> aProxy, JS::Handle<jsid> aId,
bool* aBp) MOZ_OVERRIDE;
virtual bool
isExtensible(JSContext* aCx, JS::Handle<JSObject*> aProxy,
bool* aIsExtensible) MOZ_OVERRIDE
{
*aIsExtensible = true;
return true;
}
virtual const char*
className(JSContext *aCx, JS::Handle<JSObject*> aProxy) MOZ_OVERRIDE
{
return "WindowProperties";
}
static WindowNamedPropertiesHandler*
getInstance()
{
static WindowNamedPropertiesHandler instance;
return &instance;
}
// For Install, aProto is the proto of the Window we're associated with.
static void
Install(JSContext *aCx, JS::Handle<JSObject*> aProto);
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_WindowNamedPropertiesHandler_h */

View File

@ -98,6 +98,7 @@ CPP_SOURCES += [
'nsWindowMemoryReporter.cpp',
'nsWindowRoot.cpp',
'nsWrapperCache.cpp',
'WindowNamedPropertiesHandler.cpp',
]
EXTRA_COMPONENTS += [

View File

@ -251,6 +251,7 @@ using mozilla::dom::workers::ResolveWorkerClasses;
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/Likely.h"
#include "WindowNamedPropertiesHandler.h"
#ifdef MOZ_TIME_MANAGER
#include "TimeManager.h"
@ -2316,70 +2317,17 @@ nsWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx,
return SetParentToWindow(win, parentObj);
}
static JSClass sGlobalScopePolluterClass = {
"Global Scope Polluter",
JSCLASS_NEW_RESOLVE,
JS_PropertyStub,
JS_DeletePropertyStub,
nsWindowSH::GlobalScopePolluterGetProperty,
JS_StrictPropertyStub,
JS_EnumerateStub,
(JSResolveOp)nsWindowSH::GlobalScopePolluterNewResolve,
JS_ConvertStub,
nullptr
};
// static
bool
nsWindowSH::GlobalScopePolluterGetProperty(JSContext *cx, JS::Handle<JSObject*> obj,
JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp)
NS_IMETHODIMP
nsWindowSH::PostCreatePrototype(JSContext* aCx, JSObject* aProto)
{
// Someone is accessing a element by referencing its name/id in the
// global scope, do a security check to make sure that's ok.
nsresult rv = nsDOMClassInfo::PostCreatePrototype(aCx, aProto);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv =
sSecMan->CheckPropertyAccess(cx, ::JS_GetGlobalForObject(cx, obj),
"Window", id,
nsIXPCSecurityManager::ACCESS_GET_PROPERTY);
if (NS_FAILED(rv)) {
// The security check failed. The security manager set a JS
// exception for us.
return false;
}
return true;
}
// Gets a subframe.
static bool
ChildWindowGetter(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
JS::MutableHandle<JS::Value> vp)
{
MOZ_ASSERT(JSID_IS_STRING(id));
// Grab the native DOM window.
vp.setUndefined();
nsCOMPtr<nsISupports> winSupports =
do_QueryInterface(nsDOMClassInfo::XPConnect()->GetNativeOfWrapper(cx, obj));
if (!winSupports)
return true;
nsGlobalWindow *win = nsGlobalWindow::FromSupports(winSupports);
// Find the child, if it exists.
nsDependentJSString name(id);
nsCOMPtr<nsIDOMWindow> child = win->GetChildWindow(name);
if (!child)
return true;
// Wrap the child for JS.
JS::Rooted<JS::Value> v(cx);
nsresult rv = WrapNative(cx, JS::CurrentGlobalOrNull(cx), child,
/* aAllowWrapping = */ true, v.address());
NS_ENSURE_SUCCESS(rv, false);
vp.set(v);
return true;
// We should probably move this into the CreateInterfaceObjects for Window
// once it is on WebIDL bindings.
JS::Rooted<JSObject*> proto(aCx, aProto);
WindowNamedPropertiesHandler::Install(aCx, proto);
return NS_OK;
}
static nsHTMLDocument*
@ -2390,177 +2338,6 @@ GetDocument(JSObject *obj)
static_cast<nsINode*>(JS_GetPrivate(obj)));
}
// static
bool
nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JS::Handle<JSObject*> obj,
JS::Handle<jsid> id, unsigned flags,
JS::MutableHandle<JSObject*> objp)
{
if (!JSID_IS_STRING(id)) {
// Nothing to do if we're resolving a non-string property.
return true;
}
// Crash reports from the wild seem to get here during shutdown when there's
// no more XPConnect singleton.
nsIXPConnect *xpc = XPConnect();
NS_ENSURE_TRUE(xpc, true);
// Grab the DOM window.
JSObject *global = JS_GetGlobalForObject(cx, obj);
nsISupports *globalNative = xpc->GetNativeOfWrapper(cx, global);
nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(globalNative);
MOZ_ASSERT(piWin);
nsGlobalWindow* win = static_cast<nsGlobalWindow*>(piWin.get());
if (win->GetLength() > 0) {
nsDependentJSString name(id);
nsCOMPtr<nsIDOMWindow> child_win = win->GetChildWindow(name);
if (child_win) {
// We found a subframe of the right name, so define the property
// on the GSP. This property is a read-only accessor. Shadowing via
// |var foo| in global scope is still allowed, since |var| only looks
// up |own| properties. But unqualified shadowing will fail, per-spec.
if (!JS_DefinePropertyById(cx, obj, id, JS::UndefinedValue(),
ChildWindowGetter, JS_StrictPropertyStub,
JSPROP_SHARED | JSPROP_ENUMERATE))
{
return false;
}
objp.set(obj);
return true;
}
}
JS::Rooted<JSObject*> proto(cx);
if (!::JS_GetPrototype(cx, obj, &proto)) {
return false;
}
bool hasProp;
if (!proto || !::JS_HasPropertyById(cx, proto, id, &hasProp) ||
hasProp) {
// No prototype, or the property exists on the prototype. Do
// nothing.
return true;
}
//
// The rest of this function is for HTML documents only.
//
nsCOMPtr<nsIHTMLDocument> htmlDoc =
do_QueryInterface(win->GetExtantDoc());
if (!htmlDoc)
return true;
nsHTMLDocument *document = static_cast<nsHTMLDocument*>(htmlDoc.get());
nsDependentJSString str(id);
nsCOMPtr<nsISupports> result;
nsWrapperCache *cache;
{
Element *element = document->GetElementById(str);
result = element;
cache = element;
}
if (!result) {
result = document->ResolveName(str, &cache);
}
if (result) {
JS::Rooted<JS::Value> v(cx);
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
nsresult rv = WrapNative(cx, obj, result, cache, true, v.address(),
getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, false);
if (!JS_WrapValue(cx, v.address()) ||
!JS_DefinePropertyById(cx, obj, id, v, JS_PropertyStub, JS_StrictPropertyStub, 0)) {
return false;
}
objp.set(obj);
}
return true;
}
// static
bool
nsWindowSH::InvalidateGlobalScopePolluter(JSContext *cx,
JS::Handle<JSObject*> aObj)
{
JS::Rooted<JSObject*> proto(cx);
JS::Rooted<JSObject*> obj(cx, aObj);
for (;;) {
if (!::JS_GetPrototype(cx, obj, &proto)) {
return false;
}
if (!proto) {
break;
}
if (JS_GetClass(proto) == &sGlobalScopePolluterClass) {
JS::Rooted<JSObject*> proto_proto(cx);
if (!::JS_GetPrototype(cx, proto, &proto_proto)) {
return false;
}
// Pull the global scope polluter out of the prototype chain so
// that it can be freed.
::JS_SplicePrototype(cx, obj, proto_proto);
break;
}
obj = proto;
}
return true;
}
// static
nsresult
nsWindowSH::InstallGlobalScopePolluter(JSContext *cx, JS::Handle<JSObject*> obj)
{
JS::Rooted<JSObject*> gsp(cx, ::JS_NewObjectWithUniqueType(cx, &sGlobalScopePolluterClass, nullptr, obj));
if (!gsp) {
return NS_ERROR_OUT_OF_MEMORY;
}
JS::Rooted<JSObject*> o(cx, obj), proto(cx);
// Find the place in the prototype chain where we want this global
// scope polluter (right before Object.prototype).
for (;;) {
if (!::JS_GetPrototype(cx, o, &proto)) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!proto) {
break;
}
if (JS_GetClass(proto) == sObjectClass) {
// Set the global scope polluters prototype to Object.prototype
::JS_SplicePrototype(cx, gsp, proto);
break;
}
o = proto;
}
// And then set the prototype of the object whose prototype was
// Object.prototype to be the global scope polluter.
::JS_SplicePrototype(cx, o, gsp);
return NS_OK;
}
struct ResolveGlobalNameClosure
{
JSContext* cx;

View File

@ -136,6 +136,10 @@ public:
{
return sXPConnect;
}
static nsIScriptSecurityManager *ScriptSecurityManager()
{
return sSecMan;
}
protected:
friend nsIClassInfo* NS_GetDOMClassInfoInstance(nsDOMClassInfoID aID);
@ -307,6 +311,7 @@ protected:
public:
NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
JSObject *globalObj, JSObject **parentObj) MOZ_OVERRIDE;
NS_IMETHOD PostCreatePrototype(JSContext * cx, JSObject * proto) MOZ_OVERRIDE;
#ifdef DEBUG
NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
JSObject *obj) MOZ_OVERRIDE

View File

@ -86,14 +86,9 @@ nsDOMWindowList::GetLength(uint32_t* aLength)
already_AddRefed<nsIDOMWindow>
nsDOMWindowList::IndexedGetter(uint32_t aIndex, bool& aFound)
{
EnsureFresh();
aFound = false;
NS_ENSURE_TRUE(mDocShellNode, nullptr);
nsCOMPtr<nsIDocShellTreeItem> item;
mDocShellNode->GetChildAt(aIndex, getter_AddRefs(item));
nsCOMPtr<nsIDocShellTreeItem> item = GetDocShellTreeItemAt(aIndex);
if (!item) {
return nullptr;
}

View File

@ -10,6 +10,7 @@
#include "nsIDOMWindowCollection.h"
#include "nsString.h"
#include <stdint.h>
#include "nsIDocShellTreeItem.h"
class nsIDocShellTreeNode;
class nsIDocShell;
@ -29,6 +30,15 @@ public:
//local methods
NS_IMETHOD SetDocShell(nsIDocShell* aDocShell);
already_AddRefed<nsIDocShellTreeItem> GetDocShellTreeItemAt(uint32_t aIndex)
{
EnsureFresh();
nsCOMPtr<nsIDocShellTreeItem> item;
if (mDocShellNode) {
mDocShellNode->GetChildAt(aIndex, getter_AddRefs(item));
}
return item.forget();
}
protected:
// Note: this function may flush and cause mDocShellNode to become null.

View File

@ -2279,9 +2279,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
if (aDocument != oldDoc) {
JS::Rooted<JSObject*> obj(cx, currentInner->mJSObject);
xpc_UnmarkGrayObject(obj);
if (!nsWindowSH::InvalidateGlobalScopePolluter(cx, obj)) {
return NS_ERROR_FAILURE;
}
}
// We're reusing the inner window, but this still counts as a navigation,
@ -2499,22 +2496,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
// Add an extra ref in case we release mContext during GC.
nsCOMPtr<nsIScriptContext> kungFuDeathGrip(mContext);
// Now that the prototype is all set up, install the global scope
// polluter. This must happen after the above prototype fixup. If
// the GSP was to be installed on the inner window's real
// prototype (as it would be if this was done before the prototype
// fixup above) we would end up holding the GSP alive (through
// XPConnect's internal marking of wrapper prototypes) as long as
// the inner window was around, and if the GSP had properties on
// it that held an element alive we'd hold the document alive,
// which could hold event handlers alive, which hold the context
// alive etc.
if ((!reUseInnerWindow || aDocument != oldDoc) && !aState) {
JS::Rooted<JSObject*> obj(cx, newInnerWindow->mJSObject);
nsWindowSH::InstallGlobalScopePolluter(cx, obj);
}
aDocument->SetScriptGlobalObject(newInnerWindow);
if (!aState) {
@ -3843,6 +3824,23 @@ nsGlobalWindow::IndexedGetter(uint32_t aIndex, bool& aFound)
return windows->IndexedGetter(aIndex, aFound);
}
void
nsGlobalWindow::GetSupportedNames(nsTArray<nsString>& aNames)
{
FORWARD_TO_OUTER_VOID(GetSupportedNames, (aNames));
nsDOMWindowList* windows = GetWindowList();
if (windows) {
uint32_t length = windows->GetLength();
nsString* name = aNames.AppendElements(length);
for (uint32_t i = 0; i < length; ++i, ++name) {
nsCOMPtr<nsIDocShellTreeItem> item =
windows->GetDocShellTreeItemAt(i);
item->GetName(*name);
}
}
}
NS_IMETHODIMP
nsGlobalWindow::GetApplicationCache(nsIDOMOfflineResourceList **aApplicationCache)
{

View File

@ -470,6 +470,8 @@ public:
uint32_t GetLength();
already_AddRefed<nsIDOMWindow> IndexedGetter(uint32_t aIndex, bool& aFound);
void GetSupportedNames(nsTArray<nsString>& aNames);
// Object Management
nsGlobalWindow(nsGlobalWindow *aOuterWindow);

View File

@ -1336,7 +1336,6 @@ GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
bool
HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
DOMProxyHandler* handler,
JS::Handle<jsid> id)
{
JS::Rooted<JSObject*> obj(cx, proxy);
@ -1345,11 +1344,38 @@ HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
obj = js::UncheckedUnwrap(obj);
ac.construct(cx, obj);
}
MOZ_ASSERT(js::IsProxy(obj) && js::GetProxyHandler(obj) == handler);
bool found;
// We ignore an error from GetPropertyOnPrototype.
return !GetPropertyOnPrototype(cx, obj, id, &found, NULL) || found;
// We ignore an error from GetPropertyOnPrototype. We pass nullptr
// for vp so that GetPropertyOnPrototype won't actually do a get.
return !GetPropertyOnPrototype(cx, obj, id, &found, nullptr) || found;
}
bool
AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
nsTArray<nsString>& names,
bool shadowPrototypeProperties,
JS::AutoIdVector& props)
{
for (uint32_t i = 0; i < names.Length(); ++i) {
JS::Rooted<JS::Value> v(cx);
if (!xpc::NonVoidStringToJsval(cx, names[i], v.address())) {
return false;
}
JS::Rooted<jsid> id(cx);
if (!JS_ValueToId(cx, v, id.address())) {
return false;
}
if (shadowPrototypeProperties || !HasPropertyOnPrototype(cx, proxy, id)) {
if (!props.append(id)) {
return false;
}
}
}
return true;
}
JSObject*

View File

@ -1417,6 +1417,8 @@ WantsQueryInterface<T, true>
bool
ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
// vp is allowed to be null; in that case no get will be attempted,
// and *found will simply indicate whether the property exists.
bool
GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id, bool* found,
@ -1424,9 +1426,18 @@ GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
bool
HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
DOMProxyHandler* handler,
JS::Handle<jsid> id);
// Append the property names in "names" to "props". If
// shadowPrototypeProperties is false then skip properties that are also
// present on the proto chain of proxy. If shadowPrototypeProperties is true,
// then the "proxy" argument is ignored.
bool
AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
nsTArray<nsString>& names,
bool shadowPrototypeProperties, JS::AutoIdVector& props);
template<class T>
class OwningNonNull
{

View File

@ -6981,7 +6981,7 @@ class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
"}\n"
'// OK to pass null as "proxy" because it\'s ignored if\n'
"// shadowPrototypeProperties is true\n"
"return DOMProxyHandler::AppendNamedPropertyIds(cx, JS::NullPtr(), names, true, nullptr, props);"))
"return AppendNamedPropertyIds(cx, JS::NullPtr(), names, true, props);"))
class CGPrototypeTraitsClass(CGClass):
def __init__(self, descriptor, indent=''):
@ -7343,7 +7343,7 @@ MOZ_ASSERT_IF(desc.object(), desc.object() == ${holder});"""
fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
templateValues = {'jsvalRef': 'desc.value()', 'jsvalHandle': 'desc.value()',
'obj': 'proxy', 'successCode': fillDescriptor}
condition = "!HasPropertyOnPrototype(cx, proxy, this, id)"
condition = "!HasPropertyOnPrototype(cx, proxy, id)"
if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
condition = "(!isXray || %s)" % condition
condition = "!(flags & JSRESOLVE_ASSIGNING) && " + condition
@ -7514,7 +7514,7 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
"}\n")
if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
delete = CGIfWrapper(CGGeneric(delete),
"!HasPropertyOnPrototype(cx, proxy, this, id)").define()
"!HasPropertyOnPrototype(cx, proxy, id)").define()
delete += """
return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);"""
@ -7551,7 +7551,7 @@ for (int32_t i = 0; i < int32_t(length); ++i) {
addNames = """
nsTArray<nsString> names;
UnwrapProxy(proxy)->GetSupportedNames(names);
if (!AppendNamedPropertyIds(cx, proxy, names, %s, this, props)) {
if (!AppendNamedPropertyIds(cx, proxy, names, %s, props)) {
return false;
}
""" % shadow
@ -7615,7 +7615,7 @@ class CGDOMJSProxyHandler_hasOwn(ClassMethod):
"*bp = found;\n")
if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
named = CGIfWrapper(CGGeneric(named + "return true;\n"),
"!HasPropertyOnPrototype(cx, proxy, this, id)").define()
"!HasPropertyOnPrototype(cx, proxy, id)").define()
named += ("\n"
"*bp = false;")
else:

View File

@ -160,8 +160,11 @@ DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy)
}
bool
DOMProxyHandler::getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
MutableHandle<JSPropertyDescriptor> desc, unsigned flags)
BaseDOMProxyHandler::getPropertyDescriptor(JSContext* cx,
JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id,
MutableHandle<JSPropertyDescriptor> desc,
unsigned flags)
{
if (!getOwnPropertyDescriptor(cx, proxy, id, desc, flags)) {
return false;
@ -221,7 +224,8 @@ DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
}
bool
DOMProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy, AutoIdVector& props)
BaseDOMProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
AutoIdVector& props)
{
JS::Rooted<JSObject*> proto(cx);
if (!JS_GetPrototype(cx, proxy, &proto)) {
@ -260,37 +264,6 @@ DOMProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid
return ok;
}
/* static */
bool
DOMProxyHandler::AppendNamedPropertyIds(JSContext* cx,
JS::Handle<JSObject*> proxy,
nsTArray<nsString>& names,
bool shadowPrototypeProperties,
DOMProxyHandler* handler,
JS::AutoIdVector& props)
{
for (uint32_t i = 0; i < names.Length(); ++i) {
JS::Rooted<JS::Value> v(cx);
if (!xpc::NonVoidStringToJsval(cx, names[i], v.address())) {
return false;
}
JS::Rooted<jsid> id(cx);
if (!JS_ValueToId(cx, v, id.address())) {
return false;
}
if (shadowPrototypeProperties ||
!HasPropertyOnPrototype(cx, proxy, handler, id)) {
if (!props.append(id)) {
return false;
}
}
}
return true;
}
int32_t
IdToInt32(JSContext* cx, JS::Handle<jsid> id)
{

View File

@ -27,18 +27,33 @@ enum {
template<typename T> struct Prefable;
class DOMProxyHandler : public js::BaseProxyHandler
class BaseDOMProxyHandler : public js::BaseProxyHandler
{
public:
BaseDOMProxyHandler(void* aProxyFamily)
: js::BaseProxyHandler(aProxyFamily)
{}
// Implementations of traps that can be implemented in terms of
// fundamental traps.
bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::AutoIdVector& props) MOZ_OVERRIDE;
bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc,
unsigned flags) MOZ_OVERRIDE;
};
class DOMProxyHandler : public BaseDOMProxyHandler
{
public:
DOMProxyHandler(const DOMClass& aClass)
: js::BaseProxyHandler(ProxyFamily()),
: BaseDOMProxyHandler(ProxyFamily()),
mClass(aClass)
{
}
bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy) MOZ_OVERRIDE;
bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc, unsigned flags) MOZ_OVERRIDE;
bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
JS::MutableHandle<JSPropertyDescriptor> desc) MOZ_OVERRIDE
{
@ -49,7 +64,6 @@ public:
JS::MutableHandle<JSPropertyDescriptor> desc, bool* defined);
bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
JS::Handle<jsid> id, bool* bp) MOZ_OVERRIDE;
bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy, JS::AutoIdVector& props) MOZ_OVERRIDE;
bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp) MOZ_OVERRIDE;
bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) MOZ_OVERRIDE;
@ -76,16 +90,6 @@ public:
JS::Handle<JSObject*> obj);
const DOMClass& mClass;
// Append the property names in "names" to "props". If
// shadowPrototypeProperties is false then skip properties that are also
// present on our proto chain. If shadowPrototypeProperties is true,
// then the "proxy" and "handler" arguments are ignored.
static bool AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
nsTArray<nsString>& names,
bool shadowPrototypeProperties,
DOMProxyHandler* handler,
JS::AutoIdVector& props);
};
extern jsid s_length_id;

View File

@ -45,3 +45,4 @@ MSG_DEF(MSG_INVALID_BYTESTRING, 2, "Cannot convert string to ByteString because
" at index {0} has value {1} which is greater than 255.")
MSG_DEF(MSG_NOT_DATE, 1, "{0} is not a date.")
MSG_DEF(MSG_INVALID_ADVANCE_COUNT, 0, "0 (Zero) is not a valid advance count.")
MSG_DEF(MSG_DEFINEPROPERTY_ON_GSP, 0, "Not allowed to define a property on the named properties object.")

View File

@ -1,5 +1,4 @@
{
"Static name on the prototype": true,
"constructor": true,
"Ghost name": true
"constructor": true
}

View File

@ -1,6 +1,5 @@
{
"Window.prototype": true,
"Global scope polluter": true,
"EventTarget.prototype": true,
"Object.prototype": true
}