mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-25 01:47:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
58
js/xpconnect/src/BackstagePass.h
Normal file
58
js/xpconnect/src/BackstagePass.h
Normal 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__
|
||||
491
js/xpconnect/src/ExportHelpers.cpp
Normal file
491
js/xpconnect/src/ExportHelpers.cpp
Normal 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
3
js/xpconnect/src/README
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
see http://www.mozilla.org/scriptable
|
||||
|
||||
1966
js/xpconnect/src/Sandbox.cpp
Normal file
1966
js/xpconnect/src/Sandbox.cpp
Normal file
File diff suppressed because it is too large
Load diff
67
js/xpconnect/src/SandboxPrivate.h
Normal file
67
js/xpconnect/src/SandboxPrivate.h
Normal 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__
|
||||
276
js/xpconnect/src/XPCCallContext.cpp
Normal file
276
js/xpconnect/src/XPCCallContext.cpp
Normal 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;
|
||||
}
|
||||
3563
js/xpconnect/src/XPCComponents.cpp
Normal file
3563
js/xpconnect/src/XPCComponents.cpp
Normal file
File diff suppressed because it is too large
Load diff
1799
js/xpconnect/src/XPCConvert.cpp
Normal file
1799
js/xpconnect/src/XPCConvert.cpp
Normal file
File diff suppressed because it is too large
Load diff
63
js/xpconnect/src/XPCDebug.cpp
Normal file
63
js/xpconnect/src/XPCDebug.cpp
Normal 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;
|
||||
}
|
||||
80
js/xpconnect/src/XPCException.cpp
Normal file
80
js/xpconnect/src/XPCException.cpp
Normal 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;
|
||||
}
|
||||
64
js/xpconnect/src/XPCForwards.h
Normal file
64
js/xpconnect/src/XPCForwards.h
Normal 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___ */
|
||||
545
js/xpconnect/src/XPCInlines.h
Normal file
545
js/xpconnect/src/XPCInlines.h
Normal 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___ */
|
||||
3771
js/xpconnect/src/XPCJSContext.cpp
Normal file
3771
js/xpconnect/src/XPCJSContext.cpp
Normal file
File diff suppressed because it is too large
Load diff
816
js/xpconnect/src/XPCJSID.cpp
Normal file
816
js/xpconnect/src/XPCJSID.cpp
Normal 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)));
|
||||
}
|
||||
|
||||
|
||||
33
js/xpconnect/src/XPCJSMemoryReporter.h
Normal file
33
js/xpconnect/src/XPCJSMemoryReporter.h
Normal 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
|
||||
94
js/xpconnect/src/XPCJSWeakReference.cpp
Normal file
94
js/xpconnect/src/XPCJSWeakReference.cpp
Normal 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;
|
||||
}
|
||||
29
js/xpconnect/src/XPCJSWeakReference.h
Normal file
29
js/xpconnect/src/XPCJSWeakReference.h
Normal 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___
|
||||
289
js/xpconnect/src/XPCLocale.cpp
Normal file
289
js/xpconnect/src/XPCLocale.cpp
Normal 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;
|
||||
}
|
||||
94
js/xpconnect/src/XPCLog.cpp
Normal file
94
js/xpconnect/src/XPCLog.cpp
Normal 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
64
js/xpconnect/src/XPCLog.h
Normal 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___ */
|
||||
405
js/xpconnect/src/XPCMaps.cpp
Normal file
405
js/xpconnect/src/XPCMaps.cpp
Normal 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
606
js/xpconnect/src/XPCMaps.h
Normal 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___ */
|
||||
24
js/xpconnect/src/XPCModule.cpp
Normal file
24
js/xpconnect/src/XPCModule.cpp
Normal 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();
|
||||
}
|
||||
57
js/xpconnect/src/XPCModule.h
Normal file
57
js/xpconnect/src/XPCModule.h
Normal 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();
|
||||
189
js/xpconnect/src/XPCRuntimeService.cpp
Normal file
189
js/xpconnect/src/XPCRuntimeService.cpp
Normal 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;
|
||||
}
|
||||
1765
js/xpconnect/src/XPCShellImpl.cpp
Normal file
1765
js/xpconnect/src/XPCShellImpl.cpp
Normal file
File diff suppressed because it is too large
Load diff
143
js/xpconnect/src/XPCString.cpp
Normal file
143
js/xpconnect/src/XPCString.cpp
Normal 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
|
||||
179
js/xpconnect/src/XPCThrower.cpp
Normal file
179
js/xpconnect/src/XPCThrower.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
800
js/xpconnect/src/XPCVariant.cpp
Normal file
800
js/xpconnect/src/XPCVariant.cpp
Normal 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);
|
||||
}
|
||||
731
js/xpconnect/src/XPCWrappedJS.cpp
Normal file
731
js/xpconnect/src/XPCWrappedJS.cpp
Normal 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;
|
||||
}
|
||||
1457
js/xpconnect/src/XPCWrappedJSClass.cpp
Normal file
1457
js/xpconnect/src/XPCWrappedJSClass.cpp
Normal file
File diff suppressed because it is too large
Load diff
2325
js/xpconnect/src/XPCWrappedNative.cpp
Normal file
2325
js/xpconnect/src/XPCWrappedNative.cpp
Normal file
File diff suppressed because it is too large
Load diff
800
js/xpconnect/src/XPCWrappedNativeInfo.cpp
Normal file
800
js/xpconnect/src/XPCWrappedNativeInfo.cpp
Normal 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
|
||||
}
|
||||
1331
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
Normal file
1331
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
Normal file
File diff suppressed because it is too large
Load diff
208
js/xpconnect/src/XPCWrappedNativeProto.cpp
Normal file
208
js/xpconnect/src/XPCWrappedNativeProto.cpp
Normal 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
|
||||
}
|
||||
|
||||
|
||||
934
js/xpconnect/src/XPCWrappedNativeScope.cpp
Normal file
934
js/xpconnect/src/XPCWrappedNativeScope.cpp
Normal 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.
|
||||
}
|
||||
97
js/xpconnect/src/XPCWrapper.cpp
Normal file
97
js/xpconnect/src/XPCWrapper.cpp
Normal 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
|
||||
40
js/xpconnect/src/XPCWrapper.h
Normal file
40
js/xpconnect/src/XPCWrapper.h
Normal 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
|
||||
12
js/xpconnect/src/jsshell.msg
Normal file
12
js/xpconnect/src/jsshell.msg
Normal 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}")
|
||||
70
js/xpconnect/src/moz.build
Normal file
70
js/xpconnect/src/moz.build
Normal 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']
|
||||
345
js/xpconnect/src/nsScriptError.cpp
Normal file
345
js/xpconnect/src/nsScriptError.cpp
Normal 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)
|
||||
119
js/xpconnect/src/nsScriptErrorWithStack.cpp
Normal file
119
js/xpconnect/src/nsScriptErrorWithStack.cpp
Normal 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;
|
||||
}
|
||||
1336
js/xpconnect/src/nsXPConnect.cpp
Normal file
1336
js/xpconnect/src/nsXPConnect.cpp
Normal file
File diff suppressed because it is too large
Load diff
53
js/xpconnect/src/qsObjectHelper.h
Normal file
53
js/xpconnect/src/qsObjectHelper.h
Normal 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
228
js/xpconnect/src/xpc.msg
Normal 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.")
|
||||
137
js/xpconnect/src/xpcObjectHelper.h
Normal file
137
js/xpconnect/src/xpcObjectHelper.h
Normal 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
|
||||
3426
js/xpconnect/src/xpcprivate.h
Normal file
3426
js/xpconnect/src/xpcprivate.h
Normal file
File diff suppressed because it is too large
Load diff
635
js/xpconnect/src/xpcpublic.h
Normal file
635
js/xpconnect/src/xpcpublic.h
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue