import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 BackstagePass_h__
#define BackstagePass_h__
#include "nsISupports.h"
#include "nsWeakReference.h"
#include "nsIGlobalObject.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIXPCScriptable.h"
#include "js/HeapAPI.h"
class XPCWrappedNative;
class BackstagePass : public nsIGlobalObject,
public nsIScriptObjectPrincipal,
public nsIXPCScriptable,
public nsIClassInfo,
public nsSupportsWeakReference
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCSCRIPTABLE
NS_DECL_NSICLASSINFO
virtual nsIPrincipal* GetPrincipal() override {
return mPrincipal;
}
virtual JSObject* GetGlobalJSObject() override;
void ForgetGlobalObject() {
mWrapper = nullptr;
}
void SetGlobalObject(JSObject* global);
explicit BackstagePass(nsIPrincipal* prin) :
mPrincipal(prin)
{
}
private:
virtual ~BackstagePass() { }
nsCOMPtr<nsIPrincipal> mPrincipal;
XPCWrappedNative* mWrapper;
};
nsresult
NS_NewBackstagePass(BackstagePass** ret);
#endif // BackstagePass_h__

View file

@ -0,0 +1,491 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 "xpcprivate.h"
#include "WrapperFactory.h"
#include "AccessCheck.h"
#include "jsfriendapi.h"
#include "jswrapper.h"
#include "js/Proxy.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileListBinding.h"
#include "mozilla/dom/StructuredCloneHolder.h"
#include "nsGlobalWindow.h"
#include "nsJSUtils.h"
#include "nsIDOMFileList.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace JS;
namespace xpc {
bool
IsReflector(JSObject* obj)
{
obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
if (!obj)
return false;
return IS_WN_REFLECTOR(obj) || dom::IsDOMObject(obj);
}
enum StackScopedCloneTags {
SCTAG_BASE = JS_SCTAG_USER_MIN,
SCTAG_REFLECTOR,
SCTAG_BLOB,
SCTAG_FUNCTION,
};
// The HTML5 structured cloning algorithm includes a few DOM objects, notably
// FileList. That wouldn't in itself be a reason to support them here,
// but we've historically supported them for Cu.cloneInto (where we didn't support
// other reflectors), so we need to continue to do so in the wrapReflectors == false
// case to maintain compatibility.
//
// FileList clones are supposed to give brand new objects, rather than
// cross-compartment wrappers. For this, our current implementation relies on the
// fact that these objects are implemented with XPConnect and have one reflector
// per scope.
bool IsFileList(JSObject* obj)
{
return IS_INSTANCE_OF(FileList, obj);
}
class MOZ_STACK_CLASS StackScopedCloneData
: public StructuredCloneHolderBase
{
public:
StackScopedCloneData(JSContext* aCx, StackScopedCloneOptions* aOptions)
: mOptions(aOptions)
, mReflectors(aCx)
, mFunctions(aCx)
{}
~StackScopedCloneData()
{
Clear();
}
JSObject* CustomReadHandler(JSContext* aCx,
JSStructuredCloneReader* aReader,
uint32_t aTag,
uint32_t aData)
{
if (aTag == SCTAG_REFLECTOR) {
MOZ_ASSERT(!aData);
size_t idx;
if (!JS_ReadBytes(aReader, &idx, sizeof(size_t)))
return nullptr;
RootedObject reflector(aCx, mReflectors[idx]);
MOZ_ASSERT(reflector, "No object pointer?");
MOZ_ASSERT(IsReflector(reflector), "Object pointer must be a reflector!");
if (!JS_WrapObject(aCx, &reflector))
return nullptr;
return reflector;
}
if (aTag == SCTAG_FUNCTION) {
MOZ_ASSERT(aData < mFunctions.length());
RootedValue functionValue(aCx);
RootedObject obj(aCx, mFunctions[aData]);
if (!JS_WrapObject(aCx, &obj))
return nullptr;
FunctionForwarderOptions forwarderOptions;
if (!xpc::NewFunctionForwarder(aCx, JSID_VOIDHANDLE, obj, forwarderOptions,
&functionValue))
{
return nullptr;
}
return &functionValue.toObject();
}
if (aTag == SCTAG_BLOB) {
MOZ_ASSERT(!aData);
size_t idx;
if (!JS_ReadBytes(aReader, &idx, sizeof(size_t))) {
return nullptr;
}
nsIGlobalObject* global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
MOZ_ASSERT(global);
// RefPtr<File> needs to go out of scope before toObjectOrNull() is called because
// otherwise the static analysis thinks it can gc the JSObject via the stack.
JS::Rooted<JS::Value> val(aCx);
{
RefPtr<Blob> blob = Blob::Create(global, mBlobImpls[idx]);
if (!ToJSValue(aCx, blob, &val)) {
return nullptr;
}
}
return val.toObjectOrNull();
}
MOZ_ASSERT_UNREACHABLE("Encountered garbage in the clone stream!");
return nullptr;
}
bool CustomWriteHandler(JSContext* aCx,
JSStructuredCloneWriter* aWriter,
JS::Handle<JSObject*> aObj)
{
{
JS::Rooted<JSObject*> obj(aCx, aObj);
Blob* blob = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, &obj, blob))) {
BlobImpl* blobImpl = blob->Impl();
MOZ_ASSERT(blobImpl);
if (!mBlobImpls.AppendElement(blobImpl))
return false;
size_t idx = mBlobImpls.Length() - 1;
return JS_WriteUint32Pair(aWriter, SCTAG_BLOB, 0) &&
JS_WriteBytes(aWriter, &idx, sizeof(size_t));
}
}
if ((mOptions->wrapReflectors && IsReflector(aObj)) ||
IsFileList(aObj))
{
if (!mReflectors.append(aObj))
return false;
size_t idx = mReflectors.length() - 1;
if (!JS_WriteUint32Pair(aWriter, SCTAG_REFLECTOR, 0))
return false;
if (!JS_WriteBytes(aWriter, &idx, sizeof(size_t)))
return false;
return true;
}
if (JS::IsCallable(aObj)) {
if (mOptions->cloneFunctions) {
if (!mFunctions.append(aObj))
return false;
return JS_WriteUint32Pair(aWriter, SCTAG_FUNCTION, mFunctions.length() - 1);
} else {
JS_ReportErrorASCII(aCx, "Permission denied to pass a Function via structured clone");
return false;
}
}
JS_ReportErrorASCII(aCx, "Encountered unsupported value type writing stack-scoped structured clone");
return false;
}
StackScopedCloneOptions* mOptions;
AutoObjectVector mReflectors;
AutoObjectVector mFunctions;
nsTArray<RefPtr<BlobImpl>> mBlobImpls;
};
/*
* General-purpose structured-cloning utility for cases where the structured
* clone buffer is only used in stack-scope (that is to say, the buffer does
* not escape from this function). The stack-scoping allows us to pass
* references to various JSObjects directly in certain situations without
* worrying about lifetime issues.
*
* This function assumes that |cx| is already entered the compartment we want
* to clone to, and that |val| may not be same-compartment with cx. When the
* function returns, |val| is set to the result of the clone.
*/
bool
StackScopedClone(JSContext* cx, StackScopedCloneOptions& options,
MutableHandleValue val)
{
StackScopedCloneData data(cx, &options);
{
// For parsing val we have to enter its compartment.
// (unless it's a primitive)
Maybe<JSAutoCompartment> ac;
if (val.isObject()) {
ac.emplace(cx, &val.toObject());
} else if (val.isString() && !JS_WrapValue(cx, val)) {
return false;
}
if (!data.Write(cx, val))
return false;
}
// Now recreate the clones in the target compartment.
if (!data.Read(cx, val))
return false;
// Deep-freeze if requested.
if (options.deepFreeze && val.isObject()) {
RootedObject obj(cx, &val.toObject());
if (!JS_DeepFreezeObject(cx, obj))
return false;
}
return true;
}
// Note - This function mirrors the logic of CheckPassToChrome in
// ChromeObjectWrapper.cpp.
static bool
CheckSameOriginArg(JSContext* cx, FunctionForwarderOptions& options, HandleValue v)
{
// Consumers can explicitly opt out of this security check. This is used in
// the web console to allow the utility functions to accept cross-origin Windows.
if (options.allowCrossOriginArguments)
return true;
// Primitives are fine.
if (!v.isObject())
return true;
RootedObject obj(cx, &v.toObject());
MOZ_ASSERT(js::GetObjectCompartment(obj) != js::GetContextCompartment(cx),
"This should be invoked after entering the compartment but before "
"wrapping the values");
// Non-wrappers are fine.
if (!js::IsWrapper(obj))
return true;
// Wrappers leading back to the scope of the exported function are fine.
if (js::GetObjectCompartment(js::UncheckedUnwrap(obj)) == js::GetContextCompartment(cx))
return true;
// Same-origin wrappers are fine.
if (AccessCheck::wrapperSubsumes(obj))
return true;
// Badness.
JS_ReportErrorASCII(cx, "Permission denied to pass object to exported function");
return false;
}
static bool
FunctionForwarder(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
// Grab the options from the reserved slot.
RootedObject optionsObj(cx, &js::GetFunctionNativeReserved(&args.callee(), 1).toObject());
FunctionForwarderOptions options(cx, optionsObj);
if (!options.Parse())
return false;
// Grab and unwrap the underlying callable.
RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
RootedObject unwrappedFun(cx, js::UncheckedUnwrap(&v.toObject()));
RootedObject thisObj(cx, args.isConstructing() ? nullptr : JS_THIS_OBJECT(cx, vp));
{
// We manually implement the contents of CrossCompartmentWrapper::call
// here, because certain function wrappers (notably content->nsEP) are
// not callable.
JSAutoCompartment ac(cx, unwrappedFun);
RootedValue thisVal(cx, ObjectOrNullValue(thisObj));
if (!CheckSameOriginArg(cx, options, thisVal) || !JS_WrapObject(cx, &thisObj))
return false;
for (size_t n = 0; n < args.length(); ++n) {
if (!CheckSameOriginArg(cx, options, args[n]) || !JS_WrapValue(cx, args[n]))
return false;
}
RootedValue fval(cx, ObjectValue(*unwrappedFun));
if (args.isConstructing()) {
RootedObject obj(cx);
if (!JS::Construct(cx, fval, args, &obj))
return false;
args.rval().setObject(*obj);
} else {
if (!JS_CallFunctionValue(cx, thisObj, fval, args, args.rval()))
return false;
}
}
// Rewrap the return value into our compartment.
return JS_WrapValue(cx, args.rval());
}
bool
NewFunctionForwarder(JSContext* cx, HandleId idArg, HandleObject callable,
FunctionForwarderOptions& options, MutableHandleValue vp)
{
RootedId id(cx, idArg);
if (id == JSID_VOIDHANDLE)
id = GetJSIDByIndex(cx, XPCJSContext::IDX_EMPTYSTRING);
// We have no way of knowing whether the underlying function wants to be a
// constructor or not, so we just mark all forwarders as constructors, and
// let the underlying function throw for construct calls if it wants.
JSFunction* fun = js::NewFunctionByIdWithReserved(cx, FunctionForwarder,
0, JSFUN_CONSTRUCTOR, id);
if (!fun)
return false;
// Stash the callable in slot 0.
AssertSameCompartment(cx, callable);
RootedObject funobj(cx, JS_GetFunctionObject(fun));
js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable));
// Stash the options in slot 1.
RootedObject optionsObj(cx, options.ToJSObject(cx));
if (!optionsObj)
return false;
js::SetFunctionNativeReserved(funobj, 1, ObjectValue(*optionsObj));
vp.setObject(*funobj);
return true;
}
bool
ExportFunction(JSContext* cx, HandleValue vfunction, HandleValue vscope, HandleValue voptions,
MutableHandleValue rval)
{
bool hasOptions = !voptions.isUndefined();
if (!vscope.isObject() || !vfunction.isObject() || (hasOptions && !voptions.isObject())) {
JS_ReportErrorASCII(cx, "Invalid argument");
return false;
}
RootedObject funObj(cx, &vfunction.toObject());
RootedObject targetScope(cx, &vscope.toObject());
ExportFunctionOptions options(cx, hasOptions ? &voptions.toObject() : nullptr);
if (hasOptions && !options.Parse())
return false;
// Restrictions:
// * We must subsume the scope we are exporting to.
// * We must subsume the function being exported, because the function
// forwarder manually circumvents security wrapper CALL restrictions.
targetScope = js::CheckedUnwrap(targetScope);
funObj = js::CheckedUnwrap(funObj);
if (!targetScope || !funObj) {
JS_ReportErrorASCII(cx, "Permission denied to export function into scope");
return false;
}
if (js::IsScriptedProxy(targetScope)) {
JS_ReportErrorASCII(cx, "Defining property on proxy object is not allowed");
return false;
}
{
// We need to operate in the target scope from here on, let's enter
// its compartment.
JSAutoCompartment ac(cx, targetScope);
// Unwrapping to see if we have a callable.
funObj = UncheckedUnwrap(funObj);
if (!JS::IsCallable(funObj)) {
JS_ReportErrorASCII(cx, "First argument must be a function");
return false;
}
RootedId id(cx, options.defineAs);
if (JSID_IS_VOID(id)) {
// If there wasn't any function name specified,
// copy the name from the function being imported.
JSFunction* fun = JS_GetObjectFunction(funObj);
RootedString funName(cx, JS_GetFunctionId(fun));
if (!funName)
funName = JS_AtomizeAndPinString(cx, "");
if (!JS_StringToId(cx, funName, &id))
return false;
}
MOZ_ASSERT(JSID_IS_STRING(id));
// The function forwarder will live in the target compartment. Since
// this function will be referenced from its private slot, to avoid a
// GC hazard, we must wrap it to the same compartment.
if (!JS_WrapObject(cx, &funObj))
return false;
// And now, let's create the forwarder function in the target compartment
// for the function the be exported.
FunctionForwarderOptions forwarderOptions;
forwarderOptions.allowCrossOriginArguments = options.allowCrossOriginArguments;
if (!NewFunctionForwarder(cx, id, funObj, forwarderOptions, rval)) {
JS_ReportErrorASCII(cx, "Exporting function failed");
return false;
}
// We have the forwarder function in the target compartment. If
// defineAs was set, we also need to define it as a property on
// the target.
if (!JSID_IS_VOID(options.defineAs)) {
if (!JS_DefinePropertyById(cx, targetScope, id, rval,
JSPROP_ENUMERATE,
JS_STUBGETTER, JS_STUBSETTER)) {
return false;
}
}
}
// Finally we have to re-wrap the exported function back to the caller compartment.
if (!JS_WrapValue(cx, rval))
return false;
return true;
}
bool
CreateObjectIn(JSContext* cx, HandleValue vobj, CreateObjectInOptions& options,
MutableHandleValue rval)
{
if (!vobj.isObject()) {
JS_ReportErrorASCII(cx, "Expected an object as the target scope");
return false;
}
RootedObject scope(cx, js::CheckedUnwrap(&vobj.toObject()));
if (!scope) {
JS_ReportErrorASCII(cx, "Permission denied to create object in the target scope");
return false;
}
bool define = !JSID_IS_VOID(options.defineAs);
if (define && js::IsScriptedProxy(scope)) {
JS_ReportErrorASCII(cx, "Defining property on proxy object is not allowed");
return false;
}
RootedObject obj(cx);
{
JSAutoCompartment ac(cx, scope);
obj = JS_NewPlainObject(cx);
if (!obj)
return false;
if (define) {
if (!JS_DefinePropertyById(cx, scope, options.defineAs, obj,
JSPROP_ENUMERATE,
JS_STUBGETTER, JS_STUBSETTER))
return false;
}
}
rval.setObject(*obj);
if (!WrapperFactory::WaiveXrayAndWrap(cx, rval))
return false;
return true;
}
} /* namespace xpc */

3
js/xpconnect/src/README Normal file
View file

@ -0,0 +1,3 @@
see http://www.mozilla.org/scriptable

1966
js/xpconnect/src/Sandbox.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,67 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 __SANDBOXPRIVATE_H__
#define __SANDBOXPRIVATE_H__
#include "nsIGlobalObject.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIPrincipal.h"
#include "nsWeakReference.h"
#include "nsWrapperCache.h"
#include "js/RootingAPI.h"
class SandboxPrivate : public nsIGlobalObject,
public nsIScriptObjectPrincipal,
public nsSupportsWeakReference,
public nsWrapperCache
{
public:
SandboxPrivate(nsIPrincipal* principal, JSObject* global)
: mPrincipal(principal)
{
SetIsNotDOMBinding();
SetWrapper(global);
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(SandboxPrivate,
nsIGlobalObject)
nsIPrincipal* GetPrincipal() override
{
return mPrincipal;
}
JSObject* GetGlobalJSObject() override
{
return GetWrapper();
}
void ForgetGlobalObject()
{
ClearWrapper();
}
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override
{
MOZ_CRASH("SandboxPrivate doesn't use DOM bindings!");
}
void ObjectMoved(JSObject* obj, const JSObject* old)
{
UpdateWrapper(obj, old);
}
private:
virtual ~SandboxPrivate() { }
nsCOMPtr<nsIPrincipal> mPrincipal;
};
#endif // __SANDBOXPRIVATE_H__

View file

@ -0,0 +1,276 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Call context. */
#include "xpcprivate.h"
#include "jswrapper.h"
#include "jsfriendapi.h"
#include "nsContentUtils.h"
using namespace mozilla;
using namespace xpc;
using namespace JS;
#define IS_TEAROFF_CLASS(clazz) ((clazz) == &XPC_WN_Tearoff_JSClass)
XPCCallContext::XPCCallContext(JSContext* cx,
HandleObject obj /* = nullptr */,
HandleObject funobj /* = nullptr */,
HandleId name /* = JSID_VOID */,
unsigned argc /* = NO_ARGS */,
Value* argv /* = nullptr */,
Value* rval /* = nullptr */)
: mAr(cx),
mState(INIT_FAILED),
mXPC(nsXPConnect::XPConnect()),
mXPCJSContext(nullptr),
mJSContext(cx),
mWrapper(nullptr),
mTearOff(nullptr),
mName(cx)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
if (!mXPC)
return;
mXPCJSContext = XPCJSContext::Get();
// hook into call context chain.
mPrevCallContext = mXPCJSContext->SetCallContext(this);
mState = HAVE_CONTEXT;
if (!obj)
return;
mMethodIndex = 0xDEAD;
mState = HAVE_OBJECT;
mTearOff = nullptr;
JSObject* unwrapped = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
if (!unwrapped) {
JS_ReportErrorASCII(mJSContext, "Permission denied to call method on |this|");
mState = INIT_FAILED;
return;
}
const js::Class* clasp = js::GetObjectClass(unwrapped);
if (IS_WN_CLASS(clasp)) {
mWrapper = XPCWrappedNative::Get(unwrapped);
} else if (IS_TEAROFF_CLASS(clasp)) {
mTearOff = (XPCWrappedNativeTearOff*)js::GetObjectPrivate(unwrapped);
mWrapper = XPCWrappedNative::Get(
&js::GetReservedSlot(unwrapped,
XPC_WN_TEAROFF_FLAT_OBJECT_SLOT).toObject());
}
if (mWrapper) {
if (mTearOff)
mScriptableInfo = nullptr;
else
mScriptableInfo = mWrapper->GetScriptableInfo();
}
if (!JSID_IS_VOID(name))
SetName(name);
if (argc != NO_ARGS)
SetArgsAndResultPtr(argc, argv, rval);
CHECK_STATE(HAVE_OBJECT);
}
void
XPCCallContext::SetName(jsid name)
{
CHECK_STATE(HAVE_OBJECT);
mName = name;
if (mTearOff) {
mSet = nullptr;
mInterface = mTearOff->GetInterface();
mMember = mInterface->FindMember(mName);
mStaticMemberIsLocal = true;
if (mMember && !mMember->IsConstant())
mMethodIndex = mMember->GetIndex();
} else {
mSet = mWrapper ? mWrapper->GetSet() : nullptr;
if (mSet &&
mSet->FindMember(mName, &mMember, &mInterface,
mWrapper->HasProto() ?
mWrapper->GetProto()->GetSet() :
nullptr,
&mStaticMemberIsLocal)) {
if (mMember && !mMember->IsConstant())
mMethodIndex = mMember->GetIndex();
} else {
mMember = nullptr;
mInterface = nullptr;
mStaticMemberIsLocal = false;
}
}
mState = HAVE_NAME;
}
void
XPCCallContext::SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
bool isSetter)
{
CHECK_STATE(HAVE_CONTEXT);
// We are going straight to the method info and need not do a lookup
// by id.
// don't be tricked if method is called with wrong 'this'
if (mTearOff && mTearOff->GetInterface() != iface)
mTearOff = nullptr;
mSet = nullptr;
mInterface = iface;
mMember = member;
mMethodIndex = mMember->GetIndex() + (isSetter ? 1 : 0);
mName = mMember->GetName();
if (mState < HAVE_NAME)
mState = HAVE_NAME;
}
void
XPCCallContext::SetArgsAndResultPtr(unsigned argc,
Value* argv,
Value* rval)
{
CHECK_STATE(HAVE_OBJECT);
if (mState < HAVE_NAME) {
mSet = nullptr;
mInterface = nullptr;
mMember = nullptr;
mStaticMemberIsLocal = false;
}
mArgc = argc;
mArgv = argv;
mRetVal = rval;
mState = HAVE_ARGS;
}
nsresult
XPCCallContext::CanCallNow()
{
nsresult rv;
if (!HasInterfaceAndMember())
return NS_ERROR_UNEXPECTED;
if (mState < HAVE_ARGS)
return NS_ERROR_UNEXPECTED;
if (!mTearOff) {
mTearOff = mWrapper->FindTearOff(mInterface, false, &rv);
if (!mTearOff || mTearOff->GetInterface() != mInterface) {
mTearOff = nullptr;
return NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED;
}
}
// Refresh in case FindTearOff extended the set
mSet = mWrapper->GetSet();
mState = READY_TO_CALL;
return NS_OK;
}
void
XPCCallContext::SystemIsBeingShutDown()
{
// XXX This is pretty questionable since the per thread cleanup stuff
// can be making this call on one thread for call contexts on another
// thread.
NS_WARNING("Shutting Down XPConnect even through there is a live XPCCallContext");
mXPCJSContext = nullptr;
mState = SYSTEM_SHUTDOWN;
mSet = nullptr;
mInterface = nullptr;
if (mPrevCallContext)
mPrevCallContext->SystemIsBeingShutDown();
}
XPCCallContext::~XPCCallContext()
{
if (mXPCJSContext) {
DebugOnly<XPCCallContext*> old = mXPCJSContext->SetCallContext(mPrevCallContext);
MOZ_ASSERT(old == this, "bad pop from per thread data");
}
}
NS_IMETHODIMP
XPCCallContext::GetCallee(nsISupports * *aCallee)
{
nsCOMPtr<nsISupports> rval = mWrapper ? mWrapper->GetIdentityObject() : nullptr;
rval.forget(aCallee);
return NS_OK;
}
NS_IMETHODIMP
XPCCallContext::GetCalleeMethodIndex(uint16_t* aCalleeMethodIndex)
{
*aCalleeMethodIndex = mMethodIndex;
return NS_OK;
}
NS_IMETHODIMP
XPCCallContext::GetCalleeInterface(nsIInterfaceInfo * *aCalleeInterface)
{
nsCOMPtr<nsIInterfaceInfo> rval = mInterface->GetInterfaceInfo();
rval.forget(aCalleeInterface);
return NS_OK;
}
NS_IMETHODIMP
XPCCallContext::GetCalleeClassInfo(nsIClassInfo * *aCalleeClassInfo)
{
nsCOMPtr<nsIClassInfo> rval = mWrapper ? mWrapper->GetClassInfo() : nullptr;
rval.forget(aCalleeClassInfo);
return NS_OK;
}
NS_IMETHODIMP
XPCCallContext::GetJSContext(JSContext * *aJSContext)
{
JS_AbortIfWrongThread(mJSContext);
*aJSContext = mJSContext;
return NS_OK;
}
NS_IMETHODIMP
XPCCallContext::GetArgc(uint32_t* aArgc)
{
*aArgc = (uint32_t) mArgc;
return NS_OK;
}
NS_IMETHODIMP
XPCCallContext::GetArgvPtr(Value** aArgvPtr)
{
*aArgvPtr = mArgv;
return NS_OK;
}
NS_IMETHODIMP
XPCCallContext::GetPreviousCallContext(nsAXPCNativeCallContext** aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = GetPrevCallContext();
return NS_OK;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,63 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 "xpcprivate.h"
#include "jsprf.h"
#include "nsThreadUtils.h"
#include "nsContentUtils.h"
#include "mozilla/Sprintf.h"
#ifdef XP_WIN
#include <windows.h>
#endif
static void DebugDump(const char* fmt, ...)
{
char buffer[2048];
va_list ap;
va_start(ap, fmt);
#ifdef XPWIN
_vsnprintf(buffer, sizeof(buffer), fmt, ap);
buffer[sizeof(buffer)-1] = '\0';
#else
VsprintfLiteral(buffer, fmt, ap);
#endif
va_end(ap);
#ifdef XP_WIN
if (IsDebuggerPresent()) {
OutputDebugStringA(buffer);
}
#endif
printf("%s", buffer);
}
bool
xpc_DumpJSStack(bool showArgs, bool showLocals, bool showThisProps)
{
JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
if (!cx) {
printf("there is no JSContext on the stack!\n");
} else if (char* buf = xpc_PrintJSStack(cx, showArgs, showLocals, showThisProps)) {
DebugDump("%s\n", buf);
JS_smprintf_free(buf);
}
return true;
}
char*
xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals,
bool showThisProps)
{
JS::AutoSaveExceptionState state(cx);
char* buf = JS::FormatStackDump(cx, nullptr, showArgs, showLocals, showThisProps);
if (!buf)
DebugDump("%s", "Failed to format JavaScript stack for dump\n");
state.restore();
return buf;
}

View file

@ -0,0 +1,80 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* An implementaion of nsIException. */
#include "xpcprivate.h"
#include "nsError.h"
/***************************************************************************/
/* Quick and dirty mapping of well known result codes to strings. We only
* call this when building an exception object, so iterating the short array
* is not too bad.
*
* It sure would be nice to have exceptions declared in idl and available
* in some more global way at runtime.
*/
static const struct ResultMap
{nsresult rv; const char* name; const char* format;} map[] = {
#define XPC_MSG_DEF(val, format) \
{(val), #val, format},
#include "xpc.msg"
#undef XPC_MSG_DEF
{NS_OK,0,0} // sentinel to mark end of array
};
#define RESULT_COUNT ((sizeof(map) / sizeof(map[0]))-1)
// static
bool
nsXPCException::NameAndFormatForNSResult(nsresult rv,
const char** name,
const char** format)
{
for (const ResultMap* p = map; p->name; p++) {
if (rv == p->rv) {
if (name) *name = p->name;
if (format) *format = p->format;
return true;
}
}
return false;
}
// static
const void*
nsXPCException::IterateNSResults(nsresult* rv,
const char** name,
const char** format,
const void** iterp)
{
const ResultMap* p = (const ResultMap*) *iterp;
if (!p)
p = map;
else
p++;
if (!p->name)
p = nullptr;
else {
if (rv)
*rv = p->rv;
if (name)
*name = p->name;
if (format)
*format = p->format;
}
*iterp = p;
return p;
}
// static
uint32_t
nsXPCException::GetNSResultCount()
{
return RESULT_COUNT;
}

View file

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Private forward declarations. */
#ifndef xpcforwards_h___
#define xpcforwards_h___
// forward declarations of interally used classes...
class nsXPConnect;
class XPCJSContext;
class XPCContext;
class XPCCallContext;
class XPCJSThrower;
class nsXPCWrappedJS;
class nsXPCWrappedJSClass;
class XPCNativeMember;
class XPCNativeInterface;
class XPCNativeSet;
class XPCWrappedNative;
class XPCWrappedNativeProto;
class XPCWrappedNativeTearOff;
class XPCNativeScriptableInfo;
class XPCNativeScriptableCreateInfo;
class XPCTraceableVariant;
class XPCJSObjectHolder;
class JSObject2WrappedJSMap;
class Native2WrappedNativeMap;
class IID2WrappedJSClassMap;
class IID2NativeInterfaceMap;
class ClassInfo2NativeSetMap;
class ClassInfo2WrappedNativeProtoMap;
class NativeSetMap;
class IID2ThisTranslatorMap;
class XPCWrappedNativeProtoMap;
class JSObject2JSObjectMap;
class nsXPCComponents;
class nsXPCComponents_Interfaces;
class nsXPCComponents_InterfacesByID;
class nsXPCComponents_Classes;
class nsXPCComponents_ClassesByID;
class nsXPCComponents_Results;
class nsXPCComponents_ID;
class nsXPCComponents_Exception;
class nsXPCComponents_Constructor;
class nsXPCComponents_Utils;
class nsXPCConstructor;
class AutoMarkingPtr;
class xpcProperty;
#endif /* xpcforwards_h___ */

View file

@ -0,0 +1,545 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* private inline methods (#include'd by xpcprivate.h). */
#ifndef xpcinlines_h___
#define xpcinlines_h___
#include <algorithm>
/***************************************************************************/
inline void
XPCJSContext::AddVariantRoot(XPCTraceableVariant* variant)
{
variant->AddToRootSet(&mVariantRoots);
}
inline void
XPCJSContext::AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS)
{
wrappedJS->AddToRootSet(&mWrappedJSRoots);
}
inline void
XPCJSContext::AddObjectHolderRoot(XPCJSObjectHolder* holder)
{
holder->AddToRootSet(&mObjectHolderRoots);
}
/***************************************************************************/
inline bool
XPCCallContext::IsValid() const
{
return mState != INIT_FAILED;
}
inline XPCJSContext*
XPCCallContext::GetContext() const
{
CHECK_STATE(HAVE_CONTEXT);
return mXPCJSContext;
}
inline JSContext*
XPCCallContext::GetJSContext() const
{
CHECK_STATE(HAVE_CONTEXT);
return mJSContext;
}
inline XPCCallContext*
XPCCallContext::GetPrevCallContext() const
{
CHECK_STATE(HAVE_CONTEXT);
return mPrevCallContext;
}
inline nsISupports*
XPCCallContext::GetIdentityObject() const
{
CHECK_STATE(HAVE_OBJECT);
if (mWrapper)
return mWrapper->GetIdentityObject();
return nullptr;
}
inline XPCWrappedNative*
XPCCallContext::GetWrapper() const
{
if (mState == INIT_FAILED)
return nullptr;
CHECK_STATE(HAVE_OBJECT);
return mWrapper;
}
inline XPCWrappedNativeProto*
XPCCallContext::GetProto() const
{
CHECK_STATE(HAVE_OBJECT);
return mWrapper ? mWrapper->GetProto() : nullptr;
}
inline bool
XPCCallContext::CanGetTearOff() const
{
return mState >= HAVE_OBJECT;
}
inline XPCWrappedNativeTearOff*
XPCCallContext::GetTearOff() const
{
CHECK_STATE(HAVE_OBJECT);
return mTearOff;
}
inline XPCNativeScriptableInfo*
XPCCallContext::GetScriptableInfo() const
{
CHECK_STATE(HAVE_OBJECT);
return mScriptableInfo;
}
inline bool
XPCCallContext::CanGetSet() const
{
return mState >= HAVE_NAME;
}
inline XPCNativeSet*
XPCCallContext::GetSet() const
{
CHECK_STATE(HAVE_NAME);
return mSet;
}
inline XPCNativeInterface*
XPCCallContext::GetInterface() const
{
CHECK_STATE(HAVE_NAME);
return mInterface;
}
inline XPCNativeMember*
XPCCallContext::GetMember() const
{
CHECK_STATE(HAVE_NAME);
return mMember;
}
inline bool
XPCCallContext::HasInterfaceAndMember() const
{
return mState >= HAVE_NAME && mInterface && mMember;
}
inline jsid
XPCCallContext::GetName() const
{
CHECK_STATE(HAVE_NAME);
return mName;
}
inline bool
XPCCallContext::GetStaticMemberIsLocal() const
{
CHECK_STATE(HAVE_NAME);
return mStaticMemberIsLocal;
}
inline unsigned
XPCCallContext::GetArgc() const
{
CHECK_STATE(READY_TO_CALL);
return mArgc;
}
inline JS::Value*
XPCCallContext::GetArgv() const
{
CHECK_STATE(READY_TO_CALL);
return mArgv;
}
inline JS::Value*
XPCCallContext::GetRetVal() const
{
CHECK_STATE(READY_TO_CALL);
return mRetVal;
}
inline void
XPCCallContext::SetRetVal(const JS::Value& val)
{
CHECK_STATE(HAVE_ARGS);
if (mRetVal)
*mRetVal = val;
}
inline jsid
XPCCallContext::GetResolveName() const
{
CHECK_STATE(HAVE_CONTEXT);
return XPCJSContext::Get()->GetResolveName();
}
inline jsid
XPCCallContext::SetResolveName(JS::HandleId name)
{
CHECK_STATE(HAVE_CONTEXT);
return XPCJSContext::Get()->SetResolveName(name);
}
inline XPCWrappedNative*
XPCCallContext::GetResolvingWrapper() const
{
CHECK_STATE(HAVE_OBJECT);
return XPCJSContext::Get()->GetResolvingWrapper();
}
inline XPCWrappedNative*
XPCCallContext::SetResolvingWrapper(XPCWrappedNative* w)
{
CHECK_STATE(HAVE_OBJECT);
return XPCJSContext::Get()->SetResolvingWrapper(w);
}
inline uint16_t
XPCCallContext::GetMethodIndex() const
{
CHECK_STATE(HAVE_OBJECT);
return mMethodIndex;
}
inline void
XPCCallContext::SetMethodIndex(uint16_t index)
{
CHECK_STATE(HAVE_OBJECT);
mMethodIndex = index;
}
/***************************************************************************/
inline XPCNativeInterface*
XPCNativeMember::GetInterface() const
{
XPCNativeMember* arrayStart =
const_cast<XPCNativeMember*>(this - mIndexInInterface);
size_t arrayStartOffset = XPCNativeInterface::OffsetOfMembers();
char* xpcNativeInterfaceStart =
reinterpret_cast<char*>(arrayStart) - arrayStartOffset;
return reinterpret_cast<XPCNativeInterface*>(xpcNativeInterfaceStart);
}
/***************************************************************************/
inline const nsIID*
XPCNativeInterface::GetIID() const
{
const nsIID* iid;
return NS_SUCCEEDED(mInfo->GetIIDShared(&iid)) ? iid : nullptr;
}
inline const char*
XPCNativeInterface::GetNameString() const
{
const char* name;
return NS_SUCCEEDED(mInfo->GetNameShared(&name)) ? name : nullptr;
}
inline XPCNativeMember*
XPCNativeInterface::FindMember(jsid name) const
{
const XPCNativeMember* member = mMembers;
for (int i = (int) mMemberCount; i > 0; i--, member++)
if (member->GetName() == name)
return const_cast<XPCNativeMember*>(member);
return nullptr;
}
inline bool
XPCNativeInterface::HasAncestor(const nsIID* iid) const
{
bool found = false;
mInfo->HasAncestor(iid, &found);
return found;
}
/* static */
inline size_t
XPCNativeInterface::OffsetOfMembers()
{
return offsetof(XPCNativeInterface, mMembers);
}
/***************************************************************************/
inline XPCNativeSetKey::XPCNativeSetKey(XPCNativeSet* baseSet,
XPCNativeInterface* addition)
: mBaseSet(baseSet)
, mAddition(addition)
{
MOZ_ASSERT(mBaseSet);
MOZ_ASSERT(mAddition);
MOZ_ASSERT(!mBaseSet->HasInterface(mAddition));
}
/***************************************************************************/
inline bool
XPCNativeSet::FindMember(jsid name, XPCNativeMember** pMember,
uint16_t* pInterfaceIndex) const
{
XPCNativeInterface* const * iface;
int count = (int) mInterfaceCount;
int i;
// look for interface names first
for (i = 0, iface = mInterfaces; i < count; i++, iface++) {
if (name == (*iface)->GetName()) {
if (pMember)
*pMember = nullptr;
if (pInterfaceIndex)
*pInterfaceIndex = (uint16_t) i;
return true;
}
}
// look for method names
for (i = 0, iface = mInterfaces; i < count; i++, iface++) {
XPCNativeMember* member = (*iface)->FindMember(name);
if (member) {
if (pMember)
*pMember = member;
if (pInterfaceIndex)
*pInterfaceIndex = (uint16_t) i;
return true;
}
}
return false;
}
inline bool
XPCNativeSet::FindMember(jsid name, XPCNativeMember** pMember,
RefPtr<XPCNativeInterface>* pInterface) const
{
uint16_t index;
if (!FindMember(name, pMember, &index))
return false;
*pInterface = mInterfaces[index];
return true;
}
inline bool
XPCNativeSet::FindMember(JS::HandleId name,
XPCNativeMember** pMember,
RefPtr<XPCNativeInterface>* pInterface,
XPCNativeSet* protoSet,
bool* pIsLocal) const
{
XPCNativeMember* Member;
RefPtr<XPCNativeInterface> Interface;
XPCNativeMember* protoMember;
if (!FindMember(name, &Member, &Interface))
return false;
*pMember = Member;
*pIsLocal =
!Member ||
!protoSet ||
(protoSet != this &&
!protoSet->MatchesSetUpToInterface(this, Interface) &&
(!protoSet->FindMember(name, &protoMember, (uint16_t*)nullptr) ||
protoMember != Member));
*pInterface = Interface.forget();
return true;
}
inline XPCNativeInterface*
XPCNativeSet::FindNamedInterface(jsid name) const
{
XPCNativeInterface* const * pp = mInterfaces;
for (int i = (int) mInterfaceCount; i > 0; i--, pp++) {
XPCNativeInterface* iface = *pp;
if (name == iface->GetName())
return iface;
}
return nullptr;
}
inline XPCNativeInterface*
XPCNativeSet::FindInterfaceWithIID(const nsIID& iid) const
{
XPCNativeInterface* const * pp = mInterfaces;
for (int i = (int) mInterfaceCount; i > 0; i--, pp++) {
XPCNativeInterface* iface = *pp;
if (iface->GetIID()->Equals(iid))
return iface;
}
return nullptr;
}
inline bool
XPCNativeSet::HasInterface(XPCNativeInterface* aInterface) const
{
XPCNativeInterface* const * pp = mInterfaces;
for (int i = (int) mInterfaceCount; i > 0; i--, pp++) {
if (aInterface == *pp)
return true;
}
return false;
}
inline bool
XPCNativeSet::HasInterfaceWithAncestor(XPCNativeInterface* aInterface) const
{
return HasInterfaceWithAncestor(aInterface->GetIID());
}
inline bool
XPCNativeSet::HasInterfaceWithAncestor(const nsIID* iid) const
{
// We can safely skip the first interface which is *always* nsISupports.
XPCNativeInterface* const * pp = mInterfaces+1;
for (int i = (int) mInterfaceCount; i > 1; i--, pp++)
if ((*pp)->HasAncestor(iid))
return true;
// This is rare, so check last.
if (iid == &NS_GET_IID(nsISupports))
return true;
return false;
}
inline bool
XPCNativeSet::MatchesSetUpToInterface(const XPCNativeSet* other,
XPCNativeInterface* iface) const
{
int count = std::min(int(mInterfaceCount), int(other->mInterfaceCount));
XPCNativeInterface* const * pp1 = mInterfaces;
XPCNativeInterface* const * pp2 = other->mInterfaces;
for (int i = (int) count; i > 0; i--, pp1++, pp2++) {
XPCNativeInterface* cur = (*pp1);
if (cur != (*pp2))
return false;
if (cur == iface)
return true;
}
return false;
}
/***************************************************************************/
inline
JSObject* XPCWrappedNativeTearOff::GetJSObjectPreserveColor() const
{
return mJSObject.unbarrieredGetPtr();
}
inline
JSObject* XPCWrappedNativeTearOff::GetJSObject()
{
return mJSObject;
}
inline
void XPCWrappedNativeTearOff::SetJSObject(JSObject* JSObj)
{
MOZ_ASSERT(!IsMarked());
mJSObject = JSObj;
}
inline
void XPCWrappedNativeTearOff::JSObjectMoved(JSObject* obj, const JSObject* old)
{
MOZ_ASSERT(!IsMarked());
MOZ_ASSERT(mJSObject.unbarrieredGetPtr() == old);
mJSObject = obj;
}
inline
XPCWrappedNativeTearOff::~XPCWrappedNativeTearOff()
{
MOZ_COUNT_DTOR(XPCWrappedNativeTearOff);
MOZ_ASSERT(!(GetInterface() || GetNative() || GetJSObjectPreserveColor()),
"tearoff not empty in dtor");
}
/***************************************************************************/
inline bool
XPCWrappedNative::HasInterfaceNoQI(const nsIID& iid)
{
return nullptr != GetSet()->FindInterfaceWithIID(iid);
}
inline void
XPCWrappedNative::SweepTearOffs()
{
for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
bool marked = to->IsMarked();
to->Unmark();
if (marked)
continue;
// If this tearoff does not have a live dedicated JSObject,
// then let's recycle it.
if (!to->GetJSObjectPreserveColor()) {
to->SetNative(nullptr);
to->SetInterface(nullptr);
}
}
}
/***************************************************************************/
inline bool
xpc_ForcePropertyResolve(JSContext* cx, JS::HandleObject obj, jsid idArg)
{
JS::RootedId id(cx, idArg);
bool dummy;
return JS_HasPropertyById(cx, obj, id, &dummy);
}
inline jsid
GetJSIDByIndex(JSContext* cx, unsigned index)
{
XPCJSContext* xpcx = nsXPConnect::XPConnect()->GetContext();
return xpcx->GetStringID(index);
}
inline
bool ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx)
{
XPCThrower::ThrowBadParam(rv, paramNum, ccx);
return false;
}
inline
void ThrowBadResult(nsresult result, XPCCallContext& ccx)
{
XPCThrower::ThrowBadResult(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE,
result, ccx);
}
/***************************************************************************/
#endif /* xpcinlines_h___ */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,816 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* An xpcom implementation of the JavaScript nsIID and nsCID objects. */
#include "xpcprivate.h"
#include "xpc_make_class.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/StaticPtr.h"
using namespace mozilla::dom;
using namespace JS;
/***************************************************************************/
// nsJSID
NS_IMPL_CLASSINFO(nsJSID, nullptr, 0, NS_JS_ID_CID)
NS_IMPL_ISUPPORTS_CI(nsJSID, nsIJSID)
const char nsJSID::gNoString[] = "";
nsJSID::nsJSID()
: mID(GetInvalidIID()),
mNumber(const_cast<char*>(gNoString)),
mName(const_cast<char*>(gNoString))
{
}
nsJSID::~nsJSID()
{
if (mNumber && mNumber != gNoString)
free(mNumber);
if (mName && mName != gNoString)
free(mName);
}
void nsJSID::Reset()
{
mID = GetInvalidIID();
if (mNumber && mNumber != gNoString)
free(mNumber);
if (mName && mName != gNoString)
free(mName);
mNumber = mName = nullptr;
}
bool
nsJSID::SetName(const char* name)
{
MOZ_ASSERT(!mName || mName == gNoString ,"name already set");
MOZ_ASSERT(name,"null name");
mName = NS_strdup(name);
return mName ? true : false;
}
NS_IMETHODIMP
nsJSID::GetName(char * *aName)
{
if (!aName)
return NS_ERROR_NULL_POINTER;
if (!NameIsSet())
SetNameToNoString();
MOZ_ASSERT(mName, "name not set");
*aName = NS_strdup(mName);
return *aName ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
nsJSID::GetNumber(char * *aNumber)
{
if (!aNumber)
return NS_ERROR_NULL_POINTER;
if (!mNumber) {
if (!(mNumber = mID.ToString()))
mNumber = const_cast<char*>(gNoString);
}
*aNumber = NS_strdup(mNumber);
return *aNumber ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP_(const nsID*)
nsJSID::GetID()
{
return &mID;
}
NS_IMETHODIMP
nsJSID::GetValid(bool* aValid)
{
if (!aValid)
return NS_ERROR_NULL_POINTER;
*aValid = IsValid();
return NS_OK;
}
NS_IMETHODIMP
nsJSID::Equals(nsIJSID* other, bool* _retval)
{
if (!_retval)
return NS_ERROR_NULL_POINTER;
if (!other || mID.Equals(GetInvalidIID())) {
*_retval = false;
return NS_OK;
}
*_retval = other->GetID()->Equals(mID);
return NS_OK;
}
NS_IMETHODIMP
nsJSID::Initialize(const char* idString)
{
if (!idString)
return NS_ERROR_NULL_POINTER;
if (*idString != '\0' && mID.Equals(GetInvalidIID())) {
Reset();
if (idString[0] == '{') {
if (mID.Parse(idString)) {
return NS_OK;
}
// error - reset to invalid state
mID = GetInvalidIID();
}
}
return NS_ERROR_FAILURE;
}
bool
nsJSID::InitWithName(const nsID& id, const char* nameString)
{
MOZ_ASSERT(nameString, "no name");
Reset();
mID = id;
return SetName(nameString);
}
// try to use the name, if no name, then use the number
NS_IMETHODIMP
nsJSID::ToString(char** _retval)
{
if (mName && mName != gNoString)
return GetName(_retval);
return GetNumber(_retval);
}
const nsID&
nsJSID::GetInvalidIID() const
{
// {BB1F47B0-D137-11d2-9841-006008962422}
static const nsID invalid = {0xbb1f47b0, 0xd137, 0x11d2,
{0x98, 0x41, 0x0, 0x60, 0x8, 0x96, 0x24, 0x22}};
return invalid;
}
//static
already_AddRefed<nsJSID>
nsJSID::NewID(const char* str)
{
if (!str) {
NS_ERROR("no string");
return nullptr;
}
RefPtr<nsJSID> idObj = new nsJSID();
NS_ENSURE_SUCCESS(idObj->Initialize(str), nullptr);
return idObj.forget();
}
//static
already_AddRefed<nsJSID>
nsJSID::NewID(const nsID& id)
{
RefPtr<nsJSID> idObj = new nsJSID();
idObj->mID = id;
idObj->mName = nullptr;
idObj->mNumber = nullptr;
return idObj.forget();
}
/***************************************************************************/
// Class object support so that we can share prototypes of wrapper
// This class exists just so we can have a shared scriptable helper for
// the nsJSIID class. The instances implement their own helpers. But we
// needed to be able to indicate to the shared prototypes this single flag:
// nsIXPCScriptable::DONT_ENUM_STATIC_PROPS. And having a class to do it is
// the only means we have. Setting this flag on any given instance scriptable
// helper is not sufficient to convey the information that we don't want
// static properties enumerated on the shared proto.
class SharedScriptableHelperForJSIID final : public nsIXPCScriptable
{
~SharedScriptableHelperForJSIID() {}
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIXPCSCRIPTABLE
SharedScriptableHelperForJSIID() {}
};
NS_INTERFACE_MAP_BEGIN(SharedScriptableHelperForJSIID)
NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCScriptable)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(SharedScriptableHelperForJSIID)
NS_IMPL_RELEASE(SharedScriptableHelperForJSIID)
// The nsIXPCScriptable map declaration that will generate stubs for us...
#define XPC_MAP_CLASSNAME SharedScriptableHelperForJSIID
#define XPC_MAP_QUOTED_CLASSNAME "JSIID"
#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
#include "xpc_map_end.h" /* This will #undef the above */
static mozilla::StaticRefPtr<nsIXPCScriptable> gSharedScriptableHelperForJSIID;
static bool gClassObjectsWereInited = false;
static void EnsureClassObjectsInitialized()
{
if (!gClassObjectsWereInited) {
gSharedScriptableHelperForJSIID = new SharedScriptableHelperForJSIID();
gClassObjectsWereInited = true;
}
}
static nsresult GetSharedScriptableHelperForJSIID(nsIXPCScriptable** helper)
{
EnsureClassObjectsInitialized();
nsCOMPtr<nsIXPCScriptable> temp = gSharedScriptableHelperForJSIID.get();
temp.forget(helper);
return NS_OK;
}
/******************************************************/
#define NULL_CID \
{ 0x00000000, 0x0000, 0x0000, \
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
// We pass nsIClassInfo::DOM_OBJECT so that nsJSIID instances may be created
// in unprivileged scopes.
NS_DECL_CI_INTERFACE_GETTER(nsJSIID)
NS_IMPL_CLASSINFO(nsJSIID, GetSharedScriptableHelperForJSIID,
nsIClassInfo::DOM_OBJECT, NULL_CID)
NS_DECL_CI_INTERFACE_GETTER(nsJSCID)
NS_IMPL_CLASSINFO(nsJSCID, nullptr, 0, NULL_CID)
void xpc_DestroyJSxIDClassObjects()
{
if (gClassObjectsWereInited) {
NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSIID));
NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSCID));
gSharedScriptableHelperForJSIID = nullptr;
gClassObjectsWereInited = false;
}
}
/***************************************************************************/
NS_INTERFACE_MAP_BEGIN(nsJSIID)
NS_INTERFACE_MAP_ENTRY(nsIJSID)
NS_INTERFACE_MAP_ENTRY(nsIJSIID)
NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSID)
NS_IMPL_QUERY_CLASSINFO(nsJSIID)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(nsJSIID)
NS_IMPL_RELEASE(nsJSIID)
NS_IMPL_CI_INTERFACE_GETTER(nsJSIID, nsIJSID, nsIJSIID)
// The nsIXPCScriptable map declaration that will generate stubs for us...
#define XPC_MAP_CLASSNAME nsJSIID
#define XPC_MAP_QUOTED_CLASSNAME "nsJSIID"
#define XPC_MAP_WANT_RESOLVE
#define XPC_MAP_WANT_ENUMERATE
#define XPC_MAP_WANT_HASINSTANCE
#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
#include "xpc_map_end.h" /* This will #undef the above */
nsJSIID::nsJSIID(nsIInterfaceInfo* aInfo)
: mInfo(aInfo)
{
}
nsJSIID::~nsJSIID() {}
// If mInfo is present we use it and ignore mDetails, else we use mDetails.
NS_IMETHODIMP nsJSIID::GetName(char * *aName)
{
return mInfo->GetName(aName);
}
NS_IMETHODIMP nsJSIID::GetNumber(char * *aNumber)
{
char str[NSID_LENGTH];
const nsIID* id;
mInfo->GetIIDShared(&id);
id->ToProvidedString(str);
*aNumber = (char*) nsMemory::Clone(str, NSID_LENGTH);
return *aNumber ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP_(const nsID*) nsJSIID::GetID()
{
const nsIID* id;
mInfo->GetIIDShared(&id);
return id;
}
NS_IMETHODIMP nsJSIID::GetValid(bool* aValid)
{
*aValid = true;
return NS_OK;
}
NS_IMETHODIMP nsJSIID::Equals(nsIJSID* other, bool* _retval)
{
if (!_retval)
return NS_ERROR_NULL_POINTER;
if (!other) {
*_retval = false;
return NS_OK;
}
mInfo->IsIID(other->GetID(), _retval);
return NS_OK;
}
NS_IMETHODIMP nsJSIID::Initialize(const char* idString)
{
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP nsJSIID::ToString(char** _retval)
{
return mInfo->GetName(_retval);
}
// static
already_AddRefed<nsJSIID>
nsJSIID::NewID(nsIInterfaceInfo* aInfo)
{
if (!aInfo) {
NS_ERROR("no info");
return nullptr;
}
bool canScript;
if (NS_FAILED(aInfo->IsScriptable(&canScript)) || !canScript)
return nullptr;
RefPtr<nsJSIID> idObj = new nsJSIID(aInfo);
return idObj.forget();
}
NS_IMETHODIMP
nsJSIID::Resolve(nsIXPConnectWrappedNative* wrapper,
JSContext * cx, JSObject * objArg,
jsid idArg, bool* resolvedp,
bool* _retval)
{
RootedObject obj(cx, objArg);
RootedId id(cx, idArg);
XPCCallContext ccx(cx);
RefPtr<XPCNativeInterface> iface =
XPCNativeInterface::GetNewOrUsed(mInfo);
if (!iface)
return NS_OK;
XPCNativeMember* member = iface->FindMember(id);
if (member && member->IsConstant()) {
RootedValue val(cx);
if (!member->GetConstantValue(ccx, iface, val.address()))
return NS_ERROR_OUT_OF_MEMORY;
*resolvedp = true;
*_retval = JS_DefinePropertyById(cx, obj, id, val,
JSPROP_ENUMERATE | JSPROP_READONLY |
JSPROP_PERMANENT | JSPROP_RESOLVING);
}
return NS_OK;
}
NS_IMETHODIMP
nsJSIID::Enumerate(nsIXPConnectWrappedNative* wrapper,
JSContext * cx, JSObject * objArg, bool* _retval)
{
// In this case, let's just eagerly resolve...
RootedObject obj(cx, objArg);
XPCCallContext ccx(cx);
RefPtr<XPCNativeInterface> iface =
XPCNativeInterface::GetNewOrUsed(mInfo);
if (!iface)
return NS_OK;
uint16_t count = iface->GetMemberCount();
for (uint16_t i = 0; i < count; i++) {
XPCNativeMember* member = iface->GetMemberAt(i);
if (member && member->IsConstant() &&
!xpc_ForcePropertyResolve(cx, obj, member->GetName())) {
return NS_ERROR_UNEXPECTED;
}
}
return NS_OK;
}
/*
* HasInstance hooks need to find an appropriate reflector in order to function
* properly. There are two complexities that we need to handle:
*
* 1 - Cross-compartment wrappers. Chrome uses over 100 compartments, all with
* system principal. The success of an instanceof check should not depend
* on which compartment an object comes from. At the same time, we want to
* make sure we don't unwrap important security wrappers.
* CheckedUnwrap does the right thing here.
*
* 2 - Prototype chains. Suppose someone creates a vanilla JS object |a| and
* sets its __proto__ to some WN |b|. If |b instanceof nsIFoo| returns true,
* one would expect |a instanceof nsIFoo| to return true as well, since
* instanceof is transitive up the prototype chain in ECMAScript. Moreover,
* there's chrome code that relies on this.
*
* This static method handles both complexities, returning either an XPCWN, a
* DOM object, or null. The object may well be cross-compartment from |cx|.
*/
static nsresult
FindObjectForHasInstance(JSContext* cx, HandleObject objArg, MutableHandleObject target)
{
RootedObject obj(cx, objArg), proto(cx);
while (obj && !IS_WN_REFLECTOR(obj) &&
!IsDOMObject(obj) && !mozilla::jsipc::IsCPOW(obj))
{
if (js::IsWrapper(obj)) {
obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
continue;
}
{
JSAutoCompartment ac(cx, obj);
if (!js::GetObjectProto(cx, obj, &proto))
return NS_ERROR_FAILURE;
}
obj = proto;
}
target.set(obj);
return NS_OK;
}
nsresult
xpc::HasInstance(JSContext* cx, HandleObject objArg, const nsID* iid, bool* bp)
{
*bp = false;
RootedObject obj(cx);
nsresult rv = FindObjectForHasInstance(cx, objArg, &obj);
if (NS_WARN_IF(NS_FAILED(rv)))
return rv;
if (!obj)
return NS_OK;
if (mozilla::jsipc::IsCPOW(obj))
return mozilla::jsipc::InstanceOf(obj, iid, bp);
nsCOMPtr<nsISupports> identity = UnwrapReflectorToISupports(obj);
if (!identity)
return NS_OK;
nsCOMPtr<nsISupports> supp;
identity->QueryInterface(*iid, getter_AddRefs(supp));
*bp = supp;
// Our old HasInstance implementation operated by invoking FindTearOff on
// XPCWrappedNatives, and various bits of chrome JS came to depend on
// |instanceof| doing an implicit QI if it succeeds. Do a drive-by QI to
// preserve that behavior. This is just a compatibility hack, so we don't
// really care if it fails.
if (IS_WN_REFLECTOR(obj))
(void) XPCWrappedNative::Get(obj)->FindTearOff(*iid);
return NS_OK;
}
NS_IMETHODIMP
nsJSIID::HasInstance(nsIXPConnectWrappedNative* wrapper,
JSContext* cx, JSObject * /* unused */,
HandleValue val, bool* bp, bool* _retval)
{
*bp = false;
if (val.isPrimitive())
return NS_OK;
// we have a JSObject
RootedObject obj(cx, &val.toObject());
const nsIID* iid;
mInfo->GetIIDShared(&iid);
return xpc::HasInstance(cx, obj, iid, bp);
}
/***************************************************************************/
NS_INTERFACE_MAP_BEGIN(nsJSCID)
NS_INTERFACE_MAP_ENTRY(nsIJSID)
NS_INTERFACE_MAP_ENTRY(nsIJSCID)
NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSID)
NS_IMPL_QUERY_CLASSINFO(nsJSCID)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(nsJSCID)
NS_IMPL_RELEASE(nsJSCID)
NS_IMPL_CI_INTERFACE_GETTER(nsJSCID, nsIJSID, nsIJSCID)
// The nsIXPCScriptable map declaration that will generate stubs for us...
#define XPC_MAP_CLASSNAME nsJSCID
#define XPC_MAP_QUOTED_CLASSNAME "nsJSCID"
#define XPC_MAP_WANT_CONSTRUCT
#define XPC_MAP_WANT_HASINSTANCE
#define XPC_MAP_FLAGS 0
#include "xpc_map_end.h" /* This will #undef the above */
nsJSCID::nsJSCID() { mDetails = new nsJSID(); }
nsJSCID::~nsJSCID() {}
NS_IMETHODIMP nsJSCID::GetName(char * *aName)
{ResolveName(); return mDetails->GetName(aName);}
NS_IMETHODIMP nsJSCID::GetNumber(char * *aNumber)
{return mDetails->GetNumber(aNumber);}
NS_IMETHODIMP_(const nsID*) nsJSCID::GetID()
{return &mDetails->ID();}
NS_IMETHODIMP nsJSCID::GetValid(bool* aValid)
{return mDetails->GetValid(aValid);}
NS_IMETHODIMP nsJSCID::Equals(nsIJSID* other, bool* _retval)
{return mDetails->Equals(other, _retval);}
NS_IMETHODIMP nsJSCID::Initialize(const char* idString)
{return mDetails->Initialize(idString);}
NS_IMETHODIMP nsJSCID::ToString(char** _retval)
{ResolveName(); return mDetails->ToString(_retval);}
void
nsJSCID::ResolveName()
{
if (!mDetails->NameIsSet())
mDetails->SetNameToNoString();
}
//static
already_AddRefed<nsJSCID>
nsJSCID::NewID(const char* str)
{
if (!str) {
NS_ERROR("no string");
return nullptr;
}
RefPtr<nsJSCID> idObj = new nsJSCID();
if (str[0] == '{') {
NS_ENSURE_SUCCESS(idObj->Initialize(str), nullptr);
} else {
nsCOMPtr<nsIComponentRegistrar> registrar;
NS_GetComponentRegistrar(getter_AddRefs(registrar));
NS_ENSURE_TRUE(registrar, nullptr);
nsCID* cid;
if (NS_FAILED(registrar->ContractIDToCID(str, &cid)))
return nullptr;
bool success = idObj->mDetails->InitWithName(*cid, str);
free(cid);
if (!success)
return nullptr;
}
return idObj.forget();
}
static const nsID*
GetIIDArg(uint32_t argc, const JS::Value& val, JSContext* cx)
{
const nsID* iid;
// If an IID was passed in then use it
if (argc) {
JSObject* iidobj;
if (val.isPrimitive() ||
!(iidobj = val.toObjectOrNull()) ||
!(iid = xpc_JSObjectToID(cx, iidobj))) {
return nullptr;
}
} else
iid = &NS_GET_IID(nsISupports);
return iid;
}
NS_IMETHODIMP
nsJSCID::CreateInstance(HandleValue iidval, JSContext* cx,
uint8_t optionalArgc, MutableHandleValue retval)
{
if (!mDetails->IsValid())
return NS_ERROR_XPC_BAD_CID;
if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, mDetails->ID()))) {
NS_ERROR("how are we not being called from chrome here?");
return NS_OK;
}
// If an IID was passed in then use it
const nsID* iid = GetIIDArg(optionalArgc, iidval, cx);
if (!iid)
return NS_ERROR_XPC_BAD_IID;
nsCOMPtr<nsIComponentManager> compMgr;
nsresult rv = NS_GetComponentManager(getter_AddRefs(compMgr));
if (NS_FAILED(rv))
return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsISupports> inst;
rv = compMgr->CreateInstance(mDetails->ID(), nullptr, *iid, getter_AddRefs(inst));
MOZ_ASSERT(NS_FAILED(rv) || inst, "component manager returned success, but instance is null!");
if (NS_FAILED(rv) || !inst)
return NS_ERROR_XPC_CI_RETURNED_FAILURE;
rv = nsContentUtils::WrapNative(cx, inst, iid, retval);
if (NS_FAILED(rv) || retval.isPrimitive())
return NS_ERROR_XPC_CANT_CREATE_WN;
return NS_OK;
}
NS_IMETHODIMP
nsJSCID::GetService(HandleValue iidval, JSContext* cx, uint8_t optionalArgc,
MutableHandleValue retval)
{
if (!mDetails->IsValid())
return NS_ERROR_XPC_BAD_CID;
if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, mDetails->ID()))) {
MOZ_ASSERT(JS_IsExceptionPending(cx),
"security manager vetoed GetService without setting exception");
return NS_OK;
}
// If an IID was passed in then use it
const nsID* iid = GetIIDArg(optionalArgc, iidval, cx);
if (!iid)
return NS_ERROR_XPC_BAD_IID;
nsCOMPtr<nsIServiceManager> svcMgr;
nsresult rv = NS_GetServiceManager(getter_AddRefs(svcMgr));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsISupports> srvc;
rv = svcMgr->GetService(mDetails->ID(), *iid, getter_AddRefs(srvc));
MOZ_ASSERT(NS_FAILED(rv) || srvc, "service manager returned success, but service is null!");
if (NS_FAILED(rv) || !srvc)
return NS_ERROR_XPC_GS_RETURNED_FAILURE;
RootedValue v(cx);
rv = nsContentUtils::WrapNative(cx, srvc, iid, &v);
if (NS_FAILED(rv) || !v.isObject())
return NS_ERROR_XPC_CANT_CREATE_WN;
retval.set(v);
return NS_OK;
}
NS_IMETHODIMP
nsJSCID::Construct(nsIXPConnectWrappedNative* wrapper,
JSContext* cx, JSObject* objArg,
const CallArgs& args, bool* _retval)
{
RootedObject obj(cx, objArg);
XPCJSContext* xpccx = nsXPConnect::GetContextInstance();
if (!xpccx)
return NS_ERROR_FAILURE;
// 'push' a call context and call on it
RootedId name(cx, xpccx->GetStringID(XPCJSContext::IDX_CREATE_INSTANCE));
XPCCallContext ccx(cx, obj, nullptr, name, args.length(), args.array(),
args.rval().address());
*_retval = XPCWrappedNative::CallMethod(ccx);
return NS_OK;
}
NS_IMETHODIMP
nsJSCID::HasInstance(nsIXPConnectWrappedNative* wrapper,
JSContext* cx, JSObject * /* unused */,
HandleValue val, bool* bp, bool* _retval)
{
*bp = false;
if (!val.isObject())
return NS_OK;
RootedObject obj(cx, &val.toObject());
// is this really a native xpcom object with a wrapper?
RootedObject target(cx);
nsresult rv = FindObjectForHasInstance(cx, obj, &target);
if (NS_WARN_IF(NS_FAILED(rv)))
return rv;
if (!target || !IS_WN_REFLECTOR(target))
return NS_OK;
if (XPCWrappedNative* other_wrapper = XPCWrappedNative::Get(target)) {
if (nsIClassInfo* ci = other_wrapper->GetClassInfo()) {
// We consider CID equality to be the thing that matters here.
// This is perhaps debatable.
nsID cid;
if (NS_SUCCEEDED(ci->GetClassIDNoAlloc(&cid)))
*bp = cid.Equals(mDetails->ID());
}
}
return NS_OK;
}
/***************************************************************************/
// additional utilities...
JSObject*
xpc_NewIDObject(JSContext* cx, HandleObject jsobj, const nsID& aID)
{
RootedObject obj(cx);
nsCOMPtr<nsIJSID> iid = nsJSID::NewID(aID);
if (iid) {
nsXPConnect* xpc = nsXPConnect::XPConnect();
if (xpc) {
xpc->WrapNative(cx, jsobj, static_cast<nsISupports*>(iid),
NS_GET_IID(nsIJSID), obj.address());
}
}
return obj;
}
// note: returned pointer is only valid while |obj| remains alive!
const nsID*
xpc_JSObjectToID(JSContext* cx, JSObject* obj)
{
if (!cx || !obj)
return nullptr;
// NOTE: this call does NOT addref
XPCWrappedNative* wrapper = nullptr;
obj = js::CheckedUnwrap(obj);
if (obj && IS_WN_REFLECTOR(obj))
wrapper = XPCWrappedNative::Get(obj);
if (wrapper &&
(wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID)) ||
wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) ||
wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID)))) {
return ((nsIJSID*)wrapper->GetIdentityObject())->GetID();
}
return nullptr;
}
bool
xpc_JSObjectIsID(JSContext* cx, JSObject* obj)
{
MOZ_ASSERT(cx && obj, "bad param");
// NOTE: this call does NOT addref
XPCWrappedNative* wrapper = nullptr;
obj = js::CheckedUnwrap(obj);
if (obj && IS_WN_REFLECTOR(obj))
wrapper = XPCWrappedNative::Get(obj);
return wrapper &&
(wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSID)) ||
wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSIID)) ||
wrapper->HasInterfaceNoQI(NS_GET_IID(nsIJSCID)));
}

View file

@ -0,0 +1,33 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 XPCJSMemoryReporter_h
#define XPCJSMemoryReporter_h
class nsISupports;
class nsIMemoryReporterCallback;
namespace xpc {
// The key is the window ID.
typedef nsDataHashtable<nsUint64HashKey, nsCString> WindowPaths;
// This is very nearly an instance of nsIMemoryReporter, but it's not,
// because it's invoked by nsWindowMemoryReporter in order to get |windowPaths|
// in CollectReports.
class JSReporter
{
public:
static void CollectReports(WindowPaths* windowPaths,
WindowPaths* topWindowPaths,
nsIMemoryReporterCallback* handleReport,
nsISupports* data,
bool anonymize);
};
} // namespace xpc
#endif

View file

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 "xpcprivate.h"
#include "XPCJSWeakReference.h"
#include "nsContentUtils.h"
using namespace JS;
xpcJSWeakReference::xpcJSWeakReference()
{
}
NS_IMPL_ISUPPORTS(xpcJSWeakReference, xpcIJSWeakReference)
nsresult xpcJSWeakReference::Init(JSContext* cx, const JS::Value& object)
{
if (!object.isObject())
return NS_OK;
JS::RootedObject obj(cx, &object.toObject());
XPCCallContext ccx(cx);
// See if the object is a wrapped native that supports weak references.
nsCOMPtr<nsISupports> supports = xpc::UnwrapReflectorToISupports(obj);
nsCOMPtr<nsISupportsWeakReference> supportsWeakRef =
do_QueryInterface(supports);
if (supportsWeakRef) {
supportsWeakRef->GetWeakReference(getter_AddRefs(mReferent));
if (mReferent) {
return NS_OK;
}
}
// If it's not a wrapped native, or it is a wrapped native that does not
// support weak references, fall back to getting a weak ref to the object.
// See if object is a wrapped JSObject.
RefPtr<nsXPCWrappedJS> wrapped;
nsresult rv = nsXPCWrappedJS::GetNewOrUsed(obj,
NS_GET_IID(nsISupports),
getter_AddRefs(wrapped));
if (!wrapped) {
NS_ERROR("can't get nsISupportsWeakReference wrapper for obj");
return rv;
}
return wrapped->GetWeakReference(getter_AddRefs(mReferent));
}
NS_IMETHODIMP
xpcJSWeakReference::Get(JSContext* aCx, MutableHandleValue aRetval)
{
aRetval.setNull();
if (!mReferent) {
return NS_OK;
}
nsCOMPtr<nsISupports> supports = do_QueryReferent(mReferent);
if (!supports) {
return NS_OK;
}
nsCOMPtr<nsIXPConnectWrappedJS> wrappedObj = do_QueryInterface(supports);
if (!wrappedObj) {
// We have a generic XPCOM object that supports weak references here.
// Wrap it and pass it out.
return nsContentUtils::WrapNative(aCx, supports,
&NS_GET_IID(nsISupports),
aRetval);
}
JS::RootedObject obj(aCx, wrappedObj->GetJSObject());
if (!obj) {
return NS_OK;
}
// Most users of XPCWrappedJS don't need to worry about
// re-wrapping because things are implicitly rewrapped by
// xpcconvert. However, because we're doing this directly
// through the native call context, we need to call
// JS_WrapObject().
if (!JS_WrapObject(aCx, &obj)) {
return NS_ERROR_FAILURE;
}
aRetval.setObject(*obj);
return NS_OK;
}

View file

@ -0,0 +1,29 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 xpcjsweakreference_h___
#define xpcjsweakreference_h___
#include "xpcIJSWeakReference.h"
#include "nsIWeakReference.h"
#include "mozilla/Attributes.h"
class xpcJSWeakReference final : public xpcIJSWeakReference
{
~xpcJSWeakReference() {}
public:
xpcJSWeakReference();
nsresult Init(JSContext* cx, const JS::Value& object);
NS_DECL_ISUPPORTS
NS_DECL_XPCIJSWEAKREFERENCE
private:
nsCOMPtr<nsIWeakReference> mReferent;
};
#endif // xpcjsweakreference_h___

View file

@ -0,0 +1,289 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/Assertions.h"
#include "jsapi.h"
#include "nsCollationCID.h"
#include "nsJSUtils.h"
#include "nsIPlatformCharset.h"
#include "nsILocaleService.h"
#include "nsICollation.h"
#include "nsUnicharUtils.h"
#include "nsComponentManagerUtils.h"
#include "nsServiceManagerUtils.h"
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/Preferences.h"
#include "nsIUnicodeDecoder.h"
#include "xpcpublic.h"
using namespace JS;
using mozilla::dom::EncodingUtils;
/**
* JS locale callbacks implemented by XPCOM modules. These are theoretically
* safe for use on multiple threads. Unfortunately, the intl code underlying
* these XPCOM modules doesn't yet support this, so in practice
* XPCLocaleCallbacks are limited to the main thread.
*/
struct XPCLocaleCallbacks : public JSLocaleCallbacks
{
XPCLocaleCallbacks()
#ifdef DEBUG
: mThread(PR_GetCurrentThread())
#endif
{
MOZ_COUNT_CTOR(XPCLocaleCallbacks);
localeToUpperCase = LocaleToUpperCase;
localeToLowerCase = LocaleToLowerCase;
localeCompare = LocaleCompare;
localeToUnicode = LocaleToUnicode;
}
~XPCLocaleCallbacks()
{
AssertThreadSafety();
MOZ_COUNT_DTOR(XPCLocaleCallbacks);
}
/**
* Return the XPCLocaleCallbacks that's hidden away in |cx|. (This impl uses
* the locale callbacks struct to store away its per-context data.)
*/
static XPCLocaleCallbacks*
This(JSContext* cx)
{
// Locale information for |cx| was associated using xpc_LocalizeContext;
// assert and double-check this.
const JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(cx);
MOZ_ASSERT(lc);
MOZ_ASSERT(lc->localeToUpperCase == LocaleToUpperCase);
MOZ_ASSERT(lc->localeToLowerCase == LocaleToLowerCase);
MOZ_ASSERT(lc->localeCompare == LocaleCompare);
MOZ_ASSERT(lc->localeToUnicode == LocaleToUnicode);
const XPCLocaleCallbacks* ths = static_cast<const XPCLocaleCallbacks*>(lc);
ths->AssertThreadSafety();
return const_cast<XPCLocaleCallbacks*>(ths);
}
static bool
LocaleToUpperCase(JSContext* cx, HandleString src, MutableHandleValue rval)
{
return ChangeCase(cx, src, rval, ToUpperCase);
}
static bool
LocaleToLowerCase(JSContext* cx, HandleString src, MutableHandleValue rval)
{
return ChangeCase(cx, src, rval, ToLowerCase);
}
static bool
LocaleToUnicode(JSContext* cx, const char* src, MutableHandleValue rval)
{
return This(cx)->ToUnicode(cx, src, rval);
}
static bool
LocaleCompare(JSContext* cx, HandleString src1, HandleString src2, MutableHandleValue rval)
{
return This(cx)->Compare(cx, src1, src2, rval);
}
private:
static bool
ChangeCase(JSContext* cx, HandleString src, MutableHandleValue rval,
void(*changeCaseFnc)(const nsAString&, nsAString&))
{
nsAutoJSString autoStr;
if (!autoStr.init(cx, src)) {
return false;
}
nsAutoString result;
changeCaseFnc(autoStr, result);
JSString* ucstr =
JS_NewUCStringCopyN(cx, result.get(), result.Length());
if (!ucstr) {
return false;
}
rval.setString(ucstr);
return true;
}
bool
Compare(JSContext* cx, HandleString src1, HandleString src2, MutableHandleValue rval)
{
nsresult rv;
if (!mCollation) {
nsCOMPtr<nsILocaleService> localeService =
do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsILocale> locale;
rv = localeService->GetApplicationLocale(getter_AddRefs(locale));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsICollationFactory> colFactory =
do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation));
}
}
}
if (NS_FAILED(rv)) {
xpc::Throw(cx, rv);
return false;
}
}
nsAutoJSString autoStr1, autoStr2;
if (!autoStr1.init(cx, src1) || !autoStr2.init(cx, src2)) {
return false;
}
int32_t result;
rv = mCollation->CompareString(nsICollation::kCollationStrengthDefault,
autoStr1, autoStr2, &result);
if (NS_FAILED(rv)) {
xpc::Throw(cx, rv);
return false;
}
rval.setInt32(result);
return true;
}
bool
ToUnicode(JSContext* cx, const char* src, MutableHandleValue rval)
{
nsresult rv;
if (!mDecoder) {
// use app default locale
nsCOMPtr<nsILocaleService> localeService =
do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsILocale> appLocale;
rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
if (NS_SUCCEEDED(rv)) {
nsAutoString localeStr;
rv = appLocale->
GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr);
MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info");
nsCOMPtr<nsIPlatformCharset> platformCharset =
do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
if (NS_SUCCEEDED(rv)) {
nsAutoCString charset;
rv = platformCharset->GetDefaultCharsetForLocale(localeStr, charset);
if (NS_SUCCEEDED(rv)) {
mDecoder = EncodingUtils::DecoderForEncoding(charset);
}
}
}
}
}
int32_t srcLength = strlen(src);
if (mDecoder) {
int32_t unicharLength = srcLength;
char16_t* unichars =
(char16_t*)JS_malloc(cx, (srcLength + 1) * sizeof(char16_t));
if (unichars) {
rv = mDecoder->Convert(src, &srcLength, unichars, &unicharLength);
if (NS_SUCCEEDED(rv)) {
// terminate the returned string
unichars[unicharLength] = 0;
// nsIUnicodeDecoder::Convert may use fewer than srcLength PRUnichars
if (unicharLength + 1 < srcLength + 1) {
char16_t* shrunkUnichars =
(char16_t*)JS_realloc(cx, unichars,
(srcLength + 1) * sizeof(char16_t),
(unicharLength + 1) * sizeof(char16_t));
if (shrunkUnichars)
unichars = shrunkUnichars;
}
JSString* str = JS_NewUCString(cx, reinterpret_cast<char16_t*>(unichars), unicharLength);
if (str) {
rval.setString(str);
return true;
}
}
JS_free(cx, unichars);
}
}
xpc::Throw(cx, NS_ERROR_OUT_OF_MEMORY);
return false;
}
void AssertThreadSafety() const
{
MOZ_ASSERT(mThread == PR_GetCurrentThread(),
"XPCLocaleCallbacks used unsafely!");
}
nsCOMPtr<nsICollation> mCollation;
nsCOMPtr<nsIUnicodeDecoder> mDecoder;
#ifdef DEBUG
PRThread* mThread;
#endif
};
bool
xpc_LocalizeContext(JSContext* cx)
{
JS_SetLocaleCallbacks(cx, new XPCLocaleCallbacks());
// Set the default locale.
// Check a pref to see if we should use US English locale regardless
// of the system locale.
if (Preferences::GetBool("javascript.use_us_english_locale", false)) {
return JS_SetDefaultLocale(cx, "en-US");
}
// No pref has been found, so get the default locale from the
// application's locale.
nsCOMPtr<nsILocaleService> localeService =
do_GetService(NS_LOCALESERVICE_CONTRACTID);
if (!localeService)
return false;
nsCOMPtr<nsILocale> appLocale;
nsresult rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
if (NS_FAILED(rv))
return false;
nsAutoString localeStr;
rv = appLocale->GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr);
MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to get app locale info");
NS_LossyConvertUTF16toASCII locale(localeStr);
return JS_SetDefaultLocale(cx, locale.get());
}
void
xpc_DelocalizeContext(JSContext* cx)
{
const XPCLocaleCallbacks* lc = XPCLocaleCallbacks::This(cx);
JS_SetLocaleCallbacks(cx, nullptr);
delete lc;
}

View file

@ -0,0 +1,94 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Debug Logging support. */
#include "XPCLog.h"
#include "mozilla/Logging.h"
#include "prprf.h"
#include "mozilla/mozalloc.h"
#include <string.h>
#include <stdarg.h>
// this all only works for DEBUG...
#ifdef DEBUG
#define SPACE_COUNT 200
#define LINE_LEN 200
#define INDENT_FACTOR 2
#define CAN_RUN (g_InitState == 1 || (g_InitState == 0 && Init()))
static char* g_Spaces;
static int g_InitState = 0;
static int g_Indent = 0;
static mozilla::LazyLogModule g_LogMod("xpclog");
static bool Init()
{
g_Spaces = new char[SPACE_COUNT+1];
if (!g_Spaces || !MOZ_LOG_TEST(g_LogMod,LogLevel::Error)) {
g_InitState = 1;
XPC_Log_Finish();
return false;
}
memset(g_Spaces, ' ', SPACE_COUNT);
g_Spaces[SPACE_COUNT] = 0;
g_InitState = 1;
return true;
}
void
XPC_Log_Finish()
{
if (g_InitState == 1) {
delete [] g_Spaces;
}
g_InitState = -1;
}
void
XPC_Log_print(const char* fmt, ...)
{
va_list ap;
char line[LINE_LEN];
va_start(ap, fmt);
PR_vsnprintf(line, sizeof(line)-1, fmt, ap);
va_end(ap);
if (g_Indent)
PR_LogPrint("%s%s",g_Spaces+SPACE_COUNT-(INDENT_FACTOR*g_Indent),line);
else
PR_LogPrint("%s",line);
}
bool
XPC_Log_Check(int i)
{
return CAN_RUN && MOZ_LOG_TEST(g_LogMod,LogLevel::Error);
}
void
XPC_Log_Indent()
{
if (INDENT_FACTOR*(++g_Indent) > SPACE_COUNT)
g_Indent-- ;
}
void
XPC_Log_Outdent()
{
if (--g_Indent < 0)
g_Indent++;
}
void
XPC_Log_Clear_Indent()
{
g_Indent = 0;
}
#endif

64
js/xpconnect/src/XPCLog.h Normal file
View file

@ -0,0 +1,64 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Debug Logging support. */
#ifndef xpclog_h___
#define xpclog_h___
#include "mozilla/Logging.h"
/*
* This uses mozilla/Logging.h. The module name used here is 'xpclog'.
* These environment settings should work...
*
* SET MOZ_LOG=xpclog:5
* SET MOZ_LOG_FILE=logfile.txt
*
* usage:
* XPC_LOG_ERROR(("my comment number %d", 5)) // note the double parens
*
*/
#ifdef DEBUG
#define XPC_LOG_INTERNAL(number,_args) \
do{if (XPC_Log_Check(number)){XPC_Log_print _args;}}while (0)
#define XPC_LOG_ALWAYS(_args) XPC_LOG_INTERNAL(1,_args)
#define XPC_LOG_ERROR(_args) XPC_LOG_INTERNAL(2,_args)
#define XPC_LOG_WARNING(_args) XPC_LOG_INTERNAL(3,_args)
#define XPC_LOG_DEBUG(_args) XPC_LOG_INTERNAL(4,_args)
#define XPC_LOG_FLUSH() PR_LogFlush()
#define XPC_LOG_INDENT() XPC_Log_Indent()
#define XPC_LOG_OUTDENT() XPC_Log_Outdent()
#define XPC_LOG_CLEAR_INDENT() XPC_Log_Clear_Indent()
#define XPC_LOG_FINISH() XPC_Log_Finish()
extern "C" {
void XPC_Log_print(const char* fmt, ...);
bool XPC_Log_Check(int i);
void XPC_Log_Indent();
void XPC_Log_Outdent();
void XPC_Log_Clear_Indent();
void XPC_Log_Finish();
} // extern "C"
#else
#define XPC_LOG_ALWAYS(_args) ((void)0)
#define XPC_LOG_ERROR(_args) ((void)0)
#define XPC_LOG_WARNING(_args) ((void)0)
#define XPC_LOG_DEBUG(_args) ((void)0)
#define XPC_LOG_FLUSH() ((void)0)
#define XPC_LOG_INDENT() ((void)0)
#define XPC_LOG_OUTDENT() ((void)0)
#define XPC_LOG_CLEAR_INDENT() ((void)0)
#define XPC_LOG_FINISH() ((void)0)
#endif
#endif /* xpclog_h___ */

View file

@ -0,0 +1,405 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Private maps (hashtables). */
#include "mozilla/MathAlgorithms.h"
#include "mozilla/MemoryReporting.h"
#include "xpcprivate.h"
#include "js/HashTable.h"
using namespace mozilla;
/***************************************************************************/
// static shared...
// Note this is returning the bit pattern of the first part of the nsID, not
// the pointer to the nsID.
static PLDHashNumber
HashIIDPtrKey(const void* key)
{
return *((js::HashNumber*)key);
}
static bool
MatchIIDPtrKey(const PLDHashEntryHdr* entry, const void* key)
{
return ((const nsID*)key)->
Equals(*((const nsID*)((PLDHashEntryStub*)entry)->key));
}
static PLDHashNumber
HashNativeKey(const void* data)
{
return static_cast<const XPCNativeSetKey*>(data)->Hash();
}
/***************************************************************************/
// implement JSObject2WrappedJSMap...
void
JSObject2WrappedJSMap::UpdateWeakPointersAfterGC(XPCJSContext* context)
{
// Check all wrappers and update their JSObject pointer if it has been
// moved. Release any wrappers whose weakly held JSObject has died.
nsTArray<RefPtr<nsXPCWrappedJS>> dying;
for (Map::Enum e(mTable); !e.empty(); e.popFront()) {
nsXPCWrappedJS* wrapper = e.front().value();
MOZ_ASSERT(wrapper, "found a null JS wrapper!");
// Walk the wrapper chain and update all JSObjects.
while (wrapper) {
#ifdef DEBUG
if (!wrapper->IsSubjectToFinalization()) {
// If a wrapper is not subject to finalization then it roots its
// JS object. If so, then it will not be about to be finalized
// and any necessary pointer update will have already happened
// when it was marked.
JSObject* obj = wrapper->GetJSObjectPreserveColor();
JSObject* prior = obj;
JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
MOZ_ASSERT(obj == prior);
}
#endif
if (wrapper->IsSubjectToFinalization()) {
wrapper->UpdateObjectPointerAfterGC();
if (!wrapper->GetJSObjectPreserveColor())
dying.AppendElement(dont_AddRef(wrapper));
}
wrapper = wrapper->GetNextWrapper();
}
// Remove or update the JSObject key in the table if necessary.
JSObject* obj = e.front().key().unbarrieredGet();
JS_UpdateWeakPointerAfterGCUnbarriered(&obj);
if (!obj)
e.removeFront();
else
e.front().mutableKey() = obj;
}
}
void
JSObject2WrappedJSMap::ShutdownMarker()
{
for (Map::Range r = mTable.all(); !r.empty(); r.popFront()) {
nsXPCWrappedJS* wrapper = r.front().value();
MOZ_ASSERT(wrapper, "found a null JS wrapper!");
MOZ_ASSERT(wrapper->IsValid(), "found an invalid JS wrapper!");
wrapper->SystemIsBeingShutDown();
}
}
size_t
JSObject2WrappedJSMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{
size_t n = mallocSizeOf(this);
n += mTable.sizeOfExcludingThis(mallocSizeOf);
return n;
}
size_t
JSObject2WrappedJSMap::SizeOfWrappedJS(mozilla::MallocSizeOf mallocSizeOf) const
{
size_t n = 0;
for (Map::Range r = mTable.all(); !r.empty(); r.popFront())
n += r.front().value()->SizeOfIncludingThis(mallocSizeOf);
return n;
}
/***************************************************************************/
// implement Native2WrappedNativeMap...
// static
Native2WrappedNativeMap*
Native2WrappedNativeMap::newMap(int length)
{
return new Native2WrappedNativeMap(length);
}
Native2WrappedNativeMap::Native2WrappedNativeMap(int length)
: mTable(PLDHashTable::StubOps(), sizeof(Entry), length)
{
}
size_t
Native2WrappedNativeMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{
size_t n = mallocSizeOf(this);
n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<Native2WrappedNativeMap::Entry*>(iter.Get());
n += mallocSizeOf(entry->value);
}
return n;
}
/***************************************************************************/
// implement IID2WrappedJSClassMap...
const struct PLDHashTableOps IID2WrappedJSClassMap::Entry::sOps =
{
HashIIDPtrKey,
MatchIIDPtrKey,
PLDHashTable::MoveEntryStub,
PLDHashTable::ClearEntryStub
};
// static
IID2WrappedJSClassMap*
IID2WrappedJSClassMap::newMap(int length)
{
return new IID2WrappedJSClassMap(length);
}
IID2WrappedJSClassMap::IID2WrappedJSClassMap(int length)
: mTable(&Entry::sOps, sizeof(Entry), length)
{
}
/***************************************************************************/
// implement IID2NativeInterfaceMap...
const struct PLDHashTableOps IID2NativeInterfaceMap::Entry::sOps =
{
HashIIDPtrKey,
MatchIIDPtrKey,
PLDHashTable::MoveEntryStub,
PLDHashTable::ClearEntryStub
};
// static
IID2NativeInterfaceMap*
IID2NativeInterfaceMap::newMap(int length)
{
return new IID2NativeInterfaceMap(length);
}
IID2NativeInterfaceMap::IID2NativeInterfaceMap(int length)
: mTable(&Entry::sOps, sizeof(Entry), length)
{
}
size_t
IID2NativeInterfaceMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{
size_t n = mallocSizeOf(this);
n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<IID2NativeInterfaceMap::Entry*>(iter.Get());
n += entry->value->SizeOfIncludingThis(mallocSizeOf);
}
return n;
}
/***************************************************************************/
// implement ClassInfo2NativeSetMap...
// static
bool ClassInfo2NativeSetMap::Entry::Match(const PLDHashEntryHdr* aEntry,
const void* aKey)
{
return static_cast<const Entry*>(aEntry)->key == aKey;
}
// static
void ClassInfo2NativeSetMap::Entry::Clear(PLDHashTable* aTable,
PLDHashEntryHdr* aEntry)
{
auto entry = static_cast<Entry*>(aEntry);
NS_RELEASE(entry->value);
entry->key = nullptr;
entry->value = nullptr;
}
const PLDHashTableOps ClassInfo2NativeSetMap::Entry::sOps =
{
PLDHashTable::HashVoidPtrKeyStub,
Match,
PLDHashTable::MoveEntryStub,
Clear,
nullptr
};
// static
ClassInfo2NativeSetMap*
ClassInfo2NativeSetMap::newMap(int length)
{
return new ClassInfo2NativeSetMap(length);
}
ClassInfo2NativeSetMap::ClassInfo2NativeSetMap(int length)
: mTable(&ClassInfo2NativeSetMap::Entry::sOps, sizeof(Entry), length)
{
}
size_t
ClassInfo2NativeSetMap::ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
size_t n = mallocSizeOf(this);
n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
return n;
}
/***************************************************************************/
// implement ClassInfo2WrappedNativeProtoMap...
// static
ClassInfo2WrappedNativeProtoMap*
ClassInfo2WrappedNativeProtoMap::newMap(int length)
{
return new ClassInfo2WrappedNativeProtoMap(length);
}
ClassInfo2WrappedNativeProtoMap::ClassInfo2WrappedNativeProtoMap(int length)
: mTable(PLDHashTable::StubOps(), sizeof(Entry), length)
{
}
size_t
ClassInfo2WrappedNativeProtoMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{
size_t n = mallocSizeOf(this);
n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<ClassInfo2WrappedNativeProtoMap::Entry*>(iter.Get());
n += mallocSizeOf(entry->value);
}
return n;
}
/***************************************************************************/
// implement NativeSetMap...
bool
NativeSetMap::Entry::Match(const PLDHashEntryHdr* entry, const void* key)
{
auto Key = static_cast<const XPCNativeSetKey*>(key);
XPCNativeSet* SetInTable = ((Entry*)entry)->key_value;
XPCNativeSet* Set = Key->GetBaseSet();
XPCNativeInterface* Addition = Key->GetAddition();
if (!Set) {
// This is a special case to deal with the invariant that says:
// "All sets have exactly one nsISupports interface and it comes first."
// See XPCNativeSet::NewInstance for details.
//
// Though we might have a key that represents only one interface, we
// know that if that one interface were contructed into a set then
// it would end up really being a set with two interfaces (except for
// the case where the one interface happened to be nsISupports).
return (SetInTable->GetInterfaceCount() == 1 &&
SetInTable->GetInterfaceAt(0) == Addition) ||
(SetInTable->GetInterfaceCount() == 2 &&
SetInTable->GetInterfaceAt(1) == Addition);
}
if (!Addition && Set == SetInTable)
return true;
uint16_t count = Set->GetInterfaceCount();
if (count + (Addition ? 1 : 0) != SetInTable->GetInterfaceCount())
return false;
XPCNativeInterface** CurrentInTable = SetInTable->GetInterfaceArray();
XPCNativeInterface** Current = Set->GetInterfaceArray();
for (uint16_t i = 0; i < count; i++) {
if (*(Current++) != *(CurrentInTable++))
return false;
}
return !Addition || Addition == *(CurrentInTable++);
}
const struct PLDHashTableOps NativeSetMap::Entry::sOps =
{
HashNativeKey,
Match,
PLDHashTable::MoveEntryStub,
PLDHashTable::ClearEntryStub
};
// static
NativeSetMap*
NativeSetMap::newMap(int length)
{
return new NativeSetMap(length);
}
NativeSetMap::NativeSetMap(int length)
: mTable(&Entry::sOps, sizeof(Entry), length)
{
}
size_t
NativeSetMap::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{
size_t n = mallocSizeOf(this);
n += mTable.ShallowSizeOfExcludingThis(mallocSizeOf);
for (auto iter = mTable.ConstIter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<NativeSetMap::Entry*>(iter.Get());
n += entry->key_value->SizeOfIncludingThis(mallocSizeOf);
}
return n;
}
/***************************************************************************/
// implement IID2ThisTranslatorMap...
bool
IID2ThisTranslatorMap::Entry::Match(const PLDHashEntryHdr* entry,
const void* key)
{
return ((const nsID*)key)->Equals(((Entry*)entry)->key);
}
void
IID2ThisTranslatorMap::Entry::Clear(PLDHashTable* table, PLDHashEntryHdr* entry)
{
static_cast<Entry*>(entry)->value = nullptr;
memset(entry, 0, table->EntrySize());
}
const struct PLDHashTableOps IID2ThisTranslatorMap::Entry::sOps =
{
HashIIDPtrKey,
Match,
PLDHashTable::MoveEntryStub,
Clear
};
// static
IID2ThisTranslatorMap*
IID2ThisTranslatorMap::newMap(int length)
{
return new IID2ThisTranslatorMap(length);
}
IID2ThisTranslatorMap::IID2ThisTranslatorMap(int length)
: mTable(&Entry::sOps, sizeof(Entry), length)
{
}
/***************************************************************************/
// implement XPCWrappedNativeProtoMap...
// static
XPCWrappedNativeProtoMap*
XPCWrappedNativeProtoMap::newMap(int length)
{
return new XPCWrappedNativeProtoMap(length);
}
XPCWrappedNativeProtoMap::XPCWrappedNativeProtoMap(int length)
: mTable(PLDHashTable::StubOps(), sizeof(PLDHashEntryStub), length)
{
}
/***************************************************************************/

606
js/xpconnect/src/XPCMaps.h Normal file
View file

@ -0,0 +1,606 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Private maps (hashtables). */
#ifndef xpcmaps_h___
#define xpcmaps_h___
#include "mozilla/MemoryReporting.h"
#include "js/GCHashTable.h"
// Maps...
// Note that most of the declarations for hash table entries begin with
// a pointer to something or another. This makes them look enough like
// the PLDHashEntryStub struct that the default ops (PLDHashTable::StubOps())
// just do the right thing for most of our needs.
// no virtuals in the maps - all the common stuff inlined
// templates could be used to good effect here.
/*************************/
class JSObject2WrappedJSMap
{
using Map = js::HashMap<JS::Heap<JSObject*>,
nsXPCWrappedJS*,
js::MovableCellHasher<JS::Heap<JSObject*>>,
InfallibleAllocPolicy>;
public:
static JSObject2WrappedJSMap* newMap(int length) {
auto* map = new JSObject2WrappedJSMap();
if (!map->mTable.init(length)) {
// This is a decent estimate of the size of the hash table's
// entry storage. The |2| is because on average the capacity is
// twice the requested length.
NS_ABORT_OOM(length * 2 * sizeof(Map::Entry));
}
return map;
}
inline nsXPCWrappedJS* Find(JSObject* Obj) {
NS_PRECONDITION(Obj,"bad param");
Map::Ptr p = mTable.lookup(Obj);
return p ? p->value() : nullptr;
}
#ifdef DEBUG
inline bool HasWrapper(nsXPCWrappedJS* wrapper) {
for (auto r = mTable.all(); !r.empty(); r.popFront()) {
if (r.front().value() == wrapper)
return true;
}
return false;
}
#endif
inline nsXPCWrappedJS* Add(JSContext* cx, nsXPCWrappedJS* wrapper) {
NS_PRECONDITION(wrapper,"bad param");
JSObject* obj = wrapper->GetJSObjectPreserveColor();
Map::AddPtr p = mTable.lookupForAdd(obj);
if (p)
return p->value();
if (!mTable.add(p, obj, wrapper))
return nullptr;
return wrapper;
}
inline void Remove(nsXPCWrappedJS* wrapper) {
NS_PRECONDITION(wrapper,"bad param");
mTable.remove(wrapper->GetJSObjectPreserveColor());
}
inline uint32_t Count() {return mTable.count();}
inline void Dump(int16_t depth) {
for (Map::Range r = mTable.all(); !r.empty(); r.popFront())
r.front().value()->DebugDump(depth);
}
void UpdateWeakPointersAfterGC(XPCJSContext* context);
void ShutdownMarker();
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
// Report the sum of SizeOfIncludingThis() for all wrapped JS in the map.
// Each wrapped JS is only in one map.
size_t SizeOfWrappedJS(mozilla::MallocSizeOf mallocSizeOf) const;
private:
JSObject2WrappedJSMap() {}
Map mTable;
};
/*************************/
class Native2WrappedNativeMap
{
public:
struct Entry : public PLDHashEntryHdr
{
nsISupports* key;
XPCWrappedNative* value;
};
static Native2WrappedNativeMap* newMap(int length);
inline XPCWrappedNative* Find(nsISupports* Obj)
{
NS_PRECONDITION(Obj,"bad param");
auto entry = static_cast<Entry*>(mTable.Search(Obj));
return entry ? entry->value : nullptr;
}
inline XPCWrappedNative* Add(XPCWrappedNative* wrapper)
{
NS_PRECONDITION(wrapper,"bad param");
nsISupports* obj = wrapper->GetIdentityObject();
MOZ_ASSERT(!Find(obj), "wrapper already in new scope!");
auto entry = static_cast<Entry*>(mTable.Add(obj, mozilla::fallible));
if (!entry)
return nullptr;
if (entry->key)
return entry->value;
entry->key = obj;
entry->value = wrapper;
return wrapper;
}
inline void Remove(XPCWrappedNative* wrapper)
{
NS_PRECONDITION(wrapper,"bad param");
#ifdef DEBUG
XPCWrappedNative* wrapperInMap = Find(wrapper->GetIdentityObject());
MOZ_ASSERT(!wrapperInMap || wrapperInMap == wrapper,
"About to remove a different wrapper with the same "
"nsISupports identity! This will most likely cause serious "
"problems!");
#endif
mTable.Remove(wrapper->GetIdentityObject());
}
inline uint32_t Count() { return mTable.EntryCount(); }
PLDHashTable::Iterator Iter() { return mTable.Iter(); }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
private:
Native2WrappedNativeMap(); // no implementation
explicit Native2WrappedNativeMap(int size);
private:
PLDHashTable mTable;
};
/*************************/
class IID2WrappedJSClassMap
{
public:
struct Entry : public PLDHashEntryHdr
{
const nsIID* key;
nsXPCWrappedJSClass* value;
static const struct PLDHashTableOps sOps;
};
static IID2WrappedJSClassMap* newMap(int length);
inline nsXPCWrappedJSClass* Find(REFNSIID iid)
{
auto entry = static_cast<Entry*>(mTable.Search(&iid));
return entry ? entry->value : nullptr;
}
inline nsXPCWrappedJSClass* Add(nsXPCWrappedJSClass* clazz)
{
NS_PRECONDITION(clazz,"bad param");
const nsIID* iid = &clazz->GetIID();
auto entry = static_cast<Entry*>(mTable.Add(iid, mozilla::fallible));
if (!entry)
return nullptr;
if (entry->key)
return entry->value;
entry->key = iid;
entry->value = clazz;
return clazz;
}
inline void Remove(nsXPCWrappedJSClass* clazz)
{
NS_PRECONDITION(clazz,"bad param");
mTable.Remove(&clazz->GetIID());
}
inline uint32_t Count() { return mTable.EntryCount(); }
#ifdef DEBUG
PLDHashTable::Iterator Iter() { return mTable.Iter(); }
#endif
private:
IID2WrappedJSClassMap(); // no implementation
explicit IID2WrappedJSClassMap(int size);
private:
PLDHashTable mTable;
};
/*************************/
class IID2NativeInterfaceMap
{
public:
struct Entry : public PLDHashEntryHdr
{
const nsIID* key;
XPCNativeInterface* value;
static const struct PLDHashTableOps sOps;
};
static IID2NativeInterfaceMap* newMap(int length);
inline XPCNativeInterface* Find(REFNSIID iid)
{
auto entry = static_cast<Entry*>(mTable.Search(&iid));
return entry ? entry->value : nullptr;
}
inline XPCNativeInterface* Add(XPCNativeInterface* iface)
{
NS_PRECONDITION(iface,"bad param");
const nsIID* iid = iface->GetIID();
auto entry = static_cast<Entry*>(mTable.Add(iid, mozilla::fallible));
if (!entry)
return nullptr;
if (entry->key)
return entry->value;
entry->key = iid;
entry->value = iface;
return iface;
}
inline void Remove(XPCNativeInterface* iface)
{
NS_PRECONDITION(iface,"bad param");
mTable.Remove(iface->GetIID());
}
inline uint32_t Count() { return mTable.EntryCount(); }
PLDHashTable::Iterator Iter() { return mTable.Iter(); }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
private:
IID2NativeInterfaceMap(); // no implementation
explicit IID2NativeInterfaceMap(int size);
private:
PLDHashTable mTable;
};
/*************************/
class ClassInfo2NativeSetMap
{
public:
struct Entry : public PLDHashEntryHdr
{
nsIClassInfo* key;
XPCNativeSet* value; // strong reference
static const PLDHashTableOps sOps;
private:
static bool Match(const PLDHashEntryHdr* aEntry, const void* aKey);
static void Clear(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
};
static ClassInfo2NativeSetMap* newMap(int length);
inline XPCNativeSet* Find(nsIClassInfo* info)
{
auto entry = static_cast<Entry*>(mTable.Search(info));
return entry ? entry->value : nullptr;
}
inline XPCNativeSet* Add(nsIClassInfo* info, XPCNativeSet* set)
{
NS_PRECONDITION(info,"bad param");
auto entry = static_cast<Entry*>(mTable.Add(info, mozilla::fallible));
if (!entry)
return nullptr;
if (entry->key)
return entry->value;
entry->key = info;
NS_ADDREF(entry->value = set);
return set;
}
inline void Remove(nsIClassInfo* info)
{
NS_PRECONDITION(info,"bad param");
mTable.Remove(info);
}
inline uint32_t Count() { return mTable.EntryCount(); }
// ClassInfo2NativeSetMap holds pointers to *some* XPCNativeSets.
// So we don't want to count those XPCNativeSets, because they are better
// counted elsewhere (i.e. in XPCJSContext::mNativeSetMap, which holds
// pointers to *all* XPCNativeSets). Hence the "Shallow".
size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
private:
ClassInfo2NativeSetMap(); // no implementation
explicit ClassInfo2NativeSetMap(int size);
private:
PLDHashTable mTable;
};
/*************************/
class ClassInfo2WrappedNativeProtoMap
{
public:
struct Entry : public PLDHashEntryHdr
{
nsIClassInfo* key;
XPCWrappedNativeProto* value;
};
static ClassInfo2WrappedNativeProtoMap* newMap(int length);
inline XPCWrappedNativeProto* Find(nsIClassInfo* info)
{
auto entry = static_cast<Entry*>(mTable.Search(info));
return entry ? entry->value : nullptr;
}
inline XPCWrappedNativeProto* Add(nsIClassInfo* info, XPCWrappedNativeProto* proto)
{
NS_PRECONDITION(info,"bad param");
auto entry = static_cast<Entry*>(mTable.Add(info, mozilla::fallible));
if (!entry)
return nullptr;
if (entry->key)
return entry->value;
entry->key = info;
entry->value = proto;
return proto;
}
inline void Remove(nsIClassInfo* info)
{
NS_PRECONDITION(info,"bad param");
mTable.Remove(info);
}
inline uint32_t Count() { return mTable.EntryCount(); }
PLDHashTable::Iterator Iter() { return mTable.Iter(); }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
private:
ClassInfo2WrappedNativeProtoMap(); // no implementation
explicit ClassInfo2WrappedNativeProtoMap(int size);
private:
PLDHashTable mTable;
};
/*************************/
class NativeSetMap
{
public:
struct Entry : public PLDHashEntryHdr
{
XPCNativeSet* key_value;
static bool
Match(const PLDHashEntryHdr* entry, const void* key);
static const struct PLDHashTableOps sOps;
};
static NativeSetMap* newMap(int length);
inline XPCNativeSet* Find(XPCNativeSetKey* key)
{
auto entry = static_cast<Entry*>(mTable.Search(key));
return entry ? entry->key_value : nullptr;
}
inline XPCNativeSet* Add(const XPCNativeSetKey* key, XPCNativeSet* set)
{
MOZ_ASSERT(key, "bad param");
MOZ_ASSERT(set, "bad param");
auto entry = static_cast<Entry*>(mTable.Add(key, mozilla::fallible));
if (!entry)
return nullptr;
if (entry->key_value)
return entry->key_value;
entry->key_value = set;
return set;
}
bool AddNew(const XPCNativeSetKey* key, XPCNativeSet* set)
{
XPCNativeSet* set2 = Add(key, set);
if (!set2) {
return false;
}
#ifdef DEBUG
XPCNativeSetKey key2(set);
MOZ_ASSERT(key->Hash() == key2.Hash());
MOZ_ASSERT(set2 == set, "Should not have found an existing entry");
#endif
return true;
}
inline void Remove(XPCNativeSet* set)
{
MOZ_ASSERT(set, "bad param");
XPCNativeSetKey key(set);
mTable.Remove(&key);
}
inline uint32_t Count() { return mTable.EntryCount(); }
PLDHashTable::Iterator Iter() { return mTable.Iter(); }
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
private:
NativeSetMap(); // no implementation
explicit NativeSetMap(int size);
private:
PLDHashTable mTable;
};
/***************************************************************************/
class IID2ThisTranslatorMap
{
public:
struct Entry : public PLDHashEntryHdr
{
nsIID key;
nsCOMPtr<nsIXPCFunctionThisTranslator> value;
static bool
Match(const PLDHashEntryHdr* entry, const void* key);
static void
Clear(PLDHashTable* table, PLDHashEntryHdr* entry);
static const struct PLDHashTableOps sOps;
};
static IID2ThisTranslatorMap* newMap(int length);
inline nsIXPCFunctionThisTranslator* Find(REFNSIID iid)
{
auto entry = static_cast<Entry*>(mTable.Search(&iid));
if (!entry) {
return nullptr;
}
return entry->value;
}
inline nsIXPCFunctionThisTranslator* Add(REFNSIID iid,
nsIXPCFunctionThisTranslator* obj)
{
auto entry = static_cast<Entry*>(mTable.Add(&iid, mozilla::fallible));
if (!entry)
return nullptr;
entry->value = obj;
entry->key = iid;
return obj;
}
inline void Remove(REFNSIID iid)
{
mTable.Remove(&iid);
}
inline uint32_t Count() { return mTable.EntryCount(); }
private:
IID2ThisTranslatorMap(); // no implementation
explicit IID2ThisTranslatorMap(int size);
private:
PLDHashTable mTable;
};
/***************************************************************************/
class XPCWrappedNativeProtoMap
{
public:
typedef PLDHashEntryStub Entry;
static XPCWrappedNativeProtoMap* newMap(int length);
inline XPCWrappedNativeProto* Add(XPCWrappedNativeProto* proto)
{
NS_PRECONDITION(proto,"bad param");
auto entry = static_cast<PLDHashEntryStub*>
(mTable.Add(proto, mozilla::fallible));
if (!entry)
return nullptr;
if (entry->key)
return (XPCWrappedNativeProto*) entry->key;
entry->key = proto;
return proto;
}
inline void Remove(XPCWrappedNativeProto* proto)
{
NS_PRECONDITION(proto,"bad param");
mTable.Remove(proto);
}
inline uint32_t Count() { return mTable.EntryCount(); }
PLDHashTable::Iterator Iter() { return mTable.Iter(); }
private:
XPCWrappedNativeProtoMap(); // no implementation
explicit XPCWrappedNativeProtoMap(int size);
private:
PLDHashTable mTable;
};
/***************************************************************************/
class JSObject2JSObjectMap
{
using Map = JS::GCHashMap<JS::Heap<JSObject*>,
JS::Heap<JSObject*>,
js::MovableCellHasher<JS::Heap<JSObject*>>,
js::SystemAllocPolicy>;
public:
static JSObject2JSObjectMap* newMap(int length) {
auto* map = new JSObject2JSObjectMap();
if (!map->mTable.init(length)) {
// This is a decent estimate of the size of the hash table's
// entry storage. The |2| is because on average the capacity is
// twice the requested length.
NS_ABORT_OOM(length * 2 * sizeof(Map::Entry));
}
return map;
}
inline JSObject* Find(JSObject* key) {
NS_PRECONDITION(key, "bad param");
if (Map::Ptr p = mTable.lookup(key))
return p->value();
return nullptr;
}
/* Note: If the entry already exists, return the old value. */
inline JSObject* Add(JSContext* cx, JSObject* key, JSObject* value) {
NS_PRECONDITION(key,"bad param");
Map::AddPtr p = mTable.lookupForAdd(key);
if (p)
return p->value();
if (!mTable.add(p, key, value))
return nullptr;
MOZ_ASSERT(xpc::CompartmentPrivate::Get(key)->scope->mWaiverWrapperMap == this);
return value;
}
inline void Remove(JSObject* key) {
NS_PRECONDITION(key,"bad param");
mTable.remove(key);
}
inline uint32_t Count() { return mTable.count(); }
void Sweep() {
mTable.sweep();
}
private:
JSObject2JSObjectMap() {}
Map mTable;
};
#endif /* xpcmaps_h___ */

View file

@ -0,0 +1,24 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
#define XPCONNECT_MODULE
#include "xpcprivate.h"
nsresult
xpcModuleCtor()
{
nsXPConnect::InitStatics();
return NS_OK;
}
void
xpcModuleDtor()
{
// Release our singletons
nsXPConnect::ReleaseXPConnectSingleton();
xpc_DestroyJSxIDClassObjects();
}

View file

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 "xpcprivate.h"
#include "mozilla/ModuleUtils.h"
#include "mozJSComponentLoader.h"
#include "mozJSSubScriptLoader.h"
/* Module implementation for the xpconnect library. */
#define XPCVARIANT_CONTRACTID "@mozilla.org/xpcvariant;1"
// {FE4F7592-C1FC-4662-AC83-538841318803}
#define SCRIPTABLE_INTERFACES_CID \
{0xfe4f7592, 0xc1fc, 0x4662, \
{ 0xac, 0x83, 0x53, 0x88, 0x41, 0x31, 0x88, 0x3 } }
#define MOZJSSUBSCRIPTLOADER_CONTRACTID "@mozilla.org/moz/jssubscript-loader;1"
NS_GENERIC_FACTORY_CONSTRUCTOR(nsJSID)
NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIXPConnect,
nsXPConnect::GetSingleton)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsScriptError)
NS_GENERIC_FACTORY_CONSTRUCTOR(mozJSComponentLoader)
NS_GENERIC_FACTORY_CONSTRUCTOR(mozJSSubScriptLoader)
NS_DEFINE_NAMED_CID(NS_JS_ID_CID);
NS_DEFINE_NAMED_CID(NS_XPCONNECT_CID);
NS_DEFINE_NAMED_CID(NS_XPCEXCEPTION_CID);
NS_DEFINE_NAMED_CID(NS_SCRIPTERROR_CID);
NS_DEFINE_NAMED_CID(MOZJSCOMPONENTLOADER_CID);
NS_DEFINE_NAMED_CID(MOZ_JSSUBSCRIPTLOADER_CID);
#define XPCONNECT_CIDENTRIES \
{ &kNS_JS_ID_CID, false, nullptr, nsJSIDConstructor }, \
{ &kNS_XPCONNECT_CID, false, nullptr, nsIXPConnectConstructor }, \
{ &kNS_SCRIPTERROR_CID, false, nullptr, nsScriptErrorConstructor }, \
{ &kMOZJSCOMPONENTLOADER_CID, false, nullptr, mozJSComponentLoaderConstructor },\
{ &kMOZ_JSSUBSCRIPTLOADER_CID, false, nullptr, mozJSSubScriptLoaderConstructor },
#define XPCONNECT_CONTRACTS \
{ XPC_ID_CONTRACTID, &kNS_JS_ID_CID }, \
{ XPC_XPCONNECT_CONTRACTID, &kNS_XPCONNECT_CID }, \
{ XPC_CONTEXT_STACK_CONTRACTID, &kNS_XPCONNECT_CID }, \
{ NS_SCRIPTERROR_CONTRACTID, &kNS_SCRIPTERROR_CID }, \
{ MOZJSCOMPONENTLOADER_CONTRACTID, &kMOZJSCOMPONENTLOADER_CID }, \
{ MOZJSSUBSCRIPTLOADER_CONTRACTID, &kMOZ_JSSUBSCRIPTLOADER_CID },
#define XPCONNECT_CATEGORIES \
{ "module-loader", "js", MOZJSCOMPONENTLOADER_CONTRACTID },
nsresult xpcModuleCtor();
void xpcModuleDtor();

View file

@ -0,0 +1,189 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 "xpcprivate.h"
#include "nsContentUtils.h"
#include "BackstagePass.h"
#include "nsDOMClassInfo.h"
#include "nsIPrincipal.h"
#include "mozilla/dom/BindingUtils.h"
NS_INTERFACE_MAP_BEGIN(BackstagePass)
NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCScriptable)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(BackstagePass)
NS_IMPL_RELEASE(BackstagePass)
// The nsIXPCScriptable map declaration that will generate stubs for us...
#define XPC_MAP_CLASSNAME BackstagePass
#define XPC_MAP_QUOTED_CLASSNAME "BackstagePass"
#define XPC_MAP_WANT_RESOLVE
#define XPC_MAP_WANT_ENUMERATE
#define XPC_MAP_WANT_FINALIZE
#define XPC_MAP_WANT_PRECREATE
#define XPC_MAP_FLAGS nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY | \
nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY | \
nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY | \
nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE | \
nsIXPCScriptable::IS_GLOBAL_OBJECT | \
nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
#include "xpc_map_end.h" /* This will #undef the above */
JSObject*
BackstagePass::GetGlobalJSObject()
{
if (mWrapper)
return mWrapper->GetFlatJSObject();
return nullptr;
}
void
BackstagePass::SetGlobalObject(JSObject* global)
{
nsISupports* p = XPCWrappedNative::Get(global);
MOZ_ASSERT(p);
mWrapper = static_cast<XPCWrappedNative*>(p);
}
NS_IMETHODIMP
BackstagePass::Resolve(nsIXPConnectWrappedNative* wrapper,
JSContext * cx, JSObject * objArg,
jsid idArg, bool* resolvedp,
bool* _retval)
{
JS::RootedObject obj(cx, objArg);
JS::RootedId id(cx, idArg);
*_retval = mozilla::dom::SystemGlobalResolve(cx, obj, id, resolvedp);
return *_retval ? NS_OK : NS_ERROR_FAILURE;
}
NS_IMETHODIMP
BackstagePass::Enumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
JSObject* objArg, bool* _retval)
{
JS::RootedObject obj(cx, objArg);
*_retval = mozilla::dom::SystemGlobalEnumerate(cx, obj);
return *_retval ? NS_OK : NS_ERROR_FAILURE;
}
/***************************************************************************/
NS_IMETHODIMP
BackstagePass::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
{
const uint32_t count = 2;
*aCount = count;
nsIID** array;
*aArray = array = static_cast<nsIID**>(moz_xmalloc(count * sizeof(nsIID*)));
if (!array)
return NS_ERROR_OUT_OF_MEMORY;
uint32_t index = 0;
nsIID* clone;
#define PUSH_IID(id) \
clone = static_cast<nsIID*>(nsMemory::Clone(&NS_GET_IID( id ), \
sizeof(nsIID))); \
if (!clone) \
goto oom; \
array[index++] = clone;
PUSH_IID(nsIXPCScriptable)
PUSH_IID(nsIScriptObjectPrincipal)
#undef PUSH_IID
return NS_OK;
oom:
while (index)
free(array[--index]);
free(array);
*aArray = nullptr;
return NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
BackstagePass::GetScriptableHelper(nsIXPCScriptable** retval)
{
nsCOMPtr<nsIXPCScriptable> scriptable = this;
scriptable.forget(retval);
return NS_OK;
}
NS_IMETHODIMP
BackstagePass::GetContractID(char * *aContractID)
{
*aContractID = nullptr;
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
BackstagePass::GetClassDescription(char * *aClassDescription)
{
static const char classDescription[] = "BackstagePass";
*aClassDescription = (char*)nsMemory::Clone(classDescription, sizeof(classDescription));
return *aClassDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
NS_IMETHODIMP
BackstagePass::GetClassID(nsCID * *aClassID)
{
*aClassID = nullptr;
return NS_OK;
}
NS_IMETHODIMP
BackstagePass::GetFlags(uint32_t* aFlags)
{
*aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
return NS_OK;
}
NS_IMETHODIMP
BackstagePass::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
{
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
BackstagePass::Finalize(nsIXPConnectWrappedNative* wrapper, JSFreeOp * fop, JSObject * obj)
{
nsCOMPtr<nsIGlobalObject> bsp(do_QueryWrappedNative(wrapper));
MOZ_ASSERT(bsp);
static_cast<BackstagePass*>(bsp.get())->ForgetGlobalObject();
return NS_OK;
}
NS_IMETHODIMP
BackstagePass::PreCreate(nsISupports* nativeObj, JSContext* cx,
JSObject* globalObj, JSObject** parentObj)
{
// We do the same trick here as for WindowSH. Return the js global
// as parent, so XPConenct can find the right scope and the wrapper
// that already exists.
nsCOMPtr<nsIGlobalObject> global(do_QueryInterface(nativeObj));
MOZ_ASSERT(global, "nativeObj not a global object!");
JSObject* jsglobal = global->GetGlobalJSObject();
if (jsglobal)
*parentObj = jsglobal;
return NS_OK;
}
nsresult
NS_NewBackstagePass(BackstagePass** ret)
{
RefPtr<BackstagePass> bsp = new BackstagePass(
nsContentUtils::GetSystemPrincipal());
bsp.forget(ret);
return NS_OK;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,143 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/*
* Infrastructure for sharing DOMString data with JSStrings.
*
* Importing an nsAString into JS:
* If possible (GetSharedBufferHandle works) use the external string support in
* JS to create a JSString that points to the readable's buffer. We keep a
* reference to the buffer handle until the JSString is finalized.
*
* Exporting a JSString as an nsAReadable:
* Wrap the JSString with a root-holding XPCJSReadableStringWrapper, which roots
* the string and exposes its buffer via the nsAString interface, as
* well as providing refcounting support.
*/
#include "nsAutoPtr.h"
#include "nscore.h"
#include "nsString.h"
#include "nsStringBuffer.h"
#include "jsapi.h"
#include "xpcpublic.h"
using namespace JS;
// static
void
XPCStringConvert::FreeZoneCache(JS::Zone* zone)
{
// Put the zone user data into an AutoPtr (which will do the cleanup for us),
// and null out the user data (which may already be null).
nsAutoPtr<ZoneStringCache> cache(static_cast<ZoneStringCache*>(JS_GetZoneUserData(zone)));
JS_SetZoneUserData(zone, nullptr);
}
// static
void
XPCStringConvert::ClearZoneCache(JS::Zone* zone)
{
// Although we clear the cache in FinalizeDOMString if needed, we also clear
// the cache here to avoid a dangling JSString* pointer when compacting GC
// moves the external string in memory.
ZoneStringCache* cache = static_cast<ZoneStringCache*>(JS_GetZoneUserData(zone));
if (cache) {
cache->mBuffer = nullptr;
cache->mLength = 0;
cache->mString = nullptr;
}
}
// static
void
XPCStringConvert::FinalizeLiteral(JS::Zone* zone, const JSStringFinalizer* fin, char16_t* chars)
{
}
const JSStringFinalizer XPCStringConvert::sLiteralFinalizer =
{ XPCStringConvert::FinalizeLiteral };
// static
void
XPCStringConvert::FinalizeDOMString(JS::Zone* zone, const JSStringFinalizer* fin, char16_t* chars)
{
nsStringBuffer* buf = nsStringBuffer::FromData(chars);
// Clear the ZoneStringCache if needed, as this can be called outside GC
// when flattening an external string.
ZoneStringCache* cache = static_cast<ZoneStringCache*>(JS_GetZoneUserData(zone));
if (cache && cache->mBuffer == buf) {
cache->mBuffer = nullptr;
cache->mLength = 0;
cache->mString = nullptr;
}
buf->Release();
}
const JSStringFinalizer XPCStringConvert::sDOMStringFinalizer =
{ XPCStringConvert::FinalizeDOMString };
// convert a readable to a JSString, copying string data
// static
bool
XPCStringConvert::ReadableToJSVal(JSContext* cx,
const nsAString& readable,
nsStringBuffer** sharedBuffer,
MutableHandleValue vp)
{
*sharedBuffer = nullptr;
uint32_t length = readable.Length();
if (readable.IsLiteral()) {
JSString* str = JS_NewExternalString(cx,
static_cast<const char16_t*>(readable.BeginReading()),
length, &sLiteralFinalizer);
if (!str)
return false;
vp.setString(str);
return true;
}
nsStringBuffer* buf = nsStringBuffer::FromString(readable);
if (buf) {
bool shared;
if (!StringBufferToJSVal(cx, buf, length, vp, &shared))
return false;
if (shared)
*sharedBuffer = buf;
return true;
}
// blech, have to copy.
JSString* str = JS_NewUCStringCopyN(cx, readable.BeginReading(), length);
if (!str)
return false;
vp.setString(str);
return true;
}
namespace xpc {
bool
NonVoidStringToJsval(JSContext* cx, nsAString& str, MutableHandleValue rval)
{
nsStringBuffer* sharedBuffer;
if (!XPCStringConvert::ReadableToJSVal(cx, str, &sharedBuffer, rval))
return false;
if (sharedBuffer) {
// The string was shared but ReadableToJSVal didn't addref it.
// Move the ownership from str to jsstr.
str.ForgetSharedBuffer();
}
return true;
}
} // namespace xpc

View file

@ -0,0 +1,179 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Code for throwing errors into JavaScript. */
#include "xpcprivate.h"
#include "XPCWrapper.h"
#include "jsprf.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Exceptions.h"
#include "nsStringGlue.h"
using namespace mozilla;
using namespace mozilla::dom;
bool XPCThrower::sVerbose = true;
// static
void
XPCThrower::Throw(nsresult rv, JSContext* cx)
{
const char* format;
if (JS_IsExceptionPending(cx))
return;
if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
format = "";
dom::Throw(cx, rv, nsDependentCString(format));
}
namespace xpc {
bool
Throw(JSContext* cx, nsresult rv)
{
XPCThrower::Throw(rv, cx);
return false;
}
} // namespace xpc
/*
* If there has already been an exception thrown, see if we're throwing the
* same sort of exception, and if we are, don't clobber the old one. ccx
* should be the current call context.
*/
// static
bool
XPCThrower::CheckForPendingException(nsresult result, JSContext* cx)
{
nsCOMPtr<nsIException> e = XPCJSContext::Get()->GetPendingException();
if (!e)
return false;
XPCJSContext::Get()->SetPendingException(nullptr);
nsresult e_result;
if (NS_FAILED(e->GetResult(&e_result)) || e_result != result)
return false;
ThrowExceptionObject(cx, e);
return true;
}
// static
void
XPCThrower::Throw(nsresult rv, XPCCallContext& ccx)
{
char* sz;
const char* format;
if (CheckForPendingException(rv, ccx))
return;
if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
format = "";
sz = (char*) format;
NS_ENSURE_TRUE_VOID(sz);
if (sz && sVerbose)
Verbosify(ccx, &sz, false);
dom::Throw(ccx, rv, nsDependentCString(sz));
if (sz && sz != format)
JS_smprintf_free(sz);
}
// static
void
XPCThrower::ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx)
{
char* sz;
const char* format;
const char* name;
/*
* If there is a pending exception when the native call returns and
* it has the same error result as returned by the native call, then
* the native call may be passing through an error from a previous JS
* call. So we'll just throw that exception into our JS. Note that
* we don't need to worry about NS_ERROR_UNCATCHABLE_EXCEPTION,
* because presumably there would be no pending exception for that
* nsresult!
*/
if (CheckForPendingException(result, ccx))
return;
// else...
if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format) || !format)
format = "";
if (nsXPCException::NameAndFormatForNSResult(result, &name, nullptr) && name)
sz = JS_smprintf("%s 0x%x (%s)", format, (unsigned) result, name);
else
sz = JS_smprintf("%s 0x%x", format, (unsigned) result);
NS_ENSURE_TRUE_VOID(sz);
if (sz && sVerbose)
Verbosify(ccx, &sz, true);
dom::Throw(ccx, result, nsDependentCString(sz));
if (sz)
JS_smprintf_free(sz);
}
// static
void
XPCThrower::ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx)
{
char* sz;
const char* format;
if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
format = "";
sz = JS_smprintf("%s arg %d", format, paramNum);
NS_ENSURE_TRUE_VOID(sz);
if (sz && sVerbose)
Verbosify(ccx, &sz, true);
dom::Throw(ccx, rv, nsDependentCString(sz));
if (sz)
JS_smprintf_free(sz);
}
// static
void
XPCThrower::Verbosify(XPCCallContext& ccx,
char** psz, bool own)
{
char* sz = nullptr;
if (ccx.HasInterfaceAndMember()) {
XPCNativeInterface* iface = ccx.GetInterface();
jsid id = ccx.GetMember()->GetName();
JSAutoByteString bytes;
const char* name = JSID_IS_VOID(id) ? "Unknown" : bytes.encodeLatin1(ccx, JSID_TO_STRING(id));
if (!name) {
name = "";
}
sz = JS_smprintf("%s [%s.%s]", *psz, iface->GetNameString(), name);
}
if (sz) {
if (own)
JS_smprintf_free(*psz);
*psz = sz;
}
}

View file

@ -0,0 +1,800 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* nsIVariant implementation for xpconnect. */
#include "mozilla/Range.h"
#include "xpcprivate.h"
#include "jsfriendapi.h"
#include "jsprf.h"
#include "jswrapper.h"
using namespace JS;
using namespace mozilla;
NS_IMPL_CLASSINFO(XPCVariant, nullptr, 0, XPCVARIANT_CID)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPCVariant)
NS_INTERFACE_MAP_ENTRY(XPCVariant)
NS_INTERFACE_MAP_ENTRY(nsIVariant)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_IMPL_QUERY_CLASSINFO(XPCVariant)
NS_INTERFACE_MAP_END
NS_IMPL_CI_INTERFACE_GETTER(XPCVariant, XPCVariant, nsIVariant)
NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCVariant)
NS_IMPL_CYCLE_COLLECTING_RELEASE(XPCVariant)
XPCVariant::XPCVariant(JSContext* cx, const Value& aJSVal)
: mJSVal(aJSVal), mCCGeneration(0)
{
if (!mJSVal.isPrimitive()) {
// XXXbholley - The innerization here was from bug 638026. Blake says
// the basic problem was that we were storing the C++ inner but the JS
// outer, which meant that, after navigation, the JS inner could be
// collected, which would cause us to try to recreate the JS inner at
// some later point after teardown, which would crash. This is shouldn't
// be a problem anymore because SetParentToWindow will do the right
// thing, but I'm saving the cleanup here for another day. Blake thinks
// that we should just not store the WN if we're creating a variant for
// an outer window.
JSObject* obj = js::ToWindowIfWindowProxy(&mJSVal.toObject());
mJSVal = JS::ObjectValue(*obj);
JSObject* unwrapped = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
mReturnRawObject = !(unwrapped && IS_WN_REFLECTOR(unwrapped));
} else
mReturnRawObject = false;
}
XPCTraceableVariant::~XPCTraceableVariant()
{
Value val = GetJSValPreserveColor();
MOZ_ASSERT(val.isGCThing(), "Must be traceable or unlinked");
mData.Cleanup();
if (!val.isNull())
RemoveFromRootSet();
}
void XPCTraceableVariant::TraceJS(JSTracer* trc)
{
MOZ_ASSERT(GetJSValPreserveColor().isMarkable());
JS::TraceEdge(trc, &mJSVal, "XPCTraceableVariant::mJSVal");
}
NS_IMPL_CYCLE_COLLECTION_CLASS(XPCVariant)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant)
JS::Value val = tmp->GetJSValPreserveColor();
if (val.isObject()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSVal");
cb.NoteJSChild(JS::GCCellPtr(val));
}
tmp->mData.Traverse(cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPCVariant)
JS::Value val = tmp->GetJSValPreserveColor();
tmp->mData.Cleanup();
if (val.isMarkable()) {
XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(tmp);
v->RemoveFromRootSet();
}
tmp->mJSVal = JS::NullValue();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
// static
already_AddRefed<XPCVariant>
XPCVariant::newVariant(JSContext* cx, const Value& aJSVal)
{
RefPtr<XPCVariant> variant;
if (!aJSVal.isMarkable())
variant = new XPCVariant(cx, aJSVal);
else
variant = new XPCTraceableVariant(cx, aJSVal);
if (!variant->InitializeData(cx))
return nullptr;
return variant.forget();
}
// Helper class to give us a namespace for the table based code below.
class XPCArrayHomogenizer
{
private:
enum Type
{
tNull = 0 , // null value
tInt , // Integer
tDbl , // Double
tBool , // Boolean
tStr , // String
tID , // ID
tArr , // Array
tISup , // nsISupports (really just a plain JSObject)
tUnk , // Unknown. Used only for initial state.
tTypeCount , // Just a count for table dimensioning.
tVar , // nsVariant - last ditch if no other common type found.
tErr // No valid state or type has this value.
};
// Table has tUnk as a state (column) but not as a type (row).
static const Type StateTable[tTypeCount][tTypeCount-1];
public:
static bool GetTypeForArray(JSContext* cx, HandleObject array,
uint32_t length,
nsXPTType* resultType, nsID* resultID);
};
// Current state is the column down the side.
// Current type is the row along the top.
// New state is in the box at the intersection.
const XPCArrayHomogenizer::Type
XPCArrayHomogenizer::StateTable[tTypeCount][tTypeCount-1] = {
/* tNull,tInt ,tDbl ,tBool,tStr ,tID ,tArr ,tISup */
/* tNull */{tNull,tVar ,tVar ,tVar ,tStr ,tID ,tVar ,tISup },
/* tInt */{tVar ,tInt ,tDbl ,tVar ,tVar ,tVar ,tVar ,tVar },
/* tDbl */{tVar ,tDbl ,tDbl ,tVar ,tVar ,tVar ,tVar ,tVar },
/* tBool */{tVar ,tVar ,tVar ,tBool,tVar ,tVar ,tVar ,tVar },
/* tStr */{tStr ,tVar ,tVar ,tVar ,tStr ,tVar ,tVar ,tVar },
/* tID */{tID ,tVar ,tVar ,tVar ,tVar ,tID ,tVar ,tVar },
/* tArr */{tErr ,tErr ,tErr ,tErr ,tErr ,tErr ,tErr ,tErr },
/* tISup */{tISup,tVar ,tVar ,tVar ,tVar ,tVar ,tVar ,tISup },
/* tUnk */{tNull,tInt ,tDbl ,tBool,tStr ,tID ,tVar ,tISup }};
// static
bool
XPCArrayHomogenizer::GetTypeForArray(JSContext* cx, HandleObject array,
uint32_t length,
nsXPTType* resultType, nsID* resultID)
{
Type state = tUnk;
Type type;
RootedValue val(cx);
RootedObject jsobj(cx);
for (uint32_t i = 0; i < length; i++) {
if (!JS_GetElement(cx, array, i, &val))
return false;
if (val.isInt32()) {
type = tInt;
} else if (val.isDouble()) {
type = tDbl;
} else if (val.isBoolean()) {
type = tBool;
} else if (val.isUndefined() || val.isSymbol()) {
state = tVar;
break;
} else if (val.isNull()) {
type = tNull;
} else if (val.isString()) {
type = tStr;
} else {
MOZ_ASSERT(val.isObject(), "invalid type of jsval!");
jsobj = &val.toObject();
bool isArray;
if (!JS_IsArrayObject(cx, jsobj, &isArray))
return false;
if (isArray)
type = tArr;
else if (xpc_JSObjectIsID(cx, jsobj))
type = tID;
else
type = tISup;
}
MOZ_ASSERT(state != tErr, "bad state table!");
MOZ_ASSERT(type != tErr, "bad type!");
MOZ_ASSERT(type != tVar, "bad type!");
MOZ_ASSERT(type != tUnk, "bad type!");
state = StateTable[state][type];
MOZ_ASSERT(state != tErr, "bad state table!");
MOZ_ASSERT(state != tUnk, "bad state table!");
if (state == tVar)
break;
}
switch (state) {
case tInt :
*resultType = nsXPTType((uint8_t)TD_INT32);
break;
case tDbl :
*resultType = nsXPTType((uint8_t)TD_DOUBLE);
break;
case tBool:
*resultType = nsXPTType((uint8_t)TD_BOOL);
break;
case tStr :
*resultType = nsXPTType((uint8_t)TD_PWSTRING);
break;
case tID :
*resultType = nsXPTType((uint8_t)TD_PNSIID);
break;
case tISup:
*resultType = nsXPTType((uint8_t)TD_INTERFACE_IS_TYPE);
*resultID = NS_GET_IID(nsISupports);
break;
case tNull:
// FALL THROUGH
case tVar :
*resultType = nsXPTType((uint8_t)TD_INTERFACE_IS_TYPE);
*resultID = NS_GET_IID(nsIVariant);
break;
case tArr :
// FALL THROUGH
case tUnk :
// FALL THROUGH
case tErr :
// FALL THROUGH
default:
NS_ERROR("bad state");
return false;
}
return true;
}
bool XPCVariant::InitializeData(JSContext* cx)
{
JS_CHECK_RECURSION(cx, return false);
RootedValue val(cx, GetJSVal());
if (val.isInt32()) {
mData.SetFromInt32(val.toInt32());
return true;
}
if (val.isDouble()) {
mData.SetFromDouble(val.toDouble());
return true;
}
if (val.isBoolean()) {
mData.SetFromBool(val.toBoolean());
return true;
}
// We can't represent symbol on C++ side, so pretend it is void.
if (val.isUndefined() || val.isSymbol()) {
mData.SetToVoid();
return true;
}
if (val.isNull()) {
mData.SetToEmpty();
return true;
}
if (val.isString()) {
JSString* str = val.toString();
if (!str)
return false;
MOZ_ASSERT(mData.GetType() == nsIDataType::VTYPE_EMPTY,
"Why do we already have data?");
size_t length = JS_GetStringLength(str);
mData.AllocateWStringWithSize(length);
mozilla::Range<char16_t> destChars(mData.u.wstr.mWStringValue, length);
if (!JS_CopyStringChars(cx, destChars, str))
return false;
MOZ_ASSERT(mData.u.wstr.mWStringValue[length] == '\0');
return true;
}
// leaving only JSObject...
MOZ_ASSERT(val.isObject(), "invalid type of jsval!");
RootedObject jsobj(cx, &val.toObject());
// Let's see if it is a xpcJSID.
const nsID* id = xpc_JSObjectToID(cx, jsobj);
if (id) {
mData.SetFromID(*id);
return true;
}
// Let's see if it is a js array object.
uint32_t len;
bool isArray;
if (!JS_IsArrayObject(cx, jsobj, &isArray) ||
(isArray && !JS_GetArrayLength(cx, jsobj, &len)))
{
return false;
}
if (isArray) {
if (!len) {
// Zero length array
mData.SetToEmptyArray();
return true;
}
nsXPTType type;
nsID id;
if (!XPCArrayHomogenizer::GetTypeForArray(cx, jsobj, len, &type, &id))
return false;
if (!XPCConvert::JSArray2Native(&mData.u.array.mArrayValue,
val, len, type, &id, nullptr))
return false;
mData.mType = nsIDataType::VTYPE_ARRAY;
if (type.IsInterfacePointer())
mData.u.array.mArrayInterfaceID = id;
mData.u.array.mArrayCount = len;
mData.u.array.mArrayType = type.TagPart();
return true;
}
// XXX This could be smarter and pick some more interesting iface.
nsXPConnect* xpc = nsXPConnect::XPConnect();
nsCOMPtr<nsISupports> wrapper;
const nsIID& iid = NS_GET_IID(nsISupports);
if (NS_FAILED(xpc->WrapJS(cx, jsobj, iid, getter_AddRefs(wrapper)))) {
return false;
}
mData.SetFromInterface(iid, wrapper);
return true;
}
NS_IMETHODIMP
XPCVariant::GetAsJSVal(MutableHandleValue result)
{
result.set(GetJSVal());
return NS_OK;
}
// static
bool
XPCVariant::VariantDataToJS(nsIVariant* variant,
nsresult* pErr, MutableHandleValue pJSVal)
{
// Get the type early because we might need to spoof it below.
uint16_t type;
if (NS_FAILED(variant->GetDataType(&type)))
return false;
AutoJSContext cx;
RootedValue realVal(cx);
nsresult rv = variant->GetAsJSVal(&realVal);
if (NS_SUCCEEDED(rv) &&
(realVal.isPrimitive() ||
type == nsIDataType::VTYPE_ARRAY ||
type == nsIDataType::VTYPE_EMPTY_ARRAY ||
type == nsIDataType::VTYPE_ID)) {
if (!JS_WrapValue(cx, &realVal))
return false;
pJSVal.set(realVal);
return true;
}
nsCOMPtr<XPCVariant> xpcvariant = do_QueryInterface(variant);
if (xpcvariant && xpcvariant->mReturnRawObject) {
MOZ_ASSERT(type == nsIDataType::VTYPE_INTERFACE ||
type == nsIDataType::VTYPE_INTERFACE_IS,
"Weird variant");
if (!JS_WrapValue(cx, &realVal))
return false;
pJSVal.set(realVal);
return true;
}
// else, it's an object and we really need to double wrap it if we've
// already decided that its 'natural' type is as some sort of interface.
// We just fall through to the code below and let it do what it does.
// The nsIVariant is not a XPCVariant (or we act like it isn't).
// So we extract the data and do the Right Thing.
// We ASSUME that the variant implementation can do these conversions...
nsID iid;
switch (type) {
case nsIDataType::VTYPE_INT8:
case nsIDataType::VTYPE_INT16:
case nsIDataType::VTYPE_INT32:
case nsIDataType::VTYPE_INT64:
case nsIDataType::VTYPE_UINT8:
case nsIDataType::VTYPE_UINT16:
case nsIDataType::VTYPE_UINT32:
case nsIDataType::VTYPE_UINT64:
case nsIDataType::VTYPE_FLOAT:
case nsIDataType::VTYPE_DOUBLE:
{
double d;
if (NS_FAILED(variant->GetAsDouble(&d)))
return false;
pJSVal.setNumber(d);
return true;
}
case nsIDataType::VTYPE_BOOL:
{
bool b;
if (NS_FAILED(variant->GetAsBool(&b)))
return false;
pJSVal.setBoolean(b);
return true;
}
case nsIDataType::VTYPE_CHAR:
{
char c;
if (NS_FAILED(variant->GetAsChar(&c)))
return false;
return XPCConvert::NativeData2JS(pJSVal, (const void*)&c, TD_CHAR, &iid, pErr);
}
case nsIDataType::VTYPE_WCHAR:
{
char16_t wc;
if (NS_FAILED(variant->GetAsWChar(&wc)))
return false;
return XPCConvert::NativeData2JS(pJSVal, (const void*)&wc, TD_WCHAR, &iid, pErr);
}
case nsIDataType::VTYPE_ID:
{
if (NS_FAILED(variant->GetAsID(&iid)))
return false;
nsID* v = &iid;
return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, TD_PNSIID, &iid, pErr);
}
case nsIDataType::VTYPE_ASTRING:
{
nsAutoString astring;
if (NS_FAILED(variant->GetAsAString(astring)))
return false;
nsAutoString* v = &astring;
return XPCConvert::NativeData2JS(pJSVal, (const void*)&v, TD_ASTRING, &iid, pErr);
}
case nsIDataType::VTYPE_DOMSTRING:
{
nsAutoString astring;
if (NS_FAILED(variant->GetAsAString(astring)))
return false;
nsAutoString* v = &astring;
return XPCConvert::NativeData2JS(pJSVal, (const void*)&v,
TD_DOMSTRING, &iid, pErr);
}
case nsIDataType::VTYPE_CSTRING:
{
nsAutoCString cString;
if (NS_FAILED(variant->GetAsACString(cString)))
return false;
nsAutoCString* v = &cString;
return XPCConvert::NativeData2JS(pJSVal, (const void*)&v,
TD_CSTRING, &iid, pErr);
}
case nsIDataType::VTYPE_UTF8STRING:
{
nsUTF8String utf8String;
if (NS_FAILED(variant->GetAsAUTF8String(utf8String)))
return false;
nsUTF8String* v = &utf8String;
return XPCConvert::NativeData2JS(pJSVal, (const void*)&v,
TD_UTF8STRING, &iid, pErr);
}
case nsIDataType::VTYPE_CHAR_STR:
{
char* pc;
if (NS_FAILED(variant->GetAsString(&pc)))
return false;
bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pc,
TD_PSTRING, &iid, pErr);
free(pc);
return success;
}
case nsIDataType::VTYPE_STRING_SIZE_IS:
{
char* pc;
uint32_t size;
if (NS_FAILED(variant->GetAsStringWithSize(&size, &pc)))
return false;
bool success = XPCConvert::NativeStringWithSize2JS(pJSVal, (const void*)&pc,
TD_PSTRING_SIZE_IS, size, pErr);
free(pc);
return success;
}
case nsIDataType::VTYPE_WCHAR_STR:
{
char16_t* pwc;
if (NS_FAILED(variant->GetAsWString(&pwc)))
return false;
bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pwc,
TD_PSTRING, &iid, pErr);
free(pwc);
return success;
}
case nsIDataType::VTYPE_WSTRING_SIZE_IS:
{
char16_t* pwc;
uint32_t size;
if (NS_FAILED(variant->GetAsWStringWithSize(&size, &pwc)))
return false;
bool success = XPCConvert::NativeStringWithSize2JS(pJSVal, (const void*)&pwc,
TD_PWSTRING_SIZE_IS, size, pErr);
free(pwc);
return success;
}
case nsIDataType::VTYPE_INTERFACE:
case nsIDataType::VTYPE_INTERFACE_IS:
{
nsISupports* pi;
nsID* piid;
if (NS_FAILED(variant->GetAsInterface(&piid, (void**)&pi)))
return false;
iid = *piid;
free((char*)piid);
bool success = XPCConvert::NativeData2JS(pJSVal, (const void*)&pi,
TD_INTERFACE_IS_TYPE, &iid, pErr);
if (pi)
pi->Release();
return success;
}
case nsIDataType::VTYPE_ARRAY:
{
nsDiscriminatedUnion du;
nsresult rv;
rv = variant->GetAsArray(&du.u.array.mArrayType,
&du.u.array.mArrayInterfaceID,
&du.u.array.mArrayCount,
&du.u.array.mArrayValue);
if (NS_FAILED(rv))
return false;
// must exit via VARIANT_DONE from here on...
du.mType = nsIDataType::VTYPE_ARRAY;
nsXPTType conversionType;
uint16_t elementType = du.u.array.mArrayType;
const nsID* pid = nullptr;
switch (elementType) {
case nsIDataType::VTYPE_INT8:
case nsIDataType::VTYPE_INT16:
case nsIDataType::VTYPE_INT32:
case nsIDataType::VTYPE_INT64:
case nsIDataType::VTYPE_UINT8:
case nsIDataType::VTYPE_UINT16:
case nsIDataType::VTYPE_UINT32:
case nsIDataType::VTYPE_UINT64:
case nsIDataType::VTYPE_FLOAT:
case nsIDataType::VTYPE_DOUBLE:
case nsIDataType::VTYPE_BOOL:
case nsIDataType::VTYPE_CHAR:
case nsIDataType::VTYPE_WCHAR:
conversionType = nsXPTType((uint8_t)elementType);
break;
case nsIDataType::VTYPE_ID:
case nsIDataType::VTYPE_CHAR_STR:
case nsIDataType::VTYPE_WCHAR_STR:
conversionType = nsXPTType((uint8_t)elementType);
break;
case nsIDataType::VTYPE_INTERFACE:
pid = &NS_GET_IID(nsISupports);
conversionType = nsXPTType((uint8_t)elementType);
break;
case nsIDataType::VTYPE_INTERFACE_IS:
pid = &du.u.array.mArrayInterfaceID;
conversionType = nsXPTType((uint8_t)elementType);
break;
// The rest are illegal.
case nsIDataType::VTYPE_VOID:
case nsIDataType::VTYPE_ASTRING:
case nsIDataType::VTYPE_DOMSTRING:
case nsIDataType::VTYPE_CSTRING:
case nsIDataType::VTYPE_UTF8STRING:
case nsIDataType::VTYPE_WSTRING_SIZE_IS:
case nsIDataType::VTYPE_STRING_SIZE_IS:
case nsIDataType::VTYPE_ARRAY:
case nsIDataType::VTYPE_EMPTY_ARRAY:
case nsIDataType::VTYPE_EMPTY:
default:
NS_ERROR("bad type in array!");
return false;
}
bool success =
XPCConvert::NativeArray2JS(pJSVal,
(const void**)&du.u.array.mArrayValue,
conversionType, pid,
du.u.array.mArrayCount, pErr);
return success;
}
case nsIDataType::VTYPE_EMPTY_ARRAY:
{
JSObject* array = JS_NewArrayObject(cx, 0);
if (!array)
return false;
pJSVal.setObject(*array);
return true;
}
case nsIDataType::VTYPE_VOID:
pJSVal.setUndefined();
return true;
case nsIDataType::VTYPE_EMPTY:
pJSVal.setNull();
return true;
default:
NS_ERROR("bad type in variant!");
return false;
}
}
/***************************************************************************/
/***************************************************************************/
// XXX These default implementations need to be improved to allow for
// some more interesting conversions.
NS_IMETHODIMP XPCVariant::GetDataType(uint16_t* aDataType)
{
*aDataType = mData.GetType();
return NS_OK;
}
NS_IMETHODIMP XPCVariant::GetAsInt8(uint8_t* _retval)
{
return mData.ConvertToInt8(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsInt16(int16_t* _retval)
{
return mData.ConvertToInt16(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsInt32(int32_t* _retval)
{
return mData.ConvertToInt32(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsInt64(int64_t* _retval)
{
return mData.ConvertToInt64(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsUint8(uint8_t* _retval)
{
return mData.ConvertToUint8(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsUint16(uint16_t* _retval)
{
return mData.ConvertToUint16(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsUint32(uint32_t* _retval)
{
return mData.ConvertToUint32(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsUint64(uint64_t* _retval)
{
return mData.ConvertToUint64(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsFloat(float* _retval)
{
return mData.ConvertToFloat(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsDouble(double* _retval)
{
return mData.ConvertToDouble(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsBool(bool* _retval)
{
return mData.ConvertToBool(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsChar(char* _retval)
{
return mData.ConvertToChar(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsWChar(char16_t* _retval)
{
return mData.ConvertToWChar(_retval);
}
NS_IMETHODIMP_(nsresult) XPCVariant::GetAsID(nsID* retval)
{
return mData.ConvertToID(retval);
}
NS_IMETHODIMP XPCVariant::GetAsAString(nsAString & _retval)
{
return mData.ConvertToAString(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsDOMString(nsAString & _retval)
{
// A DOMString maps to an AString internally, so we can re-use
// ConvertToAString here.
return mData.ConvertToAString(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsACString(nsACString & _retval)
{
return mData.ConvertToACString(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsAUTF8String(nsAUTF8String & _retval)
{
return mData.ConvertToAUTF8String(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsString(char** _retval)
{
return mData.ConvertToString(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsWString(char16_t** _retval)
{
return mData.ConvertToWString(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsISupports(nsISupports** _retval)
{
return mData.ConvertToISupports(_retval);
}
NS_IMETHODIMP XPCVariant::GetAsInterface(nsIID** iid, void** iface)
{
return mData.ConvertToInterface(iid, iface);
}
NS_IMETHODIMP_(nsresult) XPCVariant::GetAsArray(uint16_t* type, nsIID* iid, uint32_t* count, void * *ptr)
{
return mData.ConvertToArray(type, iid, count, ptr);
}
NS_IMETHODIMP XPCVariant::GetAsStringWithSize(uint32_t* size, char** str)
{
return mData.ConvertToStringWithSize(size, str);
}
NS_IMETHODIMP XPCVariant::GetAsWStringWithSize(uint32_t* size, char16_t** str)
{
return mData.ConvertToWStringWithSize(size, str);
}

View file

@ -0,0 +1,731 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Class that wraps JS objects to appear as XPCOM objects. */
#include "xpcprivate.h"
#include "jsprf.h"
#include "mozilla/DeferredFinalize.h"
#include "mozilla/Sprintf.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "nsCCUncollectableMarker.h"
#include "nsContentUtils.h"
#include "nsThreadUtils.h"
using namespace mozilla;
// NOTE: much of the fancy footwork is done in xpcstubs.cpp
// nsXPCWrappedJS lifetime.
//
// An nsXPCWrappedJS is either rooting its JS object or is subject to finalization.
// The subject-to-finalization state lets wrappers support
// nsSupportsWeakReference in the case where the underlying JS object
// is strongly owned, but the wrapper itself is only weakly owned.
//
// A wrapper is rooting its JS object whenever its refcount is greater than 1. In
// this state, root wrappers are always added to the cycle collector graph. The
// wrapper keeps around an extra refcount, added in the constructor, to support
// the possibility of an eventual transition to the subject-to-finalization state.
// This extra refcount is ignored by the cycle collector, which traverses the "self"
// edge for this refcount.
//
// When the refcount of a rooting wrapper drops to 1, if there is no weak reference
// to the wrapper (which can only happen for the root wrapper), it is immediately
// Destroy()'d. Otherwise, it becomes subject to finalization.
//
// When a wrapper is subject to finalization, the wrapper has a refcount of 1. It is
// now owned exclusively by its JS object. Either a weak reference will be turned into
// a strong ref which will bring its refcount up to 2 and change the wrapper back to
// the rooting state, or it will stay alive until the JS object dies. If the JS object
// dies, then when XPCJSContext::FinalizeCallback calls FindDyingJSObjects
// it will find the wrapper and call Release() in it, destroying the wrapper.
// Otherwise, the wrapper will stay alive, even if it no longer has a weak reference
// to it.
//
// When the wrapper is subject to finalization, it is kept alive by an implicit reference
// from the JS object which is invisible to the cycle collector, so the cycle collector
// does not traverse any children of wrappers that are subject to finalization. This will
// result in a leak if a wrapper in the non-rooting state has an aggregated native that
// keeps alive the wrapper's JS object. See bug 947049.
// If traversing wrappedJS wouldn't release it, nor cause any other objects to be
// added to the graph, there is no need to add it to the graph at all.
bool
nsXPCWrappedJS::CanSkip()
{
if (!nsCCUncollectableMarker::sGeneration)
return false;
if (IsSubjectToFinalization())
return true;
// If this wrapper holds a gray object, need to trace it.
JSObject* obj = GetJSObjectPreserveColor();
if (obj && JS::ObjectIsMarkedGray(obj))
return false;
// For non-root wrappers, check if the root wrapper will be
// added to the CC graph.
if (!IsRootWrapper()) {
// mRoot points to null after unlinking.
NS_ENSURE_TRUE(mRoot, false);
return mRoot->CanSkip();
}
// For the root wrapper, check if there is an aggregated
// native object that will be added to the CC graph.
if (!IsAggregatedToNative())
return true;
nsISupports* agg = GetAggregatedNativeObject();
nsXPCOMCycleCollectionParticipant* cp = nullptr;
CallQueryInterface(agg, &cp);
nsISupports* canonical = nullptr;
agg->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
reinterpret_cast<void**>(&canonical));
return cp && canonical && cp->CanSkipThis(canonical);
}
NS_IMETHODIMP
NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse
(void* p, nsCycleCollectionTraversalCallback& cb)
{
nsISupports* s = static_cast<nsISupports*>(p);
MOZ_ASSERT(CheckForRightISupports(s), "not the nsISupports pointer we expect");
nsXPCWrappedJS* tmp = Downcast(s);
nsrefcnt refcnt = tmp->mRefCnt.get();
if (cb.WantDebugInfo()) {
char name[72];
if (tmp->GetClass())
SprintfLiteral(name, "nsXPCWrappedJS (%s)", tmp->GetClass()->GetInterfaceName());
else
SprintfLiteral(name, "nsXPCWrappedJS");
cb.DescribeRefCountedNode(refcnt, name);
} else {
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt)
}
// A wrapper that is subject to finalization will only die when its JS object dies.
if (tmp->IsSubjectToFinalization())
return NS_OK;
// Don't let the extra reference for nsSupportsWeakReference keep a wrapper that is
// not subject to finalization alive.
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self");
cb.NoteXPCOMChild(s);
if (tmp->IsValid()) {
MOZ_ASSERT(refcnt > 1);
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj");
cb.NoteJSChild(JS::GCCellPtr(tmp->GetJSObjectPreserveColor()));
}
if (tmp->IsRootWrapper()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native");
cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject());
} else {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root");
cb.NoteXPCOMChild(ToSupports(tmp->GetRootWrapper()));
}
return NS_OK;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPCWrappedJS)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS)
tmp->Unlink();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
// XPCJSContext keeps a table of WJS, so we can remove them from
// the purple buffer in between CCs.
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXPCWrappedJS)
return true;
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXPCWrappedJS)
return tmp->CanSkip();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXPCWrappedJS)
return tmp->CanSkip();
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
NS_IMETHODIMP
nsXPCWrappedJS::AggregatedQueryInterface(REFNSIID aIID, void** aInstancePtr)
{
MOZ_ASSERT(IsAggregatedToNative(), "bad AggregatedQueryInterface call");
*aInstancePtr = nullptr;
if (!IsValid())
return NS_ERROR_UNEXPECTED;
// Put this here rather that in DelegatedQueryInterface because it needs
// to be in QueryInterface before the possible delegation to 'outer', but
// we don't want to do this check twice in one call in the normal case:
// once in QueryInterface and once in DelegatedQueryInterface.
if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
NS_ADDREF(this);
*aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
return NS_OK;
}
return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
}
NS_IMETHODIMP
nsXPCWrappedJS::QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
if (nullptr == aInstancePtr) {
NS_PRECONDITION(false, "null pointer");
return NS_ERROR_NULL_POINTER;
}
*aInstancePtr = nullptr;
if ( aIID.Equals(NS_GET_IID(nsXPCOMCycleCollectionParticipant)) ) {
*aInstancePtr = NS_CYCLE_COLLECTION_PARTICIPANT(nsXPCWrappedJS);
return NS_OK;
}
if (aIID.Equals(NS_GET_IID(nsCycleCollectionISupports))) {
*aInstancePtr =
NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
return NS_OK;
}
if (!IsValid())
return NS_ERROR_UNEXPECTED;
if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJSUnmarkGray))) {
*aInstancePtr = nullptr;
mJSObj.exposeToActiveJS();
// Just return some error value since one isn't supposed to use
// nsIXPConnectWrappedJSUnmarkGray objects for anything.
return NS_ERROR_FAILURE;
}
// Always check for this first so that our 'outer' can get this interface
// from us without recurring into a call to the outer's QI!
if (aIID.Equals(NS_GET_IID(nsIXPConnectWrappedJS))) {
NS_ADDREF(this);
*aInstancePtr = (void*) static_cast<nsIXPConnectWrappedJS*>(this);
return NS_OK;
}
nsISupports* outer = GetAggregatedNativeObject();
if (outer)
return outer->QueryInterface(aIID, aInstancePtr);
// else...
return mClass->DelegatedQueryInterface(this, aIID, aInstancePtr);
}
// For a description of nsXPCWrappedJS lifetime and reference counting, see
// the comment at the top of this file.
MozExternalRefCountType
nsXPCWrappedJS::AddRef(void)
{
MOZ_RELEASE_ASSERT(NS_IsMainThread(),
"nsXPCWrappedJS::AddRef called off main thread");
MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
nsrefcnt cnt = mRefCnt.incr(base);
NS_LOG_ADDREF(this, cnt, "nsXPCWrappedJS", sizeof(*this));
if (2 == cnt && IsValid()) {
GetJSObject(); // Unmark gray JSObject.
mClass->GetContext()->AddWrappedJSRoot(this);
}
return cnt;
}
MozExternalRefCountType
nsXPCWrappedJS::Release(void)
{
MOZ_RELEASE_ASSERT(NS_IsMainThread(),
"nsXPCWrappedJS::Release called off main thread");
MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release");
NS_ASSERT_OWNINGTHREAD(nsXPCWrappedJS);
bool shouldDelete = false;
nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Upcast(this);
nsrefcnt cnt = mRefCnt.decr(base, &shouldDelete);
NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS");
if (0 == cnt) {
if (MOZ_UNLIKELY(shouldDelete)) {
mRefCnt.stabilizeForDeletion();
DeleteCycleCollectable();
} else {
mRefCnt.incr(base);
Destroy();
mRefCnt.decr(base);
}
} else if (1 == cnt) {
if (IsValid())
RemoveFromRootSet();
// If we are not a root wrapper being used from a weak reference,
// then the extra ref is not needed and we can let outselves be
// deleted.
if (!HasWeakReferences())
return Release();
MOZ_ASSERT(IsRootWrapper(), "Only root wrappers should have weak references");
}
return cnt;
}
NS_IMETHODIMP_(void)
nsXPCWrappedJS::DeleteCycleCollectable(void)
{
delete this;
}
void
nsXPCWrappedJS::TraceJS(JSTracer* trc)
{
MOZ_ASSERT(mRefCnt >= 2 && IsValid(), "must be strongly referenced");
JS::TraceEdge(trc, &mJSObj, "nsXPCWrappedJS::mJSObj");
}
NS_IMETHODIMP
nsXPCWrappedJS::GetWeakReference(nsIWeakReference** aInstancePtr)
{
if (!IsRootWrapper())
return mRoot->GetWeakReference(aInstancePtr);
return nsSupportsWeakReference::GetWeakReference(aInstancePtr);
}
JSObject*
nsXPCWrappedJS::GetJSObject()
{
return mJSObj;
}
// static
nsresult
nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj,
REFNSIID aIID,
nsXPCWrappedJS** wrapperResult)
{
// Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
MOZ_RELEASE_ASSERT(NS_IsMainThread(),
"nsXPCWrappedJS::GetNewOrUsed called off main thread");
AutoJSContext cx;
bool allowNonScriptable = mozilla::jsipc::IsWrappedCPOW(jsObj);
RefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, aIID,
allowNonScriptable);
if (!clasp)
return NS_ERROR_FAILURE;
JS::RootedObject rootJSObj(cx, clasp->GetRootJSObject(cx, jsObj));
if (!rootJSObj)
return NS_ERROR_FAILURE;
xpc::CompartmentPrivate* rootComp = xpc::CompartmentPrivate::Get(rootJSObj);
MOZ_ASSERT(rootComp);
// Find any existing wrapper.
RefPtr<nsXPCWrappedJS> root = rootComp->GetWrappedJSMap()->Find(rootJSObj);
MOZ_ASSERT_IF(root, !nsXPConnect::GetContextInstance()->GetMultiCompartmentWrappedJSMap()->
Find(rootJSObj));
if (!root) {
root = nsXPConnect::GetContextInstance()->GetMultiCompartmentWrappedJSMap()->
Find(rootJSObj);
}
nsresult rv = NS_ERROR_FAILURE;
if (root) {
RefPtr<nsXPCWrappedJS> wrapper = root->FindOrFindInherited(aIID);
if (wrapper) {
wrapper.forget(wrapperResult);
return NS_OK;
}
} else if (rootJSObj != jsObj) {
// Make a new root wrapper, because there is no existing
// root wrapper, and the wrapper we are trying to make isn't
// a root.
RefPtr<nsXPCWrappedJSClass> rootClasp =
nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports));
if (!rootClasp)
return NS_ERROR_FAILURE;
root = new nsXPCWrappedJS(cx, rootJSObj, rootClasp, nullptr, &rv);
if (NS_FAILED(rv)) {
return rv;
}
}
RefPtr<nsXPCWrappedJS> wrapper = new nsXPCWrappedJS(cx, jsObj, clasp, root, &rv);
if (NS_FAILED(rv)) {
return rv;
}
wrapper.forget(wrapperResult);
return NS_OK;
}
nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx,
JSObject* aJSObj,
nsXPCWrappedJSClass* aClass,
nsXPCWrappedJS* root,
nsresult* rv)
: mJSObj(aJSObj),
mClass(aClass),
mRoot(root ? root : this),
mNext(nullptr)
{
*rv = InitStub(GetClass()->GetIID());
// Continue even in the failure case, so that our refcounting/Destroy
// behavior works correctly.
// There is an extra AddRef to support weak references to wrappers
// that are subject to finalization. See the top of the file for more
// details.
NS_ADDREF_THIS();
if (IsRootWrapper()) {
MOZ_ASSERT(!IsMultiCompartment(), "mNext is always nullptr here");
if (!xpc::CompartmentPrivate::Get(mJSObj)->GetWrappedJSMap()->Add(cx, this)) {
*rv = NS_ERROR_OUT_OF_MEMORY;
}
} else {
NS_ADDREF(mRoot);
mNext = mRoot->mNext;
mRoot->mNext = this;
// We always start wrappers in the per-compartment table. If adding
// this wrapper to the chain causes it to cross compartments, we need
// to migrate the chain to the global table on the XPCJSContext.
if (mRoot->IsMultiCompartment()) {
xpc::CompartmentPrivate::Get(mRoot->mJSObj)->GetWrappedJSMap()->Remove(mRoot);
auto destMap = nsXPConnect::GetContextInstance()->GetMultiCompartmentWrappedJSMap();
if (!destMap->Add(cx, mRoot)) {
*rv = NS_ERROR_OUT_OF_MEMORY;
}
}
}
}
nsXPCWrappedJS::~nsXPCWrappedJS()
{
Destroy();
}
void
XPCJSContext::RemoveWrappedJS(nsXPCWrappedJS* wrapper)
{
AssertInvalidWrappedJSNotInTable(wrapper);
if (!wrapper->IsValid())
return;
// It is possible for the same JS XPCOM implementation object to be wrapped
// with a different interface in multiple JSCompartments. In this case, the
// wrapper chain will contain references to multiple compartments. While we
// always store single-compartment chains in the per-compartment wrapped-js
// table, chains in the multi-compartment wrapped-js table may contain
// single-compartment chains, if they have ever contained a wrapper in a
// different compartment. Since removal requires a lookup anyway, we just do
// the remove on both tables unconditionally.
MOZ_ASSERT_IF(wrapper->IsMultiCompartment(),
!xpc::CompartmentPrivate::Get(wrapper->GetJSObjectPreserveColor())->
GetWrappedJSMap()->HasWrapper(wrapper));
GetMultiCompartmentWrappedJSMap()->Remove(wrapper);
xpc::CompartmentPrivate::Get(wrapper->GetJSObjectPreserveColor())->GetWrappedJSMap()->
Remove(wrapper);
}
#ifdef DEBUG
static void
NotHasWrapperAssertionCallback(JSContext* cx, void* data, JSCompartment* comp)
{
auto wrapper = static_cast<nsXPCWrappedJS*>(data);
auto xpcComp = xpc::CompartmentPrivate::Get(comp);
MOZ_ASSERT_IF(xpcComp, !xpcComp->GetWrappedJSMap()->HasWrapper(wrapper));
}
#endif
void
XPCJSContext::AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const
{
#ifdef DEBUG
if (!wrapper->IsValid()) {
MOZ_ASSERT(!GetMultiCompartmentWrappedJSMap()->HasWrapper(wrapper));
if (!mGCIsRunning)
JS_IterateCompartments(Context(), wrapper, NotHasWrapperAssertionCallback);
}
#endif
}
void
nsXPCWrappedJS::Destroy()
{
MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion");
if (IsRootWrapper())
nsXPConnect::GetContextInstance()->RemoveWrappedJS(this);
Unlink();
}
void
nsXPCWrappedJS::Unlink()
{
nsXPConnect::GetContextInstance()->AssertInvalidWrappedJSNotInTable(this);
if (IsValid()) {
XPCJSContext* cx = nsXPConnect::GetContextInstance();
if (cx) {
if (IsRootWrapper())
cx->RemoveWrappedJS(this);
if (mRefCnt > 1)
RemoveFromRootSet();
}
mJSObj = nullptr;
}
if (IsRootWrapper()) {
ClearWeakReferences();
} else if (mRoot) {
// unlink this wrapper
nsXPCWrappedJS* cur = mRoot;
while (1) {
if (cur->mNext == this) {
cur->mNext = mNext;
break;
}
cur = cur->mNext;
MOZ_ASSERT(cur, "failed to find wrapper in its own chain");
}
// Note: unlinking this wrapper may have changed us from a multi-
// compartment wrapper chain to a single-compartment wrapper chain. We
// leave the wrapper in the multi-compartment table as it is likely to
// need to be multi-compartment again in the future and, moreover, we
// cannot get a JSContext here.
// let the root go
NS_RELEASE(mRoot);
}
mClass = nullptr;
if (mOuter) {
XPCJSContext* cx = nsXPConnect::GetContextInstance();
if (cx->GCIsRunning()) {
DeferredFinalize(mOuter.forget().take());
} else {
mOuter = nullptr;
}
}
}
bool
nsXPCWrappedJS::IsMultiCompartment() const
{
MOZ_ASSERT(IsRootWrapper());
JSCompartment* compartment = Compartment();
nsXPCWrappedJS* next = mNext;
while (next) {
if (next->Compartment() != compartment)
return true;
next = next->mNext;
}
return false;
}
nsXPCWrappedJS*
nsXPCWrappedJS::Find(REFNSIID aIID)
{
if (aIID.Equals(NS_GET_IID(nsISupports)))
return mRoot;
for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) {
if (aIID.Equals(cur->GetIID()))
return cur;
}
return nullptr;
}
// check if asking for an interface that some wrapper in the chain inherits from
nsXPCWrappedJS*
nsXPCWrappedJS::FindInherited(REFNSIID aIID)
{
MOZ_ASSERT(!aIID.Equals(NS_GET_IID(nsISupports)), "bad call sequence");
for (nsXPCWrappedJS* cur = mRoot; cur; cur = cur->mNext) {
bool found;
if (NS_SUCCEEDED(cur->GetClass()->GetInterfaceInfo()->
HasAncestor(&aIID, &found)) && found)
return cur;
}
return nullptr;
}
NS_IMETHODIMP
nsXPCWrappedJS::GetInterfaceInfo(nsIInterfaceInfo** infoResult)
{
MOZ_ASSERT(GetClass(), "wrapper without class");
MOZ_ASSERT(GetClass()->GetInterfaceInfo(), "wrapper class without interface");
// Since failing to get this info will crash some platforms(!), we keep
// mClass valid at shutdown time.
nsCOMPtr<nsIInterfaceInfo> info = GetClass()->GetInterfaceInfo();
if (!info)
return NS_ERROR_UNEXPECTED;
info.forget(infoResult);
return NS_OK;
}
NS_IMETHODIMP
nsXPCWrappedJS::CallMethod(uint16_t methodIndex,
const XPTMethodDescriptor* info,
nsXPTCMiniVariant* params)
{
// Do a release-mode assert against accessing nsXPCWrappedJS off-main-thread.
MOZ_RELEASE_ASSERT(NS_IsMainThread(),
"nsXPCWrappedJS::CallMethod called off main thread");
if (!IsValid())
return NS_ERROR_UNEXPECTED;
return GetClass()->CallMethod(this, methodIndex, info, params);
}
NS_IMETHODIMP
nsXPCWrappedJS::GetInterfaceIID(nsIID** iid)
{
NS_PRECONDITION(iid, "bad param");
*iid = (nsIID*) nsMemory::Clone(&(GetIID()), sizeof(nsIID));
return *iid ? NS_OK : NS_ERROR_UNEXPECTED;
}
void
nsXPCWrappedJS::SystemIsBeingShutDown()
{
// XXX It turns out that it is better to leak here then to do any Releases
// and have them propagate into all sorts of mischief as the system is being
// shutdown. This was learned the hard way :(
// mJSObj == nullptr is used to indicate that the wrapper is no longer valid
// and that calls should fail without trying to use any of the
// xpconnect mechanisms. 'IsValid' is implemented by checking this pointer.
// NOTE: that mClass is retained so that GetInterfaceInfo can continue to
// work (and avoid crashing some platforms).
// Use of unsafeGet() is to avoid triggering post barriers in shutdown, as
// this will access the chunk containing mJSObj, which may have been freed
// at this point.
*mJSObj.unsafeGet() = nullptr;
// Notify other wrappers in the chain.
if (mNext)
mNext->SystemIsBeingShutDown();
}
size_t
nsXPCWrappedJS::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{
// mJSObject is a JS pointer, so don't measure the object.
// mClass is not uniquely owned by this WrappedJS. Measure it in IID2WrappedJSClassMap.
// mRoot is not measured because it is either |this| or we have already measured it.
// mOuter is rare and probably not uniquely owned by this.
size_t n = mallocSizeOf(this);
n += nsAutoXPTCStub::SizeOfExcludingThis(mallocSizeOf);
// Wrappers form a linked list via the mNext field, so include them all
// in the measurement. Only root wrappers are stored in the map, so
// everything will be measured exactly once.
if (mNext)
n += mNext->SizeOfIncludingThis(mallocSizeOf);
return n;
}
/***************************************************************************/
NS_IMETHODIMP
nsXPCWrappedJS::GetEnumerator(nsISimpleEnumerator * *aEnumerate)
{
AutoJSContext cx;
XPCCallContext ccx(cx);
if (!ccx.IsValid())
return NS_ERROR_UNEXPECTED;
return nsXPCWrappedJSClass::BuildPropertyEnumerator(ccx, GetJSObject(),
aEnumerate);
}
NS_IMETHODIMP
nsXPCWrappedJS::GetProperty(const nsAString & name, nsIVariant** _retval)
{
AutoJSContext cx;
XPCCallContext ccx(cx);
if (!ccx.IsValid())
return NS_ERROR_UNEXPECTED;
return nsXPCWrappedJSClass::
GetNamedPropertyAsVariant(ccx, GetJSObject(), name, _retval);
}
/***************************************************************************/
NS_IMETHODIMP
nsXPCWrappedJS::DebugDump(int16_t depth)
{
#ifdef DEBUG
XPC_LOG_ALWAYS(("nsXPCWrappedJS @ %x with mRefCnt = %d", this, mRefCnt.get()));
XPC_LOG_INDENT();
XPC_LOG_ALWAYS(("%s wrapper around JSObject @ %x", \
IsRootWrapper() ? "ROOT":"non-root", mJSObj.get()));
char* name;
GetClass()->GetInterfaceInfo()->GetName(&name);
XPC_LOG_ALWAYS(("interface name is %s", name));
if (name)
free(name);
char * iid = GetClass()->GetIID().ToString();
XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
if (iid)
free(iid);
XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x", mClass.get()));
if (!IsRootWrapper())
XPC_LOG_OUTDENT();
if (mNext) {
if (IsRootWrapper()) {
XPC_LOG_ALWAYS(("Additional wrappers for this object..."));
XPC_LOG_INDENT();
}
mNext->DebugDump(depth);
if (IsRootWrapper())
XPC_LOG_OUTDENT();
}
if (IsRootWrapper())
XPC_LOG_OUTDENT();
#endif
return NS_OK;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,800 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Manage the shared info about interfaces for use by wrappedNatives. */
#include "xpcprivate.h"
#include "jswrapper.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/XPTInterfaceInfoManager.h"
#include "nsPrintfCString.h"
using namespace JS;
using namespace mozilla;
/***************************************************************************/
// XPCNativeMember
// static
bool
XPCNativeMember::GetCallInfo(JSObject* funobj,
RefPtr<XPCNativeInterface>* pInterface,
XPCNativeMember** pMember)
{
funobj = js::UncheckedUnwrap(funobj);
Value memberVal =
js::GetFunctionNativeReserved(funobj,
XPC_FUNCTION_NATIVE_MEMBER_SLOT);
*pMember = static_cast<XPCNativeMember*>(memberVal.toPrivate());
*pInterface = (*pMember)->GetInterface();
return true;
}
bool
XPCNativeMember::NewFunctionObject(XPCCallContext& ccx,
XPCNativeInterface* iface, HandleObject parent,
Value* pval)
{
MOZ_ASSERT(!IsConstant(), "Only call this if you're sure this is not a constant!");
return Resolve(ccx, iface, parent, pval);
}
bool
XPCNativeMember::Resolve(XPCCallContext& ccx, XPCNativeInterface* iface,
HandleObject parent, Value* vp)
{
MOZ_ASSERT(iface == GetInterface());
if (IsConstant()) {
RootedValue resultVal(ccx);
nsXPIDLCString name;
if (NS_FAILED(iface->GetInterfaceInfo()->GetConstant(mIndex, &resultVal,
getter_Copies(name))))
return false;
*vp = resultVal;
return true;
}
// else...
// This is a method or attribute - we'll be needing a function object
int argc;
JSNative callback;
if (IsMethod()) {
const nsXPTMethodInfo* info;
if (NS_FAILED(iface->GetInterfaceInfo()->GetMethodInfo(mIndex, &info)))
return false;
// Note: ASSUMES that retval is last arg.
argc = (int) info->GetParamCount();
if (argc && info->GetParam((uint8_t)(argc-1)).IsRetval())
argc-- ;
callback = XPC_WN_CallMethod;
} else {
argc = 0;
callback = XPC_WN_GetterSetter;
}
JSFunction* fun = js::NewFunctionByIdWithReserved(ccx, callback, argc, 0, GetName());
if (!fun)
return false;
JSObject* funobj = JS_GetFunctionObject(fun);
if (!funobj)
return false;
js::SetFunctionNativeReserved(funobj, XPC_FUNCTION_NATIVE_MEMBER_SLOT,
PrivateValue(this));
js::SetFunctionNativeReserved(funobj, XPC_FUNCTION_PARENT_OBJECT_SLOT,
ObjectValue(*parent));
vp->setObject(*funobj);
return true;
}
/***************************************************************************/
// XPCNativeInterface
XPCNativeInterface::~XPCNativeInterface()
{
XPCJSContext::Get()->GetIID2NativeInterfaceMap()->Remove(this);
}
// static
already_AddRefed<XPCNativeInterface>
XPCNativeInterface::GetNewOrUsed(const nsIID* iid)
{
RefPtr<XPCNativeInterface> iface;
XPCJSContext* cx = XPCJSContext::Get();
IID2NativeInterfaceMap* map = cx->GetIID2NativeInterfaceMap();
if (!map)
return nullptr;
iface = map->Find(*iid);
if (iface)
return iface.forget();
nsCOMPtr<nsIInterfaceInfo> info;
XPTInterfaceInfoManager::GetSingleton()->GetInfoForIID(iid, getter_AddRefs(info));
if (!info)
return nullptr;
iface = NewInstance(info);
if (!iface)
return nullptr;
XPCNativeInterface* iface2 = map->Add(iface);
if (!iface2) {
NS_ERROR("failed to add our interface!");
iface = nullptr;
} else if (iface2 != iface) {
iface = iface2;
}
return iface.forget();
}
// static
already_AddRefed<XPCNativeInterface>
XPCNativeInterface::GetNewOrUsed(nsIInterfaceInfo* info)
{
RefPtr<XPCNativeInterface> iface;
const nsIID* iid;
if (NS_FAILED(info->GetIIDShared(&iid)) || !iid)
return nullptr;
XPCJSContext* cx = XPCJSContext::Get();
IID2NativeInterfaceMap* map = cx->GetIID2NativeInterfaceMap();
if (!map)
return nullptr;
iface = map->Find(*iid);
if (iface)
return iface.forget();
iface = NewInstance(info);
if (!iface)
return nullptr;
RefPtr<XPCNativeInterface> iface2 = map->Add(iface);
if (!iface2) {
NS_ERROR("failed to add our interface!");
iface = nullptr;
} else if (iface2 != iface) {
iface = iface2;
}
return iface.forget();
}
// static
already_AddRefed<XPCNativeInterface>
XPCNativeInterface::GetNewOrUsed(const char* name)
{
nsCOMPtr<nsIInterfaceInfo> info;
XPTInterfaceInfoManager::GetSingleton()->GetInfoForName(name, getter_AddRefs(info));
return info ? GetNewOrUsed(info) : nullptr;
}
// static
already_AddRefed<XPCNativeInterface>
XPCNativeInterface::GetISupports()
{
// XXX We should optimize this to cache this common XPCNativeInterface.
return GetNewOrUsed(&NS_GET_IID(nsISupports));
}
// static
already_AddRefed<XPCNativeInterface>
XPCNativeInterface::NewInstance(nsIInterfaceInfo* aInfo)
{
AutoJSContext cx;
static const uint16_t MAX_LOCAL_MEMBER_COUNT = 16;
XPCNativeMember local_members[MAX_LOCAL_MEMBER_COUNT];
RefPtr<XPCNativeInterface> obj;
XPCNativeMember* members = nullptr;
int i;
bool failed = false;
uint16_t constCount;
uint16_t methodCount;
uint16_t totalCount;
uint16_t realTotalCount = 0;
XPCNativeMember* cur;
RootedString str(cx);
RootedId interfaceName(cx);
// XXX Investigate lazy init? This is a problem given the
// 'placement new' scheme - we need to at least know how big to make
// the object. We might do a scan of methods to determine needed size,
// then make our object, but avoid init'ing *any* members until asked?
// Find out how often we create these objects w/o really looking at
// (or using) the members.
bool canScript;
if (NS_FAILED(aInfo->IsScriptable(&canScript)) || !canScript)
return nullptr;
bool mainProcessScriptableOnly;
if (NS_FAILED(aInfo->IsMainProcessScriptableOnly(&mainProcessScriptableOnly)))
return nullptr;
if (mainProcessScriptableOnly && !XRE_IsParentProcess()) {
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
if (console) {
const char* intfNameChars;
aInfo->GetNameShared(&intfNameChars);
nsPrintfCString errorMsg("Use of %s in content process is deprecated.", intfNameChars);
nsAutoString filename;
uint32_t lineno = 0, column = 0;
nsJSUtils::GetCallingLocation(cx, filename, &lineno, &column);
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
error->Init(NS_ConvertUTF8toUTF16(errorMsg),
filename, EmptyString(),
lineno, column, nsIScriptError::warningFlag, "chrome javascript");
console->LogMessage(error);
}
}
if (NS_FAILED(aInfo->GetMethodCount(&methodCount)) ||
NS_FAILED(aInfo->GetConstantCount(&constCount)))
return nullptr;
// If the interface does not have nsISupports in its inheritance chain
// then we know we can't reflect its methods. However, some interfaces that
// are used just to reflect constants are declared this way. We need to
// go ahead and build the thing. But, we'll ignore whatever methods it may
// have.
if (!nsXPConnect::IsISupportsDescendant(aInfo))
methodCount = 0;
totalCount = methodCount + constCount;
if (totalCount > MAX_LOCAL_MEMBER_COUNT) {
members = new XPCNativeMember[totalCount];
if (!members)
return nullptr;
} else {
members = local_members;
}
// NOTE: since getters and setters share a member, we might not use all
// of the member objects.
for (i = 0; i < methodCount; i++) {
const nsXPTMethodInfo* info;
if (NS_FAILED(aInfo->GetMethodInfo(i, &info))) {
failed = true;
break;
}
// don't reflect Addref or Release
if (i == 1 || i == 2)
continue;
if (!XPCConvert::IsMethodReflectable(*info))
continue;
str = JS_AtomizeAndPinString(cx, info->GetName());
if (!str) {
NS_ERROR("bad method name");
failed = true;
break;
}
jsid name = INTERNED_STRING_TO_JSID(cx, str);
if (info->IsSetter()) {
MOZ_ASSERT(realTotalCount,"bad setter");
// Note: ASSUMES Getter/Setter pairs are next to each other
// This is a rule of the typelib spec.
cur = &members[realTotalCount-1];
MOZ_ASSERT(cur->GetName() == name,"bad setter");
MOZ_ASSERT(cur->IsReadOnlyAttribute(),"bad setter");
MOZ_ASSERT(cur->GetIndex() == i-1,"bad setter");
cur->SetWritableAttribute();
} else {
// XXX need better way to find dups
// MOZ_ASSERT(!LookupMemberByID(name),"duplicate method name");
if (realTotalCount == XPCNativeMember::GetMaxIndexInInterface()) {
NS_WARNING("Too many members in interface");
failed = true;
break;
}
cur = &members[realTotalCount];
cur->SetName(name);
if (info->IsGetter())
cur->SetReadOnlyAttribute(i);
else
cur->SetMethod(i);
cur->SetIndexInInterface(realTotalCount);
++realTotalCount;
}
}
if (!failed) {
for (i = 0; i < constCount; i++) {
RootedValue constant(cx);
nsXPIDLCString namestr;
if (NS_FAILED(aInfo->GetConstant(i, &constant, getter_Copies(namestr)))) {
failed = true;
break;
}
str = JS_AtomizeAndPinString(cx, namestr);
if (!str) {
NS_ERROR("bad constant name");
failed = true;
break;
}
jsid name = INTERNED_STRING_TO_JSID(cx, str);
// XXX need better way to find dups
//MOZ_ASSERT(!LookupMemberByID(name),"duplicate method/constant name");
if (realTotalCount == XPCNativeMember::GetMaxIndexInInterface()) {
NS_WARNING("Too many members in interface");
failed = true;
break;
}
cur = &members[realTotalCount];
cur->SetName(name);
cur->SetConstant(i);
cur->SetIndexInInterface(realTotalCount);
++realTotalCount;
}
}
if (!failed) {
const char* bytes;
if (NS_FAILED(aInfo->GetNameShared(&bytes)) || !bytes ||
nullptr == (str = JS_AtomizeAndPinString(cx, bytes))) {
failed = true;
}
interfaceName = INTERNED_STRING_TO_JSID(cx, str);
}
if (!failed) {
// Use placement new to create an object with the right amount of space
// to hold the members array
int size = sizeof(XPCNativeInterface);
if (realTotalCount > 1)
size += (realTotalCount - 1) * sizeof(XPCNativeMember);
void* place = new char[size];
if (place)
obj = new(place) XPCNativeInterface(aInfo, interfaceName);
if (obj) {
obj->mMemberCount = realTotalCount;
// copy valid members
if (realTotalCount)
memcpy(obj->mMembers, members,
realTotalCount * sizeof(XPCNativeMember));
}
}
if (members && members != local_members)
delete [] members;
return obj.forget();
}
// static
void
XPCNativeInterface::DestroyInstance(XPCNativeInterface* inst)
{
inst->~XPCNativeInterface();
delete [] (char*) inst;
}
size_t
XPCNativeInterface::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
{
return mallocSizeOf(this);
}
void
XPCNativeInterface::DebugDump(int16_t depth)
{
#ifdef DEBUG
depth--;
XPC_LOG_ALWAYS(("XPCNativeInterface @ %x", this));
XPC_LOG_INDENT();
XPC_LOG_ALWAYS(("name is %s", GetNameString()));
XPC_LOG_ALWAYS(("mMemberCount is %d", mMemberCount));
XPC_LOG_ALWAYS(("mInfo @ %x", mInfo.get()));
XPC_LOG_OUTDENT();
#endif
}
/***************************************************************************/
// XPCNativeSetKey
static PLDHashNumber
HashPointer(const void* ptr)
{
return NS_PTR_TO_UINT32(ptr) >> 2;
}
PLDHashNumber
XPCNativeSetKey::Hash() const
{
PLDHashNumber h = 0;
if (mBaseSet) {
XPCNativeInterface** current = mBaseSet->GetInterfaceArray();
uint16_t count = mBaseSet->GetInterfaceCount();
for (uint16_t i = 0; i < count; i++) {
h ^= HashPointer(*(current++));
}
} else {
// A newly created set will contain nsISupports first...
RefPtr<XPCNativeInterface> isupp = XPCNativeInterface::GetISupports();
h ^= HashPointer(isupp);
// ...but no more than once.
if (isupp == mAddition)
return h;
}
if (mAddition) {
h ^= HashPointer(mAddition);
}
return h;
}
/***************************************************************************/
// XPCNativeSet
XPCNativeSet::~XPCNativeSet()
{
// Remove |this| before we clear the interfaces to ensure that the
// hashtable look up is correct.
XPCJSContext::Get()->GetNativeSetMap()->Remove(this);
for (int i = 0; i < mInterfaceCount; i++) {
NS_RELEASE(mInterfaces[i]);
}
}
// static
already_AddRefed<XPCNativeSet>
XPCNativeSet::GetNewOrUsed(const nsIID* iid)
{
RefPtr<XPCNativeInterface> iface =
XPCNativeInterface::GetNewOrUsed(iid);
if (!iface)
return nullptr;
XPCNativeSetKey key(iface);
XPCJSContext* xpccx = XPCJSContext::Get();
NativeSetMap* map = xpccx->GetNativeSetMap();
if (!map)
return nullptr;
RefPtr<XPCNativeSet> set = map->Find(&key);
if (set)
return set.forget();
set = NewInstance({iface.forget()});
if (!set)
return nullptr;
if (!map->AddNew(&key, set)) {
NS_ERROR("failed to add our set!");
set = nullptr;
}
return set.forget();
}
// static
already_AddRefed<XPCNativeSet>
XPCNativeSet::GetNewOrUsed(nsIClassInfo* classInfo)
{
XPCJSContext* xpccx = XPCJSContext::Get();
ClassInfo2NativeSetMap* map = xpccx->GetClassInfo2NativeSetMap();
if (!map)
return nullptr;
RefPtr<XPCNativeSet> set = map->Find(classInfo);
if (set)
return set.forget();
nsIID** iidArray = nullptr;
uint32_t iidCount = 0;
if (NS_FAILED(classInfo->GetInterfaces(&iidCount, &iidArray))) {
// Note: I'm making it OK for this call to fail so that one can add
// nsIClassInfo to classes implemented in script without requiring this
// method to be implemented.
// Make sure these are set correctly...
iidArray = nullptr;
iidCount = 0;
}
MOZ_ASSERT((iidCount && iidArray) || !(iidCount || iidArray), "GetInterfaces returned bad array");
// !!! from here on we only exit through the 'out' label !!!
if (iidCount) {
nsTArray<RefPtr<XPCNativeInterface>> interfaceArray(iidCount);
nsIID** currentIID = iidArray;
for (uint32_t i = 0; i < iidCount; i++) {
nsIID* iid = *(currentIID++);
if (!iid) {
NS_ERROR("Null found in classinfo interface list");
continue;
}
RefPtr<XPCNativeInterface> iface =
XPCNativeInterface::GetNewOrUsed(iid);
if (!iface) {
// XXX warn here
continue;
}
interfaceArray.AppendElement(iface.forget());
}
if (interfaceArray.Length() > 0) {
set = NewInstance(Move(interfaceArray));
if (set) {
NativeSetMap* map2 = xpccx->GetNativeSetMap();
if (!map2)
goto out;
XPCNativeSetKey key(set);
XPCNativeSet* set2 = map2->Add(&key, set);
if (!set2) {
NS_ERROR("failed to add our set!");
set = nullptr;
goto out;
}
// It is okay to find an existing entry here because
// we did not look for one before we called Add().
if (set2 != set) {
set = set2;
}
}
} else
set = GetNewOrUsed(&NS_GET_IID(nsISupports));
} else
set = GetNewOrUsed(&NS_GET_IID(nsISupports));
if (set) {
#ifdef DEBUG
XPCNativeSet* set2 =
#endif
map->Add(classInfo, set);
MOZ_ASSERT(set2, "failed to add our set!");
MOZ_ASSERT(set2 == set, "hashtables inconsistent!");
}
out:
if (iidArray)
NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(iidCount, iidArray);
return set.forget();
}
// static
void
XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo)
{
XPCJSContext* xpccx = nsXPConnect::GetContextInstance();
ClassInfo2NativeSetMap* map = xpccx->GetClassInfo2NativeSetMap();
if (map)
map->Remove(classInfo);
}
// static
already_AddRefed<XPCNativeSet>
XPCNativeSet::GetNewOrUsed(XPCNativeSetKey* key)
{
NativeSetMap* map = XPCJSContext::Get()->GetNativeSetMap();
if (!map)
return nullptr;
RefPtr<XPCNativeSet> set = map->Find(key);
if (set)
return set.forget();
if (key->GetBaseSet())
set = NewInstanceMutate(key);
else
set = NewInstance({key->GetAddition()});
if (!set)
return nullptr;
if (!map->AddNew(key, set)) {
NS_ERROR("failed to add our set!");
set = nullptr;
}
return set.forget();
}
// static
already_AddRefed<XPCNativeSet>
XPCNativeSet::GetNewOrUsed(XPCNativeSet* firstSet,
XPCNativeSet* secondSet,
bool preserveFirstSetOrder)
{
// Figure out how many interfaces we'll need in the new set.
uint32_t uniqueCount = firstSet->mInterfaceCount;
for (uint32_t i = 0; i < secondSet->mInterfaceCount; ++i) {
if (!firstSet->HasInterface(secondSet->mInterfaces[i]))
uniqueCount++;
}
// If everything in secondSet was a duplicate, we can just use the first
// set.
if (uniqueCount == firstSet->mInterfaceCount)
return RefPtr<XPCNativeSet>(firstSet).forget();
// If the secondSet is just a superset of the first, we can use it provided
// that the caller doesn't care about ordering.
if (!preserveFirstSetOrder && uniqueCount == secondSet->mInterfaceCount)
return RefPtr<XPCNativeSet>(secondSet).forget();
// Ok, darn. Now we have to make a new set.
//
// It would be faster to just create the new set all at once, but that
// would involve wrangling with some pretty hairy code - especially since
// a lot of stuff assumes that sets are created by adding one interface to an
// existing set. So let's just do the slow and easy thing and hope that the
// above optimizations handle the common cases.
RefPtr<XPCNativeSet> currentSet = firstSet;
for (uint32_t i = 0; i < secondSet->mInterfaceCount; ++i) {
XPCNativeInterface* iface = secondSet->mInterfaces[i];
if (!currentSet->HasInterface(iface)) {
// Create a new augmented set, inserting this interface at the end.
XPCNativeSetKey key(currentSet, iface);
currentSet = XPCNativeSet::GetNewOrUsed(&key);
if (!currentSet)
return nullptr;
}
}
// We've got the union set. Hand it back to the caller.
MOZ_ASSERT(currentSet->mInterfaceCount == uniqueCount);
return currentSet.forget();
}
// static
already_AddRefed<XPCNativeSet>
XPCNativeSet::NewInstance(nsTArray<RefPtr<XPCNativeInterface>>&& array)
{
if (array.Length() == 0)
return nullptr;
// We impose the invariant:
// "All sets have exactly one nsISupports interface and it comes first."
// This is the place where we impose that rule - even if given inputs
// that don't exactly follow the rule.
RefPtr<XPCNativeInterface> isup = XPCNativeInterface::GetISupports();
uint16_t slots = array.Length() + 1;
for (auto key = array.begin(); key != array.end(); key++) {
if (*key == isup)
slots--;
}
// Use placement new to create an object with the right amount of space
// to hold the members array
int size = sizeof(XPCNativeSet);
if (slots > 1)
size += (slots - 1) * sizeof(XPCNativeInterface*);
void* place = new char[size];
RefPtr<XPCNativeSet> obj = new(place) XPCNativeSet();
// Stick the nsISupports in front and skip additional nsISupport(s)
XPCNativeInterface** outp = (XPCNativeInterface**) &obj->mInterfaces;
uint16_t memberCount = 1; // for the one member in nsISupports
NS_ADDREF(*(outp++) = isup);
for (auto key = array.begin(); key != array.end(); key++) {
RefPtr<XPCNativeInterface> cur = key->forget();
if (isup == cur)
continue;
memberCount += cur->GetMemberCount();
*(outp++) = cur.forget().take();
}
obj->mMemberCount = memberCount;
obj->mInterfaceCount = slots;
return obj.forget();
}
// static
already_AddRefed<XPCNativeSet>
XPCNativeSet::NewInstanceMutate(XPCNativeSetKey* key)
{
XPCNativeSet* otherSet = key->GetBaseSet();
XPCNativeInterface* newInterface = key->GetAddition();
MOZ_ASSERT(otherSet);
if (!newInterface)
return nullptr;
// Use placement new to create an object with the right amount of space
// to hold the members array
int size = sizeof(XPCNativeSet);
size += otherSet->mInterfaceCount * sizeof(XPCNativeInterface*);
void* place = new char[size];
RefPtr<XPCNativeSet> obj = new(place) XPCNativeSet();
obj->mMemberCount = otherSet->GetMemberCount() +
newInterface->GetMemberCount();
obj->mInterfaceCount = otherSet->mInterfaceCount + 1;
XPCNativeInterface** src = otherSet->mInterfaces;
XPCNativeInterface** dest = obj->mInterfaces;
for (uint16_t i = 0; i < otherSet->mInterfaceCount; i++) {
NS_ADDREF(*dest++ = *src++);
}
NS_ADDREF(*dest++ = newInterface);
return obj.forget();
}
// static
void
XPCNativeSet::DestroyInstance(XPCNativeSet* inst)
{
inst->~XPCNativeSet();
delete [] (char*) inst;
}
size_t
XPCNativeSet::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
{
return mallocSizeOf(this);
}
void
XPCNativeSet::DebugDump(int16_t depth)
{
#ifdef DEBUG
depth--;
XPC_LOG_ALWAYS(("XPCNativeSet @ %x", this));
XPC_LOG_INDENT();
XPC_LOG_ALWAYS(("mInterfaceCount of %d", mInterfaceCount));
if (depth) {
for (uint16_t i = 0; i < mInterfaceCount; i++)
mInterfaces[i]->DebugDump(depth);
}
XPC_LOG_ALWAYS(("mMemberCount of %d", mMemberCount));
XPC_LOG_OUTDENT();
#endif
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,208 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Shared proto object for XPCWrappedNative. */
#include "xpcprivate.h"
#include "pratom.h"
using namespace mozilla;
#ifdef DEBUG
int32_t XPCWrappedNativeProto::gDEBUG_LiveProtoCount = 0;
#endif
XPCWrappedNativeProto::XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
nsIClassInfo* ClassInfo,
already_AddRefed<XPCNativeSet>&& Set)
: mScope(Scope),
mJSProtoObject(nullptr),
mClassInfo(ClassInfo),
mSet(Set),
mScriptableInfo(nullptr)
{
// This native object lives as long as its associated JSObject - killed
// by finalization of the JSObject (or explicitly if Init fails).
MOZ_COUNT_CTOR(XPCWrappedNativeProto);
MOZ_ASSERT(mScope);
#ifdef DEBUG
gDEBUG_LiveProtoCount++;
#endif
}
XPCWrappedNativeProto::~XPCWrappedNativeProto()
{
MOZ_ASSERT(!mJSProtoObject, "JSProtoObject still alive");
MOZ_COUNT_DTOR(XPCWrappedNativeProto);
#ifdef DEBUG
gDEBUG_LiveProtoCount--;
#endif
// Note that our weak ref to mScope is not to be trusted at this point.
XPCNativeSet::ClearCacheEntryForClassInfo(mClassInfo);
delete mScriptableInfo;
}
bool
XPCWrappedNativeProto::Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
bool callPostCreatePrototype)
{
AutoJSContext cx;
nsIXPCScriptable* callback = scriptableCreateInfo ?
scriptableCreateInfo->GetCallback() :
nullptr;
if (callback) {
mScriptableInfo =
XPCNativeScriptableInfo::Construct(scriptableCreateInfo);
if (!mScriptableInfo)
return false;
}
const js::Class* jsclazz =
(mScriptableInfo &&
mScriptableInfo->GetFlags().AllowPropModsToPrototype())
? &XPC_WN_ModsAllowed_Proto_JSClass
: &XPC_WN_NoMods_Proto_JSClass;
JS::RootedObject global(cx, mScope->GetGlobalJSObject());
JS::RootedObject proto(cx, JS_GetObjectPrototype(cx, global));
mJSProtoObject = JS_NewObjectWithUniqueType(cx, js::Jsvalify(jsclazz),
proto);
bool success = !!mJSProtoObject;
if (success) {
JS_SetPrivate(mJSProtoObject, this);
if (callPostCreatePrototype)
success = CallPostCreatePrototype();
}
return success;
}
bool
XPCWrappedNativeProto::CallPostCreatePrototype()
{
AutoJSContext cx;
// Nothing to do if we don't have a scriptable callback.
nsIXPCScriptable* callback = mScriptableInfo ? mScriptableInfo->GetCallback()
: nullptr;
if (!callback)
return true;
// Call the helper. This can handle being called if it's not implemented,
// so we don't have to check any sort of "want" here. See xpc_map_end.h.
nsresult rv = callback->PostCreatePrototype(cx, mJSProtoObject);
if (NS_FAILED(rv)) {
JS_SetPrivate(mJSProtoObject, nullptr);
mJSProtoObject = nullptr;
XPCThrower::Throw(rv, cx);
return false;
}
return true;
}
void
XPCWrappedNativeProto::JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj)
{
MOZ_ASSERT(obj == mJSProtoObject.unbarrieredGet(), "huh?");
// Only remove this proto from the map if it is the one in the map.
ClassInfo2WrappedNativeProtoMap* map = GetScope()->GetWrappedNativeProtoMap();
if (map->Find(mClassInfo) == this)
map->Remove(mClassInfo);
GetContext()->GetDyingWrappedNativeProtoMap()->Add(this);
mJSProtoObject.finalize(js::CastToJSFreeOp(fop)->runtime());
}
void
XPCWrappedNativeProto::JSProtoObjectMoved(JSObject* obj, const JSObject* old)
{
MOZ_ASSERT(mJSProtoObject.unbarrieredGet() == old);
mJSProtoObject.init(obj); // Update without triggering barriers.
}
void
XPCWrappedNativeProto::SystemIsBeingShutDown()
{
// Note that the instance might receive this call multiple times
// as we walk to here from various places.
if (mJSProtoObject) {
// short circuit future finalization
JS_SetPrivate(mJSProtoObject, nullptr);
mJSProtoObject = nullptr;
}
}
// static
XPCWrappedNativeProto*
XPCWrappedNativeProto::GetNewOrUsed(XPCWrappedNativeScope* scope,
nsIClassInfo* classInfo,
const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
bool callPostCreatePrototype)
{
AutoJSContext cx;
MOZ_ASSERT(scope, "bad param");
MOZ_ASSERT(classInfo, "bad param");
AutoMarkingWrappedNativeProtoPtr proto(cx);
ClassInfo2WrappedNativeProtoMap* map = nullptr;
map = scope->GetWrappedNativeProtoMap();
proto = map->Find(classInfo);
if (proto)
return proto;
RefPtr<XPCNativeSet> set = XPCNativeSet::GetNewOrUsed(classInfo);
if (!set)
return nullptr;
proto = new XPCWrappedNativeProto(scope, classInfo, set.forget());
if (!proto || !proto->Init(scriptableCreateInfo, callPostCreatePrototype)) {
delete proto.get();
return nullptr;
}
map->Add(classInfo, proto);
return proto;
}
void
XPCWrappedNativeProto::DebugDump(int16_t depth)
{
#ifdef DEBUG
depth-- ;
XPC_LOG_ALWAYS(("XPCWrappedNativeProto @ %x", this));
XPC_LOG_INDENT();
XPC_LOG_ALWAYS(("gDEBUG_LiveProtoCount is %d", gDEBUG_LiveProtoCount));
XPC_LOG_ALWAYS(("mScope @ %x", mScope));
XPC_LOG_ALWAYS(("mJSProtoObject @ %x", mJSProtoObject.get()));
XPC_LOG_ALWAYS(("mSet @ %x", mSet.get()));
XPC_LOG_ALWAYS(("mScriptableInfo @ %x", mScriptableInfo));
if (depth && mScriptableInfo) {
XPC_LOG_INDENT();
XPC_LOG_ALWAYS(("mScriptable @ %x", mScriptableInfo->GetCallback()));
XPC_LOG_ALWAYS(("mFlags of %x", (uint32_t)mScriptableInfo->GetFlags()));
XPC_LOG_ALWAYS(("mJSClass @ %x", mScriptableInfo->GetJSClass()));
XPC_LOG_OUTDENT();
}
XPC_LOG_OUTDENT();
#endif
}

View file

@ -0,0 +1,934 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/* Class used to manage the wrapped native objects within a JS scope. */
#include "xpcprivate.h"
#include "XPCWrapper.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionNoteRootCallback.h"
#include "nsPrincipal.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Preferences.h"
#include "nsIAddonInterposition.h"
#include "nsIXULRuntime.h"
#include "mozilla/dom/BindingUtils.h"
using namespace mozilla;
using namespace xpc;
using namespace JS;
/***************************************************************************/
XPCWrappedNativeScope* XPCWrappedNativeScope::gScopes = nullptr;
XPCWrappedNativeScope* XPCWrappedNativeScope::gDyingScopes = nullptr;
bool XPCWrappedNativeScope::gShutdownObserverInitialized = false;
XPCWrappedNativeScope::InterpositionMap* XPCWrappedNativeScope::gInterpositionMap = nullptr;
InterpositionWhitelistArray* XPCWrappedNativeScope::gInterpositionWhitelists = nullptr;
XPCWrappedNativeScope::AddonSet* XPCWrappedNativeScope::gAllowCPOWAddonSet = nullptr;
NS_IMPL_ISUPPORTS(XPCWrappedNativeScope::ClearInterpositionsObserver, nsIObserver)
NS_IMETHODIMP
XPCWrappedNativeScope::ClearInterpositionsObserver::Observe(nsISupports* subject,
const char* topic,
const char16_t* data)
{
MOZ_ASSERT(strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
// The interposition map holds strong references to interpositions, which
// may themselves be involved in cycles. We need to drop these strong
// references before the cycle collector shuts down. Otherwise we'll
// leak. This observer always runs before CC shutdown.
if (gInterpositionMap) {
delete gInterpositionMap;
gInterpositionMap = nullptr;
}
if (gInterpositionWhitelists) {
delete gInterpositionWhitelists;
gInterpositionWhitelists = nullptr;
}
if (gAllowCPOWAddonSet) {
delete gAllowCPOWAddonSet;
gAllowCPOWAddonSet = nullptr;
}
nsContentUtils::UnregisterShutdownObserver(this);
return NS_OK;
}
static bool
RemoteXULForbidsXBLScope(nsIPrincipal* aPrincipal, HandleObject aGlobal)
{
MOZ_ASSERT(aPrincipal);
// Certain singleton sandoxes are created very early in startup - too early
// to call into AllowXULXBLForPrincipal. We never create XBL scopes for
// sandboxes anway, and certainly not for these singleton scopes. So we just
// short-circuit here.
if (IsSandbox(aGlobal))
return false;
// AllowXULXBLForPrincipal will return true for system principal, but we
// don't want that here.
MOZ_ASSERT(nsContentUtils::IsInitialized());
if (nsContentUtils::IsSystemPrincipal(aPrincipal))
return false;
// If this domain isn't whitelisted, we're done.
if (!nsContentUtils::AllowXULXBLForPrincipal(aPrincipal))
return false;
// Check the pref to determine how we should behave.
return !Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false);
}
XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext* cx,
JS::HandleObject aGlobal)
: mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_LENGTH)),
mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_LENGTH)),
mComponents(nullptr),
mNext(nullptr),
mGlobalJSObject(aGlobal),
mHasCallInterpositions(false),
mIsContentXBLScope(false),
mIsAddonScope(false)
{
// add ourselves to the scopes list
{
MOZ_ASSERT(aGlobal);
DebugOnly<const js::Class*> clasp = js::GetObjectClass(aGlobal);
MOZ_ASSERT(clasp->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
JSCLASS_HAS_PRIVATE) ||
mozilla::dom::IsDOMClass(clasp));
#ifdef DEBUG
for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
MOZ_ASSERT(aGlobal != cur->GetGlobalJSObjectPreserveColor(), "dup object");
#endif
mNext = gScopes;
gScopes = this;
}
MOZ_COUNT_CTOR(XPCWrappedNativeScope);
// Create the compartment private.
JSCompartment* c = js::GetObjectCompartment(aGlobal);
MOZ_ASSERT(!JS_GetCompartmentPrivate(c));
CompartmentPrivate* priv = new CompartmentPrivate(c);
JS_SetCompartmentPrivate(c, priv);
// Attach ourselves to the compartment private.
priv->scope = this;
// Determine whether we would allow an XBL scope in this situation.
// In addition to being pref-controlled, we also disable XBL scopes for
// remote XUL domains, _except_ if we have an additional pref override set.
nsIPrincipal* principal = GetPrincipal();
mAllowContentXBLScope = !RemoteXULForbidsXBLScope(principal, aGlobal);
// Determine whether to use an XBL scope.
mUseContentXBLScope = mAllowContentXBLScope;
if (mUseContentXBLScope) {
const js::Class* clasp = js::GetObjectClass(mGlobalJSObject);
mUseContentXBLScope = !strcmp(clasp->name, "Window");
}
if (mUseContentXBLScope) {
mUseContentXBLScope = principal && !nsContentUtils::IsSystemPrincipal(principal);
}
JSAddonId* addonId = JS::AddonIdOfObject(aGlobal);
if (gInterpositionMap) {
bool isSystem = nsContentUtils::IsSystemPrincipal(principal);
bool waiveInterposition = priv->waiveInterposition;
InterpositionMap::Ptr interposition = gInterpositionMap->lookup(addonId);
if (!waiveInterposition && interposition) {
MOZ_RELEASE_ASSERT(isSystem);
mInterposition = interposition->value();
}
// We also want multiprocessCompatible add-ons to have a default interposition.
if (!mInterposition && addonId && isSystem) {
bool interpositionEnabled = mozilla::Preferences::GetBool(
"extensions.interposition.enabled", false);
if (interpositionEnabled) {
mInterposition = do_GetService("@mozilla.org/addons/default-addon-shims;1");
MOZ_ASSERT(mInterposition);
UpdateInterpositionWhitelist(cx, mInterposition);
}
}
}
if (addonId) {
// We forbid CPOWs unless they're specifically allowed.
priv->allowCPOWs = gAllowCPOWAddonSet ? gAllowCPOWAddonSet->has(addonId) : false;
}
}
// static
bool
XPCWrappedNativeScope::IsDyingScope(XPCWrappedNativeScope* scope)
{
for (XPCWrappedNativeScope* cur = gDyingScopes; cur; cur = cur->mNext) {
if (scope == cur)
return true;
}
return false;
}
bool
XPCWrappedNativeScope::GetComponentsJSObject(JS::MutableHandleObject obj)
{
AutoJSContext cx;
if (!mComponents) {
nsIPrincipal* p = GetPrincipal();
bool system = nsXPConnect::SecurityManager()->IsSystemPrincipal(p);
mComponents = system ? new nsXPCComponents(this)
: new nsXPCComponentsBase(this);
}
RootedValue val(cx);
xpcObjectHelper helper(mComponents);
bool ok = XPCConvert::NativeInterface2JSObject(&val, nullptr, helper,
nullptr, false,
nullptr);
if (NS_WARN_IF(!ok))
return false;
if (NS_WARN_IF(!val.isObject()))
return false;
// The call to wrap() here is necessary even though the object is same-
// compartment, because it applies our security wrapper.
obj.set(&val.toObject());
if (NS_WARN_IF(!JS_WrapObject(cx, obj)))
return false;
return true;
}
void
XPCWrappedNativeScope::ForcePrivilegedComponents()
{
nsCOMPtr<nsIXPCComponents> c = do_QueryInterface(mComponents);
if (!c)
mComponents = new nsXPCComponents(this);
}
bool
XPCWrappedNativeScope::AttachComponentsObject(JSContext* aCx)
{
RootedObject components(aCx);
if (!GetComponentsJSObject(&components))
return false;
RootedObject global(aCx, GetGlobalJSObject());
MOZ_ASSERT(js::IsObjectInContextCompartment(global, aCx));
// The global Components property is non-configurable if it's a full
// nsXPCComponents object. That way, if it's an nsXPCComponentsBase,
// enableUniversalXPConnect can upgrade it later.
unsigned attrs = JSPROP_READONLY | JSPROP_RESOLVING;
nsCOMPtr<nsIXPCComponents> c = do_QueryInterface(mComponents);
if (c)
attrs |= JSPROP_PERMANENT;
RootedId id(aCx, XPCJSContext::Get()->GetStringID(XPCJSContext::IDX_COMPONENTS));
return JS_DefinePropertyById(aCx, global, id, components, attrs);
}
static bool
CompartmentPerAddon()
{
static bool initialized = false;
static bool pref = false;
if (!initialized) {
pref = Preferences::GetBool("dom.compartment_per_addon", false) ||
BrowserTabsRemoteAutostart();
initialized = true;
}
return pref;
}
JSObject*
XPCWrappedNativeScope::EnsureContentXBLScope(JSContext* cx)
{
JS::RootedObject global(cx, GetGlobalJSObject());
MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx));
MOZ_ASSERT(!mIsContentXBLScope);
MOZ_ASSERT(strcmp(js::GetObjectClass(global)->name,
"nsXBLPrototypeScript compilation scope"));
// If we already have a special XBL scope object, we know what to use.
if (mContentXBLScope)
return mContentXBLScope;
// If this scope doesn't need an XBL scope, just return the global.
if (!mUseContentXBLScope)
return global;
// Set up the sandbox options. Note that we use the DOM global as the
// sandboxPrototype so that the XBL scope can access all the DOM objects
// it's accustomed to accessing.
//
// In general wantXrays shouldn't matter much here, but there are weird
// cases when adopting bound content between same-origin globals where a
// <destructor> in one content XBL scope sees anonymous content in another
// content XBL scope. When that happens, we hit LookupBindingMember for an
// anonymous element that lives in a content XBL scope, which isn't a tested
// or audited codepath. So let's avoid hitting that case by opting out of
// same-origin Xrays.
SandboxOptions options;
options.wantXrays = false;
options.wantComponents = true;
options.proto = global;
options.sameZoneAs = global;
// Use an nsExpandedPrincipal to create asymmetric security.
nsIPrincipal* principal = GetPrincipal();
MOZ_ASSERT(!nsContentUtils::IsExpandedPrincipal(principal));
nsTArray<nsCOMPtr<nsIPrincipal>> principalAsArray(1);
principalAsArray.AppendElement(principal);
nsCOMPtr<nsIExpandedPrincipal> ep =
new nsExpandedPrincipal(principalAsArray,
BasePrincipal::Cast(principal)->OriginAttributesRef());
// Create the sandbox.
RootedValue v(cx);
nsresult rv = CreateSandboxObject(cx, &v, ep, options);
NS_ENSURE_SUCCESS(rv, nullptr);
mContentXBLScope = &v.toObject();
// Tag it.
CompartmentPrivate::Get(js::UncheckedUnwrap(mContentXBLScope))->scope->mIsContentXBLScope = true;
// Good to go!
return mContentXBLScope;
}
bool
XPCWrappedNativeScope::AllowContentXBLScope()
{
// We only disallow XBL scopes in remote XUL situations.
MOZ_ASSERT_IF(!mAllowContentXBLScope,
nsContentUtils::AllowXULXBLForPrincipal(GetPrincipal()));
return mAllowContentXBLScope;
}
namespace xpc {
JSObject*
GetXBLScope(JSContext* cx, JSObject* contentScopeArg)
{
MOZ_ASSERT(!IsInAddonScope(contentScopeArg));
JS::RootedObject contentScope(cx, contentScopeArg);
JSAutoCompartment ac(cx, contentScope);
JSObject* scope = CompartmentPrivate::Get(contentScope)->scope->EnsureContentXBLScope(cx);
NS_ENSURE_TRUE(scope, nullptr); // See bug 858642.
scope = js::UncheckedUnwrap(scope);
JS::ExposeObjectToActiveJS(scope);
return scope;
}
JSObject*
GetScopeForXBLExecution(JSContext* cx, HandleObject contentScope, JSAddonId* addonId)
{
MOZ_RELEASE_ASSERT(!IsInAddonScope(contentScope));
RootedObject global(cx, js::GetGlobalForObjectCrossCompartment(contentScope));
if (IsInContentXBLScope(contentScope))
return global;
JSAutoCompartment ac(cx, contentScope);
XPCWrappedNativeScope* nativeScope = CompartmentPrivate::Get(contentScope)->scope;
bool isSystem = nsContentUtils::IsSystemPrincipal(nativeScope->GetPrincipal());
RootedObject scope(cx);
if (nativeScope->UseContentXBLScope())
scope = nativeScope->EnsureContentXBLScope(cx);
else if (addonId && CompartmentPerAddon() && isSystem)
scope = nativeScope->EnsureAddonScope(cx, addonId);
else
scope = global;
NS_ENSURE_TRUE(scope, nullptr); // See bug 858642.
scope = js::UncheckedUnwrap(scope);
JS::ExposeObjectToActiveJS(scope);
return scope;
}
bool
AllowContentXBLScope(JSCompartment* c)
{
XPCWrappedNativeScope* scope = CompartmentPrivate::Get(c)->scope;
return scope && scope->AllowContentXBLScope();
}
bool
UseContentXBLScope(JSCompartment* c)
{
XPCWrappedNativeScope* scope = CompartmentPrivate::Get(c)->scope;
return scope && scope->UseContentXBLScope();
}
void
ClearContentXBLScope(JSObject* global)
{
CompartmentPrivate::Get(global)->scope->ClearContentXBLScope();
}
} /* namespace xpc */
JSObject*
XPCWrappedNativeScope::EnsureAddonScope(JSContext* cx, JSAddonId* addonId)
{
JS::RootedObject global(cx, GetGlobalJSObject());
MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx));
MOZ_ASSERT(!mIsContentXBLScope);
MOZ_ASSERT(!mIsAddonScope);
MOZ_ASSERT(addonId);
MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(GetPrincipal()));
// In bug 1092156, we found that add-on scopes don't work correctly when the
// window navigates. The add-on global's prototype is an outer window, so,
// after the navigation, looking up window properties in the add-on scope
// will fail. However, in most cases where the window can be navigated, the
// entire window is part of the add-on. To solve the problem, we avoid
// returning an add-on scope for a window that is already tagged with the
// add-on ID.
if (AddonIdOfObject(global) == addonId)
return global;
// If we already have an addon scope object, we know what to use.
for (size_t i = 0; i < mAddonScopes.Length(); i++) {
if (JS::AddonIdOfObject(js::UncheckedUnwrap(mAddonScopes[i])) == addonId)
return mAddonScopes[i];
}
SandboxOptions options;
options.wantComponents = true;
options.proto = global;
options.sameZoneAs = global;
options.addonId = JS::StringOfAddonId(addonId);
options.writeToGlobalPrototype = true;
RootedValue v(cx);
nsresult rv = CreateSandboxObject(cx, &v, GetPrincipal(), options);
NS_ENSURE_SUCCESS(rv, nullptr);
mAddonScopes.AppendElement(&v.toObject());
CompartmentPrivate::Get(js::UncheckedUnwrap(&v.toObject()))->scope->mIsAddonScope = true;
return &v.toObject();
}
JSObject*
xpc::GetAddonScope(JSContext* cx, JS::HandleObject contentScope, JSAddonId* addonId)
{
MOZ_RELEASE_ASSERT(!IsInAddonScope(contentScope));
if (!addonId || !CompartmentPerAddon()) {
return js::GetGlobalForObjectCrossCompartment(contentScope);
}
JSAutoCompartment ac(cx, contentScope);
XPCWrappedNativeScope* nativeScope = CompartmentPrivate::Get(contentScope)->scope;
if (nativeScope->GetPrincipal() != nsXPConnect::SystemPrincipal()) {
// This can happen if, for example, Jetpack loads an unprivileged HTML
// page from the add-on. It's not clear what to do there, so we just use
// the normal global.
return js::GetGlobalForObjectCrossCompartment(contentScope);
}
JSObject* scope = nativeScope->EnsureAddonScope(cx, addonId);
NS_ENSURE_TRUE(scope, nullptr);
scope = js::UncheckedUnwrap(scope);
JS::ExposeObjectToActiveJS(scope);
return scope;
}
XPCWrappedNativeScope::~XPCWrappedNativeScope()
{
MOZ_COUNT_DTOR(XPCWrappedNativeScope);
// We can do additional cleanup assertions here...
MOZ_ASSERT(0 == mWrappedNativeMap->Count(), "scope has non-empty map");
delete mWrappedNativeMap;
MOZ_ASSERT(0 == mWrappedNativeProtoMap->Count(), "scope has non-empty map");
delete mWrappedNativeProtoMap;
// This should not be necessary, since the Components object should die
// with the scope but just in case.
if (mComponents)
mComponents->mScope = nullptr;
// XXX we should assert that we are dead or that xpconnect has shutdown
// XXX might not want to do this at xpconnect shutdown time???
mComponents = nullptr;
if (mXrayExpandos.initialized())
mXrayExpandos.destroy();
JSContext* cx = dom::danger::GetJSContext();
mContentXBLScope.finalize(cx);
for (size_t i = 0; i < mAddonScopes.Length(); i++)
mAddonScopes[i].finalize(cx);
mGlobalJSObject.finalize(cx);
}
// static
void
XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSContext* cx)
{
// Do JS::TraceEdge for all wrapped natives with external references, as
// well as any DOM expando objects.
for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
for (auto i = cur->mWrappedNativeMap->Iter(); !i.Done(); i.Next()) {
auto entry = static_cast<Native2WrappedNativeMap::Entry*>(i.Get());
XPCWrappedNative* wrapper = entry->value;
if (wrapper->HasExternalReference() && !wrapper->IsWrapperExpired())
wrapper->TraceSelf(trc);
}
if (cur->mDOMExpandoSet) {
for (DOMExpandoSet::Enum e(*cur->mDOMExpandoSet); !e.empty(); e.popFront())
JS::TraceEdge(trc, &e.mutableFront(), "DOM expando object");
}
}
}
static void
SuspectDOMExpandos(JSObject* obj, nsCycleCollectionNoteRootCallback& cb)
{
MOZ_ASSERT(dom::GetDOMClass(obj) && dom::GetDOMClass(obj)->mDOMObjectIsISupports);
nsISupports* native = dom::UnwrapDOMObject<nsISupports>(obj);
cb.NoteXPCOMRoot(native);
}
// static
void
XPCWrappedNativeScope::SuspectAllWrappers(XPCJSContext* cx,
nsCycleCollectionNoteRootCallback& cb)
{
for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
for (auto i = cur->mWrappedNativeMap->Iter(); !i.Done(); i.Next()) {
static_cast<Native2WrappedNativeMap::Entry*>(i.Get())->value->Suspect(cb);
}
if (cur->mDOMExpandoSet) {
for (DOMExpandoSet::Range r = cur->mDOMExpandoSet->all(); !r.empty(); r.popFront())
SuspectDOMExpandos(r.front().unbarrieredGet(), cb);
}
}
}
// static
void
XPCWrappedNativeScope::UpdateWeakPointersAfterGC(XPCJSContext* cx)
{
// If this is called from the finalization callback in JSGC_MARK_END then
// JSGC_FINALIZE_END must always follow it calling
// FinishedFinalizationPhaseOfGC and clearing gDyingScopes in
// KillDyingScopes.
MOZ_ASSERT(!gDyingScopes, "JSGC_MARK_END without JSGC_FINALIZE_END");
XPCWrappedNativeScope* prev = nullptr;
XPCWrappedNativeScope* cur = gScopes;
while (cur) {
// Sweep waivers.
if (cur->mWaiverWrapperMap)
cur->mWaiverWrapperMap->Sweep();
XPCWrappedNativeScope* next = cur->mNext;
if (cur->mContentXBLScope)
cur->mContentXBLScope.updateWeakPointerAfterGC();
for (size_t i = 0; i < cur->mAddonScopes.Length(); i++)
cur->mAddonScopes[i].updateWeakPointerAfterGC();
// Check for finalization of the global object or update our pointer if
// it was moved.
if (cur->mGlobalJSObject) {
cur->mGlobalJSObject.updateWeakPointerAfterGC();
if (!cur->mGlobalJSObject) {
// Move this scope from the live list to the dying list.
if (prev)
prev->mNext = next;
else
gScopes = next;
cur->mNext = gDyingScopes;
gDyingScopes = cur;
cur = nullptr;
}
}
if (cur)
prev = cur;
cur = next;
}
}
// static
void
XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs()
{
for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
for (auto i = cur->mWrappedNativeMap->Iter(); !i.Done(); i.Next()) {
auto entry = static_cast<Native2WrappedNativeMap::Entry*>(i.Get());
entry->value->SweepTearOffs();
}
}
}
// static
void
XPCWrappedNativeScope::KillDyingScopes()
{
XPCWrappedNativeScope* cur = gDyingScopes;
while (cur) {
XPCWrappedNativeScope* next = cur->mNext;
if (cur->mGlobalJSObject)
CompartmentPrivate::Get(cur->mGlobalJSObject)->scope = nullptr;
delete cur;
cur = next;
}
gDyingScopes = nullptr;
}
//static
void
XPCWrappedNativeScope::SystemIsBeingShutDown()
{
int liveScopeCount = 0;
XPCWrappedNativeScope* cur;
// First move all the scopes to the dying list.
cur = gScopes;
while (cur) {
XPCWrappedNativeScope* next = cur->mNext;
cur->mNext = gDyingScopes;
gDyingScopes = cur;
cur = next;
liveScopeCount++;
}
gScopes = nullptr;
// We're forcibly killing scopes, rather than allowing them to go away
// when they're ready. As such, we need to do some cleanup before they
// can safely be destroyed.
for (cur = gDyingScopes; cur; cur = cur->mNext) {
// Give the Components object a chance to try to clean up.
if (cur->mComponents)
cur->mComponents->SystemIsBeingShutDown();
// Walk the protos first. Wrapper shutdown can leave dangling
// proto pointers in the proto map.
for (auto i = cur->mWrappedNativeProtoMap->Iter(); !i.Done(); i.Next()) {
auto entry = static_cast<ClassInfo2WrappedNativeProtoMap::Entry*>(i.Get());
entry->value->SystemIsBeingShutDown();
i.Remove();
}
for (auto i = cur->mWrappedNativeMap->Iter(); !i.Done(); i.Next()) {
auto entry = static_cast<Native2WrappedNativeMap::Entry*>(i.Get());
XPCWrappedNative* wrapper = entry->value;
if (wrapper->IsValid()) {
wrapper->SystemIsBeingShutDown();
}
i.Remove();
}
}
// Now it is safe to kill all the scopes.
KillDyingScopes();
}
/***************************************************************************/
JSObject*
XPCWrappedNativeScope::GetExpandoChain(HandleObject target)
{
MOZ_ASSERT(ObjectScope(target) == this);
if (!mXrayExpandos.initialized())
return nullptr;
return mXrayExpandos.lookup(target);
}
bool
XPCWrappedNativeScope::SetExpandoChain(JSContext* cx, HandleObject target,
HandleObject chain)
{
MOZ_ASSERT(ObjectScope(target) == this);
MOZ_ASSERT(js::IsObjectInContextCompartment(target, cx));
MOZ_ASSERT_IF(chain, ObjectScope(chain) == this);
if (!mXrayExpandos.initialized() && !mXrayExpandos.init(cx))
return false;
return mXrayExpandos.put(cx, target, chain);
}
/* static */ bool
XPCWrappedNativeScope::SetAddonInterposition(JSContext* cx,
JSAddonId* addonId,
nsIAddonInterposition* interp)
{
if (!gInterpositionMap) {
gInterpositionMap = new InterpositionMap();
bool ok = gInterpositionMap->init();
NS_ENSURE_TRUE(ok, false);
if (!gShutdownObserverInitialized) {
gShutdownObserverInitialized = true;
nsContentUtils::RegisterShutdownObserver(new ClearInterpositionsObserver());
}
}
if (interp) {
bool ok = gInterpositionMap->put(addonId, interp);
NS_ENSURE_TRUE(ok, false);
UpdateInterpositionWhitelist(cx, interp);
} else {
gInterpositionMap->remove(addonId);
}
return true;
}
/* static */ bool
XPCWrappedNativeScope::AllowCPOWsInAddon(JSContext* cx,
JSAddonId* addonId,
bool allow)
{
if (!gAllowCPOWAddonSet) {
gAllowCPOWAddonSet = new AddonSet();
bool ok = gAllowCPOWAddonSet->init();
NS_ENSURE_TRUE(ok, false);
if (!gShutdownObserverInitialized) {
gShutdownObserverInitialized = true;
nsContentUtils::RegisterShutdownObserver(new ClearInterpositionsObserver());
}
}
if (allow) {
bool ok = gAllowCPOWAddonSet->put(addonId);
NS_ENSURE_TRUE(ok, false);
} else {
gAllowCPOWAddonSet->remove(addonId);
}
return true;
}
nsCOMPtr<nsIAddonInterposition>
XPCWrappedNativeScope::GetInterposition()
{
return mInterposition;
}
/* static */ InterpositionWhitelist*
XPCWrappedNativeScope::GetInterpositionWhitelist(nsIAddonInterposition* interposition)
{
if (!gInterpositionWhitelists)
return nullptr;
InterpositionWhitelistArray& wls = *gInterpositionWhitelists;
for (size_t i = 0; i < wls.Length(); i++) {
if (wls[i].interposition == interposition)
return &wls[i].whitelist;
}
return nullptr;
}
/* static */ bool
XPCWrappedNativeScope::UpdateInterpositionWhitelist(JSContext* cx,
nsIAddonInterposition* interposition)
{
// We want to set the interpostion whitelist only once.
InterpositionWhitelist* whitelist = GetInterpositionWhitelist(interposition);
if (whitelist)
return true;
// The hashsets in gInterpositionWhitelists do not have a copy constructor so
// a reallocation for the array will lead to a memory corruption. If you
// need more interpositions, change the capacity of the array please.
static const size_t MAX_INTERPOSITION = 8;
if (!gInterpositionWhitelists)
gInterpositionWhitelists = new InterpositionWhitelistArray(MAX_INTERPOSITION);
MOZ_RELEASE_ASSERT(MAX_INTERPOSITION > gInterpositionWhitelists->Length() + 1);
InterpositionWhitelistPair* newPair = gInterpositionWhitelists->AppendElement();
newPair->interposition = interposition;
if (!newPair->whitelist.init()) {
JS_ReportOutOfMemory(cx);
return false;
}
whitelist = &newPair->whitelist;
RootedValue whitelistVal(cx);
nsresult rv = interposition->GetWhitelist(&whitelistVal);
if (NS_FAILED(rv)) {
JS_ReportErrorASCII(cx, "Could not get the whitelist from the interposition.");
return false;
}
if (!whitelistVal.isObject()) {
JS_ReportErrorASCII(cx, "Whitelist must be an array.");
return false;
}
// We want to enter the whitelist's compartment to avoid any wrappers.
// To be on the safe side let's make sure that it's a system compartment
// and we don't accidentally trigger some content function here by parsing
// the whitelist object.
RootedObject whitelistObj(cx, &whitelistVal.toObject());
whitelistObj = js::UncheckedUnwrap(whitelistObj);
if (!AccessCheck::isChrome(whitelistObj)) {
JS_ReportErrorASCII(cx, "Whitelist must be from system scope.");
return false;
}
{
JSAutoCompartment ac(cx, whitelistObj);
bool isArray;
if (!JS_IsArrayObject(cx, whitelistObj, &isArray))
return false;
if (!isArray) {
JS_ReportErrorASCII(cx, "Whitelist must be an array.");
return false;
}
uint32_t length;
if (!JS_GetArrayLength(cx, whitelistObj, &length))
return false;
for (uint32_t i = 0; i < length; i++) {
RootedValue idval(cx);
if (!JS_GetElement(cx, whitelistObj, i, &idval))
return false;
if (!idval.isString()) {
JS_ReportErrorASCII(cx, "Whitelist must contain strings only.");
return false;
}
RootedString str(cx, idval.toString());
str = JS_AtomizeAndPinJSString(cx, str);
if (!str) {
JS_ReportErrorASCII(cx, "String internization failed.");
return false;
}
// By internizing the id's we ensure that they won't get
// GCed so we can use them as hash keys.
jsid id = INTERNED_STRING_TO_JSID(cx, str);
if (!whitelist->put(JSID_BITS(id))) {
JS_ReportOutOfMemory(cx);
return false;
}
}
}
return true;
}
/***************************************************************************/
// static
void
XPCWrappedNativeScope::DebugDumpAllScopes(int16_t depth)
{
#ifdef DEBUG
depth-- ;
// get scope count.
int count = 0;
XPCWrappedNativeScope* cur;
for (cur = gScopes; cur; cur = cur->mNext)
count++ ;
XPC_LOG_ALWAYS(("chain of %d XPCWrappedNativeScope(s)", count));
XPC_LOG_INDENT();
XPC_LOG_ALWAYS(("gDyingScopes @ %x", gDyingScopes));
if (depth)
for (cur = gScopes; cur; cur = cur->mNext)
cur->DebugDump(depth);
XPC_LOG_OUTDENT();
#endif
}
void
XPCWrappedNativeScope::DebugDump(int16_t depth)
{
#ifdef DEBUG
depth-- ;
XPC_LOG_ALWAYS(("XPCWrappedNativeScope @ %x", this));
XPC_LOG_INDENT();
XPC_LOG_ALWAYS(("mNext @ %x", mNext));
XPC_LOG_ALWAYS(("mComponents @ %x", mComponents.get()));
XPC_LOG_ALWAYS(("mGlobalJSObject @ %x", mGlobalJSObject.get()));
XPC_LOG_ALWAYS(("mWrappedNativeMap @ %x with %d wrappers(s)",
mWrappedNativeMap, mWrappedNativeMap->Count()));
// iterate contexts...
if (depth && mWrappedNativeMap->Count()) {
XPC_LOG_INDENT();
for (auto i = mWrappedNativeMap->Iter(); !i.Done(); i.Next()) {
auto entry = static_cast<Native2WrappedNativeMap::Entry*>(i.Get());
entry->value->DebugDump(depth);
}
XPC_LOG_OUTDENT();
}
XPC_LOG_ALWAYS(("mWrappedNativeProtoMap @ %x with %d protos(s)",
mWrappedNativeProtoMap,
mWrappedNativeProtoMap->Count()));
// iterate contexts...
if (depth && mWrappedNativeProtoMap->Count()) {
XPC_LOG_INDENT();
for (auto i = mWrappedNativeProtoMap->Iter(); !i.Done(); i.Next()) {
auto entry = static_cast<ClassInfo2WrappedNativeProtoMap::Entry*>(i.Get());
entry->value->DebugDump(depth);
}
XPC_LOG_OUTDENT();
}
XPC_LOG_OUTDENT();
#endif
}
void
XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(ScopeSizeInfo* scopeSizeInfo)
{
for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
cur->AddSizeOfIncludingThis(scopeSizeInfo);
}
void
XPCWrappedNativeScope::AddSizeOfIncludingThis(ScopeSizeInfo* scopeSizeInfo)
{
scopeSizeInfo->mScopeAndMapSize += scopeSizeInfo->mMallocSizeOf(this);
scopeSizeInfo->mScopeAndMapSize +=
mWrappedNativeMap->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
scopeSizeInfo->mScopeAndMapSize +=
mWrappedNativeProtoMap->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
if (dom::HasProtoAndIfaceCache(mGlobalJSObject)) {
dom::ProtoAndIfaceCache* cache = dom::GetProtoAndIfaceCache(mGlobalJSObject);
scopeSizeInfo->mProtoAndIfaceCacheSize +=
cache->SizeOfIncludingThis(scopeSizeInfo->mMallocSizeOf);
}
// There are other XPCWrappedNativeScope members that could be measured;
// the above ones have been seen by DMD to be worth measuring. More stuff
// may be added later.
}

View file

@ -0,0 +1,97 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "xpcprivate.h"
#include "XPCWrapper.h"
#include "WrapperFactory.h"
#include "AccessCheck.h"
using namespace xpc;
using namespace mozilla;
using namespace JS;
namespace XPCNativeWrapper {
static inline
bool
ThrowException(nsresult ex, JSContext* cx)
{
XPCThrower::Throw(ex, cx);
return false;
}
static bool
UnwrapNW(JSContext* cx, unsigned argc, Value* vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (args.length() != 1) {
return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx);
}
JS::RootedValue v(cx, args[0]);
if (!v.isObject() || !js::IsCrossCompartmentWrapper(&v.toObject()) ||
!WrapperFactory::AllowWaiver(&v.toObject())) {
args.rval().set(v);
return true;
}
bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
NS_ENSURE_TRUE(ok, false);
args.rval().set(v);
return true;
}
static bool
XrayWrapperConstructor(JSContext* cx, unsigned argc, Value* vp)
{
JS::CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() == 0) {
return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx);
}
if (!args[0].isObject()) {
args.rval().set(args[0]);
return true;
}
args.rval().setObject(*js::UncheckedUnwrap(&args[0].toObject()));
return JS_WrapValue(cx, args.rval());
}
// static
bool
AttachNewConstructorObject(JSContext* aCx, JS::HandleObject aGlobalObject)
{
// Pushing a JSContext calls ActivateDebugger which calls this function, so
// we can't use an AutoJSContext here until JSD is gone.
JSAutoCompartment ac(aCx, aGlobalObject);
JSFunction* xpcnativewrapper =
JS_DefineFunction(aCx, aGlobalObject, "XPCNativeWrapper",
XrayWrapperConstructor, 1,
JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_STUB_GSOPS | JSFUN_CONSTRUCTOR);
if (!xpcnativewrapper) {
return false;
}
JS::RootedObject obj(aCx, JS_GetFunctionObject(xpcnativewrapper));
return JS_DefineFunction(aCx, obj, "unwrap", UnwrapNW, 1,
JSPROP_READONLY | JSPROP_PERMANENT) != nullptr;
}
} // namespace XPCNativeWrapper
namespace XPCWrapper {
JSObject*
UnsafeUnwrapSecurityWrapper(JSObject* obj)
{
if (js::IsProxy(obj)) {
return js::UncheckedUnwrap(obj);
}
return obj;
}
} // namespace XPCWrapper

View file

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 XPC_WRAPPER_H
#define XPC_WRAPPER_H 1
#include "js/TypeDecls.h"
namespace XPCNativeWrapper {
// Given an XPCWrappedNative pointer and the name of the function on
// XPCNativeScriptableFlags corresponding with a flag, returns 'true'
// if the flag is set.
// XXX Convert to using GetFlags() and not a macro.
#define NATIVE_HAS_FLAG(_wn, _flag) \
((_wn)->GetScriptableInfo() && \
(_wn)->GetScriptableInfo()->GetFlags()._flag())
bool
AttachNewConstructorObject(JSContext* aCx, JS::HandleObject aGlobalObject);
} // namespace XPCNativeWrapper
// This namespace wraps some common functionality between the three existing
// wrappers. Its main purpose is to allow XPCCrossOriginWrapper to act both
// as an XPCSafeJSObjectWrapper and as an XPCNativeWrapper when required to
// do so (the decision is based on the principals of the wrapper and wrapped
// objects).
namespace XPCWrapper {
JSObject*
UnsafeUnwrapSecurityWrapper(JSObject* obj);
} // namespace XPCWrapper
#endif

View file

@ -0,0 +1,12 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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/. */
/*
Error messages for JSShell. See js.msg for format.
*/
MSG_DEF(JSSMSG_NOT_AN_ERROR, 0, 0, JSEXN_ERR, "<Error #0 is reserved>")
MSG_DEF(JSSMSG_CANT_OPEN, 1, 2, JSEXN_ERR, "can't open {0}: {1}")

View file

@ -0,0 +1,70 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
EXPORTS += [
'BackstagePass.h',
'qsObjectHelper.h',
'XPCJSMemoryReporter.h',
'xpcObjectHelper.h',
'xpcpublic.h',
]
UNIFIED_SOURCES += [
'ExportHelpers.cpp',
'nsScriptError.cpp',
'nsScriptErrorWithStack.cpp',
'nsXPConnect.cpp',
'Sandbox.cpp',
'XPCCallContext.cpp',
'XPCConvert.cpp',
'XPCDebug.cpp',
'XPCException.cpp',
'XPCJSContext.cpp',
'XPCJSID.cpp',
'XPCJSWeakReference.cpp',
'XPCLocale.cpp',
'XPCLog.cpp',
'XPCMaps.cpp',
'XPCModule.cpp',
'XPCRuntimeService.cpp',
'XPCShellImpl.cpp',
'XPCString.cpp',
'XPCThrower.cpp',
'XPCVariant.cpp',
'XPCWrappedJS.cpp',
'XPCWrappedJSClass.cpp',
'XPCWrappedNative.cpp',
'XPCWrappedNativeInfo.cpp',
'XPCWrappedNativeJSOps.cpp',
'XPCWrappedNativeProto.cpp',
'XPCWrappedNativeScope.cpp',
'XPCWrapper.cpp',
]
# XPCComponents.cpp cannot be built in unified mode because it uses plarena.h.
SOURCES += [
'XPCComponents.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
'../loader',
'../wrappers',
'/caps',
'/dom/base',
'/dom/html',
'/dom/svg',
'/dom/workers',
'/layout/base',
'/layout/style',
'/xpcom/reflect/xptinfo',
]
if CONFIG['GNU_CXX']:
CXXFLAGS += ['-Wno-shadow', '-Werror=format']

View file

@ -0,0 +1,345 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/*
* nsIScriptError implementation. Defined here, lacking a JS-specific
* place to put XPCOM things.
*/
#include "xpcprivate.h"
#include "jsprf.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "nsGlobalWindow.h"
#include "nsPIDOMWindow.h"
#include "nsILoadContext.h"
#include "nsIDocShell.h"
#include "nsIScriptError.h"
#include "nsISensitiveInfoHiddenURI.h"
static_assert(nsIScriptError::errorFlag == JSREPORT_ERROR &&
nsIScriptError::warningFlag == JSREPORT_WARNING &&
nsIScriptError::exceptionFlag == JSREPORT_EXCEPTION &&
nsIScriptError::strictFlag == JSREPORT_STRICT &&
nsIScriptError::infoFlag == JSREPORT_USER_1,
"flags should be consistent");
nsScriptErrorBase::nsScriptErrorBase()
: mMessage(),
mMessageName(),
mSourceName(),
mLineNumber(0),
mSourceLine(),
mColumnNumber(0),
mFlags(0),
mCategory(),
mOuterWindowID(0),
mInnerWindowID(0),
mTimeStamp(0),
mInitializedOnMainThread(false),
mIsFromPrivateWindow(false)
{
}
nsScriptErrorBase::~nsScriptErrorBase() {}
void
nsScriptErrorBase::InitializeOnMainThread()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mInitializedOnMainThread);
if (mInnerWindowID) {
nsGlobalWindow* window =
nsGlobalWindow::GetInnerWindowWithId(mInnerWindowID);
if (window) {
nsPIDOMWindowOuter* outer = window->GetOuterWindow();
if (outer)
mOuterWindowID = outer->WindowID();
nsIDocShell* docShell = window->GetDocShell();
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
if (loadContext) {
// Never mark exceptions from chrome windows as having come from
// private windows, since we always want them to be reported.
nsIPrincipal* winPrincipal = window->GetPrincipal();
mIsFromPrivateWindow = loadContext->UsePrivateBrowsing() &&
!nsContentUtils::IsSystemPrincipal(winPrincipal);
}
}
}
mInitializedOnMainThread = true;
}
// nsIConsoleMessage methods
NS_IMETHODIMP
nsScriptErrorBase::GetMessageMoz(char16_t** result) {
nsresult rv;
nsAutoCString message;
rv = ToString(message);
if (NS_FAILED(rv))
return rv;
*result = UTF8ToNewUnicode(message);
if (!*result)
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetLogLevel(uint32_t* aLogLevel)
{
if (mFlags & (uint32_t)nsIScriptError::infoFlag) {
*aLogLevel = nsIConsoleMessage::info;
} else if (mFlags & (uint32_t)nsIScriptError::warningFlag) {
*aLogLevel = nsIConsoleMessage::warn;
} else {
*aLogLevel = nsIConsoleMessage::error;
}
return NS_OK;
}
// nsIScriptError methods
NS_IMETHODIMP
nsScriptErrorBase::GetErrorMessage(nsAString& aResult) {
aResult.Assign(mMessage);
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetSourceName(nsAString& aResult) {
aResult.Assign(mSourceName);
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetSourceLine(nsAString& aResult) {
aResult.Assign(mSourceLine);
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetLineNumber(uint32_t* result) {
*result = mLineNumber;
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetColumnNumber(uint32_t* result) {
*result = mColumnNumber;
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetFlags(uint32_t* result) {
*result = mFlags;
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetCategory(char** result) {
*result = ToNewCString(mCategory);
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetStack(JS::MutableHandleValue aStack) {
aStack.setUndefined();
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::SetStack(JS::HandleValue aStack) {
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetErrorMessageName(nsAString& aErrorMessageName) {
aErrorMessageName = mMessageName;
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::SetErrorMessageName(const nsAString& aErrorMessageName) {
mMessageName = aErrorMessageName;
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::Init(const nsAString& message,
const nsAString& sourceName,
const nsAString& sourceLine,
uint32_t lineNumber,
uint32_t columnNumber,
uint32_t flags,
const char* category)
{
return InitWithWindowID(message, sourceName, sourceLine, lineNumber,
columnNumber, flags,
category ? nsDependentCString(category)
: EmptyCString(),
0);
}
NS_IMETHODIMP
nsScriptErrorBase::InitWithWindowID(const nsAString& message,
const nsAString& sourceName,
const nsAString& sourceLine,
uint32_t lineNumber,
uint32_t columnNumber,
uint32_t flags,
const nsACString& category,
uint64_t aInnerWindowID)
{
mMessage.Assign(message);
if (!sourceName.IsEmpty()) {
mSourceName.Assign(sourceName);
nsCOMPtr<nsIURI> uri;
nsAutoCString pass;
if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), sourceName)) &&
NS_SUCCEEDED(uri->GetPassword(pass)) &&
!pass.IsEmpty()) {
nsCOMPtr<nsISensitiveInfoHiddenURI> safeUri =
do_QueryInterface(uri);
nsAutoCString loc;
if (safeUri &&
NS_SUCCEEDED(safeUri->GetSensitiveInfoHiddenSpec(loc))) {
mSourceName.Assign(NS_ConvertUTF8toUTF16(loc));
}
}
}
mLineNumber = lineNumber;
mSourceLine.Assign(sourceLine);
mColumnNumber = columnNumber;
mFlags = flags;
mCategory = category;
mTimeStamp = JS_Now() / 1000;
mInnerWindowID = aInnerWindowID;
if (aInnerWindowID && NS_IsMainThread()) {
InitializeOnMainThread();
}
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::ToString(nsACString& /*UTF8*/ aResult)
{
static const char format0[] =
"[%s: \"%s\" {file: \"%s\" line: %d column: %d source: \"%s\"}]";
static const char format1[] =
"[%s: \"%s\" {file: \"%s\" line: %d}]";
static const char format2[] =
"[%s: \"%s\"]";
static const char error[] = "JavaScript Error";
static const char warning[] = "JavaScript Warning";
const char* severity = !(mFlags & JSREPORT_WARNING) ? error : warning;
char* temp;
char* tempMessage = nullptr;
char* tempSourceName = nullptr;
char* tempSourceLine = nullptr;
if (!mMessage.IsEmpty())
tempMessage = ToNewUTF8String(mMessage);
if (!mSourceName.IsEmpty())
// Use at most 512 characters from mSourceName.
tempSourceName = ToNewUTF8String(StringHead(mSourceName, 512));
if (!mSourceLine.IsEmpty())
// Use at most 512 characters from mSourceLine.
tempSourceLine = ToNewUTF8String(StringHead(mSourceLine, 512));
if (nullptr != tempSourceName && nullptr != tempSourceLine)
temp = JS_smprintf(format0,
severity,
tempMessage,
tempSourceName,
mLineNumber,
mColumnNumber,
tempSourceLine);
else if (!mSourceName.IsEmpty())
temp = JS_smprintf(format1,
severity,
tempMessage,
tempSourceName,
mLineNumber);
else
temp = JS_smprintf(format2,
severity,
tempMessage);
if (nullptr != tempMessage)
free(tempMessage);
if (nullptr != tempSourceName)
free(tempSourceName);
if (nullptr != tempSourceLine)
free(tempSourceLine);
if (!temp)
return NS_ERROR_OUT_OF_MEMORY;
aResult.Assign(temp);
JS_smprintf_free(temp);
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetOuterWindowID(uint64_t* aOuterWindowID)
{
NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread,
"This can't be safely determined off the main thread, "
"returning an inaccurate value!");
if (!mInitializedOnMainThread && NS_IsMainThread()) {
InitializeOnMainThread();
}
*aOuterWindowID = mOuterWindowID;
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetInnerWindowID(uint64_t* aInnerWindowID)
{
*aInnerWindowID = mInnerWindowID;
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetTimeStamp(int64_t* aTimeStamp)
{
*aTimeStamp = mTimeStamp;
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorBase::GetIsFromPrivateWindow(bool* aIsFromPrivateWindow)
{
NS_WARNING_ASSERTION(NS_IsMainThread() || mInitializedOnMainThread,
"This can't be safely determined off the main thread, "
"returning an inaccurate value!");
if (!mInitializedOnMainThread && NS_IsMainThread()) {
InitializeOnMainThread();
}
*aIsFromPrivateWindow = mIsFromPrivateWindow;
return NS_OK;
}
NS_IMPL_ISUPPORTS(nsScriptError, nsIConsoleMessage, nsIScriptError)

View file

@ -0,0 +1,119 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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/. */
/*
* nsScriptErrorWithStack implementation.
* a main-thread-only, cycle-collected subclass of nsScriptErrorBase
* that can store a SavedFrame stack trace object.
*/
#include "xpcprivate.h"
#include "MainThreadUtils.h"
#include "mozilla/Assertions.h"
#include "nsGlobalWindow.h"
#include "nsCycleCollectionParticipant.h"
namespace {
static nsCString
FormatStackString(JSContext* cx, HandleObject aStack) {
JS::RootedString formattedStack(cx);
if (!JS::BuildStackString(cx, aStack, &formattedStack)) {
return nsCString();
}
nsAutoJSString stackJSString;
if (!stackJSString.init(cx, formattedStack)) {
return nsCString();
}
return NS_ConvertUTF16toUTF8(stackJSString.get());
}
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptErrorWithStack)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptErrorWithStack)
tmp->mStack = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptErrorWithStack)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsScriptErrorWithStack)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptErrorWithStack)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptErrorWithStack)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptErrorWithStack)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIConsoleMessage)
NS_INTERFACE_MAP_ENTRY(nsIScriptError)
NS_INTERFACE_MAP_END
nsScriptErrorWithStack::nsScriptErrorWithStack(JS::HandleObject aStack)
: mStack(aStack)
{
MOZ_ASSERT(NS_IsMainThread(), "You can't use this class on workers.");
mozilla::HoldJSObjects(this);
}
nsScriptErrorWithStack::~nsScriptErrorWithStack() {
mozilla::DropJSObjects(this);
}
NS_IMETHODIMP
nsScriptErrorWithStack::Init(const nsAString& message,
const nsAString& sourceName,
const nsAString& sourceLine,
uint32_t lineNumber,
uint32_t columnNumber,
uint32_t flags,
const char* category)
{
MOZ_CRASH("nsScriptErrorWithStack requires to be initialized with a document, by using InitWithWindowID");
}
NS_IMETHODIMP
nsScriptErrorWithStack::GetStack(JS::MutableHandleValue aStack) {
aStack.setObjectOrNull(mStack);
return NS_OK;
}
NS_IMETHODIMP
nsScriptErrorWithStack::ToString(nsACString& /*UTF8*/ aResult)
{
MOZ_ASSERT(NS_IsMainThread());
nsCString message;
nsresult rv = nsScriptErrorBase::ToString(message);
NS_ENSURE_SUCCESS(rv, rv);
if (!mStack) {
aResult.Assign(message);
return NS_OK;
}
AutoJSAPI jsapi;
if (!jsapi.Init(mStack)) {
return NS_ERROR_FAILURE;
}
JSContext* cx = jsapi.cx();
RootedObject stack(cx, mStack);
nsCString stackString = FormatStackString(cx, stack);
nsCString combined = message + NS_LITERAL_CSTRING("\n") + stackString;
aResult.Assign(combined);
return NS_OK;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,53 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 qsObjectHelper_h
#define qsObjectHelper_h
#include "xpcObjectHelper.h"
#include "nsCOMPtr.h"
#include "nsWrapperCache.h"
#include "mozilla/TypeTraits.h"
class qsObjectHelper : public xpcObjectHelper
{
public:
template <class T>
inline
qsObjectHelper(T* aObject, nsWrapperCache* aCache)
: xpcObjectHelper(ToSupports(aObject), ToCanonicalSupports(aObject),
aCache)
{}
template <class T>
inline
qsObjectHelper(nsCOMPtr<T>& aObject, nsWrapperCache* aCache)
: xpcObjectHelper(ToSupports(aObject.get()),
ToCanonicalSupports(aObject.get()), aCache)
{
if (mCanonical) {
// Transfer the strong reference.
mCanonicalStrong = dont_AddRef(mCanonical);
aObject.forget();
}
}
template <class T>
inline
qsObjectHelper(RefPtr<T>& aObject, nsWrapperCache* aCache)
: xpcObjectHelper(ToSupports(aObject.get()),
ToCanonicalSupports(aObject.get()), aCache)
{
if (mCanonical) {
// Transfer the strong reference.
mCanonicalStrong = dont_AddRef(mCanonical);
aObject.forget();
}
}
};
#endif

228
js/xpconnect/src/xpc.msg Normal file
View file

@ -0,0 +1,228 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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/. */
/* Error Message definitions. */
/* xpconnect specific codes (from nsIXPConnect.h) */
XPC_MSG_DEF(NS_ERROR_XPC_NOT_ENOUGH_ARGS , "Not enough arguments")
XPC_MSG_DEF(NS_ERROR_XPC_NEED_OUT_OBJECT , "'Out' argument must be an object")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_SET_OUT_VAL , "Cannot set 'value' property of 'out' argument")
XPC_MSG_DEF(NS_ERROR_XPC_NATIVE_RETURNED_FAILURE , "Component returned failure code:")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_GET_INTERFACE_INFO , "Cannot find interface information")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO , "Cannot find interface information for parameter")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_GET_METHOD_INFO , "Cannot find method information")
XPC_MSG_DEF(NS_ERROR_XPC_UNEXPECTED , "Unexpected error in XPConnect")
XPC_MSG_DEF(NS_ERROR_XPC_BAD_CONVERT_JS , "Could not convert JavaScript argument")
XPC_MSG_DEF(NS_ERROR_XPC_BAD_CONVERT_NATIVE , "Could not convert Native argument")
XPC_MSG_DEF(NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF , "Could not convert JavaScript argument (NULL value cannot be used for a C++ reference type)")
XPC_MSG_DEF(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO , "Illegal operation on WrappedNative prototype object")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_CONVERT_WN_TO_FUN , "Cannot convert WrappedNative to function")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_DEFINE_PROP_ON_WN , "Cannot define new property in a WrappedNative")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_WATCH_WN_STATIC , "Cannot place watchpoints on WrappedNative object static properties")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_EXPORT_WN_STATIC , "Cannot export a WrappedNative object's static properties")
XPC_MSG_DEF(NS_ERROR_XPC_SCRIPTABLE_CALL_FAILED , "nsIXPCScriptable::Call failed")
XPC_MSG_DEF(NS_ERROR_XPC_SCRIPTABLE_CTOR_FAILED , "nsIXPCScriptable::Construct failed")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_CALL_WO_SCRIPTABLE , "Cannot use wrapper as function unless it implements nsIXPCScriptable")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_CTOR_WO_SCRIPTABLE , "Cannot use wrapper as constructor unless it implements nsIXPCScriptable")
XPC_MSG_DEF(NS_ERROR_XPC_CI_RETURNED_FAILURE , "ComponentManager::CreateInstance returned failure code:")
XPC_MSG_DEF(NS_ERROR_XPC_GS_RETURNED_FAILURE , "ServiceManager::GetService returned failure code:")
XPC_MSG_DEF(NS_ERROR_XPC_BAD_CID , "Invalid ClassID or ContractID")
XPC_MSG_DEF(NS_ERROR_XPC_BAD_IID , "Invalid InterfaceID")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_CREATE_WN , "Cannot create wrapper around native interface")
XPC_MSG_DEF(NS_ERROR_XPC_JS_THREW_EXCEPTION , "JavaScript component threw exception")
XPC_MSG_DEF(NS_ERROR_XPC_JS_THREW_NATIVE_OBJECT , "JavaScript component threw a native object that is not an exception")
XPC_MSG_DEF(NS_ERROR_XPC_JS_THREW_JS_OBJECT , "JavaScript component threw a JavaScript object")
XPC_MSG_DEF(NS_ERROR_XPC_JS_THREW_NULL , "JavaScript component threw a null value as an exception")
XPC_MSG_DEF(NS_ERROR_XPC_JS_THREW_STRING , "JavaScript component threw a string as an exception")
XPC_MSG_DEF(NS_ERROR_XPC_JS_THREW_NUMBER , "JavaScript component threw a number as an exception")
XPC_MSG_DEF(NS_ERROR_XPC_JAVASCRIPT_ERROR , "JavaScript component caused a JavaScript error")
XPC_MSG_DEF(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS , "JavaScript component caused a JavaScript error (detailed report attached)")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY, "Cannot convert primitive JavaScript value into an array")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY , "Cannot convert JavaScript object into an array")
XPC_MSG_DEF(NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY , "JavaScript Array does not have as many elements as indicated by size argument")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_GET_ARRAY_INFO , "Cannot find array information")
XPC_MSG_DEF(NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING , "JavaScript String does not have as many characters as indicated by size argument")
XPC_MSG_DEF(NS_ERROR_XPC_SECURITY_MANAGER_VETO , "Security Manager vetoed action")
XPC_MSG_DEF(NS_ERROR_XPC_INTERFACE_NOT_SCRIPTABLE , "Failed to build a wrapper because the interface that was not declared [scriptable]")
XPC_MSG_DEF(NS_ERROR_XPC_INTERFACE_NOT_FROM_NSISUPPORTS , "Failed to build a wrapper because the interface does not inherit from nsISupports")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_GET_JSOBJECT_OF_DOM_OBJECT, "Cannot get JavaScript object for DOM object")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_SET_READ_ONLY_CONSTANT , "Property is a constant and cannot be changed")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_SET_READ_ONLY_ATTRIBUTE , "Property is a read only attribute and cannot be changed")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_SET_READ_ONLY_METHOD , "Property is an interface method and cannot be changed")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_ADD_PROP_TO_WRAPPED_NATIVE, "Cannot add property to WrappedNative object")
XPC_MSG_DEF(NS_ERROR_XPC_CALL_TO_SCRIPTABLE_FAILED , "Call to nsIXPCScriptable interface for WrappedNative failed unexpecedly")
XPC_MSG_DEF(NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED , "JavaScript component does not have a method named:")
XPC_MSG_DEF(NS_ERROR_XPC_BAD_ID_STRING , "Bad ID string")
XPC_MSG_DEF(NS_ERROR_XPC_BAD_INITIALIZER_NAME , "Bad initializer name in Constructor - Component has no method with that name")
XPC_MSG_DEF(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN , "Operation failed because the XPConnect subsystem has been shutdown")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN , "Cannot modify properties of a WrappedNative")
XPC_MSG_DEF(NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL , "Could not convert JavaScript argument - 0 was passed, expected object. Did you mean null?")
XPC_MSG_DEF(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE , "It's illegal to pass a CPOW to native code")
/* common global codes (from nsError.h) */
XPC_MSG_DEF(NS_OK , "Success")
XPC_MSG_DEF(NS_ERROR_NOT_INITIALIZED , "Component not initialized")
XPC_MSG_DEF(NS_ERROR_ALREADY_INITIALIZED , "Component already initialized")
XPC_MSG_DEF(NS_ERROR_NOT_IMPLEMENTED , "Method not implemented")
XPC_MSG_DEF(NS_NOINTERFACE , "Component does not have requested interface")
XPC_MSG_DEF(NS_ERROR_NO_INTERFACE , "Component does not have requested interface")
XPC_MSG_DEF(NS_ERROR_ILLEGAL_VALUE , "Illegal value")
XPC_MSG_DEF(NS_ERROR_INVALID_POINTER , "Invalid pointer")
XPC_MSG_DEF(NS_ERROR_NULL_POINTER , "Null pointer")
XPC_MSG_DEF(NS_ERROR_ABORT , "Abort")
XPC_MSG_DEF(NS_ERROR_FAILURE , "Failure")
XPC_MSG_DEF(NS_ERROR_UNEXPECTED , "Unexpected error")
XPC_MSG_DEF(NS_ERROR_OUT_OF_MEMORY , "Out of Memory")
XPC_MSG_DEF(NS_ERROR_INVALID_ARG , "Invalid argument")
XPC_MSG_DEF(NS_ERROR_NO_AGGREGATION , "Component does not support aggregation")
XPC_MSG_DEF(NS_ERROR_NOT_AVAILABLE , "Component is not available")
XPC_MSG_DEF(NS_ERROR_FACTORY_NOT_REGISTERED , "Factory not registered")
XPC_MSG_DEF(NS_ERROR_FACTORY_REGISTER_AGAIN , "Factory not registered (may be tried again)")
XPC_MSG_DEF(NS_ERROR_FACTORY_NOT_LOADED , "Factory not loaded")
XPC_MSG_DEF(NS_ERROR_FACTORY_NO_SIGNATURE_SUPPORT , "Factory does not support signatures")
XPC_MSG_DEF(NS_ERROR_FACTORY_EXISTS , "Factory already exists")
/* added from nsError.h on Feb 28 2001... */
XPC_MSG_DEF(NS_BASE_STREAM_CLOSED , "Stream closed")
XPC_MSG_DEF(NS_BASE_STREAM_OSERROR , "Error from the operating system")
XPC_MSG_DEF(NS_BASE_STREAM_ILLEGAL_ARGS , "Illegal arguments")
XPC_MSG_DEF(NS_BASE_STREAM_NO_CONVERTER , "No converter for unichar streams")
XPC_MSG_DEF(NS_BASE_STREAM_BAD_CONVERSION , "Bad converter for unichar streams")
XPC_MSG_DEF(NS_BASE_STREAM_WOULD_BLOCK , "Stream would block")
XPC_MSG_DEF(NS_ERROR_FILE_UNRECOGNIZED_PATH , "File error: Unrecognized path")
XPC_MSG_DEF(NS_ERROR_FILE_UNRESOLVABLE_SYMLINK , "File error: Unresolvable symlink")
XPC_MSG_DEF(NS_ERROR_FILE_EXECUTION_FAILED , "File error: Execution failed")
XPC_MSG_DEF(NS_ERROR_FILE_UNKNOWN_TYPE , "File error: Unknown type")
XPC_MSG_DEF(NS_ERROR_FILE_DESTINATION_NOT_DIR , "File error: Destination not dir")
XPC_MSG_DEF(NS_ERROR_FILE_TARGET_DOES_NOT_EXIST , "File error: Target does not exist")
XPC_MSG_DEF(NS_ERROR_FILE_COPY_OR_MOVE_FAILED , "File error: Copy or move failed")
XPC_MSG_DEF(NS_ERROR_FILE_ALREADY_EXISTS , "File error: Already exists")
XPC_MSG_DEF(NS_ERROR_FILE_INVALID_PATH , "File error: Invalid path")
XPC_MSG_DEF(NS_ERROR_FILE_DISK_FULL , "File error: Disk full")
XPC_MSG_DEF(NS_ERROR_FILE_CORRUPTED , "File error: Corrupted")
XPC_MSG_DEF(NS_ERROR_FILE_NOT_DIRECTORY , "File error: Not directory")
XPC_MSG_DEF(NS_ERROR_FILE_IS_DIRECTORY , "File error: Is directory")
XPC_MSG_DEF(NS_ERROR_FILE_IS_LOCKED , "File error: Is locked")
XPC_MSG_DEF(NS_ERROR_FILE_TOO_BIG , "File error: Too big")
XPC_MSG_DEF(NS_ERROR_FILE_NO_DEVICE_SPACE , "File error: No device space")
XPC_MSG_DEF(NS_ERROR_FILE_NAME_TOO_LONG , "File error: Name too long")
XPC_MSG_DEF(NS_ERROR_FILE_NOT_FOUND , "File error: Not found")
XPC_MSG_DEF(NS_ERROR_FILE_READ_ONLY , "File error: Read only")
XPC_MSG_DEF(NS_ERROR_FILE_DIR_NOT_EMPTY , "File error: Dir not empty")
XPC_MSG_DEF(NS_ERROR_FILE_ACCESS_DENIED , "File error: Access denied")
/* added from nsError.h on Sept 6 2001... */
XPC_MSG_DEF(NS_ERROR_CANNOT_CONVERT_DATA , "Data conversion error")
XPC_MSG_DEF(NS_ERROR_OBJECT_IS_IMMUTABLE , "Can not modify immutable data container")
XPC_MSG_DEF(NS_ERROR_LOSS_OF_SIGNIFICANT_DATA , "Data conversion failed because significant data would be lost")
XPC_MSG_DEF(NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA , "Data conversion succeeded but data was rounded to fit")
/* network related codes (from nsNetError.h) */
XPC_MSG_DEF(NS_BINDING_FAILED , "The async request failed for some unknown reason")
XPC_MSG_DEF(NS_BINDING_ABORTED , "The async request failed because it was aborted by some user action")
XPC_MSG_DEF(NS_BINDING_REDIRECTED , "The async request has been redirected to a different async request")
XPC_MSG_DEF(NS_BINDING_RETARGETED , "The async request has been retargeted to a different handler")
XPC_MSG_DEF(NS_ERROR_MALFORMED_URI , "The URI is malformed")
XPC_MSG_DEF(NS_ERROR_UNKNOWN_PROTOCOL , "The URI scheme corresponds to an unknown protocol handler")
XPC_MSG_DEF(NS_ERROR_NO_CONTENT , "Channel opened successfully but no data will be returned")
XPC_MSG_DEF(NS_ERROR_IN_PROGRESS , "The requested action could not be completed while the object is busy")
XPC_MSG_DEF(NS_ERROR_ALREADY_OPENED , "Channel is already open")
XPC_MSG_DEF(NS_ERROR_INVALID_CONTENT_ENCODING , "The content encoding of the source document is incorrect")
XPC_MSG_DEF(NS_ERROR_CORRUPTED_CONTENT , "Corrupted content received from server (potentially MIME type mismatch because of 'X-Content-Type-Options: nosniff')")
XPC_MSG_DEF(NS_ERROR_FIRST_HEADER_FIELD_COMPONENT_EMPTY, "Couldn't extract first component from potentially corrupted header field")
XPC_MSG_DEF(NS_ERROR_ALREADY_CONNECTED , "The connection is already established")
XPC_MSG_DEF(NS_ERROR_NOT_CONNECTED , "The connection does not exist")
XPC_MSG_DEF(NS_ERROR_CONNECTION_REFUSED , "The connection was refused")
XPC_MSG_DEF(NS_ERROR_PROXY_CONNECTION_REFUSED , "The connection to the proxy server was refused")
XPC_MSG_DEF(NS_ERROR_NET_TIMEOUT , "The connection has timed out")
XPC_MSG_DEF(NS_ERROR_OFFLINE , "The requested action could not be completed in the offline state")
XPC_MSG_DEF(NS_ERROR_PORT_ACCESS_NOT_ALLOWED , "Establishing a connection to an unsafe or otherwise banned port was prohibited")
XPC_MSG_DEF(NS_ERROR_NET_RESET , "The connection was established, but no data was ever received")
XPC_MSG_DEF(NS_ERROR_NET_INTERRUPT , "The connection was established, but the data transfer was interrupted")
XPC_MSG_DEF(NS_ERROR_NET_PARTIAL_TRANSFER , "A transfer was only partially done when it completed")
XPC_MSG_DEF(NS_ERROR_NOT_RESUMABLE , "This request is not resumable, but it was tried to resume it, or to request resume-specific data")
XPC_MSG_DEF(NS_ERROR_ENTITY_CHANGED , "It was attempted to resume the request, but the entity has changed in the meantime")
XPC_MSG_DEF(NS_ERROR_REDIRECT_LOOP , "The request failed as a result of a detected redirection loop")
XPC_MSG_DEF(NS_ERROR_UNSAFE_CONTENT_TYPE , "The request failed because the content type returned by the server was not a type expected by the channel")
XPC_MSG_DEF(NS_ERROR_REMOTE_XUL , "Attempt to access remote XUL document that is not in website's whitelist")
XPC_MSG_DEF(NS_ERROR_LOAD_SHOWED_ERRORPAGE , "The load caused an error page to be displayed.")
XPC_MSG_DEF(NS_ERROR_FTP_LOGIN , "FTP error while logging in")
XPC_MSG_DEF(NS_ERROR_FTP_CWD , "FTP error while changing directory")
XPC_MSG_DEF(NS_ERROR_FTP_PASV , "FTP error while changing to passive mode")
XPC_MSG_DEF(NS_ERROR_FTP_PWD , "FTP error while retrieving current directory")
XPC_MSG_DEF(NS_ERROR_FTP_LIST , "FTP error while retrieving a directory listing")
XPC_MSG_DEF(NS_ERROR_UNKNOWN_HOST , "The lookup of the hostname failed")
XPC_MSG_DEF(NS_ERROR_DNS_LOOKUP_QUEUE_FULL , "The DNS lookup queue is full")
XPC_MSG_DEF(NS_ERROR_UNKNOWN_PROXY_HOST , "The lookup of the proxy hostname failed")
XPC_MSG_DEF(NS_ERROR_UNKNOWN_SOCKET_TYPE , "The specified socket type does not exist")
XPC_MSG_DEF(NS_ERROR_SOCKET_CREATE_FAILED , "The specified socket type could not be created")
XPC_MSG_DEF(NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED , "The specified socket address type is not supported")
XPC_MSG_DEF(NS_ERROR_SOCKET_ADDRESS_IN_USE , "Some other socket is already using the specified address.")
XPC_MSG_DEF(NS_ERROR_CACHE_KEY_NOT_FOUND , "Cache key could not be found")
XPC_MSG_DEF(NS_ERROR_CACHE_DATA_IS_STREAM , "Cache data is a stream")
XPC_MSG_DEF(NS_ERROR_CACHE_DATA_IS_NOT_STREAM , "Cache data is not a stream")
XPC_MSG_DEF(NS_ERROR_CACHE_WAIT_FOR_VALIDATION , "Cache entry exists but needs to be validated first")
XPC_MSG_DEF(NS_ERROR_CACHE_ENTRY_DOOMED , "Cache entry has been doomed")
XPC_MSG_DEF(NS_ERROR_CACHE_READ_ACCESS_DENIED , "Read access to cache denied")
XPC_MSG_DEF(NS_ERROR_CACHE_WRITE_ACCESS_DENIED , "Write access to cache denied")
XPC_MSG_DEF(NS_ERROR_CACHE_IN_USE , "Cache is currently in use")
XPC_MSG_DEF(NS_ERROR_DOCUMENT_NOT_CACHED , "Document does not exist in cache")
XPC_MSG_DEF(NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS , "The requested number of domain levels exceeds those present in the host string")
XPC_MSG_DEF(NS_ERROR_HOST_IS_IP_ADDRESS , "The host string is an IP address")
XPC_MSG_DEF(NS_ERROR_NOT_SAME_THREAD , "Can't access a wrapped JS object from a different thread")
/* storage related codes (from mozStorage.h) */
XPC_MSG_DEF(NS_ERROR_STORAGE_BUSY , "SQLite database connection is busy")
XPC_MSG_DEF(NS_ERROR_STORAGE_IOERR , "SQLite encountered an IO error")
XPC_MSG_DEF(NS_ERROR_STORAGE_CONSTRAINT , "SQLite database operation failed because a constraint was violated")
/* plugin related codes (from nsPluginError.h) */
XPC_MSG_DEF(NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED, "Clearing site data by time range not supported by plugin")
/* character converter related codes (from nsIUnicodeDecoder.h) */
XPC_MSG_DEF(NS_ERROR_ILLEGAL_INPUT , "The input characters have illegal sequences")
/* Codes related to signd jars */
XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_NOT_SIGNED , "The JAR is not signed.")
XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY , "An entry in the JAR has been modified after the JAR was signed.")
XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY , "An entry in the JAR has not been signed.")
XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_ENTRY_MISSING , "An entry is missing from the JAR file.")
XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE , "The JAR's signature is wrong.")
XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_ENTRY_TOO_LARGE , "An entry in the JAR is too large.")
XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_ENTRY_INVALID , "An entry in the JAR is invalid.")
XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_MANIFEST_INVALID , "The JAR's manifest or signature file is invalid.")
/* Codes related to signed manifests */
XPC_MSG_DEF(NS_ERROR_SIGNED_APP_MANIFEST_INVALID , "The signed app manifest or signature file is invalid.")
/* Codes for printing-related errors. */
XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE , "No printers available.")
XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND , "The selected printer could not be found.")
XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_COULD_NOT_OPEN_FILE , "Failed to open output file for print to file.")
XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_STARTDOC , "Printing failed while starting the print job.")
XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_ENDDOC , "Printing failed while completing the print job.")
XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_STARTPAGE , "Printing failed while starting a new page.")
XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY , "Cannot print this document yet, it is still being loaded.")
/* Codes related to content */
XPC_MSG_DEF(NS_ERROR_CONTENT_CRASHED , "The process that hosted this content has crashed.")
/* Codes for the JS-implemented Push DOM API. These can be removed as part of bug 1252660. */
XPC_MSG_DEF(NS_ERROR_DOM_PUSH_INVALID_KEY_ERR , "Invalid raw ECDSA P-256 public key.")
XPC_MSG_DEF(NS_ERROR_DOM_PUSH_MISMATCHED_KEY_ERR , "A subscription with a different application server key already exists.")
/* Codes defined in WebIDL https://heycam.github.io/webidl/#idl-DOMException-error-names */
XPC_MSG_DEF(NS_ERROR_DOM_NOT_FOUND_ERR , "The object can not be found here.")
XPC_MSG_DEF(NS_ERROR_DOM_NOT_ALLOWED_ERR , "The request is not allowed.")

View file

@ -0,0 +1,137 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 xpcObjectHelper_h
#define xpcObjectHelper_h
// Including 'windows.h' will #define GetClassInfo to something else.
#ifdef XP_WIN
#ifdef GetClassInfo
#undef GetClassInfo
#endif
#endif
#include "mozilla/Attributes.h"
#include <stdint.h>
#include "nsCOMPtr.h"
#include "nsIClassInfo.h"
#include "nsISupports.h"
#include "nsIXPCScriptable.h"
#include "nsWrapperCache.h"
class xpcObjectHelper
{
public:
explicit xpcObjectHelper(nsISupports* aObject, nsWrapperCache* aCache = nullptr)
: mCanonical(nullptr)
, mObject(aObject)
, mCache(aCache)
{
if (!mCache) {
if (aObject)
CallQueryInterface(aObject, &mCache);
else
mCache = nullptr;
}
}
nsISupports* Object()
{
return mObject;
}
nsISupports* GetCanonical()
{
if (!mCanonical) {
mCanonicalStrong = do_QueryInterface(mObject);
mCanonical = mCanonicalStrong;
}
return mCanonical;
}
already_AddRefed<nsISupports> forgetCanonical()
{
MOZ_ASSERT(mCanonical, "Huh, no canonical to forget?");
if (!mCanonicalStrong)
mCanonicalStrong = mCanonical;
mCanonical = nullptr;
return mCanonicalStrong.forget();
}
nsIClassInfo* GetClassInfo()
{
if (mXPCClassInfo)
return mXPCClassInfo;
if (!mClassInfo)
mClassInfo = do_QueryInterface(mObject);
return mClassInfo;
}
nsXPCClassInfo* GetXPCClassInfo()
{
if (!mXPCClassInfo) {
CallQueryInterface(mObject, getter_AddRefs(mXPCClassInfo));
}
return mXPCClassInfo;
}
already_AddRefed<nsXPCClassInfo> forgetXPCClassInfo()
{
GetXPCClassInfo();
return mXPCClassInfo.forget();
}
// We assert that we can reach an nsIXPCScriptable somehow.
uint32_t GetScriptableFlags()
{
// Try getting an nsXPCClassInfo - this handles DOM scriptable helpers.
nsCOMPtr<nsIXPCScriptable> sinfo = GetXPCClassInfo();
// If that didn't work, try just QI-ing. This handles BackstagePass.
if (!sinfo)
sinfo = do_QueryInterface(GetCanonical());
// We should have something by now.
MOZ_ASSERT(sinfo);
// Grab the flags.
return sinfo->GetScriptableFlags();
}
nsWrapperCache* GetWrapperCache()
{
return mCache;
}
protected:
xpcObjectHelper(nsISupports* aObject, nsISupports* aCanonical,
nsWrapperCache* aCache)
: mCanonical(aCanonical)
, mObject(aObject)
, mCache(aCache)
{
if (!mCache && aObject)
CallQueryInterface(aObject, &mCache);
}
nsCOMPtr<nsISupports> mCanonicalStrong;
nsISupports* MOZ_UNSAFE_REF("xpcObjectHelper has been specifically optimized "
"to avoid unnecessary AddRefs and Releases. "
"(see bug 565742)") mCanonical;
private:
xpcObjectHelper(xpcObjectHelper& aOther) = delete;
nsISupports* MOZ_UNSAFE_REF("xpcObjectHelper has been specifically optimized "
"to avoid unnecessary AddRefs and Releases. "
"(see bug 565742)") mObject;
nsWrapperCache* mCache;
nsCOMPtr<nsIClassInfo> mClassInfo;
RefPtr<nsXPCClassInfo> mXPCClassInfo;
};
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,635 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set ts=8 sts=4 et sw=4 tw=99: */
/* 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 xpcpublic_h
#define xpcpublic_h
#include "jsapi.h"
#include "js/HeapAPI.h"
#include "js/GCAPI.h"
#include "js/Proxy.h"
#include "nsISupports.h"
#include "nsIURI.h"
#include "nsIPrincipal.h"
#include "nsIGlobalObject.h"
#include "nsPIDOMWindow.h"
#include "nsWrapperCache.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
#include "mozilla/dom/JSSlots.h"
#include "mozilla/fallible.h"
#include "nsMathUtils.h"
#include "nsStringBuffer.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/Preferences.h"
class nsGlobalWindow;
class nsIPrincipal;
class nsScriptNameSpaceManager;
class nsIMemoryReporterCallback;
namespace mozilla {
namespace dom {
class Exception;
}
}
typedef void (* xpcGCCallback)(JSGCStatus status);
namespace xpc {
class Scriptability {
public:
explicit Scriptability(JSCompartment* c);
bool Allowed();
bool IsImmuneToScriptPolicy();
void Block();
void Unblock();
void SetDocShellAllowsScript(bool aAllowed);
static Scriptability& Get(JSObject* aScope);
private:
// Whenever a consumer wishes to prevent script from running on a global,
// it increments this value with a call to Block(). When it wishes to
// re-enable it (if ever), it decrements this value with a call to Unblock().
// Script may not run if this value is non-zero.
uint32_t mScriptBlocks;
// Whether the docshell allows javascript in this scope. If this scope
// doesn't have a docshell, this value is always true.
bool mDocShellAllowsScript;
// Whether this scope is immune to user-defined or addon-defined script
// policy.
bool mImmuneToScriptPolicy;
// Whether the new-style domain policy when this compartment was created
// forbids script execution.
bool mScriptBlockedByPolicy;
};
JSObject*
TransplantObject(JSContext* cx, JS::HandleObject origobj, JS::HandleObject target);
bool IsContentXBLScope(JSCompartment* compartment);
bool IsInContentXBLScope(JSObject* obj);
// Return a raw XBL scope object corresponding to contentScope, which must
// be an object whose global is a DOM window.
//
// The return value is not wrapped into cx->compartment, so be sure to enter
// its compartment before doing anything meaningful.
//
// Also note that XBL scopes are lazily created, so the return-value should be
// null-checked unless the caller can ensure that the scope must already
// exist.
//
// This function asserts if |contentScope| is itself in an XBL scope to catch
// sloppy consumers. Conversely, GetXBLScopeOrGlobal will handle objects that
// are in XBL scope (by just returning the global).
JSObject*
GetXBLScope(JSContext* cx, JSObject* contentScope);
inline JSObject*
GetXBLScopeOrGlobal(JSContext* cx, JSObject* obj)
{
if (IsInContentXBLScope(obj))
return js::GetGlobalForObjectCrossCompartment(obj);
return GetXBLScope(cx, obj);
}
// This function is similar to GetXBLScopeOrGlobal. However, if |obj| is a
// chrome scope, then it will return an add-on scope if addonId is non-null.
// Like GetXBLScopeOrGlobal, it returns the scope of |obj| if it's already a
// content XBL scope. But it asserts that |obj| is not an add-on scope.
JSObject*
GetScopeForXBLExecution(JSContext* cx, JS::HandleObject obj, JSAddonId* addonId);
// Returns whether XBL scopes have been explicitly disabled for code running
// in this compartment. See the comment around mAllowContentXBLScope.
bool
AllowContentXBLScope(JSCompartment* c);
// Returns whether we will use an XBL scope for this compartment. This is
// semantically equivalent to comparing global != GetXBLScope(global), but it
// does not have the side-effect of eagerly creating the XBL scope if it does
// not already exist.
bool
UseContentXBLScope(JSCompartment* c);
// Clear out the content XBL scope (if any) on the given global. This will
// force creation of a new one if one is needed again.
void
ClearContentXBLScope(JSObject* global);
bool
IsInAddonScope(JSObject* obj);
JSObject*
GetAddonScope(JSContext* cx, JS::HandleObject contentScope, JSAddonId* addonId);
bool
IsSandboxPrototypeProxy(JSObject* obj);
bool
IsReflector(JSObject* obj);
bool
IsXrayWrapper(JSObject* obj);
// If this function was created for a given XrayWrapper, returns the global of
// the Xrayed object. Otherwise, returns the global of the function.
//
// To emphasize the obvious: the return value here is not necessarily same-
// compartment with the argument.
JSObject*
XrayAwareCalleeGlobal(JSObject* fun);
void
TraceXPCGlobal(JSTracer* trc, JSObject* obj);
} /* namespace xpc */
namespace JS {
struct RuntimeStats;
} // namespace JS
#define XPC_WRAPPER_FLAGS (JSCLASS_HAS_PRIVATE | JSCLASS_FOREGROUND_FINALIZE)
#define XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(n) \
JSCLASS_DOM_GLOBAL | JSCLASS_HAS_PRIVATE | \
JSCLASS_PRIVATE_IS_NSISUPPORTS | \
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS + n)
#define XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET (JSCLASS_GLOBAL_SLOT_COUNT + DOM_GLOBAL_SLOTS)
#define XPCONNECT_GLOBAL_FLAGS XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(0)
inline JSObject*
xpc_FastGetCachedWrapper(JSContext* cx, nsWrapperCache* cache, JS::MutableHandleValue vp)
{
if (cache) {
JSObject* wrapper = cache->GetWrapper();
if (wrapper &&
js::GetObjectCompartment(wrapper) == js::GetContextCompartment(cx))
{
vp.setObject(*wrapper);
return wrapper;
}
}
return nullptr;
}
// If aVariant is an XPCVariant, this marks the object to be in aGeneration.
// This also unmarks the gray JSObject.
extern void
xpc_MarkInCCGeneration(nsISupports* aVariant, uint32_t aGeneration);
// If aWrappedJS is a JS wrapper, unmark its JSObject.
extern void
xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS);
extern void
xpc_UnmarkSkippableJSHolders();
// readable string conversions, static methods and members only
class XPCStringConvert
{
// One-slot cache, because it turns out it's common for web pages to
// get the same string a few times in a row. We get about a 40% cache
// hit rate on this cache last it was measured. We'd get about 70%
// hit rate with a hashtable with removal on finalization, but that
// would take a lot more machinery.
struct ZoneStringCache
{
// mString owns mBuffer. mString is a JS thing, so it can only die
// during GC, though it can drop its ref to the buffer if it gets
// flattened and wasn't null-terminated. We clear mString and mBuffer
// during GC and in our finalizer (to catch the flatterning case). As
// long as the above holds, mBuffer should not be a dangling pointer, so
// using this as a cache key should be safe.
//
// We also need to include the string's length in the cache key, because
// now that we allow non-null-terminated buffers we can have two strings
// with the same mBuffer but different lengths.
void* mBuffer = nullptr;
uint32_t mLength = 0;
JSString* mString = nullptr;
};
public:
// If the string shares the readable's buffer, that buffer will
// get assigned to *sharedBuffer. Otherwise null will be
// assigned.
static bool ReadableToJSVal(JSContext* cx, const nsAString& readable,
nsStringBuffer** sharedBuffer,
JS::MutableHandleValue vp);
// Convert the given stringbuffer/length pair to a jsval
static MOZ_ALWAYS_INLINE bool
StringBufferToJSVal(JSContext* cx, nsStringBuffer* buf, uint32_t length,
JS::MutableHandleValue rval, bool* sharedBuffer)
{
JS::Zone* zone = js::GetContextZone(cx);
ZoneStringCache* cache = static_cast<ZoneStringCache*>(JS_GetZoneUserData(zone));
if (cache && buf == cache->mBuffer && length == cache->mLength) {
MOZ_ASSERT(JS::GetStringZone(cache->mString) == zone);
JS::MarkStringAsLive(zone, cache->mString);
rval.setString(cache->mString);
*sharedBuffer = false;
return true;
}
JSString* str = JS_NewExternalString(cx,
static_cast<char16_t*>(buf->Data()),
length, &sDOMStringFinalizer);
if (!str) {
return false;
}
rval.setString(str);
if (!cache) {
cache = new ZoneStringCache();
JS_SetZoneUserData(zone, cache);
}
cache->mBuffer = buf;
cache->mLength = length;
cache->mString = str;
*sharedBuffer = true;
return true;
}
static void FreeZoneCache(JS::Zone* zone);
static void ClearZoneCache(JS::Zone* zone);
static MOZ_ALWAYS_INLINE bool IsLiteral(JSString* str)
{
return JS_IsExternalString(str) &&
JS_GetExternalStringFinalizer(str) == &sLiteralFinalizer;
}
static MOZ_ALWAYS_INLINE bool IsDOMString(JSString* str)
{
return JS_IsExternalString(str) &&
JS_GetExternalStringFinalizer(str) == &sDOMStringFinalizer;
}
private:
static const JSStringFinalizer sLiteralFinalizer, sDOMStringFinalizer;
static void FinalizeLiteral(JS::Zone* zone, const JSStringFinalizer* fin, char16_t* chars);
static void FinalizeDOMString(JS::Zone* zone, const JSStringFinalizer* fin, char16_t* chars);
XPCStringConvert() = delete;
};
class nsIAddonInterposition;
namespace xpc {
// If these functions return false, then an exception will be set on cx.
bool Base64Encode(JSContext* cx, JS::HandleValue val, JS::MutableHandleValue out);
bool Base64Decode(JSContext* cx, JS::HandleValue val, JS::MutableHandleValue out);
/**
* Convert an nsString to jsval, returning true on success.
* Note, the ownership of the string buffer may be moved from str to rval.
* If that happens, str will point to an empty string after this call.
*/
bool NonVoidStringToJsval(JSContext* cx, nsAString& str, JS::MutableHandleValue rval);
inline bool StringToJsval(JSContext* cx, nsAString& str, JS::MutableHandleValue rval)
{
// From the T_DOMSTRING case in XPCConvert::NativeData2JS.
if (str.IsVoid()) {
rval.setNull();
return true;
}
return NonVoidStringToJsval(cx, str, rval);
}
inline bool
NonVoidStringToJsval(JSContext* cx, const nsAString& str, JS::MutableHandleValue rval)
{
nsString mutableCopy;
if (!mutableCopy.Assign(str, mozilla::fallible)) {
JS_ReportOutOfMemory(cx);
return false;
}
return NonVoidStringToJsval(cx, mutableCopy, rval);
}
inline bool
StringToJsval(JSContext* cx, const nsAString& str, JS::MutableHandleValue rval)
{
nsString mutableCopy;
if (!mutableCopy.Assign(str, mozilla::fallible)) {
JS_ReportOutOfMemory(cx);
return false;
}
return StringToJsval(cx, mutableCopy, rval);
}
/**
* As above, but for mozilla::dom::DOMString.
*/
inline
bool NonVoidStringToJsval(JSContext* cx, mozilla::dom::DOMString& str,
JS::MutableHandleValue rval)
{
if (!str.HasStringBuffer()) {
// It's an actual XPCOM string
return NonVoidStringToJsval(cx, str.AsAString(), rval);
}
uint32_t length = str.StringBufferLength();
if (length == 0) {
rval.set(JS_GetEmptyStringValue(cx));
return true;
}
nsStringBuffer* buf = str.StringBuffer();
bool shared;
if (!XPCStringConvert::StringBufferToJSVal(cx, buf, length, rval,
&shared)) {
return false;
}
if (shared) {
// JS now needs to hold a reference to the buffer
str.RelinquishBufferOwnership();
}
return true;
}
MOZ_ALWAYS_INLINE
bool StringToJsval(JSContext* cx, mozilla::dom::DOMString& str,
JS::MutableHandleValue rval)
{
if (str.IsNull()) {
rval.setNull();
return true;
}
return NonVoidStringToJsval(cx, str, rval);
}
nsIPrincipal* GetCompartmentPrincipal(JSCompartment* compartment);
void SetLocationForGlobal(JSObject* global, const nsACString& location);
void SetLocationForGlobal(JSObject* global, nsIURI* locationURI);
// ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member
// of JS::ZoneStats.
class ZoneStatsExtras {
public:
ZoneStatsExtras() {}
nsCString pathPrefix;
private:
ZoneStatsExtras(const ZoneStatsExtras& other) = delete;
ZoneStatsExtras& operator=(const ZoneStatsExtras& other) = delete;
};
// ReportJSRuntimeExplicitTreeStats will expect this in the |extra| member
// of JS::CompartmentStats.
class CompartmentStatsExtras {
public:
CompartmentStatsExtras() {}
nsCString jsPathPrefix;
nsCString domPathPrefix;
nsCOMPtr<nsIURI> location;
private:
CompartmentStatsExtras(const CompartmentStatsExtras& other) = delete;
CompartmentStatsExtras& operator=(const CompartmentStatsExtras& other) = delete;
};
// This reports all the stats in |rtStats| that belong in the "explicit" tree,
// (which isn't all of them).
// @see ZoneStatsExtras
// @see CompartmentStatsExtras
void
ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats,
const nsACString& rtPath,
nsIMemoryReporterCallback* handleReport,
nsISupports* data,
bool anonymize,
size_t* rtTotal = nullptr);
/**
* Throws an exception on cx and returns false.
*/
bool
Throw(JSContext* cx, nsresult rv);
/**
* Returns the nsISupports native behind a given reflector (either DOM or
* XPCWN).
*/
already_AddRefed<nsISupports>
UnwrapReflectorToISupports(JSObject* reflector);
/**
* Singleton scopes for stuff that really doesn't fit anywhere else.
*
* If you find yourself wanting to use these compartments, you're probably doing
* something wrong. Callers MUST consult with the XPConnect module owner before
* using this compartment. If you don't, bholley will hunt you down.
*/
JSObject*
UnprivilegedJunkScope();
JSObject*
PrivilegedJunkScope();
/**
* Shared compilation scope for XUL prototype documents and XBL
* precompilation. This compartment has a null principal. No code may run, and
* it is invisible to the debugger.
*/
JSObject*
CompilationScope();
/**
* Returns the nsIGlobalObject corresponding to |aObj|'s JS global.
*/
nsIGlobalObject*
NativeGlobal(JSObject* aObj);
/**
* If |aObj| is a window, returns the associated nsGlobalWindow.
* Otherwise, returns null.
*/
nsGlobalWindow*
WindowOrNull(JSObject* aObj);
/**
* If |aObj| has a window for a global, returns the associated nsGlobalWindow.
* Otherwise, returns null.
*/
nsGlobalWindow*
WindowGlobalOrNull(JSObject* aObj);
/**
* If |aObj| is in an addon scope and that addon scope is associated with a
* live DOM Window, returns the associated nsGlobalWindow. Otherwise, returns
* null.
*/
nsGlobalWindow*
AddonWindowOrNull(JSObject* aObj);
/**
* If |cx| is in a compartment whose global is a window, returns the associated
* nsGlobalWindow. Otherwise, returns null.
*/
nsGlobalWindow*
CurrentWindowOrNull(JSContext* cx);
void
SimulateActivityCallback(bool aActive);
// This function may be used off-main-thread, in which case it is benignly
// racey.
bool
ShouldDiscardSystemSource();
bool
SharedMemoryEnabled();
bool
SetAddonInterposition(const nsACString& addonId, nsIAddonInterposition* interposition);
bool
AllowCPOWsInAddon(const nsACString& addonId, bool allow);
bool
ExtraWarningsForSystemJS();
class ErrorReport {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ErrorReport);
ErrorReport() : mWindowID(0)
, mLineNumber(0)
, mColumn(0)
, mFlags(0)
, mIsMuted(false)
{}
void Init(JSErrorReport* aReport, const char* aToStringResult,
bool aIsChrome, uint64_t aWindowID);
void Init(JSContext* aCx, mozilla::dom::Exception* aException,
bool aIsChrome, uint64_t aWindowID);
// Log the error report to the console. Which console will depend on the
// window id it was initialized with.
void LogToConsole();
// Log to console, using the given stack object (which should be a stack of
// the sort that JS::CaptureCurrentStack produces). aStack is allowed to be
// null.
void LogToConsoleWithStack(JS::HandleObject aStack);
// Produce an error event message string from the given JSErrorReport. Note
// that this may produce an empty string if aReport doesn't have a
// message attached.
static void ErrorReportToMessageString(JSErrorReport* aReport,
nsAString& aString);
public:
nsCString mCategory;
nsString mErrorMsgName;
nsString mErrorMsg;
nsString mFileName;
nsString mSourceLine;
uint64_t mWindowID;
uint32_t mLineNumber;
uint32_t mColumn;
uint32_t mFlags;
bool mIsMuted;
private:
~ErrorReport() {}
};
void
DispatchScriptErrorEvent(nsPIDOMWindowInner* win, JS::RootingContext* rootingCx,
xpc::ErrorReport* xpcReport, JS::Handle<JS::Value> exception);
// Get a stack of the sort that can be passed to
// xpc::ErrorReport::LogToConsoleWithStack from the given exception value. Can
// return null if the exception value doesn't have an associated stack. The
// returned stack, if any, may also not be in the same compartment as
// exceptionValue.
//
// The "win" argument passed in here should be the same as the window whose
// WindowID() is used to initialize the xpc::ErrorReport. This may be null, of
// course. If it's not null, this function may return a null stack object if
// the window is far enough gone, because in those cases we don't want to have
// the stack in the console message keeping the window alive.
JSObject*
FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
JS::HandleValue exceptionValue);
// Return a name for the compartment.
// This function makes reasonable efforts to make this name both mostly human-readable
// and unique. However, there are no guarantees of either property.
extern void
GetCurrentCompartmentName(JSContext*, nsCString& name);
void AddGCCallback(xpcGCCallback cb);
void RemoveGCCallback(xpcGCCallback cb);
inline bool
AreNonLocalConnectionsDisabled()
{
static int disabledForTest = -1;
if (disabledForTest == -1) {
char *s = getenv("MOZ_DISABLE_NONLOCAL_CONNECTIONS");
if (s) {
disabledForTest = *s != '0';
} else {
disabledForTest = 0;
}
}
return disabledForTest;
}
inline bool
IsInAutomation()
{
const char* prefName =
"security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
return mozilla::Preferences::GetBool(prefName) &&
AreNonLocalConnectionsDisabled();
}
} // namespace xpc
namespace mozilla {
namespace dom {
/**
* A test for whether WebIDL methods that should only be visible to
* chrome or XBL scopes should be exposed.
*/
bool IsChromeOrXBL(JSContext* cx, JSObject* /* unused */);
/**
* Same as IsChromeOrXBL but can be used in worker threads as well.
*/
bool ThreadSafeIsChromeOrXBL(JSContext* cx, JSObject* obj);
} // namespace dom
} // namespace mozilla
#endif