2012-05-22 17:46:20 +04:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
|
|
* vim: set ts=2 sw=2 et tw=99 ft=cpp: */
|
|
|
|
/* 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 "mozilla/Util.h"
|
|
|
|
|
|
|
|
#include "DOMJSProxyHandler.h"
|
|
|
|
#include "xpcpublic.h"
|
|
|
|
#include "xpcprivate.h"
|
|
|
|
#include "XPCQuickStubs.h"
|
|
|
|
#include "XPCWrapper.h"
|
|
|
|
#include "WrapperFactory.h"
|
|
|
|
#include "nsDOMClassInfo.h"
|
|
|
|
#include "nsGlobalWindow.h"
|
|
|
|
#include "nsWrapperCacheInlines.h"
|
|
|
|
#include "mozilla/dom/BindingUtils.h"
|
|
|
|
|
|
|
|
#include "jsapi.h"
|
|
|
|
|
|
|
|
using namespace JS;
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
|
|
|
jsid s_length_id = JSID_VOID;
|
|
|
|
|
|
|
|
bool
|
|
|
|
DefineStaticJSVals(JSContext* cx)
|
|
|
|
{
|
|
|
|
return InternJSString(cx, s_length_id, "length");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int HandlerFamily;
|
|
|
|
|
2013-06-06 22:32:26 +04:00
|
|
|
js::DOMProxyShadowsResult
|
2013-06-21 17:12:46 +04:00
|
|
|
DOMProxyShadows(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id)
|
2013-04-29 17:17:59 +04:00
|
|
|
{
|
|
|
|
JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
|
|
|
|
if (v.isObject()) {
|
2013-08-09 02:53:04 +04:00
|
|
|
bool hasOwn;
|
2013-04-29 17:17:59 +04:00
|
|
|
if (!JS_AlreadyHasOwnPropertyById(cx, &v.toObject(), id, &hasOwn))
|
|
|
|
return js::ShadowCheckFailed;
|
|
|
|
|
|
|
|
return hasOwn ? js::Shadows : js::DoesntShadow;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v.isUndefined()) {
|
|
|
|
return js::DoesntShadow;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool hasOwn;
|
|
|
|
if (!GetProxyHandler(proxy)->hasOwn(cx, proxy, id, &hasOwn))
|
|
|
|
return js::ShadowCheckFailed;
|
|
|
|
|
|
|
|
return hasOwn ? js::Shadows : js::DoesntShadowUnique;
|
|
|
|
}
|
|
|
|
|
2012-08-24 20:32:26 +04:00
|
|
|
// Store the information for the specialized ICs.
|
2013-06-06 22:32:26 +04:00
|
|
|
struct SetDOMProxyInformation
|
2012-08-24 20:32:26 +04:00
|
|
|
{
|
2013-06-06 22:32:26 +04:00
|
|
|
SetDOMProxyInformation() {
|
|
|
|
js::SetDOMProxyInformation((void*) &HandlerFamily,
|
2013-06-21 09:39:22 +04:00
|
|
|
js::PROXY_EXTRA_SLOT + JSPROXYSLOT_EXPANDO, DOMProxyShadows);
|
2012-08-24 20:32:26 +04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-06-06 22:32:26 +04:00
|
|
|
SetDOMProxyInformation gSetDOMProxyInformation;
|
2012-08-24 20:32:26 +04:00
|
|
|
|
2013-04-16 21:02:57 +04:00
|
|
|
// static
|
|
|
|
JSObject*
|
|
|
|
DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
|
|
|
|
{
|
2013-04-20 20:04:09 +04:00
|
|
|
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());
|
2013-05-17 19:04:08 +04:00
|
|
|
xpc::GetObjectScope(obj)->RemoveDOMExpandoObject(obj);
|
2012-12-12 06:45:36 +04:00
|
|
|
} else {
|
|
|
|
js::ExpandoAndGeneration* expandoAndGeneration =
|
|
|
|
static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
|
|
|
|
v = expandoAndGeneration->expando;
|
|
|
|
if (v.isUndefined()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
expandoAndGeneration->expando = UndefinedValue();
|
2013-04-20 20:04:09 +04:00
|
|
|
}
|
|
|
|
|
2012-12-12 06:45:36 +04:00
|
|
|
|
2013-04-20 20:04:09 +04:00
|
|
|
return &v.toObject();
|
2013-04-16 21:02:57 +04:00
|
|
|
}
|
2012-08-24 20:32:26 +04:00
|
|
|
|
2012-05-22 17:46:20 +04:00
|
|
|
// static
|
|
|
|
JSObject*
|
2013-05-04 03:29:09 +04:00
|
|
|
DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
|
2012-05-22 17:46:20 +04:00
|
|
|
{
|
|
|
|
NS_ASSERTION(IsDOMProxy(obj), "expected a DOM proxy object");
|
2013-04-20 20:04:09 +04:00
|
|
|
JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
|
|
|
|
if (v.isObject()) {
|
|
|
|
return &v.toObject();
|
|
|
|
}
|
2012-05-22 17:46:20 +04:00
|
|
|
|
2013-04-20 20:04:09 +04:00
|
|
|
js::ExpandoAndGeneration* expandoAndGeneration;
|
|
|
|
if (!v.isUndefined()) {
|
|
|
|
expandoAndGeneration = static_cast<js::ExpandoAndGeneration*>(v.toPrivate());
|
|
|
|
if (expandoAndGeneration->expando.isObject()) {
|
|
|
|
return &expandoAndGeneration->expando.toObject();
|
2013-05-05 03:27:20 +04:00
|
|
|
}
|
2013-04-20 20:04:09 +04:00
|
|
|
} else {
|
|
|
|
expandoAndGeneration = nullptr;
|
|
|
|
}
|
2013-04-20 20:04:09 +04:00
|
|
|
|
2013-05-08 06:34:56 +04:00
|
|
|
JS::Rooted<JSObject*> expando(cx,
|
|
|
|
JS_NewObjectWithGivenProto(cx, nullptr, nullptr, js::GetObjectParent(obj)));
|
2013-04-20 20:04:09 +04:00
|
|
|
if (!expando) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
2013-04-20 20:04:09 +04:00
|
|
|
|
2013-05-17 19:04:08 +04:00
|
|
|
nsISupports* native = UnwrapDOMObject<nsISupports>(obj);
|
|
|
|
nsWrapperCache* cache;
|
|
|
|
CallQueryInterface(native, &cache);
|
|
|
|
if (expandoAndGeneration) {
|
2013-06-23 11:15:42 +04:00
|
|
|
cache->PreserveWrapper(native);
|
2013-05-17 19:04:08 +04:00
|
|
|
expandoAndGeneration->expando.setObject(*expando);
|
|
|
|
|
|
|
|
return expando;
|
|
|
|
}
|
|
|
|
|
2013-04-20 20:04:09 +04:00
|
|
|
XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
|
|
|
|
if (!scope->RegisterDOMExpandoObject(obj)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
cache->SetPreservingWrapper(true);
|
2013-05-17 19:04:08 +04:00
|
|
|
js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(*expando));
|
2013-04-20 20:04:09 +04:00
|
|
|
|
2012-05-22 17:46:20 +04:00
|
|
|
return expando;
|
|
|
|
}
|
|
|
|
|
2013-03-23 06:43:03 +04:00
|
|
|
bool
|
2013-06-29 01:01:09 +04:00
|
|
|
DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
|
2013-03-23 06:43:03 +04:00
|
|
|
{
|
2013-06-29 01:01:09 +04:00
|
|
|
// always extensible per WebIDL
|
|
|
|
*extensible = true;
|
|
|
|
return true;
|
2013-03-23 06:43:03 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
DOMProxyHandler::preventExtensions(JSContext *cx, JS::Handle<JSObject*> proxy)
|
|
|
|
{
|
|
|
|
// Throw a TypeError, per WebIDL.
|
|
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-05-22 17:46:20 +04:00
|
|
|
bool
|
2013-03-22 02:23:48 +04:00
|
|
|
DOMProxyHandler::getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
|
2013-01-04 01:31:36 +04:00
|
|
|
JSPropertyDescriptor* desc, unsigned flags)
|
2012-05-22 17:46:20 +04:00
|
|
|
{
|
2013-01-04 01:31:36 +04:00
|
|
|
if (!getOwnPropertyDescriptor(cx, proxy, id, desc, flags)) {
|
2012-05-22 17:46:20 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (desc->obj) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-05-04 03:29:09 +04:00
|
|
|
JS::Rooted<JSObject*> proto(cx);
|
2013-05-05 11:03:14 +04:00
|
|
|
if (!js::GetObjectProto(cx, proxy, &proto)) {
|
2012-09-04 03:42:10 +04:00
|
|
|
return false;
|
|
|
|
}
|
2012-05-22 17:46:20 +04:00
|
|
|
if (!proto) {
|
|
|
|
desc->obj = NULL;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-09-15 22:19:55 +04:00
|
|
|
return JS_GetPropertyDescriptorById(cx, proto, id, 0, desc);
|
2012-05-22 17:46:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2013-03-22 02:23:48 +04:00
|
|
|
DOMProxyHandler::defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
|
2013-05-29 13:38:57 +04:00
|
|
|
JSPropertyDescriptor* desc, bool* defined)
|
2012-05-22 17:46:20 +04:00
|
|
|
{
|
|
|
|
if ((desc->attrs & JSPROP_GETTER) && desc->setter == JS_StrictPropertyStub) {
|
|
|
|
return JS_ReportErrorFlagsAndNumber(cx,
|
|
|
|
JSREPORT_WARNING | JSREPORT_STRICT |
|
|
|
|
JSREPORT_STRICT_MODE_ERROR,
|
|
|
|
js_GetErrorMessage, NULL,
|
|
|
|
JSMSG_GETTER_ONLY);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject* expando = EnsureExpandoObject(cx, proxy);
|
|
|
|
if (!expando) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-08-09 02:53:04 +04:00
|
|
|
bool dummy;
|
2012-12-20 13:56:11 +04:00
|
|
|
return js_DefineOwnProperty(cx, expando, id, *desc, &dummy);
|
2012-05-22 17:46:20 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2013-03-22 02:23:48 +04:00
|
|
|
DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
|
2013-03-22 02:23:48 +04:00
|
|
|
JS::Handle<jsid> id, bool* bp)
|
2012-05-22 17:46:20 +04:00
|
|
|
{
|
2013-05-04 03:29:09 +04:00
|
|
|
JS::Rooted<JSObject*> expando(cx);
|
2012-05-22 17:46:20 +04:00
|
|
|
if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
|
2013-08-05 17:01:53 +04:00
|
|
|
return JS_DeletePropertyById2(cx, expando, id, bp);
|
2012-05-22 17:46:20 +04:00
|
|
|
}
|
|
|
|
|
2013-08-05 17:01:53 +04:00
|
|
|
*bp = true;
|
2012-05-22 17:46:20 +04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2013-03-22 02:23:48 +04:00
|
|
|
DOMProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy, AutoIdVector& props)
|
2012-05-22 17:46:20 +04:00
|
|
|
{
|
2013-05-04 03:29:09 +04:00
|
|
|
JS::Rooted<JSObject*> proto(cx);
|
2013-07-31 20:20:33 +04:00
|
|
|
if (!JS_GetPrototype(cx, proxy, &proto)) {
|
2012-09-04 03:42:10 +04:00
|
|
|
return false;
|
|
|
|
}
|
2012-05-22 17:46:20 +04:00
|
|
|
return getOwnPropertyNames(cx, proxy, props) &&
|
|
|
|
(!proto || js::GetPropertyNames(cx, proto, 0, &props));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2013-03-22 02:23:48 +04:00
|
|
|
DOMProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp)
|
2012-05-22 17:46:20 +04:00
|
|
|
{
|
|
|
|
if (!hasOwn(cx, proxy, id, bp)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*bp) {
|
|
|
|
// We have the property ourselves; no need to worry about our prototype
|
|
|
|
// chain.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// OK, now we have to look at the proto
|
2013-05-04 03:29:09 +04:00
|
|
|
JS::Rooted<JSObject*> proto(cx);
|
2013-05-05 11:03:14 +04:00
|
|
|
if (!js::GetObjectProto(cx, proxy, &proto)) {
|
2012-09-04 03:42:10 +04:00
|
|
|
return false;
|
|
|
|
}
|
2012-05-22 17:46:20 +04:00
|
|
|
if (!proto) {
|
|
|
|
return true;
|
|
|
|
}
|
2013-08-09 02:53:04 +04:00
|
|
|
bool protoHasProp;
|
2012-05-22 17:46:20 +04:00
|
|
|
bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp);
|
|
|
|
if (ok) {
|
|
|
|
*bp = protoHasProp;
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2013-08-04 07:38:55 +04:00
|
|
|
/* static */
|
2012-11-05 20:58:03 +04:00
|
|
|
bool
|
2013-05-04 03:29:08 +04:00
|
|
|
DOMProxyHandler::AppendNamedPropertyIds(JSContext* cx,
|
|
|
|
JS::Handle<JSObject*> proxy,
|
2012-11-05 20:58:03 +04:00
|
|
|
nsTArray<nsString>& names,
|
2013-04-20 20:04:09 +04:00
|
|
|
bool shadowPrototypeProperties,
|
2013-08-04 07:38:55 +04:00
|
|
|
DOMProxyHandler* handler,
|
2012-11-05 20:58:03 +04:00
|
|
|
JS::AutoIdVector& props)
|
|
|
|
{
|
|
|
|
for (uint32_t i = 0; i < names.Length(); ++i) {
|
2013-05-04 03:29:09 +04:00
|
|
|
JS::Rooted<JS::Value> v(cx);
|
|
|
|
if (!xpc::NonVoidStringToJsval(cx, names[i], v.address())) {
|
2012-11-05 20:58:03 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-05-04 03:29:08 +04:00
|
|
|
JS::Rooted<jsid> id(cx);
|
|
|
|
if (!JS_ValueToId(cx, v, id.address())) {
|
2012-11-05 20:58:03 +04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-04-20 20:04:09 +04:00
|
|
|
if (shadowPrototypeProperties ||
|
2013-08-04 07:38:55 +04:00
|
|
|
!HasPropertyOnPrototype(cx, proxy, handler, id)) {
|
2012-11-05 20:58:03 +04:00
|
|
|
if (!props.append(id)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-05-22 17:46:20 +04:00
|
|
|
int32_t
|
2013-05-04 03:29:09 +04:00
|
|
|
IdToInt32(JSContext* cx, JS::Handle<jsid> id)
|
2012-05-22 17:46:20 +04:00
|
|
|
{
|
2013-04-03 03:05:37 +04:00
|
|
|
JS::Value idval;
|
2012-05-22 17:46:20 +04:00
|
|
|
double array_index;
|
|
|
|
int32_t i;
|
|
|
|
if (!::JS_IdToValue(cx, id, &idval) ||
|
|
|
|
!::JS_ValueToNumber(cx, idval, &array_index) ||
|
|
|
|
!::JS_DoubleIsInt32(array_index, &i)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|