summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/src
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /js/xpconnect/src
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/xpconnect/src')
-rw-r--r--js/xpconnect/src/BackstagePass.h58
-rw-r--r--js/xpconnect/src/ExportHelpers.cpp491
-rw-r--r--js/xpconnect/src/README3
-rw-r--r--js/xpconnect/src/Sandbox.cpp1966
-rw-r--r--js/xpconnect/src/SandboxPrivate.h67
-rw-r--r--js/xpconnect/src/XPCCallContext.cpp276
-rw-r--r--js/xpconnect/src/XPCComponents.cpp3563
-rw-r--r--js/xpconnect/src/XPCConvert.cpp1799
-rw-r--r--js/xpconnect/src/XPCDebug.cpp63
-rw-r--r--js/xpconnect/src/XPCException.cpp80
-rw-r--r--js/xpconnect/src/XPCForwards.h64
-rw-r--r--js/xpconnect/src/XPCInlines.h545
-rw-r--r--js/xpconnect/src/XPCJSContext.cpp3771
-rw-r--r--js/xpconnect/src/XPCJSID.cpp816
-rw-r--r--js/xpconnect/src/XPCJSMemoryReporter.h33
-rw-r--r--js/xpconnect/src/XPCJSWeakReference.cpp94
-rw-r--r--js/xpconnect/src/XPCJSWeakReference.h29
-rw-r--r--js/xpconnect/src/XPCLocale.cpp289
-rw-r--r--js/xpconnect/src/XPCLog.cpp94
-rw-r--r--js/xpconnect/src/XPCLog.h64
-rw-r--r--js/xpconnect/src/XPCMaps.cpp405
-rw-r--r--js/xpconnect/src/XPCMaps.h606
-rw-r--r--js/xpconnect/src/XPCModule.cpp24
-rw-r--r--js/xpconnect/src/XPCModule.h57
-rw-r--r--js/xpconnect/src/XPCRuntimeService.cpp189
-rw-r--r--js/xpconnect/src/XPCShellImpl.cpp1765
-rw-r--r--js/xpconnect/src/XPCString.cpp143
-rw-r--r--js/xpconnect/src/XPCThrower.cpp179
-rw-r--r--js/xpconnect/src/XPCVariant.cpp800
-rw-r--r--js/xpconnect/src/XPCWrappedJS.cpp731
-rw-r--r--js/xpconnect/src/XPCWrappedJSClass.cpp1457
-rw-r--r--js/xpconnect/src/XPCWrappedNative.cpp2325
-rw-r--r--js/xpconnect/src/XPCWrappedNativeInfo.cpp800
-rw-r--r--js/xpconnect/src/XPCWrappedNativeJSOps.cpp1331
-rw-r--r--js/xpconnect/src/XPCWrappedNativeProto.cpp208
-rw-r--r--js/xpconnect/src/XPCWrappedNativeScope.cpp934
-rw-r--r--js/xpconnect/src/XPCWrapper.cpp97
-rw-r--r--js/xpconnect/src/XPCWrapper.h40
-rw-r--r--js/xpconnect/src/jsshell.msg12
-rw-r--r--js/xpconnect/src/moz.build70
-rw-r--r--js/xpconnect/src/nsScriptError.cpp345
-rw-r--r--js/xpconnect/src/nsScriptErrorWithStack.cpp119
-rw-r--r--js/xpconnect/src/nsXPConnect.cpp1336
-rw-r--r--js/xpconnect/src/qsObjectHelper.h53
-rw-r--r--js/xpconnect/src/xpc.msg228
-rw-r--r--js/xpconnect/src/xpcObjectHelper.h137
-rw-r--r--js/xpconnect/src/xpcprivate.h3426
-rw-r--r--js/xpconnect/src/xpcpublic.h635
48 files changed, 32617 insertions, 0 deletions
diff --git a/js/xpconnect/src/BackstagePass.h b/js/xpconnect/src/BackstagePass.h
new file mode 100644
index 000000000..be7d15cdf
--- /dev/null
+++ b/js/xpconnect/src/BackstagePass.h
@@ -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__
diff --git a/js/xpconnect/src/ExportHelpers.cpp b/js/xpconnect/src/ExportHelpers.cpp
new file mode 100644
index 000000000..3dbf83e3b
--- /dev/null
+++ b/js/xpconnect/src/ExportHelpers.cpp
@@ -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 */
diff --git a/js/xpconnect/src/README b/js/xpconnect/src/README
new file mode 100644
index 000000000..260eed6bc
--- /dev/null
+++ b/js/xpconnect/src/README
@@ -0,0 +1,3 @@
+
+see http://www.mozilla.org/scriptable
+
diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp
new file mode 100644
index 000000000..120772ed2
--- /dev/null
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -0,0 +1,1966 @@
+/* -*- 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/. */
+
+/*
+ * The Components.Sandbox object.
+ */
+
+#include "AccessCheck.h"
+#include "jsfriendapi.h"
+#include "js/Proxy.h"
+#include "js/StructuredClone.h"
+#include "nsContentUtils.h"
+#include "nsGlobalWindow.h"
+#include "nsIScriptContext.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsIURI.h"
+#include "nsJSUtils.h"
+#include "nsNetUtil.h"
+#include "nsNullPrincipal.h"
+#include "nsPrincipal.h"
+#include "WrapperFactory.h"
+#include "xpcprivate.h"
+#include "xpc_make_class.h"
+#include "XPCWrapper.h"
+#include "XrayWrapper.h"
+#include "Crypto.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/cache/CacheStorage.h"
+#include "mozilla/dom/CSSBinding.h"
+#include "mozilla/dom/DirectoryBinding.h"
+#include "mozilla/dom/IndexedDatabaseManager.h"
+#include "mozilla/dom/Fetch.h"
+#include "mozilla/dom/FileBinding.h"
+#include "mozilla/dom/PromiseBinding.h"
+#include "mozilla/dom/RequestBinding.h"
+#include "mozilla/dom/ResponseBinding.h"
+#ifdef MOZ_WEBRTC
+#include "mozilla/dom/RTCIdentityProviderRegistrar.h"
+#endif
+#include "mozilla/dom/FileReaderBinding.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/TextDecoderBinding.h"
+#include "mozilla/dom/TextEncoderBinding.h"
+#include "mozilla/dom/UnionConversions.h"
+#include "mozilla/dom/URLBinding.h"
+#include "mozilla/dom/URLSearchParamsBinding.h"
+#include "mozilla/dom/XMLHttpRequest.h"
+#include "mozilla/DeferredFinalize.h"
+
+using namespace mozilla;
+using namespace JS;
+using namespace xpc;
+
+using mozilla::dom::DestroyProtoAndIfaceCache;
+using mozilla::dom::IndexedDatabaseManager;
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(SandboxPrivate)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SandboxPrivate)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+ tmp->UnlinkHostObjectURIs();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SandboxPrivate)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+ tmp->TraverseHostObjectURIs(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(SandboxPrivate)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(SandboxPrivate)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(SandboxPrivate)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SandboxPrivate)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIScriptObjectPrincipal)
+ NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
+ NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END
+
+const char kScriptSecurityManagerContractID[] = NS_SCRIPTSECURITYMANAGER_CONTRACTID;
+
+class nsXPCComponents_utils_Sandbox : public nsIXPCComponents_utils_Sandbox,
+ public nsIXPCScriptable
+{
+public:
+ // Aren't macros nice?
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTS_UTILS_SANDBOX
+ NS_DECL_NSIXPCSCRIPTABLE
+
+public:
+ nsXPCComponents_utils_Sandbox();
+
+private:
+ virtual ~nsXPCComponents_utils_Sandbox();
+
+ static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, HandleObject obj,
+ const CallArgs& args, bool* _retval);
+};
+
+already_AddRefed<nsIXPCComponents_utils_Sandbox>
+xpc::NewSandboxConstructor()
+{
+ nsCOMPtr<nsIXPCComponents_utils_Sandbox> sbConstructor =
+ new nsXPCComponents_utils_Sandbox();
+ return sbConstructor.forget();
+}
+
+static bool
+SandboxDump(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ if (args.length() == 0)
+ return true;
+
+ RootedString str(cx, ToString(cx, args[0]));
+ if (!str)
+ return false;
+
+ JSAutoByteString utf8str;
+ char* cstr = utf8str.encodeUtf8(cx, str);
+ if (!cstr)
+ return false;
+
+#if defined(XP_MACOSX)
+ // Be nice and convert all \r to \n.
+ char* c = cstr;
+ char* cEnd = cstr + strlen(cstr);
+ while (c < cEnd) {
+ if (*c == '\r')
+ *c = '\n';
+ c++;
+ }
+#endif
+#ifdef ANDROID
+ __android_log_write(ANDROID_LOG_INFO, "GeckoDump", cstr);
+#endif
+
+ fputs(cstr, stdout);
+ fflush(stdout);
+ args.rval().setBoolean(true);
+ return true;
+}
+
+static bool
+SandboxDebug(JSContext* cx, unsigned argc, Value* vp)
+{
+#ifdef DEBUG
+ return SandboxDump(cx, argc, vp);
+#else
+ return true;
+#endif
+}
+
+static bool
+SandboxImport(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ if (args.length() < 1 || args[0].isPrimitive()) {
+ XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
+ return false;
+ }
+
+ RootedString funname(cx);
+ if (args.length() > 1) {
+ // Use the second parameter as the function name.
+ funname = ToString(cx, args[1]);
+ if (!funname)
+ return false;
+ } else {
+ // NB: funobj must only be used to get the JSFunction out.
+ RootedObject funobj(cx, &args[0].toObject());
+ if (js::IsProxy(funobj)) {
+ funobj = XPCWrapper::UnsafeUnwrapSecurityWrapper(funobj);
+ }
+
+ JSAutoCompartment ac(cx, funobj);
+
+ RootedValue funval(cx, ObjectValue(*funobj));
+ JSFunction* fun = JS_ValueToFunction(cx, funval);
+ if (!fun) {
+ XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
+ return false;
+ }
+
+ // Use the actual function name as the name.
+ funname = JS_GetFunctionId(fun);
+ if (!funname) {
+ XPCThrower::Throw(NS_ERROR_INVALID_ARG, cx);
+ return false;
+ }
+ }
+
+ RootedId id(cx);
+ if (!JS_StringToId(cx, funname, &id))
+ return false;
+
+ // We need to resolve the this object, because this function is used
+ // unbound and should still work and act on the original sandbox.
+ RootedObject thisObject(cx, JS_THIS_OBJECT(cx, vp));
+ if (!thisObject) {
+ XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
+ return false;
+ }
+ if (!JS_SetPropertyById(cx, thisObject, id, args[0]))
+ return false;
+
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool
+SandboxCreateCrypto(JSContext* cx, JS::HandleObject obj)
+{
+ MOZ_ASSERT(JS_IsGlobalObject(obj));
+
+ nsIGlobalObject* native = xpc::NativeGlobal(obj);
+ MOZ_ASSERT(native);
+
+ dom::Crypto* crypto = new dom::Crypto();
+ crypto->Init(native);
+ JS::RootedObject wrapped(cx, crypto->WrapObject(cx, nullptr));
+ return JS_DefineProperty(cx, obj, "crypto", wrapped, JSPROP_ENUMERATE);
+}
+
+#ifdef MOZ_WEBRTC
+static bool
+SandboxCreateRTCIdentityProvider(JSContext* cx, JS::HandleObject obj)
+{
+ MOZ_ASSERT(JS_IsGlobalObject(obj));
+
+ nsCOMPtr<nsIGlobalObject> nativeGlobal = xpc::NativeGlobal(obj);
+ MOZ_ASSERT(nativeGlobal);
+
+ dom::RTCIdentityProviderRegistrar* registrar =
+ new dom::RTCIdentityProviderRegistrar(nativeGlobal);
+ JS::RootedObject wrapped(cx, registrar->WrapObject(cx, nullptr));
+ return JS_DefineProperty(cx, obj, "rtcIdentityProvider", wrapped, JSPROP_ENUMERATE);
+}
+#endif
+
+static bool
+SetFetchRequestFromValue(JSContext *cx, RequestOrUSVString& request,
+ const MutableHandleValue& requestOrUrl)
+{
+ RequestOrUSVStringArgument requestHolder(request);
+ bool noMatch = true;
+ if (requestOrUrl.isObject() &&
+ !requestHolder.TrySetToRequest(cx, requestOrUrl, noMatch, false)) {
+ return false;
+ }
+ if (noMatch &&
+ !requestHolder.TrySetToUSVString(cx, requestOrUrl, noMatch)) {
+ return false;
+ }
+ if (noMatch) {
+ return false;
+ }
+ return true;
+}
+
+static bool
+SandboxFetch(JSContext* cx, JS::HandleObject scope, const CallArgs& args)
+{
+ if (args.length() < 1) {
+ JS_ReportErrorASCII(cx, "fetch requires at least 1 argument");
+ return false;
+ }
+
+ RequestOrUSVString request;
+ if (!SetFetchRequestFromValue(cx, request, args[0])) {
+ JS_ReportErrorASCII(cx, "fetch requires a string or Request in argument 1");
+ return false;
+ }
+ RootedDictionary<dom::RequestInit> options(cx);
+ if (!options.Init(cx, args.hasDefined(1) ? args[1] : JS::NullHandleValue,
+ "Argument 2 of fetch", false)) {
+ return false;
+ }
+ nsCOMPtr<nsIGlobalObject> global = xpc::NativeGlobal(scope);
+ if (!global) {
+ return false;
+ }
+ ErrorResult rv;
+ RefPtr<dom::Promise> response =
+ FetchRequest(global, Constify(request), Constify(options), rv);
+ if (rv.MaybeSetPendingException(cx)) {
+ return false;
+ }
+
+ args.rval().setObject(*response->PromiseObj());
+ return true;
+}
+
+static bool SandboxFetchPromise(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RootedObject callee(cx, &args.callee());
+ RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
+ if (SandboxFetch(cx, scope, args)) {
+ return true;
+ }
+ return ConvertExceptionToPromise(cx, scope, args.rval());
+}
+
+
+static bool
+SandboxCreateFetch(JSContext* cx, HandleObject obj)
+{
+ MOZ_ASSERT(JS_IsGlobalObject(obj));
+
+ return JS_DefineFunction(cx, obj, "fetch", SandboxFetchPromise, 2, 0) &&
+ dom::RequestBinding::GetConstructorObject(cx) &&
+ dom::ResponseBinding::GetConstructorObject(cx) &&
+ dom::HeadersBinding::GetConstructorObject(cx);
+}
+
+static bool
+SandboxIsProxy(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() < 1) {
+ JS_ReportErrorASCII(cx, "Function requires at least 1 argument");
+ return false;
+ }
+ if (!args[0].isObject()) {
+ args.rval().setBoolean(false);
+ return true;
+ }
+
+ RootedObject obj(cx, &args[0].toObject());
+ obj = js::CheckedUnwrap(obj);
+ NS_ENSURE_TRUE(obj, false);
+
+ args.rval().setBoolean(js::IsScriptedProxy(obj));
+ return true;
+}
+
+/*
+ * Expected type of the arguments and the return value:
+ * function exportFunction(function funToExport,
+ * object targetScope,
+ * [optional] object options)
+ */
+static bool
+SandboxExportFunction(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() < 2) {
+ JS_ReportErrorASCII(cx, "Function requires at least 2 arguments");
+ return false;
+ }
+
+ RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
+ return ExportFunction(cx, args[0], args[1], options, args.rval());
+}
+
+static bool
+SandboxCreateObjectIn(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() < 1) {
+ JS_ReportErrorASCII(cx, "Function requires at least 1 argument");
+ return false;
+ }
+
+ RootedObject optionsObj(cx);
+ bool calledWithOptions = args.length() > 1;
+ if (calledWithOptions) {
+ if (!args[1].isObject()) {
+ JS_ReportErrorASCII(cx, "Expected the 2nd argument (options) to be an object");
+ return false;
+ }
+ optionsObj = &args[1].toObject();
+ }
+
+ CreateObjectInOptions options(cx, optionsObj);
+ if (calledWithOptions && !options.Parse())
+ return false;
+
+ return xpc::CreateObjectIn(cx, args[0], options, args.rval());
+}
+
+static bool
+SandboxCloneInto(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() < 2) {
+ JS_ReportErrorASCII(cx, "Function requires at least 2 arguments");
+ return false;
+ }
+
+ RootedValue options(cx, args.length() > 2 ? args[2] : UndefinedValue());
+ return xpc::CloneInto(cx, args[0], args[1], options, args.rval());
+}
+
+static void
+sandbox_finalize(js::FreeOp* fop, JSObject* obj)
+{
+ nsIScriptObjectPrincipal* sop =
+ static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
+ if (!sop) {
+ // sop can be null if CreateSandboxObject fails in the middle.
+ return;
+ }
+
+ static_cast<SandboxPrivate*>(sop)->ForgetGlobalObject();
+ DestroyProtoAndIfaceCache(obj);
+ DeferredFinalize(sop);
+}
+
+static void
+sandbox_moved(JSObject* obj, const JSObject* old)
+{
+ // Note that this hook can be called before the private pointer is set. In
+ // this case the SandboxPrivate will not exist yet, so there is nothing to
+ // do.
+ nsIScriptObjectPrincipal* sop =
+ static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
+ if (sop)
+ static_cast<SandboxPrivate*>(sop)->ObjectMoved(obj, old);
+}
+
+static bool
+writeToProto_setProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+ JS::MutableHandleValue vp, JS::ObjectOpResult& result)
+{
+ RootedObject proto(cx);
+ if (!JS_GetPrototype(cx, obj, &proto))
+ return false;
+
+ RootedValue receiver(cx, ObjectValue(*proto));
+ return JS_ForwardSetPropertyTo(cx, proto, id, vp, receiver, result);
+}
+
+static bool
+writeToProto_getProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
+ JS::MutableHandleValue vp)
+{
+ RootedObject proto(cx);
+ if (!JS_GetPrototype(cx, obj, &proto))
+ return false;
+
+ return JS_GetPropertyById(cx, proto, id, vp);
+}
+
+struct AutoSkipPropertyMirroring
+{
+ explicit AutoSkipPropertyMirroring(CompartmentPrivate* priv) : priv(priv) {
+ MOZ_ASSERT(!priv->skipWriteToGlobalPrototype);
+ priv->skipWriteToGlobalPrototype = true;
+ }
+ ~AutoSkipPropertyMirroring() {
+ MOZ_ASSERT(priv->skipWriteToGlobalPrototype);
+ priv->skipWriteToGlobalPrototype = false;
+ }
+
+ private:
+ CompartmentPrivate* priv;
+};
+
+// This hook handles the case when writeToGlobalPrototype is set on the
+// sandbox. This flag asks that any properties defined on the sandbox global
+// also be defined on the sandbox global's prototype. Whenever one of these
+// properties is changed (on either side), the change should be reflected on
+// both sides. We use this functionality to create sandboxes that are
+// essentially "sub-globals" of another global. This is useful for running
+// add-ons in a separate compartment while still giving them access to the
+// chrome window.
+static bool
+sandbox_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
+{
+ CompartmentPrivate* priv = CompartmentPrivate::Get(obj);
+ MOZ_ASSERT(priv->writeToGlobalPrototype);
+
+ // Whenever JS_EnumerateStandardClasses is called, it defines the
+ // "undefined" property, even if it's already defined. We don't want to do
+ // anything in that case.
+ if (id == XPCJSContext::Get()->GetStringID(XPCJSContext::IDX_UNDEFINED))
+ return true;
+
+ // Avoid recursively triggering sandbox_addProperty in the
+ // JS_DefinePropertyById call below.
+ if (priv->skipWriteToGlobalPrototype)
+ return true;
+
+ AutoSkipPropertyMirroring askip(priv);
+
+ RootedObject proto(cx);
+ if (!JS_GetPrototype(cx, obj, &proto))
+ return false;
+
+ // After bug 1015790 is fixed, we should be able to remove this unwrapping.
+ RootedObject unwrappedProto(cx, js::UncheckedUnwrap(proto, /* stopAtWindowProxy = */ false));
+
+ Rooted<JS::PropertyDescriptor> pd(cx);
+ if (!JS_GetPropertyDescriptorById(cx, proto, id, &pd))
+ return false;
+
+ // This is a little icky. If the property exists and is not configurable,
+ // then JS_CopyPropertyFrom will throw an exception when we try to do a
+ // normal assignment since it will think we're trying to remove the
+ // non-configurability. So we do JS_SetPropertyById in that case.
+ //
+ // However, in the case of |const x = 3|, we get called once for
+ // JSOP_DEFCONST and once for JSOP_SETCONST. The first one creates the
+ // property as readonly and configurable. The second one changes the
+ // attributes to readonly and not configurable. If we use JS_SetPropertyById
+ // for the second call, it will throw an exception because the property is
+ // readonly. We have to use JS_CopyPropertyFrom since it ignores the
+ // readonly attribute (as it calls JSObject::defineProperty). See bug
+ // 1019181.
+ if (pd.object() && !pd.configurable()) {
+ if (!JS_SetPropertyById(cx, proto, id, v))
+ return false;
+ } else {
+ if (!JS_CopyPropertyFrom(cx, id, unwrappedProto, obj,
+ MakeNonConfigurableIntoConfigurable))
+ return false;
+ }
+
+ if (!JS_GetPropertyDescriptorById(cx, obj, id, &pd))
+ return false;
+ unsigned attrs = pd.attributes() & ~(JSPROP_GETTER | JSPROP_SETTER);
+ if (!JS_DefinePropertyById(cx, obj, id, v,
+ attrs | JSPROP_PROPOP_ACCESSORS | JSPROP_REDEFINE_NONCONFIGURABLE,
+ JS_PROPERTYOP_GETTER(writeToProto_getProperty),
+ JS_PROPERTYOP_SETTER(writeToProto_setProperty)))
+ return false;
+
+ return true;
+}
+
+#define XPCONNECT_SANDBOX_CLASS_METADATA_SLOT (XPCONNECT_GLOBAL_EXTRA_SLOT_OFFSET)
+
+static const js::ClassOps SandboxClassOps = {
+ nullptr, nullptr, nullptr, nullptr,
+ JS_EnumerateStandardClasses, JS_ResolveStandardClass,
+ JS_MayResolveStandardClass,
+ sandbox_finalize,
+ nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
+};
+
+static const js::ClassExtension SandboxClassExtension = {
+ nullptr, /* weakmapKeyDelegateOp */
+ sandbox_moved /* objectMovedOp */
+};
+
+static const js::Class SandboxClass = {
+ "Sandbox",
+ XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1) |
+ JSCLASS_FOREGROUND_FINALIZE,
+ &SandboxClassOps,
+ JS_NULL_CLASS_SPEC,
+ &SandboxClassExtension,
+ JS_NULL_OBJECT_OPS
+};
+
+// Note to whomever comes here to remove addProperty hooks: billm has promised
+// to do the work for this class.
+static const js::ClassOps SandboxWriteToProtoClassOps = {
+ sandbox_addProperty, nullptr, nullptr, nullptr,
+ JS_EnumerateStandardClasses, JS_ResolveStandardClass,
+ JS_MayResolveStandardClass,
+ sandbox_finalize,
+ nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook,
+};
+
+static const js::Class SandboxWriteToProtoClass = {
+ "Sandbox",
+ XPCONNECT_GLOBAL_FLAGS_WITH_EXTRA_SLOTS(1) |
+ JSCLASS_FOREGROUND_FINALIZE,
+ &SandboxWriteToProtoClassOps,
+ JS_NULL_CLASS_SPEC,
+ &SandboxClassExtension,
+ JS_NULL_OBJECT_OPS
+};
+
+static const JSFunctionSpec SandboxFunctions[] = {
+ JS_FS("dump", SandboxDump, 1,0),
+ JS_FS("debug", SandboxDebug, 1,0),
+ JS_FS("importFunction", SandboxImport, 1,0),
+ JS_FS_END
+};
+
+bool
+xpc::IsSandbox(JSObject* obj)
+{
+ const js::Class* clasp = js::GetObjectClass(obj);
+ return clasp == &SandboxClass || clasp == &SandboxWriteToProtoClass;
+}
+
+/***************************************************************************/
+nsXPCComponents_utils_Sandbox::nsXPCComponents_utils_Sandbox()
+{
+}
+
+nsXPCComponents_utils_Sandbox::~nsXPCComponents_utils_Sandbox()
+{
+}
+
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents_utils_Sandbox)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_utils_Sandbox)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_utils_Sandbox)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCComponents_utils_Sandbox)
+NS_IMPL_RELEASE(nsXPCComponents_utils_Sandbox)
+
+// We use the nsIXPScriptable macros to generate lots of stuff for us.
+#define XPC_MAP_CLASSNAME nsXPCComponents_utils_Sandbox
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_utils_Sandbox"
+#define XPC_MAP_WANT_CALL
+#define XPC_MAP_WANT_CONSTRUCT
+#define XPC_MAP_FLAGS 0
+#include "xpc_map_end.h" /* This #undef's the above. */
+
+const xpc::SandboxProxyHandler xpc::sandboxProxyHandler;
+
+bool
+xpc::IsSandboxPrototypeProxy(JSObject* obj)
+{
+ return js::IsProxy(obj) &&
+ js::GetProxyHandler(obj) == &xpc::sandboxProxyHandler;
+}
+
+bool
+xpc::SandboxCallableProxyHandler::call(JSContext* cx, JS::Handle<JSObject*> proxy,
+ const JS::CallArgs& args) const
+{
+ // We forward the call to our underlying callable.
+
+ // Get our SandboxProxyHandler proxy.
+ RootedObject sandboxProxy(cx, getSandboxProxy(proxy));
+ MOZ_ASSERT(js::IsProxy(sandboxProxy) &&
+ js::GetProxyHandler(sandboxProxy) == &xpc::sandboxProxyHandler);
+
+ // The global of the sandboxProxy is the sandbox global, and the
+ // target object is the original proto.
+ RootedObject sandboxGlobal(cx,
+ js::GetGlobalForObjectCrossCompartment(sandboxProxy));
+ MOZ_ASSERT(IsSandbox(sandboxGlobal));
+
+ // If our this object is the sandbox global, we call with this set to the
+ // original proto instead.
+ //
+ // There are two different ways we can compute |this|. If we use
+ // JS_THIS_VALUE, we'll get the bonafide |this| value as passed by the
+ // caller, which may be undefined if a global function was invoked without
+ // an explicit invocant. If we use JS_THIS or JS_THIS_OBJECT, the |this|
+ // in |vp| will be coerced to the global, which is not the correct
+ // behavior in ES5 strict mode. And we have no way to compute strictness
+ // here.
+ //
+ // The naive approach is simply to use JS_THIS_VALUE here. If |this| was
+ // explicit, we can remap it appropriately. If it was implicit, then we
+ // leave it as undefined, and let the callee sort it out. Since the callee
+ // is generally in the same compartment as its global (eg the Window's
+ // compartment, not the Sandbox's), the callee will generally compute the
+ // correct |this|.
+ //
+ // However, this breaks down in the Xray case. If the sandboxPrototype
+ // is an Xray wrapper, then we'll end up reifying the native methods in
+ // the Sandbox's scope, which means that they'll compute |this| to be the
+ // Sandbox, breaking old-style XPC_WN_CallMethod methods.
+ //
+ // Luckily, the intent of Xrays is to provide a vanilla view of a foreign
+ // DOM interface, which means that we don't care about script-enacted
+ // strictness in the prototype's home compartment. Indeed, since DOM
+ // methods are always non-strict, we can just assume non-strict semantics
+ // if the sandboxPrototype is an Xray Wrapper, which lets us appropriately
+ // remap |this|.
+ bool isXray = WrapperFactory::IsXrayWrapper(sandboxProxy);
+ RootedValue thisVal(cx, isXray ? args.computeThis(cx) : args.thisv());
+ if (thisVal == ObjectValue(*sandboxGlobal)) {
+ thisVal = ObjectValue(*js::GetProxyTargetObject(sandboxProxy));
+ }
+
+ RootedValue func(cx, js::GetProxyPrivate(proxy));
+ return JS::Call(cx, thisVal, func, args, args.rval());
+}
+
+const xpc::SandboxCallableProxyHandler xpc::sandboxCallableProxyHandler;
+
+/*
+ * Wrap a callable such that if we're called with oldThisObj as the
+ * "this" we will instead call it with newThisObj as the this.
+ */
+static JSObject*
+WrapCallable(JSContext* cx, HandleObject callable, HandleObject sandboxProtoProxy)
+{
+ MOZ_ASSERT(JS::IsCallable(callable));
+ // Our proxy is wrapping the callable. So we need to use the
+ // callable as the private. We put the given sandboxProtoProxy in
+ // an extra slot, and our call() hook depends on that.
+ MOZ_ASSERT(js::IsProxy(sandboxProtoProxy) &&
+ js::GetProxyHandler(sandboxProtoProxy) ==
+ &xpc::sandboxProxyHandler);
+
+ RootedValue priv(cx, ObjectValue(*callable));
+ // We want to claim to have the same proto as our wrapped callable, so set
+ // ourselves up with a lazy proto.
+ js::ProxyOptions options;
+ options.setLazyProto(true);
+ JSObject* obj = js::NewProxyObject(cx, &xpc::sandboxCallableProxyHandler,
+ priv, nullptr, options);
+ if (obj) {
+ js::SetProxyExtra(obj, SandboxCallableProxyHandler::SandboxProxySlot,
+ ObjectValue(*sandboxProtoProxy));
+ }
+
+ return obj;
+}
+
+template<typename Op>
+bool WrapAccessorFunction(JSContext* cx, Op& op, PropertyDescriptor* desc,
+ unsigned attrFlag, HandleObject sandboxProtoProxy)
+{
+ if (!op) {
+ return true;
+ }
+
+ if (!(desc->attrs & attrFlag)) {
+ XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
+ return false;
+ }
+
+ RootedObject func(cx, JS_FUNC_TO_DATA_PTR(JSObject*, op));
+ func = WrapCallable(cx, func, sandboxProtoProxy);
+ if (!func)
+ return false;
+ op = JS_DATA_TO_FUNC_PTR(Op, func.get());
+ return true;
+}
+
+bool
+xpc::SandboxProxyHandler::getPropertyDescriptor(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id,
+ JS::MutableHandle<PropertyDescriptor> desc) const
+{
+ JS::RootedObject obj(cx, wrappedObject(proxy));
+
+ MOZ_ASSERT(js::GetObjectCompartment(obj) == js::GetObjectCompartment(proxy));
+ if (!JS_GetPropertyDescriptorById(cx, obj, id, desc))
+ return false;
+
+ if (!desc.object())
+ return true; // No property, nothing to do
+
+ // Now fix up the getter/setter/value as needed to be bound to desc->obj.
+ if (!WrapAccessorFunction(cx, desc.getter(), desc.address(),
+ JSPROP_GETTER, proxy))
+ return false;
+ if (!WrapAccessorFunction(cx, desc.setter(), desc.address(),
+ JSPROP_SETTER, proxy))
+ return false;
+ if (desc.value().isObject()) {
+ RootedObject val (cx, &desc.value().toObject());
+ if (JS::IsCallable(val)) {
+ val = WrapCallable(cx, val, proxy);
+ if (!val)
+ return false;
+ desc.value().setObject(*val);
+ }
+ }
+
+ return true;
+}
+
+bool
+xpc::SandboxProxyHandler::getOwnPropertyDescriptor(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id,
+ JS::MutableHandle<PropertyDescriptor> desc)
+ const
+{
+ if (!getPropertyDescriptor(cx, proxy, id, desc))
+ return false;
+
+ if (desc.object() != wrappedObject(proxy))
+ desc.object().set(nullptr);
+
+ return true;
+}
+
+/*
+ * Reuse the BaseProxyHandler versions of the derived traps that are implemented
+ * in terms of the fundamental traps.
+ */
+
+bool
+xpc::SandboxProxyHandler::has(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id, bool* bp) const
+{
+ // This uses getPropertyDescriptor for backward compatibility with
+ // the old BaseProxyHandler::has implementation.
+ Rooted<PropertyDescriptor> desc(cx);
+ if (!getPropertyDescriptor(cx, proxy, id, &desc))
+ return false;
+
+ *bp = !!desc.object();
+ return true;
+}
+bool
+xpc::SandboxProxyHandler::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id, bool* bp) const
+{
+ return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
+}
+
+bool
+xpc::SandboxProxyHandler::get(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<JS::Value> receiver,
+ JS::Handle<jsid> id,
+ JS::MutableHandle<Value> vp) const
+{
+ // This uses getPropertyDescriptor for backward compatibility with
+ // the old BaseProxyHandler::get implementation.
+ Rooted<PropertyDescriptor> desc(cx);
+ if (!getPropertyDescriptor(cx, proxy, id, &desc))
+ return false;
+ desc.assertCompleteIfFound();
+
+ if (!desc.object()) {
+ vp.setUndefined();
+ return true;
+ }
+
+ // Everything after here follows [[Get]] for ordinary objects.
+ if (desc.isDataDescriptor()) {
+ vp.set(desc.value());
+ return true;
+ }
+
+ MOZ_ASSERT(desc.isAccessorDescriptor());
+ RootedObject getter(cx, desc.getterObject());
+
+ if (!getter) {
+ vp.setUndefined();
+ return true;
+ }
+
+ return Call(cx, receiver, getter, HandleValueArray::empty(), vp);
+}
+
+bool
+xpc::SandboxProxyHandler::set(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::Handle<jsid> id,
+ JS::Handle<Value> v,
+ JS::Handle<Value> receiver,
+ JS::ObjectOpResult& result) const
+{
+ return BaseProxyHandler::set(cx, proxy, id, v, receiver, result);
+}
+
+bool
+xpc::SandboxProxyHandler::getOwnEnumerablePropertyKeys(JSContext* cx,
+ JS::Handle<JSObject*> proxy,
+ AutoIdVector& props) const
+{
+ return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
+}
+
+bool
+xpc::SandboxProxyHandler::enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
+ JS::MutableHandle<JSObject*> objp) const
+{
+ return BaseProxyHandler::enumerate(cx, proxy, objp);
+}
+
+bool
+xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj)
+{
+ uint32_t length;
+ bool ok = JS_GetArrayLength(cx, obj, &length);
+ NS_ENSURE_TRUE(ok, false);
+ for (uint32_t i = 0; i < length; i++) {
+ RootedValue nameValue(cx);
+ ok = JS_GetElement(cx, obj, i, &nameValue);
+ NS_ENSURE_TRUE(ok, false);
+ if (!nameValue.isString()) {
+ JS_ReportErrorASCII(cx, "Property names must be strings");
+ return false;
+ }
+ RootedString nameStr(cx, nameValue.toString());
+ JSAutoByteString name;
+ if (!name.encodeUtf8(cx, nameStr))
+ return false;
+ if (!strcmp(name.ptr(), "CSS")) {
+ CSS = true;
+ } else if (!strcmp(name.ptr(), "indexedDB")) {
+ indexedDB = true;
+ } else if (!strcmp(name.ptr(), "XMLHttpRequest")) {
+ XMLHttpRequest = true;
+ } else if (!strcmp(name.ptr(), "TextEncoder")) {
+ TextEncoder = true;
+ } else if (!strcmp(name.ptr(), "TextDecoder")) {
+ TextDecoder = true;
+ } else if (!strcmp(name.ptr(), "URL")) {
+ URL = true;
+ } else if (!strcmp(name.ptr(), "URLSearchParams")) {
+ URLSearchParams = true;
+ } else if (!strcmp(name.ptr(), "atob")) {
+ atob = true;
+ } else if (!strcmp(name.ptr(), "btoa")) {
+ btoa = true;
+ } else if (!strcmp(name.ptr(), "Blob")) {
+ Blob = true;
+ } else if (!strcmp(name.ptr(), "Directory")) {
+ Directory = true;
+ } else if (!strcmp(name.ptr(), "File")) {
+ File = true;
+ } else if (!strcmp(name.ptr(), "crypto")) {
+ crypto = true;
+#ifdef MOZ_WEBRTC
+ } else if (!strcmp(name.ptr(), "rtcIdentityProvider")) {
+ rtcIdentityProvider = true;
+#endif
+ } else if (!strcmp(name.ptr(), "fetch")) {
+ fetch = true;
+ } else if (!strcmp(name.ptr(), "caches")) {
+ caches = true;
+ } else if (!strcmp(name.ptr(), "FileReader")) {
+ fileReader = true;
+ } else {
+ JS_ReportErrorUTF8(cx, "Unknown property name: %s", name.ptr());
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj)
+{
+ MOZ_ASSERT(js::GetContextCompartment(cx) == js::GetObjectCompartment(obj));
+ // Properties will be exposed to System automatically but not to Sandboxes
+ // if |[Exposed=System]| is specified.
+ // This function holds common properties not exposed automatically but able
+ // to be requested either in |Cu.importGlobalProperties| or
+ // |wantGlobalProperties| of a sandbox.
+ if (CSS && !dom::CSSBinding::GetConstructorObject(cx))
+ return false;
+
+ if (XMLHttpRequest &&
+ !dom::XMLHttpRequestBinding::GetConstructorObject(cx))
+ return false;
+
+ if (TextEncoder &&
+ !dom::TextEncoderBinding::GetConstructorObject(cx))
+ return false;
+
+ if (TextDecoder &&
+ !dom::TextDecoderBinding::GetConstructorObject(cx))
+ return false;
+
+ if (URL &&
+ !dom::URLBinding::GetConstructorObject(cx))
+ return false;
+
+ if (URLSearchParams &&
+ !dom::URLSearchParamsBinding::GetConstructorObject(cx))
+ return false;
+
+ if (atob &&
+ !JS_DefineFunction(cx, obj, "atob", Atob, 1, 0))
+ return false;
+
+ if (btoa &&
+ !JS_DefineFunction(cx, obj, "btoa", Btoa, 1, 0))
+ return false;
+
+ if (Blob &&
+ !dom::BlobBinding::GetConstructorObject(cx))
+ return false;
+
+ if (Directory &&
+ !dom::DirectoryBinding::GetConstructorObject(cx))
+ return false;
+
+ if (File &&
+ !dom::FileBinding::GetConstructorObject(cx))
+ return false;
+
+ if (crypto && !SandboxCreateCrypto(cx, obj))
+ return false;
+
+#ifdef MOZ_WEBRTC
+ if (rtcIdentityProvider && !SandboxCreateRTCIdentityProvider(cx, obj))
+ return false;
+#endif
+
+ if (fetch && !SandboxCreateFetch(cx, obj))
+ return false;
+
+ if (caches && !dom::cache::CacheStorage::DefineCaches(cx, obj))
+ return false;
+
+ if (fileReader && !dom::FileReaderBinding::GetConstructorObject(cx))
+ return false;
+
+ return true;
+}
+
+bool
+xpc::GlobalProperties::DefineInXPCComponents(JSContext* cx, JS::HandleObject obj)
+{
+ if (indexedDB &&
+ !IndexedDatabaseManager::DefineIndexedDB(cx, obj))
+ return false;
+
+ return Define(cx, obj);
+}
+
+bool
+xpc::GlobalProperties::DefineInSandbox(JSContext* cx, JS::HandleObject obj)
+{
+ MOZ_ASSERT(IsSandbox(obj));
+ MOZ_ASSERT(js::GetContextCompartment(cx) == js::GetObjectCompartment(obj));
+
+ if (indexedDB &&
+ !(IndexedDatabaseManager::ResolveSandboxBinding(cx) &&
+ IndexedDatabaseManager::DefineIndexedDB(cx, obj)))
+ return false;
+
+ return Define(cx, obj);
+}
+
+nsresult
+xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prinOrSop,
+ SandboxOptions& options)
+{
+ // Create the sandbox global object
+ nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(prinOrSop);
+ if (!principal) {
+ nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(prinOrSop);
+ if (sop) {
+ principal = sop->GetPrincipal();
+ } else {
+ RefPtr<nsNullPrincipal> nullPrin = nsNullPrincipal::Create();
+ principal = nullPrin;
+ }
+ }
+ MOZ_ASSERT(principal);
+
+ JS::CompartmentOptions compartmentOptions;
+
+ auto& creationOptions = compartmentOptions.creationOptions();
+
+ // XXXjwatt: Consider whether/when sandboxes should be able to see
+ // [SecureContext] API (bug 1273687). In that case we'd call
+ // creationOptions.setSecureContext(true).
+
+ if (xpc::SharedMemoryEnabled())
+ creationOptions.setSharedMemoryAndAtomicsEnabled(true);
+
+ if (options.sameZoneAs)
+ creationOptions.setSameZoneAs(js::UncheckedUnwrap(options.sameZoneAs));
+ else if (options.freshZone)
+ creationOptions.setZone(JS::FreshZone);
+ else
+ creationOptions.setZone(JS::SystemZone);
+
+ creationOptions.setInvisibleToDebugger(options.invisibleToDebugger)
+ .setTrace(TraceXPCGlobal);
+
+ // Try to figure out any addon this sandbox should be associated with.
+ // The addon could have been passed in directly, as part of the metadata,
+ // or by being constructed from an addon's code.
+ JSAddonId* addonId = nullptr;
+ if (options.addonId) {
+ addonId = JS::NewAddonId(cx, options.addonId);
+ NS_ENSURE_TRUE(addonId, NS_ERROR_FAILURE);
+ } else if (JSObject* obj = JS::CurrentGlobalOrNull(cx)) {
+ if (JSAddonId* id = JS::AddonIdOfObject(obj))
+ addonId = id;
+ }
+
+ creationOptions.setAddonId(addonId);
+
+ compartmentOptions.behaviors().setDiscardSource(options.discardSource);
+
+ const js::Class* clasp = options.writeToGlobalPrototype
+ ? &SandboxWriteToProtoClass
+ : &SandboxClass;
+
+ RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp),
+ principal, compartmentOptions));
+ if (!sandbox)
+ return NS_ERROR_FAILURE;
+
+ CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox);
+ priv->allowWaivers = options.allowWaivers;
+ priv->writeToGlobalPrototype = options.writeToGlobalPrototype;
+ priv->isWebExtensionContentScript = options.isWebExtensionContentScript;
+ priv->waiveInterposition = options.waiveInterposition;
+
+ // Set up the wantXrays flag, which indicates whether xrays are desired even
+ // for same-origin access.
+ //
+ // This flag has historically been ignored for chrome sandboxes due to
+ // quirks in the wrapping implementation that have now been removed. Indeed,
+ // same-origin Xrays for chrome->chrome access seems a bit superfluous.
+ // Arguably we should just flip the default for chrome and still honor the
+ // flag, but such a change would break code in subtle ways for minimal
+ // benefit. So we just switch it off here.
+ priv->wantXrays =
+ AccessCheck::isChrome(sandbox) ? false : options.wantXrays;
+
+ {
+ JSAutoCompartment ac(cx, sandbox);
+
+ nsCOMPtr<nsIScriptObjectPrincipal> sbp =
+ new SandboxPrivate(principal, sandbox);
+
+ // Pass on ownership of sbp to |sandbox|.
+ JS_SetPrivate(sandbox, sbp.forget().take());
+
+ {
+ // Don't try to mirror standard class properties, if we're using a
+ // mirroring sandbox. (This is meaningless for non-mirroring
+ // sandboxes.)
+ AutoSkipPropertyMirroring askip(CompartmentPrivate::Get(sandbox));
+
+ // Ensure |Object.prototype| is instantiated before prototype-
+ // splicing below. For write-to-global-prototype behavior, extend
+ // this to all builtin properties.
+ if (options.writeToGlobalPrototype) {
+ if (!JS_EnumerateStandardClasses(cx, sandbox))
+ return NS_ERROR_XPC_UNEXPECTED;
+ } else {
+ if (!JS_GetObjectPrototype(cx, sandbox))
+ return NS_ERROR_XPC_UNEXPECTED;
+ }
+ }
+
+ if (options.proto) {
+ bool ok = JS_WrapObject(cx, &options.proto);
+ if (!ok)
+ return NS_ERROR_XPC_UNEXPECTED;
+
+ // Now check what sort of thing we've got in |proto|, and figure out
+ // if we need a SandboxProxyHandler.
+ //
+ // Note that, in the case of a window, we can't require that the
+ // Sandbox subsumes the prototype, because we have to hold our
+ // reference to it via an outer window, and the window may navigate
+ // at any time. So we have to handle that case separately.
+ bool useSandboxProxy = !!WindowOrNull(js::UncheckedUnwrap(options.proto, false));
+ if (!useSandboxProxy) {
+ JSObject* unwrappedProto = js::CheckedUnwrap(options.proto, false);
+ if (!unwrappedProto) {
+ JS_ReportErrorASCII(cx, "Sandbox must subsume sandboxPrototype");
+ return NS_ERROR_INVALID_ARG;
+ }
+ const js::Class* unwrappedClass = js::GetObjectClass(unwrappedProto);
+ useSandboxProxy = IS_WN_CLASS(unwrappedClass) ||
+ mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass));
+ }
+
+ if (useSandboxProxy) {
+ // Wrap it up in a proxy that will do the right thing in terms
+ // of this-binding for methods.
+ RootedValue priv(cx, ObjectValue(*options.proto));
+ options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler,
+ priv, nullptr);
+ if (!options.proto)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ ok = JS_SplicePrototype(cx, sandbox, options.proto);
+ if (!ok)
+ return NS_ERROR_XPC_UNEXPECTED;
+ }
+
+ // Don't try to mirror the properties that are set below.
+ AutoSkipPropertyMirroring askip(CompartmentPrivate::Get(sandbox));
+
+ bool allowComponents = principal == nsXPConnect::SystemPrincipal() ||
+ nsContentUtils::IsExpandedPrincipal(principal);
+ if (options.wantComponents && allowComponents &&
+ !ObjectScope(sandbox)->AttachComponentsObject(cx))
+ return NS_ERROR_XPC_UNEXPECTED;
+
+ if (!XPCNativeWrapper::AttachNewConstructorObject(cx, sandbox))
+ return NS_ERROR_XPC_UNEXPECTED;
+
+ if (!JS_DefineFunctions(cx, sandbox, SandboxFunctions))
+ return NS_ERROR_XPC_UNEXPECTED;
+
+ if (options.wantExportHelpers &&
+ (!JS_DefineFunction(cx, sandbox, "exportFunction", SandboxExportFunction, 3, 0) ||
+ !JS_DefineFunction(cx, sandbox, "createObjectIn", SandboxCreateObjectIn, 2, 0) ||
+ !JS_DefineFunction(cx, sandbox, "cloneInto", SandboxCloneInto, 3, 0) ||
+ !JS_DefineFunction(cx, sandbox, "isProxy", SandboxIsProxy, 1, 0)))
+ return NS_ERROR_XPC_UNEXPECTED;
+
+ if (!options.globalProperties.DefineInSandbox(cx, sandbox))
+ return NS_ERROR_XPC_UNEXPECTED;
+
+#ifndef SPIDERMONKEY_PROMISE
+ // Promise is supposed to be part of ES, and therefore should appear on
+ // every global.
+ if (!dom::PromiseBinding::GetConstructorObject(cx))
+ return NS_ERROR_XPC_UNEXPECTED;
+#endif // SPIDERMONKEY_PROMISE
+ }
+
+ // We handle the case where the context isn't in a compartment for the
+ // benefit of InitSingletonScopes.
+ vp.setObject(*sandbox);
+ if (js::GetContextCompartment(cx) && !JS_WrapValue(cx, vp))
+ return NS_ERROR_UNEXPECTED;
+
+ // Set the location information for the new global, so that tools like
+ // about:memory may use that information
+ xpc::SetLocationForGlobal(sandbox, options.sandboxName);
+
+ xpc::SetSandboxMetadata(cx, sandbox, options.metadata);
+
+ JS_FireOnNewGlobalObject(cx, sandbox);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_utils_Sandbox::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
+ JSObject* objArg, const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ return CallOrConstruct(wrapper, cx, obj, args, _retval);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_utils_Sandbox::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
+ JSObject* objArg, const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ return CallOrConstruct(wrapper, cx, obj, args, _retval);
+}
+
+/*
+ * For sandbox constructor the first argument can be a URI string in which case
+ * we use the related Codebase Principal for the sandbox.
+ */
+bool
+ParsePrincipal(JSContext* cx, HandleString codebase, const PrincipalOriginAttributes& aAttrs,
+ nsIPrincipal** principal)
+{
+ MOZ_ASSERT(principal);
+ MOZ_ASSERT(codebase);
+ nsCOMPtr<nsIURI> uri;
+ nsAutoJSString codebaseStr;
+ NS_ENSURE_TRUE(codebaseStr.init(cx, codebase), false);
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), codebaseStr);
+ if (NS_FAILED(rv)) {
+ JS_ReportErrorASCII(cx, "Creating URI from string failed");
+ return false;
+ }
+
+ // We could allow passing in the app-id and browser-element info to the
+ // sandbox constructor. But creating a sandbox based on a string is a
+ // deprecated API so no need to add features to it.
+ nsCOMPtr<nsIPrincipal> prin =
+ BasePrincipal::CreateCodebasePrincipal(uri, aAttrs);
+ prin.forget(principal);
+
+ if (!*principal) {
+ JS_ReportErrorASCII(cx, "Creating Principal from URI failed");
+ return false;
+ }
+ return true;
+}
+
+/*
+ * For sandbox constructor the first argument can be a principal object or
+ * a script object principal (Document, Window).
+ */
+static bool
+GetPrincipalOrSOP(JSContext* cx, HandleObject from, nsISupports** out)
+{
+ MOZ_ASSERT(out);
+ *out = nullptr;
+
+ nsCOMPtr<nsISupports> native = xpc::UnwrapReflectorToISupports(from);
+
+ if (nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native)) {
+ sop.forget(out);
+ return true;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(native);
+ principal.forget(out);
+ NS_ENSURE_TRUE(*out, false);
+
+ return true;
+}
+
+/*
+ * The first parameter of the sandbox constructor might be an array of principals, either in string
+ * format or actual objects (see GetPrincipalOrSOP)
+ */
+static bool
+GetExpandedPrincipal(JSContext* cx, HandleObject arrayObj,
+ const SandboxOptions& options, nsIExpandedPrincipal** out)
+{
+ MOZ_ASSERT(out);
+ uint32_t length;
+
+ if (!JS_GetArrayLength(cx, arrayObj, &length))
+ return false;
+ if (!length) {
+ // We need a whitelist of principals or uri strings to create an
+ // expanded principal, if we got an empty array or something else
+ // report error.
+ JS_ReportErrorASCII(cx, "Expected an array of URI strings");
+ return false;
+ }
+
+ nsTArray< nsCOMPtr<nsIPrincipal> > allowedDomains(length);
+ allowedDomains.SetLength(length);
+
+ // If an originAttributes option has been specified, we will use that as the
+ // OriginAttribute of all of the string arguments passed to this function.
+ // Otherwise, we will use the OriginAttributes of a principal or SOP object
+ // in the array, if any. If no such object is present, and all we have are
+ // strings, then we will use a default OriginAttribute.
+ // Otherwise, we will use the origin attributes of the passed object(s). If
+ // more than one object is specified, we ensure that the OAs match.
+ Maybe<PrincipalOriginAttributes> attrs;
+ if (options.originAttributes) {
+ attrs.emplace();
+ JS::RootedValue val(cx, JS::ObjectValue(*options.originAttributes));
+ if (!attrs->Init(cx, val)) {
+ // The originAttributes option, if specified, must be valid!
+ JS_ReportErrorASCII(cx, "Expected a valid OriginAttributes object");
+ return false;
+ }
+ }
+
+ // Now we go over the array in two passes. In the first pass, we ignore
+ // strings, and only process objects. Assuming that no originAttributes
+ // option has been passed, if we encounter a principal or SOP object, we
+ // grab its OA and save it if it's the first OA encountered, otherwise
+ // check to make sure that it is the same as the OA found before.
+ // In the second pass, we ignore objects, and use the OA found in pass 0
+ // (or the previously computed OA if we have obtained it from the options)
+ // to construct codebase principals.
+ //
+ // The effective OA selected above will also be set as the OA of the
+ // expanded principal object.
+
+ // First pass:
+ for (uint32_t i = 0; i < length; ++i) {
+ RootedValue allowed(cx);
+ if (!JS_GetElement(cx, arrayObj, i, &allowed))
+ return false;
+
+ nsresult rv;
+ nsCOMPtr<nsIPrincipal> principal;
+ if (allowed.isObject()) {
+ // In case of object let's see if it's a Principal or a ScriptObjectPrincipal.
+ nsCOMPtr<nsISupports> prinOrSop;
+ RootedObject obj(cx, &allowed.toObject());
+ if (!GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop)))
+ return false;
+
+ nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(prinOrSop));
+ principal = do_QueryInterface(prinOrSop);
+ if (sop)
+ principal = sop->GetPrincipal();
+ NS_ENSURE_TRUE(principal, false);
+
+ if (!options.originAttributes) {
+ const PrincipalOriginAttributes prinAttrs =
+ BasePrincipal::Cast(principal)->OriginAttributesRef();
+ if (attrs.isNothing()) {
+ attrs.emplace(prinAttrs);
+ } else if (prinAttrs != attrs.ref()) {
+ // If attrs is from a previously encountered principal in the
+ // array, we need to ensure that it matches the OA of the
+ // principal we have here.
+ // If attrs comes from OriginAttributes, we don't need
+ // this check.
+ return false;
+ }
+ }
+
+ // We do not allow ExpandedPrincipals to contain any system principals.
+ bool isSystem;
+ rv = nsXPConnect::SecurityManager()->IsSystemPrincipal(principal, &isSystem);
+ NS_ENSURE_SUCCESS(rv, false);
+ if (isSystem) {
+ JS_ReportErrorASCII(cx, "System principal is not allowed in an expanded principal");
+ return false;
+ }
+ allowedDomains[i] = principal;
+ } else if (allowed.isString()) {
+ // Skip any string arguments - we handle them in the next pass.
+ } else {
+ // Don't know what this is.
+ return false;
+ }
+ }
+
+ if (attrs.isNothing()) {
+ // If no OriginAttributes was found in the first pass, fall back to a default one.
+ attrs.emplace();
+ }
+
+ // Second pass:
+ for (uint32_t i = 0; i < length; ++i) {
+ RootedValue allowed(cx);
+ if (!JS_GetElement(cx, arrayObj, i, &allowed))
+ return false;
+
+ nsCOMPtr<nsIPrincipal> principal;
+ if (allowed.isString()) {
+ // In case of string let's try to fetch a codebase principal from it.
+ RootedString str(cx, allowed.toString());
+
+ // attrs here is either a default OriginAttributes in case the
+ // originAttributes option isn't specified, and no object in the array
+ // provides a principal. Otherwise it's either the forced principal, or
+ // the principal found before, so we can use it here.
+ if (!ParsePrincipal(cx, str, attrs.ref(), getter_AddRefs(principal)))
+ return false;
+ NS_ENSURE_TRUE(principal, false);
+ allowedDomains[i] = principal;
+ } else {
+ MOZ_ASSERT(allowed.isObject());
+ }
+ }
+
+ nsCOMPtr<nsIExpandedPrincipal> result =
+ new nsExpandedPrincipal(allowedDomains, attrs.ref());
+ result.forget(out);
+ return true;
+}
+
+/*
+ * Helper that tries to get a property from the options object.
+ */
+bool
+OptionsBase::ParseValue(const char* name, MutableHandleValue prop, bool* aFound)
+{
+ bool found;
+ bool ok = JS_HasProperty(mCx, mObject, name, &found);
+ NS_ENSURE_TRUE(ok, false);
+
+ if (aFound)
+ *aFound = found;
+
+ if (!found)
+ return true;
+
+ return JS_GetProperty(mCx, mObject, name, prop);
+}
+
+/*
+ * Helper that tries to get a boolean property from the options object.
+ */
+bool
+OptionsBase::ParseBoolean(const char* name, bool* prop)
+{
+ MOZ_ASSERT(prop);
+ RootedValue value(mCx);
+ bool found;
+ bool ok = ParseValue(name, &value, &found);
+ NS_ENSURE_TRUE(ok, false);
+
+ if (!found)
+ return true;
+
+ if (!value.isBoolean()) {
+ JS_ReportErrorASCII(mCx, "Expected a boolean value for property %s", name);
+ return false;
+ }
+
+ *prop = value.toBoolean();
+ return true;
+}
+
+/*
+ * Helper that tries to get an object property from the options object.
+ */
+bool
+OptionsBase::ParseObject(const char* name, MutableHandleObject prop)
+{
+ RootedValue value(mCx);
+ bool found;
+ bool ok = ParseValue(name, &value, &found);
+ NS_ENSURE_TRUE(ok, false);
+
+ if (!found)
+ return true;
+
+ if (!value.isObject()) {
+ JS_ReportErrorASCII(mCx, "Expected an object value for property %s", name);
+ return false;
+ }
+ prop.set(&value.toObject());
+ return true;
+}
+
+/*
+ * Helper that tries to get an object property from the options object.
+ */
+bool
+OptionsBase::ParseJSString(const char* name, MutableHandleString prop)
+{
+ RootedValue value(mCx);
+ bool found;
+ bool ok = ParseValue(name, &value, &found);
+ NS_ENSURE_TRUE(ok, false);
+
+ if (!found)
+ return true;
+
+ if (!value.isString()) {
+ JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
+ return false;
+ }
+ prop.set(value.toString());
+ return true;
+}
+
+/*
+ * Helper that tries to get a string property from the options object.
+ */
+bool
+OptionsBase::ParseString(const char* name, nsCString& prop)
+{
+ RootedValue value(mCx);
+ bool found;
+ bool ok = ParseValue(name, &value, &found);
+ NS_ENSURE_TRUE(ok, false);
+
+ if (!found)
+ return true;
+
+ if (!value.isString()) {
+ JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
+ return false;
+ }
+
+ char* tmp = JS_EncodeString(mCx, value.toString());
+ NS_ENSURE_TRUE(tmp, false);
+ prop.Assign(tmp, strlen(tmp));
+ js_free(tmp);
+ return true;
+}
+
+/*
+ * Helper that tries to get a string property from the options object.
+ */
+bool
+OptionsBase::ParseString(const char* name, nsString& prop)
+{
+ RootedValue value(mCx);
+ bool found;
+ bool ok = ParseValue(name, &value, &found);
+ NS_ENSURE_TRUE(ok, false);
+
+ if (!found)
+ return true;
+
+ if (!value.isString()) {
+ JS_ReportErrorASCII(mCx, "Expected a string value for property %s", name);
+ return false;
+ }
+
+ nsAutoJSString strVal;
+ if (!strVal.init(mCx, value.toString()))
+ return false;
+
+ prop = strVal;
+ return true;
+}
+
+/*
+ * Helper that tries to get jsid property from the options object.
+ */
+bool
+OptionsBase::ParseId(const char* name, MutableHandleId prop)
+{
+ RootedValue value(mCx);
+ bool found;
+ bool ok = ParseValue(name, &value, &found);
+ NS_ENSURE_TRUE(ok, false);
+
+ if (!found)
+ return true;
+
+ return JS_ValueToId(mCx, value, prop);
+}
+
+/*
+ * Helper that tries to get a uint32_t property from the options object.
+ */
+bool
+OptionsBase::ParseUInt32(const char* name, uint32_t* prop)
+{
+ MOZ_ASSERT(prop);
+ RootedValue value(mCx);
+ bool found;
+ bool ok = ParseValue(name, &value, &found);
+ NS_ENSURE_TRUE(ok, false);
+
+ if (!found)
+ return true;
+
+ if(!JS::ToUint32(mCx, value, prop)) {
+ JS_ReportErrorASCII(mCx, "Expected a uint32_t value for property %s", name);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Helper that tries to get a list of DOM constructors and other helpers from the options object.
+ */
+bool
+SandboxOptions::ParseGlobalProperties()
+{
+ RootedValue value(mCx);
+ bool found;
+ bool ok = ParseValue("wantGlobalProperties", &value, &found);
+ NS_ENSURE_TRUE(ok, false);
+ if (!found)
+ return true;
+
+ if (!value.isObject()) {
+ JS_ReportErrorASCII(mCx, "Expected an array value for wantGlobalProperties");
+ return false;
+ }
+
+ RootedObject ctors(mCx, &value.toObject());
+ bool isArray;
+ if (!JS_IsArrayObject(mCx, ctors, &isArray))
+ return false;
+ if (!isArray) {
+ JS_ReportErrorASCII(mCx, "Expected an array value for wantGlobalProperties");
+ return false;
+ }
+
+ return globalProperties.Parse(mCx, ctors);
+}
+
+/*
+ * Helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options).
+ */
+bool
+SandboxOptions::Parse()
+{
+ /* All option names must be ASCII-only. */
+ bool ok = ParseObject("sandboxPrototype", &proto) &&
+ ParseBoolean("wantXrays", &wantXrays) &&
+ ParseBoolean("allowWaivers", &allowWaivers) &&
+ ParseBoolean("wantComponents", &wantComponents) &&
+ ParseBoolean("wantExportHelpers", &wantExportHelpers) &&
+ ParseBoolean("isWebExtensionContentScript", &isWebExtensionContentScript) &&
+ ParseBoolean("waiveInterposition", &waiveInterposition) &&
+ ParseString("sandboxName", sandboxName) &&
+ ParseObject("sameZoneAs", &sameZoneAs) &&
+ ParseBoolean("freshZone", &freshZone) &&
+ ParseBoolean("invisibleToDebugger", &invisibleToDebugger) &&
+ ParseBoolean("discardSource", &discardSource) &&
+ ParseJSString("addonId", &addonId) &&
+ ParseBoolean("writeToGlobalPrototype", &writeToGlobalPrototype) &&
+ ParseGlobalProperties() &&
+ ParseValue("metadata", &metadata) &&
+ ParseUInt32("userContextId", &userContextId) &&
+ ParseObject("originAttributes", &originAttributes);
+ if (!ok)
+ return false;
+
+ if (freshZone && sameZoneAs) {
+ JS_ReportErrorASCII(mCx, "Cannot use both sameZoneAs and freshZone");
+ return false;
+ }
+
+ return true;
+}
+
+static nsresult
+AssembleSandboxMemoryReporterName(JSContext* cx, nsCString& sandboxName)
+{
+ // Use a default name when the caller did not provide a sandboxName.
+ if (sandboxName.IsEmpty())
+ sandboxName = NS_LITERAL_CSTRING("[anonymous sandbox]");
+
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ // Get the xpconnect native call context.
+ nsAXPCNativeCallContext* cc = nullptr;
+ xpc->GetCurrentNativeCallContext(&cc);
+ NS_ENSURE_TRUE(cc, NS_ERROR_INVALID_ARG);
+
+ // Get the current source info from xpc.
+ nsCOMPtr<nsIStackFrame> frame;
+ xpc->GetCurrentJSStack(getter_AddRefs(frame));
+
+ // Append the caller's location information.
+ if (frame) {
+ nsString location;
+ int32_t lineNumber = 0;
+ frame->GetFilename(cx, location);
+ frame->GetLineNumber(cx, &lineNumber);
+
+ sandboxName.AppendLiteral(" (from: ");
+ sandboxName.Append(NS_ConvertUTF16toUTF8(location));
+ sandboxName.Append(':');
+ sandboxName.AppendInt(lineNumber);
+ sandboxName.Append(')');
+ }
+
+ return NS_OK;
+}
+
+// static
+nsresult
+nsXPCComponents_utils_Sandbox::CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, HandleObject obj,
+ const CallArgs& args, bool* _retval)
+{
+ if (args.length() < 1)
+ return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval);
+
+ nsresult rv;
+ bool ok = false;
+ bool calledWithOptions = args.length() > 1;
+ if (calledWithOptions && !args[1].isObject())
+ return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
+
+ RootedObject optionsObject(cx, calledWithOptions ? &args[1].toObject()
+ : nullptr);
+
+ SandboxOptions options(cx, optionsObject);
+ if (calledWithOptions && !options.Parse())
+ return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
+
+ // Make sure to set up principals on the sandbox before initing classes.
+ nsCOMPtr<nsIPrincipal> principal;
+ nsCOMPtr<nsIExpandedPrincipal> expanded;
+ nsCOMPtr<nsISupports> prinOrSop;
+
+ if (args[0].isString()) {
+ RootedString str(cx, args[0].toString());
+ PrincipalOriginAttributes attrs;
+ if (options.originAttributes) {
+ JS::RootedValue val(cx, JS::ObjectValue(*options.originAttributes));
+ if (!attrs.Init(cx, val)) {
+ // The originAttributes option, if specified, must be valid!
+ JS_ReportErrorASCII(cx, "Expected a valid OriginAttributes object");
+ return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
+ }
+ }
+ attrs.mUserContextId = options.userContextId;
+ ok = ParsePrincipal(cx, str, attrs, getter_AddRefs(principal));
+ prinOrSop = principal;
+ } else if (args[0].isObject()) {
+ RootedObject obj(cx, &args[0].toObject());
+ bool isArray;
+ if (!JS_IsArrayObject(cx, obj, &isArray)) {
+ ok = false;
+ } else if (isArray) {
+ if (options.userContextId != 0) {
+ // We don't support passing a userContextId with an array.
+ ok = false;
+ } else {
+ ok = GetExpandedPrincipal(cx, obj, options, getter_AddRefs(expanded));
+ prinOrSop = expanded;
+ }
+ } else {
+ ok = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
+ }
+ } else if (args[0].isNull()) {
+ // Null means that we just pass prinOrSop = nullptr, and get an
+ // nsNullPrincipal.
+ ok = true;
+ }
+
+ if (!ok)
+ return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
+
+
+ if (NS_FAILED(AssembleSandboxMemoryReporterName(cx, options.sandboxName)))
+ return ThrowAndFail(NS_ERROR_INVALID_ARG, cx, _retval);
+
+ if (options.metadata.isNullOrUndefined()) {
+ // If the caller is running in a sandbox, inherit.
+ RootedObject callerGlobal(cx, CurrentGlobalOrNull(cx));
+ if (IsSandbox(callerGlobal)) {
+ rv = GetSandboxMetadata(cx, callerGlobal, &options.metadata);
+ if (NS_WARN_IF(NS_FAILED(rv)))
+ return rv;
+ }
+ }
+
+ rv = CreateSandboxObject(cx, args.rval(), prinOrSop, options);
+
+ if (NS_FAILED(rv))
+ return ThrowAndFail(rv, cx, _retval);
+
+ // We have this crazy behavior where wantXrays=false also implies that the
+ // returned sandbox is implicitly waived. We've stopped advertising it, but
+ // keep supporting it for now.
+ if (!options.wantXrays && !xpc::WrapperFactory::WaiveXrayAndWrap(cx, args.rval()))
+ return NS_ERROR_UNEXPECTED;
+
+ *_retval = true;
+ return NS_OK;
+}
+
+nsresult
+xpc::EvalInSandbox(JSContext* cx, HandleObject sandboxArg, const nsAString& source,
+ const nsACString& filename, int32_t lineNo,
+ JSVersion jsVersion, MutableHandleValue rval)
+{
+ JS_AbortIfWrongThread(cx);
+ rval.set(UndefinedValue());
+
+ bool waiveXray = xpc::WrapperFactory::HasWaiveXrayFlag(sandboxArg);
+ RootedObject sandbox(cx, js::CheckedUnwrap(sandboxArg));
+ if (!sandbox || !IsSandbox(sandbox)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ nsIScriptObjectPrincipal* sop =
+ static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(sandbox));
+ MOZ_ASSERT(sop, "Invalid sandbox passed");
+ SandboxPrivate* priv = static_cast<SandboxPrivate*>(sop);
+ nsCOMPtr<nsIPrincipal> prin = sop->GetPrincipal();
+ NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE);
+
+ nsAutoCString filenameBuf;
+ if (!filename.IsVoid() && filename.Length() != 0) {
+ filenameBuf.Assign(filename);
+ } else {
+ // Default to the spec of the principal.
+ nsresult rv = nsJSPrincipals::get(prin)->GetScriptLocation(filenameBuf);
+ NS_ENSURE_SUCCESS(rv, rv);
+ lineNo = 1;
+ }
+
+ // We create a separate cx to do the sandbox evaluation. Scope it.
+ RootedValue v(cx, UndefinedValue());
+ RootedValue exn(cx, UndefinedValue());
+ bool ok = true;
+ {
+ // We're about to evaluate script, so make an AutoEntryScript.
+ // This is clearly Gecko-specific and not in any spec.
+ mozilla::dom::AutoEntryScript aes(priv, "XPConnect sandbox evaluation");
+ JSContext* sandcx = aes.cx();
+ JSAutoCompartment ac(sandcx, sandbox);
+
+ JS::CompileOptions options(sandcx);
+ options.setFileAndLine(filenameBuf.get(), lineNo)
+ .setVersion(jsVersion);
+ MOZ_ASSERT(JS_IsGlobalObject(sandbox));
+ ok = JS::Evaluate(sandcx, options,
+ PromiseFlatString(source).get(), source.Length(), &v);
+
+ // If the sandbox threw an exception, grab it off the context.
+ if (aes.HasException()) {
+ if (!aes.StealException(&exn)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ }
+
+ //
+ // Alright, we're back on the caller's cx. If an error occured, try to
+ // wrap and set the exception. Otherwise, wrap the return value.
+ //
+
+ if (!ok) {
+ // If we end up without an exception, it was probably due to OOM along
+ // the way, in which case we thow. Otherwise, wrap it.
+ if (exn.isUndefined() || !JS_WrapValue(cx, &exn))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ // Set the exception on our caller's cx.
+ JS_SetPendingException(cx, exn);
+ return NS_ERROR_FAILURE;
+ }
+
+ // Transitively apply Xray waivers if |sb| was waived.
+ if (waiveXray) {
+ ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v);
+ } else {
+ ok = JS_WrapValue(cx, &v);
+ }
+ NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+
+ // Whew!
+ rval.set(v);
+ return NS_OK;
+}
+
+nsresult
+xpc::GetSandboxAddonId(JSContext* cx, HandleObject sandbox, MutableHandleValue rval)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(IsSandbox(sandbox));
+
+ JSAddonId* id = JS::AddonIdOfObject(sandbox);
+ if (!id) {
+ rval.setNull();
+ return NS_OK;
+ }
+
+ JS::RootedValue idStr(cx, StringValue(JS::StringOfAddonId(id)));
+ if (!JS_WrapValue(cx, &idStr))
+ return NS_ERROR_UNEXPECTED;
+
+ rval.set(idStr);
+ return NS_OK;
+}
+
+nsresult
+xpc::GetSandboxMetadata(JSContext* cx, HandleObject sandbox, MutableHandleValue rval)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(IsSandbox(sandbox));
+
+ RootedValue metadata(cx);
+ {
+ JSAutoCompartment ac(cx, sandbox);
+ metadata = JS_GetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT);
+ }
+
+ if (!JS_WrapValue(cx, &metadata))
+ return NS_ERROR_UNEXPECTED;
+
+ rval.set(metadata);
+ return NS_OK;
+}
+
+nsresult
+xpc::SetSandboxMetadata(JSContext* cx, HandleObject sandbox, HandleValue metadataArg)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(IsSandbox(sandbox));
+
+ RootedValue metadata(cx);
+
+ JSAutoCompartment ac(cx, sandbox);
+ if (!JS_StructuredClone(cx, metadataArg, &metadata, nullptr, nullptr))
+ return NS_ERROR_UNEXPECTED;
+
+ JS_SetReservedSlot(sandbox, XPCONNECT_SANDBOX_CLASS_METADATA_SLOT, metadata);
+
+ return NS_OK;
+}
diff --git a/js/xpconnect/src/SandboxPrivate.h b/js/xpconnect/src/SandboxPrivate.h
new file mode 100644
index 000000000..9f84ec788
--- /dev/null
+++ b/js/xpconnect/src/SandboxPrivate.h
@@ -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__
diff --git a/js/xpconnect/src/XPCCallContext.cpp b/js/xpconnect/src/XPCCallContext.cpp
new file mode 100644
index 000000000..22081e3cf
--- /dev/null
+++ b/js/xpconnect/src/XPCCallContext.cpp
@@ -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;
+}
diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp
new file mode 100644
index 000000000..ca78234c9
--- /dev/null
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -0,0 +1,3563 @@
+/* -*- 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/. */
+
+/* The "Components" xpcom objects for JavaScript. */
+
+#include "xpcprivate.h"
+#include "xpc_make_class.h"
+#include "xpcIJSModuleLoader.h"
+#include "XPCJSWeakReference.h"
+#include "WrapperFactory.h"
+#include "nsJSUtils.h"
+#include "mozJSComponentLoader.h"
+#include "nsContentUtils.h"
+#include "nsCycleCollector.h"
+#include "jsfriendapi.h"
+#include "js/StructuredClone.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/Preferences.h"
+#include "nsJSEnvironment.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/XPTInterfaceInfoManager.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/DOMExceptionBinding.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/dom/WindowBinding.h"
+#include "nsZipArchive.h"
+#include "nsIDOMFileList.h"
+#include "nsWindowMemoryReporter.h"
+#include "nsDOMClassInfo.h"
+#include "ShimInterfaceInfo.h"
+#include "nsIAddonInterposition.h"
+#include "nsISimpleEnumerator.h"
+#include "nsPIDOMWindow.h"
+#include "nsGlobalWindow.h"
+
+using namespace mozilla;
+using namespace JS;
+using namespace js;
+using namespace xpc;
+using mozilla::dom::Exception;
+
+/***************************************************************************/
+// stuff used by all
+
+nsresult
+xpc::ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval)
+{
+ XPCThrower::Throw(errNum, cx);
+ *retval = false;
+ return NS_OK;
+}
+
+static bool
+JSValIsInterfaceOfType(JSContext* cx, HandleValue v, REFNSIID iid)
+{
+
+ nsCOMPtr<nsIXPConnectWrappedNative> wn;
+ nsCOMPtr<nsISupports> iface;
+
+ if (v.isPrimitive())
+ return false;
+
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ RootedObject obj(cx, &v.toObject());
+ return NS_SUCCEEDED(xpc->GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wn))) && wn &&
+ NS_SUCCEEDED(wn->Native()->QueryInterface(iid, getter_AddRefs(iface))) && iface;
+}
+
+char*
+xpc::CloneAllAccess()
+{
+ static const char allAccess[] = "AllAccess";
+ return (char*)nsMemory::Clone(allAccess, sizeof(allAccess));
+}
+
+char*
+xpc::CheckAccessList(const char16_t* wideName, const char* const list[])
+{
+ nsAutoCString asciiName;
+ CopyUTF16toUTF8(nsDependentString(wideName), asciiName);
+
+ for (const char* const* p = list; *p; p++)
+ if (!strcmp(*p, asciiName.get()))
+ return CloneAllAccess();
+
+ return nullptr;
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+
+class nsXPCComponents_Interfaces final :
+ public nsIXPCComponents_Interfaces,
+ public nsIXPCScriptable,
+ public nsIClassInfo
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTS_INTERFACES
+ NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSICLASSINFO
+
+public:
+ nsXPCComponents_Interfaces();
+
+private:
+ virtual ~nsXPCComponents_Interfaces();
+
+ nsCOMArray<nsIInterfaceInfo> mInterfaces;
+};
+
+NS_IMETHODIMP
+nsXPCComponents_Interfaces::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(nsIXPCComponents_Interfaces)
+ PUSH_IID(nsIXPCScriptable)
+#undef PUSH_IID
+
+ return NS_OK;
+oom:
+ while (index)
+ free(array[--index]);
+ free(array);
+ *aArray = nullptr;
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Interfaces::GetScriptableHelper(nsIXPCScriptable** retval)
+{
+ *retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Interfaces::GetContractID(char * *aContractID)
+{
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Interfaces::GetClassDescription(char * *aClassDescription)
+{
+ static const char classDescription[] = "XPCComponents_Interfaces";
+ *aClassDescription = (char*)nsMemory::Clone(classDescription, sizeof(classDescription));
+ return *aClassDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Interfaces::GetClassID(nsCID * *aClassID)
+{
+ *aClassID = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Interfaces::GetFlags(uint32_t* aFlags)
+{
+ // Mark ourselves as a DOM object so that instances may be created in
+ // unprivileged scopes.
+ *aFlags = nsIClassInfo::DOM_OBJECT;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Interfaces::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsXPCComponents_Interfaces::nsXPCComponents_Interfaces()
+{
+}
+
+nsXPCComponents_Interfaces::~nsXPCComponents_Interfaces()
+{
+ // empty
+}
+
+
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents_Interfaces)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_Interfaces)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Interfaces)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCComponents_Interfaces)
+NS_IMPL_RELEASE(nsXPCComponents_Interfaces)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsXPCComponents_Interfaces
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Interfaces"
+#define XPC_MAP_WANT_RESOLVE
+#define XPC_MAP_WANT_NEWENUMERATE
+#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#include "xpc_map_end.h" /* This will #undef the above */
+
+
+NS_IMETHODIMP
+nsXPCComponents_Interfaces::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* obj,
+ JS::AutoIdVector& properties,
+ bool* _retval)
+{
+
+ // Lazily init the list of interfaces when someone tries to
+ // enumerate them.
+ if (mInterfaces.IsEmpty()) {
+ XPTInterfaceInfoManager::GetSingleton()->
+ GetScriptableInterfaces(mInterfaces);
+ }
+
+ if (!properties.reserve(mInterfaces.Length())) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ for (uint32_t index = 0; index < mInterfaces.Length(); index++) {
+ nsIInterfaceInfo* interface = mInterfaces.SafeElementAt(index);
+ if (!interface)
+ continue;
+
+ const char* name;
+ if (NS_SUCCEEDED(interface->GetNameShared(&name)) && name) {
+ RootedString idstr(cx, JS_NewStringCopyZ(cx, name));
+ if (!idstr) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ RootedId id(cx);
+ if (!JS_StringToId(cx, idstr, &id)) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ properties.infallibleAppend(id);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Interfaces::Resolve(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* objArg,
+ jsid idArg, bool* resolvedp,
+ bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ RootedId id(cx, idArg);
+
+ if (!JSID_IS_STRING(id))
+ return NS_OK;
+
+ JSAutoByteString name;
+ RootedString str(cx, JSID_TO_STRING(id));
+
+ // we only allow interfaces by name here
+ if (name.encodeLatin1(cx, str) && name.ptr()[0] != '{') {
+ nsCOMPtr<nsIInterfaceInfo> info =
+ ShimInterfaceInfo::MaybeConstruct(name.ptr(), cx);
+ if (!info) {
+ XPTInterfaceInfoManager::GetSingleton()->
+ GetInfoForName(name.ptr(), getter_AddRefs(info));
+ }
+ if (!info)
+ return NS_OK;
+
+ nsCOMPtr<nsIJSIID> nsid = nsJSIID::NewID(info);
+
+ if (nsid) {
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ RootedObject idobj(cx);
+ if (NS_SUCCEEDED(xpc->WrapNative(cx, obj,
+ static_cast<nsIJSIID*>(nsid),
+ NS_GET_IID(nsIJSIID),
+ idobj.address()))) {
+ if (idobj) {
+ *resolvedp = true;
+ *_retval = JS_DefinePropertyById(cx, obj, id, idobj,
+ JSPROP_ENUMERATE |
+ JSPROP_READONLY |
+ JSPROP_PERMANENT |
+ JSPROP_RESOLVING);
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class nsXPCComponents_InterfacesByID final :
+ public nsIXPCComponents_InterfacesByID,
+ public nsIXPCScriptable,
+ public nsIClassInfo
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTS_INTERFACESBYID
+ NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSICLASSINFO
+
+public:
+ nsXPCComponents_InterfacesByID();
+
+private:
+ virtual ~nsXPCComponents_InterfacesByID();
+
+ nsCOMArray<nsIInterfaceInfo> mInterfaces;
+};
+
+/***************************************************************************/
+NS_IMETHODIMP
+nsXPCComponents_InterfacesByID::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(nsIXPCComponents_InterfacesByID)
+ PUSH_IID(nsIXPCScriptable)
+#undef PUSH_IID
+
+ return NS_OK;
+oom:
+ while (index)
+ free(array[--index]);
+ free(array);
+ *aArray = nullptr;
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_InterfacesByID::GetScriptableHelper(nsIXPCScriptable** retval)
+{
+ *retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_InterfacesByID::GetContractID(char * *aContractID)
+{
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_InterfacesByID::GetClassDescription(char * *aClassDescription)
+{
+ static const char classDescription[] = "XPCComponents_InterfacesByID";
+ *aClassDescription = (char*)nsMemory::Clone(classDescription, sizeof(classDescription));
+ return *aClassDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_InterfacesByID::GetClassID(nsCID * *aClassID)
+{
+ *aClassID = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_InterfacesByID::GetFlags(uint32_t* aFlags)
+{
+ // Mark ourselves as a DOM object so that instances may be created in
+ // unprivileged scopes.
+ *aFlags = nsIClassInfo::DOM_OBJECT;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_InterfacesByID::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsXPCComponents_InterfacesByID::nsXPCComponents_InterfacesByID()
+{
+}
+
+nsXPCComponents_InterfacesByID::~nsXPCComponents_InterfacesByID()
+{
+ // empty
+}
+
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents_InterfacesByID)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_InterfacesByID)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_InterfacesByID)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCComponents_InterfacesByID)
+NS_IMPL_RELEASE(nsXPCComponents_InterfacesByID)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsXPCComponents_InterfacesByID
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_InterfacesByID"
+#define XPC_MAP_WANT_RESOLVE
+#define XPC_MAP_WANT_NEWENUMERATE
+#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#include "xpc_map_end.h" /* This will #undef the above */
+
+NS_IMETHODIMP
+nsXPCComponents_InterfacesByID::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* obj,
+ JS::AutoIdVector& properties,
+ bool* _retval)
+{
+
+ if (mInterfaces.IsEmpty()) {
+ XPTInterfaceInfoManager::GetSingleton()->
+ GetScriptableInterfaces(mInterfaces);
+ }
+
+ if (!properties.reserve(mInterfaces.Length())) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ for (uint32_t index = 0; index < mInterfaces.Length(); index++) {
+ nsIInterfaceInfo* interface = mInterfaces.SafeElementAt(index);
+ if (!interface)
+ continue;
+
+ nsIID const* iid;
+ if (NS_SUCCEEDED(interface->GetIIDShared(&iid))) {
+ char idstr[NSID_LENGTH];
+ iid->ToProvidedString(idstr);
+ RootedString jsstr(cx, JS_NewStringCopyZ(cx, idstr));
+ if (!jsstr) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ RootedId id(cx);
+ if (!JS_StringToId(cx, jsstr, &id)) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ properties.infallibleAppend(id);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_InterfacesByID::Resolve(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* objArg,
+ jsid idArg, bool* resolvedp,
+ bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ RootedId id(cx, idArg);
+
+ if (!JSID_IS_STRING(id))
+ return NS_OK;
+
+ RootedString str(cx, JSID_TO_STRING(id));
+ if (38 != JS_GetStringLength(str))
+ return NS_OK;
+
+ JSAutoByteString utf8str;
+ if (utf8str.encodeUtf8(cx, str)) {
+ nsID iid;
+ if (!iid.Parse(utf8str.ptr()))
+ return NS_OK;
+
+ nsCOMPtr<nsIInterfaceInfo> info;
+ XPTInterfaceInfoManager::GetSingleton()->
+ GetInfoForIID(&iid, getter_AddRefs(info));
+ if (!info)
+ return NS_OK;
+
+ nsCOMPtr<nsIJSIID> nsid = nsJSIID::NewID(info);
+
+ if (!nsid)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ RootedObject idobj(cx);
+ if (NS_SUCCEEDED(xpc->WrapNative(cx, obj,
+ static_cast<nsIJSIID*>(nsid),
+ NS_GET_IID(nsIJSIID),
+ idobj.address()))) {
+ if (idobj) {
+ *resolvedp = true;
+ *_retval =
+ JS_DefinePropertyById(cx, obj, id, idobj,
+ JSPROP_ENUMERATE |
+ JSPROP_READONLY |
+ JSPROP_PERMANENT |
+ JSPROP_RESOLVING);
+ }
+ }
+ }
+ return NS_OK;
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+
+
+class nsXPCComponents_Classes final :
+ public nsIXPCComponents_Classes,
+ public nsIXPCScriptable,
+ public nsIClassInfo
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTS_CLASSES
+ NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSICLASSINFO
+
+public:
+ nsXPCComponents_Classes();
+
+private:
+ virtual ~nsXPCComponents_Classes();
+};
+
+/***************************************************************************/
+NS_IMETHODIMP
+nsXPCComponents_Classes::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(nsIXPCComponents_Classes)
+ PUSH_IID(nsIXPCScriptable)
+#undef PUSH_IID
+
+ return NS_OK;
+oom:
+ while (index)
+ free(array[--index]);
+ free(array);
+ *aArray = nullptr;
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Classes::GetScriptableHelper(nsIXPCScriptable** retval)
+{
+ *retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Classes::GetContractID(char * *aContractID)
+{
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Classes::GetClassDescription(char * *aClassDescription)
+{
+ static const char classDescription[] = "XPCComponents_Classes";
+ *aClassDescription = (char*)nsMemory::Clone(classDescription, sizeof(classDescription));
+ return *aClassDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Classes::GetClassID(nsCID * *aClassID)
+{
+ *aClassID = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Classes::GetFlags(uint32_t* aFlags)
+{
+ *aFlags = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Classes::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsXPCComponents_Classes::nsXPCComponents_Classes()
+{
+}
+
+nsXPCComponents_Classes::~nsXPCComponents_Classes()
+{
+ // empty
+}
+
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents_Classes)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_Classes)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Classes)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCComponents_Classes)
+NS_IMPL_RELEASE(nsXPCComponents_Classes)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsXPCComponents_Classes
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Classes"
+#define XPC_MAP_WANT_RESOLVE
+#define XPC_MAP_WANT_NEWENUMERATE
+#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#include "xpc_map_end.h" /* This will #undef the above */
+
+NS_IMETHODIMP
+nsXPCComponents_Classes::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* obj,
+ JS::AutoIdVector& properties,
+ bool* _retval)
+{
+ nsCOMPtr<nsIComponentRegistrar> compMgr;
+ if (NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) || !compMgr)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsISimpleEnumerator> e;
+ if (NS_FAILED(compMgr->EnumerateContractIDs(getter_AddRefs(e))) || !e)
+ return NS_ERROR_UNEXPECTED;
+
+ bool hasMore;
+ nsCOMPtr<nsISupports> isup;
+ while(NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore &&
+ NS_SUCCEEDED(e->GetNext(getter_AddRefs(isup))) && isup) {
+ nsCOMPtr<nsISupportsCString> holder(do_QueryInterface(isup));
+ if (!holder)
+ continue;
+
+ nsAutoCString name;
+ if (NS_SUCCEEDED(holder->GetData(name))) {
+ RootedString idstr(cx, JS_NewStringCopyN(cx, name.get(), name.Length()));
+ if (!idstr) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ RootedId id(cx);
+ if (!JS_StringToId(cx, idstr, &id)) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ if (!properties.append(id)) {
+ *_retval = false;
+ return NS_OK;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Classes::Resolve(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* objArg,
+ jsid idArg, bool* resolvedp,
+ bool* _retval)
+
+{
+ RootedId id(cx, idArg);
+ RootedObject obj(cx, objArg);
+
+ JSAutoByteString name;
+ if (JSID_IS_STRING(id) &&
+ name.encodeLatin1(cx, JSID_TO_STRING(id)) &&
+ name.ptr()[0] != '{') { // we only allow contractids here
+ nsCOMPtr<nsIJSCID> nsid = nsJSCID::NewID(name.ptr());
+ if (nsid) {
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ RootedObject idobj(cx);
+ if (NS_SUCCEEDED(xpc->WrapNative(cx, obj,
+ static_cast<nsIJSCID*>(nsid),
+ NS_GET_IID(nsIJSCID),
+ idobj.address()))) {
+ if (idobj) {
+ *resolvedp = true;
+ *_retval = JS_DefinePropertyById(cx, obj, id, idobj,
+ JSPROP_ENUMERATE |
+ JSPROP_READONLY |
+ JSPROP_PERMANENT |
+ JSPROP_RESOLVING);
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+class nsXPCComponents_ClassesByID final :
+ public nsIXPCComponents_ClassesByID,
+ public nsIXPCScriptable,
+ public nsIClassInfo
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTS_CLASSESBYID
+ NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSICLASSINFO
+
+public:
+ nsXPCComponents_ClassesByID();
+
+private:
+ virtual ~nsXPCComponents_ClassesByID();
+};
+
+/***************************************************************************/
+NS_IMETHODIMP
+nsXPCComponents_ClassesByID::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(nsIXPCComponents_ClassesByID)
+ PUSH_IID(nsIXPCScriptable)
+#undef PUSH_IID
+
+ return NS_OK;
+oom:
+ while (index)
+ free(array[--index]);
+ free(array);
+ *aArray = nullptr;
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ClassesByID::GetScriptableHelper(nsIXPCScriptable** retval)
+{
+ *retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ClassesByID::GetContractID(char * *aContractID)
+{
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ClassesByID::GetClassDescription(char * *aClassDescription)
+{
+ static const char classDescription[] = "XPCComponents_ClassesByID";
+ *aClassDescription = (char*)nsMemory::Clone(classDescription, sizeof(classDescription));
+ return *aClassDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ClassesByID::GetClassID(nsCID * *aClassID)
+{
+ *aClassID = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ClassesByID::GetFlags(uint32_t* aFlags)
+{
+ *aFlags = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ClassesByID::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsXPCComponents_ClassesByID::nsXPCComponents_ClassesByID()
+{
+}
+
+nsXPCComponents_ClassesByID::~nsXPCComponents_ClassesByID()
+{
+ // empty
+}
+
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents_ClassesByID)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_ClassesByID)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_ClassesByID)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCComponents_ClassesByID)
+NS_IMPL_RELEASE(nsXPCComponents_ClassesByID)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsXPCComponents_ClassesByID
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_ClassesByID"
+#define XPC_MAP_WANT_RESOLVE
+#define XPC_MAP_WANT_NEWENUMERATE
+#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#include "xpc_map_end.h" /* This will #undef the above */
+
+NS_IMETHODIMP
+nsXPCComponents_ClassesByID::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* obj,
+ JS::AutoIdVector& properties,
+ bool* _retval)
+{
+
+ nsCOMPtr<nsIComponentRegistrar> compMgr;
+ if (NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) || !compMgr)
+ return NS_ERROR_UNEXPECTED;
+
+ nsISimpleEnumerator* e;
+ if (NS_FAILED(compMgr->EnumerateCIDs(&e)) || !e)
+ return NS_ERROR_UNEXPECTED;
+
+ bool hasMore;
+ nsCOMPtr<nsISupports> isup;
+ while(NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore &&
+ NS_SUCCEEDED(e->GetNext(getter_AddRefs(isup))) && isup) {
+ nsCOMPtr<nsISupportsID> holder(do_QueryInterface(isup));
+ if (!holder)
+ continue;
+
+ char* name;
+ if (NS_SUCCEEDED(holder->ToString(&name)) && name) {
+ RootedString idstr(cx, JS_NewStringCopyZ(cx, name));
+ if (!idstr) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ RootedId id(cx);
+ if (!JS_StringToId(cx, idstr, &id)) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ if (!properties.append(id)) {
+ *_retval = false;
+ return NS_OK;
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+static bool
+IsRegisteredCLSID(const char* str)
+{
+ bool registered;
+ nsID id;
+
+ if (!id.Parse(str))
+ return false;
+
+ nsCOMPtr<nsIComponentRegistrar> compMgr;
+ if (NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) || !compMgr ||
+ NS_FAILED(compMgr->IsCIDRegistered(id, &registered)))
+ return false;
+
+ return registered;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ClassesByID::Resolve(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* objArg,
+ jsid idArg, bool* resolvedp,
+ bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ RootedId id(cx, idArg);
+
+ if (!JSID_IS_STRING(id))
+ return NS_OK;
+
+ JSAutoByteString name;
+ RootedString str(cx, JSID_TO_STRING(id));
+ if (name.encodeLatin1(cx, str) && name.ptr()[0] == '{' &&
+ IsRegisteredCLSID(name.ptr())) // we only allow canonical CLSIDs here
+ {
+ nsCOMPtr<nsIJSCID> nsid = nsJSCID::NewID(name.ptr());
+ if (nsid) {
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ RootedObject idobj(cx);
+ if (NS_SUCCEEDED(xpc->WrapNative(cx, obj,
+ static_cast<nsIJSCID*>(nsid),
+ NS_GET_IID(nsIJSCID),
+ idobj.address()))) {
+ if (idobj) {
+ *resolvedp = true;
+ *_retval = JS_DefinePropertyById(cx, obj, id, idobj,
+ JSPROP_ENUMERATE |
+ JSPROP_READONLY |
+ JSPROP_PERMANENT |
+ JSPROP_RESOLVING);
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
+
+
+/***************************************************************************/
+
+// Currently the possible results do not change at runtime, so they are only
+// cached once (unlike ContractIDs, CLSIDs, and IIDs)
+
+class nsXPCComponents_Results final :
+ public nsIXPCComponents_Results,
+ public nsIXPCScriptable,
+ public nsIClassInfo
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTS_RESULTS
+ NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSICLASSINFO
+
+public:
+ nsXPCComponents_Results();
+
+private:
+ virtual ~nsXPCComponents_Results();
+};
+
+/***************************************************************************/
+NS_IMETHODIMP
+nsXPCComponents_Results::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(nsIXPCComponents_Results)
+ PUSH_IID(nsIXPCScriptable)
+#undef PUSH_IID
+
+ return NS_OK;
+oom:
+ while (index)
+ free(array[--index]);
+ free(array);
+ *aArray = nullptr;
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Results::GetScriptableHelper(nsIXPCScriptable** retval)
+{
+ *retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Results::GetContractID(char * *aContractID)
+{
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Results::GetClassDescription(char * *aClassDescription)
+{
+ static const char classDescription[] = "XPCComponents_Results";
+ *aClassDescription = (char*)nsMemory::Clone(classDescription, sizeof(classDescription));
+ return *aClassDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Results::GetClassID(nsCID * *aClassID)
+{
+ *aClassID = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Results::GetFlags(uint32_t* aFlags)
+{
+ // Mark ourselves as a DOM object so that instances may be created in
+ // unprivileged scopes.
+ *aFlags = nsIClassInfo::DOM_OBJECT;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Results::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsXPCComponents_Results::nsXPCComponents_Results()
+{
+}
+
+nsXPCComponents_Results::~nsXPCComponents_Results()
+{
+ // empty
+}
+
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents_Results)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_Results)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Results)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCComponents_Results)
+NS_IMPL_RELEASE(nsXPCComponents_Results)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsXPCComponents_Results
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Results"
+#define XPC_MAP_WANT_RESOLVE
+#define XPC_MAP_WANT_NEWENUMERATE
+#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#include "xpc_map_end.h" /* This will #undef the above */
+
+NS_IMETHODIMP
+nsXPCComponents_Results::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* obj,
+ JS::AutoIdVector& properties,
+ bool* _retval)
+{
+ const char* name;
+ const void* iter = nullptr;
+ while (nsXPCException::IterateNSResults(nullptr, &name, nullptr, &iter)) {
+ RootedString idstr(cx, JS_NewStringCopyZ(cx, name));
+ if (!idstr) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ RootedId id(cx);
+ if (!JS_StringToId(cx, idstr, &id)) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ if (!properties.append(id)) {
+ *_retval = false;
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Results::Resolve(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* objArg,
+ jsid idArg, bool* resolvedp,
+ bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ RootedId id(cx, idArg);
+ JSAutoByteString name;
+
+ if (JSID_IS_STRING(id) && name.encodeLatin1(cx, JSID_TO_STRING(id))) {
+ const char* rv_name;
+ const void* iter = nullptr;
+ nsresult rv;
+ while (nsXPCException::IterateNSResults(&rv, &rv_name, nullptr, &iter)) {
+ if (!strcmp(name.ptr(), rv_name)) {
+ *resolvedp = true;
+ if (!JS_DefinePropertyById(cx, obj, id, (uint32_t)rv,
+ JSPROP_ENUMERATE |
+ JSPROP_READONLY |
+ JSPROP_PERMANENT |
+ JSPROP_RESOLVING)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
+
+/***************************************************************************/
+// JavaScript Constructor for nsIJSID objects (Components.ID)
+
+class nsXPCComponents_ID final :
+ public nsIXPCComponents_ID,
+ public nsIXPCScriptable,
+ public nsIClassInfo
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTS_ID
+ NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSICLASSINFO
+
+
+public:
+ nsXPCComponents_ID();
+
+private:
+ virtual ~nsXPCComponents_ID();
+ static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, HandleObject obj,
+ const CallArgs& args, bool* _retval);
+};
+
+/***************************************************************************/
+NS_IMETHODIMP
+nsXPCComponents_ID::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(nsIXPCComponents_ID)
+ PUSH_IID(nsIXPCScriptable)
+#undef PUSH_IID
+
+ return NS_OK;
+oom:
+ while (index)
+ free(array[--index]);
+ free(array);
+ *aArray = nullptr;
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ID::GetScriptableHelper(nsIXPCScriptable** retval)
+{
+ *retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ID::GetContractID(char * *aContractID)
+{
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ID::GetClassDescription(char * *aClassDescription)
+{
+ static const char classDescription[] = "XPCComponents_ID";
+ *aClassDescription = (char*)nsMemory::Clone(classDescription, sizeof(classDescription));
+ return *aClassDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ID::GetClassID(nsCID * *aClassID)
+{
+ *aClassID = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ID::GetFlags(uint32_t* aFlags)
+{
+ *aFlags = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ID::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsXPCComponents_ID::nsXPCComponents_ID()
+{
+}
+
+nsXPCComponents_ID::~nsXPCComponents_ID()
+{
+ // empty
+}
+
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents_ID)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_ID)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_ID)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCComponents_ID)
+NS_IMPL_RELEASE(nsXPCComponents_ID)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsXPCComponents_ID
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_ID"
+#define XPC_MAP_WANT_CALL
+#define XPC_MAP_WANT_CONSTRUCT
+#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 */
+
+
+NS_IMETHODIMP
+nsXPCComponents_ID::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg,
+ const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ return CallOrConstruct(wrapper, cx, obj, args, _retval);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ID::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg,
+ const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ return CallOrConstruct(wrapper, cx, obj, args, _retval);
+}
+
+// static
+nsresult
+nsXPCComponents_ID::CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, HandleObject obj,
+ const CallArgs& args, bool* _retval)
+{
+ // make sure we have at least one arg
+
+ if (args.length() < 1)
+ return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval);
+
+ // Do the security check if necessary
+
+ if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, nsJSID::GetCID()))) {
+ // the security manager vetoed. It should have set an exception.
+ *_retval = false;
+ return NS_OK;
+ }
+
+ // convert the first argument into a string and see if it looks like an id
+
+ JSString* jsstr;
+ JSAutoByteString bytes;
+ nsID id;
+
+ if (!(jsstr = ToString(cx, args[0])) ||
+ !bytes.encodeLatin1(cx, jsstr) ||
+ !id.Parse(bytes.ptr())) {
+ return ThrowAndFail(NS_ERROR_XPC_BAD_ID_STRING, cx, _retval);
+ }
+
+ // make the new object and return it.
+
+ JSObject* newobj = xpc_NewIDObject(cx, obj, id);
+ if (!newobj)
+ return NS_ERROR_UNEXPECTED;
+
+ args.rval().setObject(*newobj);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_ID::HasInstance(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, JSObject* obj,
+ HandleValue val, bool* bp, bool* _retval)
+{
+ if (bp)
+ *bp = JSValIsInterfaceOfType(cx, val, NS_GET_IID(nsIJSID));
+ return NS_OK;
+}
+
+/***************************************************************************/
+// JavaScript Constructor for nsIXPCException objects (Components.Exception)
+
+class nsXPCComponents_Exception final :
+ public nsIXPCComponents_Exception,
+ public nsIXPCScriptable,
+ public nsIClassInfo
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTS_EXCEPTION
+ NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSICLASSINFO
+
+
+public:
+ nsXPCComponents_Exception();
+
+private:
+ virtual ~nsXPCComponents_Exception();
+ static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, HandleObject obj,
+ const CallArgs& args, bool* _retval);
+};
+
+/***************************************************************************/
+NS_IMETHODIMP
+nsXPCComponents_Exception::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(nsIXPCComponents_Exception)
+ PUSH_IID(nsIXPCScriptable)
+#undef PUSH_IID
+
+ return NS_OK;
+oom:
+ while (index)
+ free(array[--index]);
+ free(array);
+ *aArray = nullptr;
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Exception::GetScriptableHelper(nsIXPCScriptable** retval)
+{
+ *retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Exception::GetContractID(char * *aContractID)
+{
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Exception::GetClassDescription(char * *aClassDescription)
+{
+ static const char classDescription[] = "XPCComponents_Exception";
+ *aClassDescription = (char*)nsMemory::Clone(classDescription, sizeof(classDescription));
+ return *aClassDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Exception::GetClassID(nsCID * *aClassID)
+{
+ *aClassID = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Exception::GetFlags(uint32_t* aFlags)
+{
+ *aFlags = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Exception::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsXPCComponents_Exception::nsXPCComponents_Exception()
+{
+}
+
+nsXPCComponents_Exception::~nsXPCComponents_Exception()
+{
+ // empty
+}
+
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents_Exception)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_Exception)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Exception)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCComponents_Exception)
+NS_IMPL_RELEASE(nsXPCComponents_Exception)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsXPCComponents_Exception
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Exception"
+#define XPC_MAP_WANT_CALL
+#define XPC_MAP_WANT_CONSTRUCT
+#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 */
+
+
+NS_IMETHODIMP
+nsXPCComponents_Exception::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg,
+ const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ return CallOrConstruct(wrapper, cx, obj, args, _retval);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Exception::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
+ JSObject* objArg, const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ return CallOrConstruct(wrapper, cx, obj, args, _retval);
+}
+
+struct MOZ_STACK_CLASS ExceptionArgParser
+{
+ ExceptionArgParser(JSContext* context,
+ nsXPConnect* xpconnect)
+ : eMsg("exception")
+ , eResult(NS_ERROR_FAILURE)
+ , cx(context)
+ , xpc(xpconnect)
+ {}
+
+ // Public exception parameter values. During construction, these are
+ // initialized to the appropriate defaults.
+ const char* eMsg;
+ nsresult eResult;
+ nsCOMPtr<nsIStackFrame> eStack;
+ nsCOMPtr<nsISupports> eData;
+
+ // Parse the constructor arguments into the above |eFoo| parameter values.
+ bool parse(const CallArgs& args) {
+ /*
+ * The Components.Exception takes a series of arguments, all of them
+ * optional:
+ *
+ * Argument 0: Exception message (defaults to 'exception').
+ * Argument 1: Result code (defaults to NS_ERROR_FAILURE) _or_ options
+ * object (see below).
+ * Argument 2: Stack (defaults to the current stack, which we trigger
+ * by leaving this nullptr in the parser).
+ * Argument 3: Optional user data (defaults to nullptr).
+ *
+ * To dig our way out of this clunky API, we now support passing an
+ * options object as the second parameter (as opposed to a result code).
+ * If this is the case, all subsequent arguments are ignored, and the
+ * following properties are parsed out of the object (using the
+ * associated default if the property does not exist):
+ *
+ * result: Result code (see argument 1).
+ * stack: Call stack (see argument 2).
+ * data: User data (see argument 3).
+ */
+ if (args.length() > 0 && !parseMessage(args[0]))
+ return false;
+ if (args.length() > 1) {
+ if (args[1].isObject()) {
+ RootedObject obj(cx, &args[1].toObject());
+ return parseOptionsObject(obj);
+ }
+ if (!parseResult(args[1]))
+ return false;
+ }
+ if (args.length() > 2) {
+ if (!parseStack(args[2]))
+ return false;
+ }
+ if (args.length() > 3) {
+ if (!parseData(args[3]))
+ return false;
+ }
+ return true;
+ }
+
+ protected:
+
+ /*
+ * Parsing helpers.
+ */
+
+ bool parseMessage(HandleValue v) {
+ JSString* str = ToString(cx, v);
+ if (!str)
+ return false;
+ eMsg = messageBytes.encodeLatin1(cx, str);
+ return !!eMsg;
+ }
+
+ bool parseResult(HandleValue v) {
+ return JS::ToUint32(cx, v, (uint32_t*) &eResult);
+ }
+
+ bool parseStack(HandleValue v) {
+ if (!v.isObject()) {
+ // eStack has already been initialized to null, which is what we want
+ // for any non-object values (including null).
+ return true;
+ }
+
+ return NS_SUCCEEDED(xpc->WrapJS(cx, &v.toObject(),
+ NS_GET_IID(nsIStackFrame),
+ getter_AddRefs(eStack)));
+ }
+
+ bool parseData(HandleValue v) {
+ if (!v.isObject()) {
+ // eData has already been initialized to null, which is what we want
+ // for any non-object values (including null).
+ return true;
+ }
+
+ return NS_SUCCEEDED(xpc->WrapJS(cx, &v.toObject(),
+ NS_GET_IID(nsISupports),
+ getter_AddRefs(eData)));
+ }
+
+ bool parseOptionsObject(HandleObject obj) {
+ RootedValue v(cx);
+
+ if (!getOption(obj, "result", &v) ||
+ (!v.isUndefined() && !parseResult(v)))
+ return false;
+
+ if (!getOption(obj, "stack", &v) ||
+ (!v.isUndefined() && !parseStack(v)))
+ return false;
+
+ if (!getOption(obj, "data", &v) ||
+ (!v.isUndefined() && !parseData(v)))
+ return false;
+
+ return true;
+ }
+
+ bool getOption(HandleObject obj, const char* name, MutableHandleValue rv) {
+ // Look for the property.
+ bool found;
+ if (!JS_HasProperty(cx, obj, name, &found))
+ return false;
+
+ // If it wasn't found, indicate with undefined.
+ if (!found) {
+ rv.setUndefined();
+ return true;
+ }
+
+ // Get the property.
+ return JS_GetProperty(cx, obj, name, rv);
+ }
+
+ /*
+ * Internal data members.
+ */
+
+ // If there's a non-default exception string, hold onto the allocated bytes.
+ JSAutoByteString messageBytes;
+
+ // Various bits and pieces that are helpful to have around.
+ JSContext* cx;
+ nsXPConnect* xpc;
+};
+
+// static
+nsresult
+nsXPCComponents_Exception::CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, HandleObject obj,
+ const CallArgs& args, bool* _retval)
+{
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+
+ // Do the security check if necessary
+
+ if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, Exception::GetCID()))) {
+ // the security manager vetoed. It should have set an exception.
+ *_retval = false;
+ return NS_OK;
+ }
+
+ // Parse the arguments to the Exception constructor.
+ ExceptionArgParser parser(cx, xpc);
+ if (!parser.parse(args))
+ return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
+
+ nsCOMPtr<nsIException> e = new Exception(nsCString(parser.eMsg),
+ parser.eResult,
+ EmptyCString(),
+ parser.eStack,
+ parser.eData);
+
+ RootedObject newObj(cx);
+ if (NS_FAILED(xpc->WrapNative(cx, obj, e, NS_GET_IID(nsIXPCException), newObj.address())) || !newObj) {
+ return ThrowAndFail(NS_ERROR_XPC_CANT_CREATE_WN, cx, _retval);
+ }
+
+ args.rval().setObject(*newObj);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Exception::HasInstance(nsIXPConnectWrappedNative* wrapper,
+ JSContext * cx, JSObject * obj,
+ HandleValue val, bool* bp,
+ bool* _retval)
+{
+ using namespace mozilla::dom;
+
+ if (bp) {
+ *bp = (val.isObject() &&
+ IS_INSTANCE_OF(Exception, &val.toObject())) ||
+ JSValIsInterfaceOfType(cx, val, NS_GET_IID(nsIException));
+ }
+ return NS_OK;
+}
+
+/***************************************************************************/
+// This class is for the thing returned by "new Component.Constructor".
+
+// XXXjband we use this CID for security check, but security system can't see
+// it since it has no registed factory. Security really kicks in when we try
+// to build a wrapper around an instance.
+
+// {B4A95150-E25A-11d3-8F61-0010A4E73D9A}
+#define NS_XPCCONSTRUCTOR_CID \
+{ 0xb4a95150, 0xe25a, 0x11d3, \
+ { 0x8f, 0x61, 0x0, 0x10, 0xa4, 0xe7, 0x3d, 0x9a } }
+
+class nsXPCConstructor :
+ public nsIXPCConstructor,
+ public nsIXPCScriptable,
+ public nsIClassInfo
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_XPCCONSTRUCTOR_CID)
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCONSTRUCTOR
+ NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSICLASSINFO
+
+public:
+ nsXPCConstructor() = delete;
+ nsXPCConstructor(nsIJSCID* aClassID,
+ nsIJSIID* aInterfaceID,
+ const char* aInitializer);
+
+private:
+ virtual ~nsXPCConstructor();
+ nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, HandleObject obj,
+ const CallArgs& args, bool* _retval);
+private:
+ RefPtr<nsIJSCID> mClassID;
+ RefPtr<nsIJSIID> mInterfaceID;
+ char* mInitializer;
+};
+
+/***************************************************************************/
+NS_IMETHODIMP
+nsXPCConstructor::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(nsIXPCConstructor)
+ PUSH_IID(nsIXPCScriptable)
+#undef PUSH_IID
+
+ return NS_OK;
+oom:
+ while (index)
+ free(array[--index]);
+ free(array);
+ *aArray = nullptr;
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCConstructor::GetScriptableHelper(nsIXPCScriptable** retval)
+{
+ *retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCConstructor::GetContractID(char * *aContractID)
+{
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsXPCConstructor::GetClassDescription(char * *aClassDescription)
+{
+ static const char classDescription[] = "XPCConstructor";
+ *aClassDescription = (char*)nsMemory::Clone(classDescription, sizeof(classDescription));
+ return *aClassDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCConstructor::GetClassID(nsCID * *aClassID)
+{
+ *aClassID = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCConstructor::GetFlags(uint32_t* aFlags)
+{
+ *aFlags = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCConstructor::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsXPCConstructor::nsXPCConstructor(nsIJSCID* aClassID,
+ nsIJSIID* aInterfaceID,
+ const char* aInitializer)
+ : mClassID(aClassID),
+ mInterfaceID(aInterfaceID)
+{
+ mInitializer = aInitializer ?
+ (char*) nsMemory::Clone(aInitializer, strlen(aInitializer)+1) :
+ nullptr;
+}
+
+nsXPCConstructor::~nsXPCConstructor()
+{
+ if (mInitializer)
+ free(mInitializer);
+}
+
+NS_IMETHODIMP
+nsXPCConstructor::GetClassID(nsIJSCID * *aClassID)
+{
+ RefPtr<nsIJSCID> rval = mClassID;
+ rval.forget(aClassID);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCConstructor::GetInterfaceID(nsIJSIID * *aInterfaceID)
+{
+ RefPtr<nsIJSIID> rval = mInterfaceID;
+ rval.forget(aInterfaceID);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCConstructor::GetInitializer(char * *aInitializer)
+{
+ XPC_STRING_GETTER_BODY(aInitializer, mInitializer);
+}
+
+NS_INTERFACE_MAP_BEGIN(nsXPCConstructor)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCConstructor)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCConstructor)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCConstructor)
+NS_IMPL_RELEASE(nsXPCConstructor)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsXPCConstructor
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCConstructor"
+#define XPC_MAP_WANT_CALL
+#define XPC_MAP_WANT_CONSTRUCT
+#define XPC_MAP_FLAGS 0
+#include "xpc_map_end.h" /* This will #undef the above */
+
+
+NS_IMETHODIMP
+nsXPCConstructor::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg,
+ const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ return CallOrConstruct(wrapper, cx, obj, args, _retval);
+
+}
+
+NS_IMETHODIMP
+nsXPCConstructor::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx, JSObject* objArg,
+ const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ return CallOrConstruct(wrapper, cx, obj, args, _retval);
+}
+
+// static
+nsresult
+nsXPCConstructor::CallOrConstruct(nsIXPConnectWrappedNative* wrapper,JSContext* cx,
+ HandleObject obj, const CallArgs& args, bool* _retval)
+{
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+
+ // security check not required because we are going to call through the
+ // code which is reflected into JS which will do that for us later.
+
+ RootedObject cidObj(cx);
+ RootedObject iidObj(cx);
+
+ if (NS_FAILED(xpc->WrapNative(cx, obj, mClassID, NS_GET_IID(nsIJSCID), cidObj.address())) || !cidObj ||
+ NS_FAILED(xpc->WrapNative(cx, obj, mInterfaceID, NS_GET_IID(nsIJSIID), iidObj.address())) || !iidObj) {
+ return ThrowAndFail(NS_ERROR_XPC_CANT_CREATE_WN, cx, _retval);
+ }
+
+ JS::Rooted<JS::Value> arg(cx, ObjectValue(*iidObj));
+ RootedValue rval(cx);
+ if (!JS_CallFunctionName(cx, cidObj, "createInstance", JS::HandleValueArray(arg), &rval) ||
+ rval.isPrimitive()) {
+ // createInstance will have thrown an exception
+ *_retval = false;
+ return NS_OK;
+ }
+
+ args.rval().set(rval);
+
+ // call initializer method if supplied
+ if (mInitializer) {
+ RootedObject newObj(cx, &rval.toObject());
+ // first check existence of function property for better error reporting
+ RootedValue fun(cx);
+ if (!JS_GetProperty(cx, newObj, mInitializer, &fun) ||
+ fun.isPrimitive()) {
+ return ThrowAndFail(NS_ERROR_XPC_BAD_INITIALIZER_NAME, cx, _retval);
+ }
+
+ RootedValue dummy(cx);
+ if (!JS_CallFunctionValue(cx, newObj, fun, args, &dummy)) {
+ // function should have thrown an exception
+ *_retval = false;
+ return NS_OK;
+ }
+ }
+
+ return NS_OK;
+}
+
+/*******************************************************/
+// JavaScript Constructor for nsIXPCConstructor objects (Components.Constructor)
+
+class nsXPCComponents_Constructor final :
+ public nsIXPCComponents_Constructor,
+ public nsIXPCScriptable,
+ public nsIClassInfo
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTS_CONSTRUCTOR
+ NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSICLASSINFO
+
+public:
+ nsXPCComponents_Constructor();
+
+private:
+ virtual ~nsXPCComponents_Constructor();
+ static nsresult CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, HandleObject obj,
+ const CallArgs& args, bool* _retval);
+};
+
+/***************************************************************************/
+NS_IMETHODIMP
+nsXPCComponents_Constructor::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(nsIXPCComponents_Constructor)
+ PUSH_IID(nsIXPCScriptable)
+#undef PUSH_IID
+
+ return NS_OK;
+oom:
+ while (index)
+ free(array[--index]);
+ free(array);
+ *aArray = nullptr;
+ return NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Constructor::GetScriptableHelper(nsIXPCScriptable** retval)
+{
+ *retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Constructor::GetContractID(char * *aContractID)
+{
+ *aContractID = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Constructor::GetClassDescription(char * *aClassDescription)
+{
+ static const char classDescription[] = "XPCComponents_Constructor";
+ *aClassDescription = (char*)nsMemory::Clone(classDescription, sizeof(classDescription));
+ return *aClassDescription ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Constructor::GetClassID(nsCID * *aClassID)
+{
+ *aClassID = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Constructor::GetFlags(uint32_t* aFlags)
+{
+ *aFlags = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Constructor::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+nsXPCComponents_Constructor::nsXPCComponents_Constructor()
+{
+}
+
+nsXPCComponents_Constructor::~nsXPCComponents_Constructor()
+{
+ // empty
+}
+
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents_Constructor)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_Constructor)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY(nsIClassInfo)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Constructor)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCComponents_Constructor)
+NS_IMPL_RELEASE(nsXPCComponents_Constructor)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsXPCComponents_Constructor
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Constructor"
+#define XPC_MAP_WANT_CALL
+#define XPC_MAP_WANT_CONSTRUCT
+#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 */
+
+
+NS_IMETHODIMP
+nsXPCComponents_Constructor::Call(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
+ JSObject* objArg, const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ return CallOrConstruct(wrapper, cx, obj, args, _retval);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Constructor::Construct(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
+ JSObject* objArg, const CallArgs& args, bool* _retval)
+{
+ RootedObject obj(cx, objArg);
+ return CallOrConstruct(wrapper, cx, obj, args, _retval);
+}
+
+// static
+nsresult
+nsXPCComponents_Constructor::CallOrConstruct(nsIXPConnectWrappedNative* wrapper,
+ JSContext* cx, HandleObject obj,
+ const CallArgs& args, bool* _retval)
+{
+ // make sure we have at least one arg
+
+ if (args.length() < 1)
+ return ThrowAndFail(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx, _retval);
+
+ // get the various other object pointers we need
+
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ XPCWrappedNativeScope* scope = ObjectScope(obj);
+ nsCOMPtr<nsIXPCComponents> comp;
+
+ if (!xpc || !scope || !(comp = do_QueryInterface(scope->GetComponents())))
+ return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
+
+ // Do the security check if necessary
+
+ if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateInstance(cx, nsXPCConstructor::GetCID()))) {
+ // the security manager vetoed. It should have set an exception.
+ *_retval = false;
+ return NS_OK;
+ }
+
+ // initialization params for the Constructor object we will create
+ nsCOMPtr<nsIJSCID> cClassID;
+ nsCOMPtr<nsIJSIID> cInterfaceID;
+ const char* cInitializer = nullptr;
+ JSAutoByteString cInitializerBytes;
+
+ if (args.length() >= 3) {
+ // args[2] is an initializer function or property name
+ RootedString str(cx, ToString(cx, args[2]));
+ if (!str || !(cInitializer = cInitializerBytes.encodeLatin1(cx, str)))
+ return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
+ }
+
+ if (args.length() >= 2) {
+ // args[1] is an iid name string
+ // XXXjband support passing "Components.interfaces.foo"?
+
+ nsCOMPtr<nsIXPCComponents_Interfaces> ifaces;
+ RootedObject ifacesObj(cx);
+
+ // we do the lookup by asking the Components.interfaces object
+ // for the property with this name - i.e. we let its caching of these
+ // nsIJSIID objects work for us.
+
+ if (NS_FAILED(comp->GetInterfaces(getter_AddRefs(ifaces))) ||
+ NS_FAILED(xpc->WrapNative(cx, obj, ifaces,
+ NS_GET_IID(nsIXPCComponents_Interfaces),
+ ifacesObj.address())) || !ifacesObj) {
+ return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
+ }
+
+ RootedString str(cx, ToString(cx, args[1]));
+ RootedId id(cx);
+ if (!str || !JS_StringToId(cx, str, &id))
+ return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
+
+ RootedValue val(cx);
+ if (!JS_GetPropertyById(cx, ifacesObj, id, &val) || val.isPrimitive())
+ return ThrowAndFail(NS_ERROR_XPC_BAD_IID, cx, _retval);
+
+ nsCOMPtr<nsIXPConnectWrappedNative> wn;
+ if (NS_FAILED(xpc->GetWrappedNativeOfJSObject(cx, &val.toObject(),
+ getter_AddRefs(wn))) || !wn ||
+ !(cInterfaceID = do_QueryWrappedNative(wn))) {
+ return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
+ }
+ } else {
+ nsCOMPtr<nsIInterfaceInfo> info;
+ xpc->GetInfoForIID(&NS_GET_IID(nsISupports), getter_AddRefs(info));
+
+ if (info) {
+ cInterfaceID = nsJSIID::NewID(info);
+ }
+ if (!cInterfaceID)
+ return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
+ }
+
+ // a new scope to avoid warnings about shadowed names
+ {
+ // argv[0] is a contractid name string
+ // XXXjband support passing "Components.classes.foo"?
+
+ // we do the lookup by asking the Components.classes object
+ // for the property with this name - i.e. we let its caching of these
+ // nsIJSCID objects work for us.
+
+ nsCOMPtr<nsIXPCComponents_Classes> classes;
+ RootedObject classesObj(cx);
+
+ if (NS_FAILED(comp->GetClasses(getter_AddRefs(classes))) ||
+ NS_FAILED(xpc->WrapNative(cx, obj, classes,
+ NS_GET_IID(nsIXPCComponents_Classes),
+ classesObj.address())) || !classesObj) {
+ return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
+ }
+
+ RootedString str(cx, ToString(cx, args[0]));
+ RootedId id(cx);
+ if (!str || !JS_StringToId(cx, str, &id))
+ return ThrowAndFail(NS_ERROR_XPC_BAD_CONVERT_JS, cx, _retval);
+
+ RootedValue val(cx);
+ if (!JS_GetPropertyById(cx, classesObj, id, &val) || val.isPrimitive())
+ return ThrowAndFail(NS_ERROR_XPC_BAD_CID, cx, _retval);
+
+ nsCOMPtr<nsIXPConnectWrappedNative> wn;
+ if (NS_FAILED(xpc->GetWrappedNativeOfJSObject(cx, val.toObjectOrNull(),
+ getter_AddRefs(wn))) || !wn ||
+ !(cClassID = do_QueryWrappedNative(wn))) {
+ return ThrowAndFail(NS_ERROR_XPC_UNEXPECTED, cx, _retval);
+ }
+ }
+
+ nsCOMPtr<nsIXPCConstructor> ctor = new nsXPCConstructor(cClassID, cInterfaceID, cInitializer);
+ RootedObject newObj(cx);
+
+ if (NS_FAILED(xpc->WrapNative(cx, obj, ctor, NS_GET_IID(nsIXPCConstructor), newObj.address())) || !newObj) {
+ return ThrowAndFail(NS_ERROR_XPC_CANT_CREATE_WN, cx, _retval);
+ }
+
+ args.rval().setObject(*newObj);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Constructor::HasInstance(nsIXPConnectWrappedNative* wrapper,
+ JSContext * cx, JSObject * obj,
+ HandleValue val, bool* bp,
+ bool* _retval)
+{
+ if (bp)
+ *bp = JSValIsInterfaceOfType(cx, val, NS_GET_IID(nsIXPCConstructor));
+ return NS_OK;
+}
+
+class nsXPCComponents_Utils final :
+ public nsIXPCComponents_Utils,
+ public nsIXPCScriptable
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCSCRIPTABLE
+ NS_DECL_NSIXPCCOMPONENTS_UTILS
+
+public:
+ nsXPCComponents_Utils() { }
+
+private:
+ virtual ~nsXPCComponents_Utils() { }
+ nsCOMPtr<nsIXPCComponents_utils_Sandbox> mSandbox;
+};
+
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents_Utils)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents_Utils)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCComponents_Utils)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsXPCComponents_Utils)
+NS_IMPL_RELEASE(nsXPCComponents_Utils)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME nsXPCComponents_Utils
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents_Utils"
+#define XPC_MAP_FLAGS nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
+#include "xpc_map_end.h" /* This will #undef the above */
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetSandbox(nsIXPCComponents_utils_Sandbox** aSandbox)
+{
+ NS_ENSURE_ARG_POINTER(aSandbox);
+ if (!mSandbox)
+ mSandbox = NewSandboxConstructor();
+
+ nsCOMPtr<nsIXPCComponents_utils_Sandbox> rval = mSandbox;
+ rval.forget(aSandbox);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::ReportError(HandleValue error, JSContext* cx)
+{
+ // This function shall never fail! Silently eat any failure conditions.
+
+ nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+ if (!console)
+ return NS_OK;
+
+ nsGlobalWindow* globalWin = CurrentWindowOrNull(cx);
+ nsPIDOMWindowInner* win = globalWin ? globalWin->AsInner() : nullptr;
+ const uint64_t innerWindowID = win ? win->WindowID() : 0;
+
+ RootedObject errorObj(cx, error.isObject() ? &error.toObject() : nullptr);
+ JSErrorReport* err = errorObj ? JS_ErrorFromException(cx, errorObj) : nullptr;
+
+ nsCOMPtr<nsIScriptError> scripterr;
+
+ if (errorObj) {
+ JS::RootedObject stackVal(cx,
+ FindExceptionStackForConsoleReport(win, error));
+ if (stackVal) {
+ scripterr = new nsScriptErrorWithStack(stackVal);
+ }
+ }
+
+ nsString fileName;
+ int32_t lineNo = 0;
+
+ if (!scripterr) {
+ nsCOMPtr<nsIStackFrame> frame = dom::GetCurrentJSStack();
+ if (frame) {
+ frame->GetFilename(cx, fileName);
+ frame->GetLineNumber(cx, &lineNo);
+ JS::Rooted<JS::Value> stack(cx);
+ nsresult rv = frame->GetNativeSavedFrame(&stack);
+ if (NS_SUCCEEDED(rv) && stack.isObject()) {
+ JS::Rooted<JSObject*> stackObj(cx, &stack.toObject());
+ scripterr = new nsScriptErrorWithStack(stackObj);
+ }
+ }
+ }
+
+ if (!scripterr) {
+ scripterr = new nsScriptError();
+ }
+
+ if (err) {
+ // It's a proper JS Error
+ nsAutoString fileUni;
+ CopyUTF8toUTF16(err->filename, fileUni);
+
+ uint32_t column = err->tokenOffset();
+
+ const char16_t* linebuf = err->linebuf();
+
+ nsresult rv = scripterr->InitWithWindowID(
+ err->message() ? NS_ConvertUTF8toUTF16(err->message().c_str())
+ : EmptyString(),
+ fileUni,
+ linebuf ? nsDependentString(linebuf, err->linebufLength()) : EmptyString(),
+ err->lineno,
+ column, err->flags, "XPConnect JavaScript", innerWindowID);
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+
+ console->LogMessage(scripterr);
+ return NS_OK;
+ }
+
+ // It's not a JS Error object, so we synthesize as best we're able.
+ RootedString msgstr(cx, ToString(cx, error));
+ if (!msgstr)
+ return NS_OK;
+
+ nsAutoJSString msg;
+ if (!msg.init(cx, msgstr))
+ return NS_OK;
+
+ nsresult rv = scripterr->InitWithWindowID(
+ msg, fileName, EmptyString(), lineNo, 0, 0,
+ "XPConnect JavaScript", innerWindowID);
+ NS_ENSURE_SUCCESS(rv, NS_OK);
+
+ console->LogMessage(scripterr);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::EvalInSandbox(const nsAString& source,
+ HandleValue sandboxVal,
+ HandleValue version,
+ const nsACString& filenameArg,
+ int32_t lineNumber,
+ JSContext* cx,
+ uint8_t optionalArgc,
+ MutableHandleValue retval)
+{
+ RootedObject sandbox(cx);
+ if (!JS_ValueToObject(cx, sandboxVal, &sandbox) || !sandbox)
+ return NS_ERROR_INVALID_ARG;
+
+ // Optional third argument: JS version, as a string.
+ JSVersion jsVersion = JSVERSION_DEFAULT;
+ if (optionalArgc >= 1) {
+ JSString* jsVersionStr = ToString(cx, version);
+ if (!jsVersionStr)
+ return NS_ERROR_INVALID_ARG;
+
+ JSAutoByteString bytes(cx, jsVersionStr);
+ if (!bytes)
+ return NS_ERROR_INVALID_ARG;
+
+ jsVersion = JS_StringToVersion(bytes.ptr());
+ // Explicitly check for "latest", which we support for sandboxes but
+ // isn't in the set of web-exposed version strings.
+ if (jsVersion == JSVERSION_UNKNOWN &&
+ !strcmp(bytes.ptr(), "latest"))
+ {
+ jsVersion = JSVERSION_LATEST;
+ }
+ if (jsVersion == JSVERSION_UNKNOWN)
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // Optional fourth and fifth arguments: filename and line number.
+ int32_t lineNo = (optionalArgc >= 3) ? lineNumber : 1;
+ nsCString filename;
+ if (!filenameArg.IsVoid()) {
+ filename.Assign(filenameArg);
+ } else {
+ // Get the current source info from xpc.
+ nsresult rv;
+ nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID(), &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIStackFrame> frame;
+ xpc->GetCurrentJSStack(getter_AddRefs(frame));
+ if (frame) {
+ nsString frameFile;
+ frame->GetFilename(cx, frameFile);
+ CopyUTF16toUTF8(frameFile, filename);
+ frame->GetLineNumber(cx, &lineNo);
+ }
+ }
+
+ return xpc::EvalInSandbox(cx, sandbox, source, filename, lineNo,
+ jsVersion, retval);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetSandboxAddonId(HandleValue sandboxVal,
+ JSContext* cx, MutableHandleValue rval)
+{
+ if (!sandboxVal.isObject())
+ return NS_ERROR_INVALID_ARG;
+
+ RootedObject sandbox(cx, &sandboxVal.toObject());
+ sandbox = js::CheckedUnwrap(sandbox);
+ if (!sandbox || !xpc::IsSandbox(sandbox))
+ return NS_ERROR_INVALID_ARG;
+
+ return xpc::GetSandboxAddonId(cx, sandbox, rval);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetSandboxMetadata(HandleValue sandboxVal,
+ JSContext* cx, MutableHandleValue rval)
+{
+ if (!sandboxVal.isObject())
+ return NS_ERROR_INVALID_ARG;
+
+ RootedObject sandbox(cx, &sandboxVal.toObject());
+ sandbox = js::CheckedUnwrap(sandbox);
+ if (!sandbox || !xpc::IsSandbox(sandbox))
+ return NS_ERROR_INVALID_ARG;
+
+ return xpc::GetSandboxMetadata(cx, sandbox, rval);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::SetSandboxMetadata(HandleValue sandboxVal,
+ HandleValue metadataVal,
+ JSContext* cx)
+{
+ if (!sandboxVal.isObject())
+ return NS_ERROR_INVALID_ARG;
+
+ RootedObject sandbox(cx, &sandboxVal.toObject());
+ sandbox = js::CheckedUnwrap(sandbox);
+ if (!sandbox || !xpc::IsSandbox(sandbox))
+ return NS_ERROR_INVALID_ARG;
+
+ nsresult rv = xpc::SetSandboxMetadata(cx, sandbox, metadataVal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::Import(const nsACString& registryLocation,
+ HandleValue targetObj,
+ JSContext* cx,
+ uint8_t optionalArgc,
+ MutableHandleValue retval)
+{
+ nsCOMPtr<xpcIJSModuleLoader> moduleloader =
+ do_GetService(MOZJSCOMPONENTLOADER_CONTRACTID);
+ if (!moduleloader)
+ return NS_ERROR_FAILURE;
+ return moduleloader->Import(registryLocation, targetObj, cx, optionalArgc, retval);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::IsModuleLoaded(const nsACString& registryLocation, bool* retval)
+{
+ nsCOMPtr<xpcIJSModuleLoader> moduleloader =
+ do_GetService(MOZJSCOMPONENTLOADER_CONTRACTID);
+ if (!moduleloader)
+ return NS_ERROR_FAILURE;
+ return moduleloader->IsModuleLoaded(registryLocation, retval);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::Unload(const nsACString & registryLocation)
+{
+ nsCOMPtr<xpcIJSModuleLoader> moduleloader =
+ do_GetService(MOZJSCOMPONENTLOADER_CONTRACTID);
+ if (!moduleloader)
+ return NS_ERROR_FAILURE;
+ return moduleloader->Unload(registryLocation);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::ImportGlobalProperties(HandleValue aPropertyList,
+ JSContext* cx)
+{
+ RootedObject global(cx, CurrentGlobalOrNull(cx));
+ MOZ_ASSERT(global);
+
+ // Don't allow doing this if the global is a Window
+ nsGlobalWindow* win;
+ if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, &global, win))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ GlobalProperties options;
+ NS_ENSURE_TRUE(aPropertyList.isObject(), NS_ERROR_INVALID_ARG);
+
+ RootedObject propertyList(cx, &aPropertyList.toObject());
+ bool isArray;
+ if (NS_WARN_IF(!JS_IsArrayObject(cx, propertyList, &isArray))) {
+ return NS_ERROR_FAILURE;
+ }
+ if (NS_WARN_IF(!isArray)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (!options.Parse(cx, propertyList) ||
+ !options.DefineInXPCComponents(cx, global))
+ {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetWeakReference(HandleValue object, JSContext* cx,
+ xpcIJSWeakReference** _retval)
+{
+ RefPtr<xpcJSWeakReference> ref = new xpcJSWeakReference();
+ nsresult rv = ref->Init(cx, object);
+ NS_ENSURE_SUCCESS(rv, rv);
+ ref.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::ForceGC()
+{
+ JSContext* cx = nsXPConnect::GetContextInstance()->Context();
+ PrepareForFullGC(cx);
+ GCForReason(cx, GC_NORMAL, gcreason::COMPONENT_UTILS);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::ForceCC(nsICycleCollectorListener* listener)
+{
+ nsJSContext::CycleCollectNow(listener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::FinishCC()
+{
+ nsCycleCollector_finishAnyCurrentCollection();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::CcSlice(int64_t budget)
+{
+ nsJSContext::RunCycleCollectorWorkSlice(budget);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetMaxCCSliceTimeSinceClear(int32_t* out)
+{
+ *out = nsJSContext::GetMaxCCSliceTimeSinceClear();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::ClearMaxCCTime()
+{
+ nsJSContext::ClearMaxCCSliceTime();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::ForceShrinkingGC()
+{
+ JSContext* cx = dom::danger::GetJSContext();
+ PrepareForFullGC(cx);
+ GCForReason(cx, GC_SHRINK, gcreason::COMPONENT_UTILS);
+ return NS_OK;
+}
+
+class PreciseGCRunnable : public Runnable
+{
+ public:
+ PreciseGCRunnable(ScheduledGCCallback* aCallback, bool aShrinking)
+ : mCallback(aCallback), mShrinking(aShrinking) {}
+
+ NS_IMETHOD Run() override
+ {
+ JSContext* cx = dom::danger::GetJSContext();
+ if (JS_IsRunning(cx))
+ return NS_DispatchToMainThread(this);
+
+ nsJSContext::GarbageCollectNow(gcreason::COMPONENT_UTILS,
+ nsJSContext::NonIncrementalGC,
+ mShrinking ?
+ nsJSContext::ShrinkingGC :
+ nsJSContext::NonShrinkingGC);
+
+ mCallback->Callback();
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<ScheduledGCCallback> mCallback;
+ bool mShrinking;
+};
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::SchedulePreciseGC(ScheduledGCCallback* aCallback)
+{
+ RefPtr<PreciseGCRunnable> event = new PreciseGCRunnable(aCallback, false);
+ return NS_DispatchToMainThread(event);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::SchedulePreciseShrinkingGC(ScheduledGCCallback* aCallback)
+{
+ RefPtr<PreciseGCRunnable> event = new PreciseGCRunnable(aCallback, true);
+ return NS_DispatchToMainThread(event);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::UnlinkGhostWindows()
+{
+#ifdef DEBUG
+ nsWindowMemoryReporter::UnlinkGhostWindows();
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetJSTestingFunctions(JSContext* cx,
+ MutableHandleValue retval)
+{
+ JSObject* obj = js::GetTestingFunctions(cx);
+ if (!obj)
+ return NS_ERROR_XPC_JAVASCRIPT_ERROR;
+ retval.setObject(*obj);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::CallFunctionWithAsyncStack(HandleValue function,
+ nsIStackFrame* stack,
+ const nsAString& asyncCause,
+ JSContext* cx,
+ MutableHandleValue retval)
+{
+ nsresult rv;
+
+ if (!stack || asyncCause.IsEmpty()) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ JS::Rooted<JS::Value> asyncStack(cx);
+ rv = stack->GetNativeSavedFrame(&asyncStack);
+ if (NS_FAILED(rv))
+ return rv;
+ if (!asyncStack.isObject()) {
+ JS_ReportErrorASCII(cx, "Must use a native JavaScript stack frame");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ JS::Rooted<JSObject*> asyncStackObj(cx, &asyncStack.toObject());
+
+ NS_ConvertUTF16toUTF8 utf8Cause(asyncCause);
+ JS::AutoSetAsyncStackForNewCalls sas(cx, asyncStackObj, utf8Cause.get(),
+ JS::AutoSetAsyncStackForNewCalls::AsyncCallKind::EXPLICIT);
+
+ if (!JS_CallFunctionValue(cx, nullptr, function,
+ JS::HandleValueArray::empty(), retval))
+ {
+ return NS_ERROR_XPC_JAVASCRIPT_ERROR;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetGlobalForObject(HandleValue object,
+ JSContext* cx,
+ MutableHandleValue retval)
+{
+ // First argument must be an object.
+ if (object.isPrimitive())
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+
+ // Wrappers are parented to their the global in their home compartment. But
+ // when getting the global for a cross-compartment wrapper, we really want
+ // a wrapper for the foreign global. So we need to unwrap before getting the
+ // parent, enter the compartment for the duration of the call, and wrap the
+ // result.
+ Rooted<JSObject*> obj(cx, &object.toObject());
+ obj = js::UncheckedUnwrap(obj);
+ {
+ JSAutoCompartment ac(cx, obj);
+ obj = JS_GetGlobalForObject(cx, obj);
+ }
+
+ if (!JS_WrapObject(cx, &obj))
+ return NS_ERROR_FAILURE;
+
+ // Get the WindowProxy if necessary.
+ obj = js::ToWindowProxyIfWindow(obj);
+
+ retval.setObject(*obj);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::IsProxy(HandleValue vobj, JSContext* cx, bool* rval)
+{
+ if (!vobj.isObject()) {
+ *rval = false;
+ return NS_OK;
+ }
+
+ RootedObject obj(cx, &vobj.toObject());
+ obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
+ NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE);
+
+ *rval = js::IsScriptedProxy(obj);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::ExportFunction(HandleValue vfunction, HandleValue vscope,
+ HandleValue voptions, JSContext* cx,
+ MutableHandleValue rval)
+{
+ if (!xpc::ExportFunction(cx, vfunction, vscope, voptions, rval))
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::CreateObjectIn(HandleValue vobj, HandleValue voptions,
+ JSContext* cx, MutableHandleValue rval)
+{
+ RootedObject optionsObject(cx, voptions.isObject() ? &voptions.toObject()
+ : nullptr);
+ CreateObjectInOptions options(cx, optionsObject);
+ if (voptions.isObject() &&
+ !options.Parse())
+ {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!xpc::CreateObjectIn(cx, vobj, options, rval))
+ return NS_ERROR_FAILURE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::MakeObjectPropsNormal(HandleValue vobj, JSContext* cx)
+{
+ if (!cx)
+ return NS_ERROR_FAILURE;
+
+ // first argument must be an object
+ if (vobj.isPrimitive())
+ return NS_ERROR_XPC_BAD_CONVERT_JS;
+
+ RootedObject obj(cx, js::UncheckedUnwrap(&vobj.toObject()));
+ JSAutoCompartment ac(cx, obj);
+ Rooted<IdVector> ida(cx, IdVector(cx));
+ if (!JS_Enumerate(cx, obj, &ida))
+ return NS_ERROR_FAILURE;
+
+ RootedId id(cx);
+ RootedValue v(cx);
+ for (size_t i = 0; i < ida.length(); ++i) {
+ id = ida[i];
+
+ if (!JS_GetPropertyById(cx, obj, id, &v))
+ return NS_ERROR_FAILURE;
+
+ if (v.isPrimitive())
+ continue;
+
+ RootedObject propobj(cx, &v.toObject());
+ // TODO Deal with non-functions.
+ if (!js::IsWrapper(propobj) || !JS::IsCallable(propobj))
+ continue;
+
+ FunctionForwarderOptions forwarderOptions;
+ if (!NewFunctionForwarder(cx, id, propobj, forwarderOptions, &v) ||
+ !JS_SetPropertyById(cx, obj, id, v))
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::IsDeadWrapper(HandleValue obj, bool* out)
+{
+ *out = false;
+ if (obj.isPrimitive())
+ return NS_ERROR_INVALID_ARG;
+
+ // Make sure to unwrap first. Once a proxy is nuked, it ceases to be a
+ // wrapper, meaning that, if passed to another compartment, we'll generate
+ // a CCW for it. Make sure that IsDeadWrapper sees through the confusion.
+ *out = JS_IsDeadWrapper(js::CheckedUnwrap(&obj.toObject()));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::IsCrossProcessWrapper(HandleValue obj, bool* out)
+{
+ *out = false;
+ if (obj.isPrimitive())
+ return NS_ERROR_INVALID_ARG;
+
+ *out = jsipc::IsWrappedCPOW(&obj.toObject());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetCrossProcessWrapperTag(HandleValue obj, nsACString& out)
+{
+ if (obj.isPrimitive() || !jsipc::IsWrappedCPOW(&obj.toObject()))
+ return NS_ERROR_INVALID_ARG;
+
+ jsipc::GetWrappedCPOWTag(&obj.toObject(), out);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::PermitCPOWsInScope(HandleValue obj)
+{
+ if (!obj.isObject())
+ return NS_ERROR_INVALID_ARG;
+
+ JSObject* scopeObj = js::UncheckedUnwrap(&obj.toObject());
+ CompartmentPrivate::Get(scopeObj)->allowCPOWs = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::RecomputeWrappers(HandleValue vobj, JSContext* cx)
+{
+ // Determine the compartment of the given object, if any.
+ JSCompartment* c = vobj.isObject()
+ ? js::GetObjectCompartment(js::UncheckedUnwrap(&vobj.toObject()))
+ : nullptr;
+
+ // If no compartment was given, recompute all.
+ if (!c)
+ js::RecomputeWrappers(cx, js::AllCompartments(), js::AllCompartments());
+ // Otherwise, recompute wrappers for the given compartment.
+ else
+ js::RecomputeWrappers(cx, js::SingleCompartment(c), js::AllCompartments()) &&
+ js::RecomputeWrappers(cx, js::AllCompartments(), js::SingleCompartment(c));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::SetWantXrays(HandleValue vscope, JSContext* cx)
+{
+ if (!vscope.isObject())
+ return NS_ERROR_INVALID_ARG;
+ JSObject* scopeObj = js::UncheckedUnwrap(&vscope.toObject());
+ JSCompartment* compartment = js::GetObjectCompartment(scopeObj);
+ CompartmentPrivate::Get(scopeObj)->wantXrays = true;
+ bool ok = js::RecomputeWrappers(cx, js::SingleCompartment(compartment),
+ js::AllCompartments());
+ NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::ForcePermissiveCOWs(JSContext* cx)
+{
+ CrashIfNotInAutomation();
+ CompartmentPrivate::Get(CurrentGlobalOrNull(cx))->forcePermissiveCOWs = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::ForcePrivilegedComponentsForScope(HandleValue vscope,
+ JSContext* cx)
+{
+ if (!vscope.isObject())
+ return NS_ERROR_INVALID_ARG;
+ CrashIfNotInAutomation();
+ JSObject* scopeObj = js::UncheckedUnwrap(&vscope.toObject());
+ XPCWrappedNativeScope* scope = ObjectScope(scopeObj);
+ scope->ForcePrivilegedComponents();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetComponentsForScope(HandleValue vscope, JSContext* cx,
+ MutableHandleValue rval)
+{
+ if (!vscope.isObject())
+ return NS_ERROR_INVALID_ARG;
+ JSObject* scopeObj = js::UncheckedUnwrap(&vscope.toObject());
+ XPCWrappedNativeScope* scope = ObjectScope(scopeObj);
+ RootedObject components(cx);
+ if (!scope->GetComponentsJSObject(&components))
+ return NS_ERROR_FAILURE;
+ if (!JS_WrapObject(cx, &components))
+ return NS_ERROR_FAILURE;
+ rval.setObject(*components);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::Dispatch(HandleValue runnableArg, HandleValue scope,
+ JSContext* cx)
+{
+ RootedValue runnable(cx, runnableArg);
+ // Enter the given compartment, if any, and rewrap runnable.
+ Maybe<JSAutoCompartment> ac;
+ if (scope.isObject()) {
+ JSObject* scopeObj = js::UncheckedUnwrap(&scope.toObject());
+ if (!scopeObj)
+ return NS_ERROR_FAILURE;
+ ac.emplace(cx, scopeObj);
+ if (!JS_WrapValue(cx, &runnable))
+ return NS_ERROR_FAILURE;
+ }
+
+ // Get an XPCWrappedJS for |runnable|.
+ if (!runnable.isObject())
+ return NS_ERROR_INVALID_ARG;
+
+ nsCOMPtr<nsIRunnable> run;
+ nsresult rv = nsXPConnect::XPConnect()->WrapJS(cx, &runnable.toObject(),
+ NS_GET_IID(nsIRunnable),
+ getter_AddRefs(run));
+ NS_ENSURE_SUCCESS(rv, rv);
+ MOZ_ASSERT(run);
+
+ // Dispatch.
+ return NS_DispatchToMainThread(run);
+}
+
+#define GENERATE_JSCONTEXTOPTION_GETTER_SETTER(_attr, _getter, _setter) \
+ NS_IMETHODIMP \
+ nsXPCComponents_Utils::Get## _attr(JSContext* cx, bool* aValue) \
+ { \
+ *aValue = ContextOptionsRef(cx)._getter(); \
+ return NS_OK; \
+ } \
+ NS_IMETHODIMP \
+ nsXPCComponents_Utils::Set## _attr(JSContext* cx, bool aValue) \
+ { \
+ ContextOptionsRef(cx)._setter(aValue); \
+ return NS_OK; \
+ }
+
+GENERATE_JSCONTEXTOPTION_GETTER_SETTER(Strict, extraWarnings, setExtraWarnings)
+GENERATE_JSCONTEXTOPTION_GETTER_SETTER(Werror, werror, setWerror)
+GENERATE_JSCONTEXTOPTION_GETTER_SETTER(Strict_mode, strictMode, setStrictMode)
+GENERATE_JSCONTEXTOPTION_GETTER_SETTER(Ion, ion, setIon)
+
+#undef GENERATE_JSCONTEXTOPTION_GETTER_SETTER
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::SetGCZeal(int32_t aValue, JSContext* cx)
+{
+#ifdef JS_GC_ZEAL
+ JS_SetGCZeal(cx, uint8_t(aValue), JS_DEFAULT_ZEAL_FREQ);
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::NukeSandbox(HandleValue obj, JSContext* cx)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::JS);
+ NS_ENSURE_TRUE(obj.isObject(), NS_ERROR_INVALID_ARG);
+ JSObject* wrapper = &obj.toObject();
+ NS_ENSURE_TRUE(IsWrapper(wrapper), NS_ERROR_INVALID_ARG);
+ JSObject* sb = UncheckedUnwrap(wrapper);
+ NS_ENSURE_TRUE(IsSandbox(sb), NS_ERROR_INVALID_ARG);
+ NukeCrossCompartmentWrappers(cx, AllCompartments(),
+ SingleCompartment(GetObjectCompartment(sb)),
+ NukeWindowReferences);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::BlockScriptForGlobal(HandleValue globalArg,
+ JSContext* cx)
+{
+ NS_ENSURE_TRUE(globalArg.isObject(), NS_ERROR_INVALID_ARG);
+ RootedObject global(cx, UncheckedUnwrap(&globalArg.toObject(),
+ /* stopAtWindowProxy = */ false));
+ NS_ENSURE_TRUE(JS_IsGlobalObject(global), NS_ERROR_INVALID_ARG);
+ if (nsContentUtils::IsSystemPrincipal(xpc::GetObjectPrincipal(global))) {
+ JS_ReportErrorASCII(cx, "Script may not be disabled for system globals");
+ return NS_ERROR_FAILURE;
+ }
+ Scriptability::Get(global).Block();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::UnblockScriptForGlobal(HandleValue globalArg,
+ JSContext* cx)
+{
+ NS_ENSURE_TRUE(globalArg.isObject(), NS_ERROR_INVALID_ARG);
+ RootedObject global(cx, UncheckedUnwrap(&globalArg.toObject(),
+ /* stopAtWindowProxy = */ false));
+ NS_ENSURE_TRUE(JS_IsGlobalObject(global), NS_ERROR_INVALID_ARG);
+ if (nsContentUtils::IsSystemPrincipal(xpc::GetObjectPrincipal(global))) {
+ JS_ReportErrorASCII(cx, "Script may not be disabled for system globals");
+ return NS_ERROR_FAILURE;
+ }
+ Scriptability::Get(global).Unblock();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::IsXrayWrapper(HandleValue obj, bool* aRetval)
+{
+ *aRetval =
+ obj.isObject() && xpc::WrapperFactory::IsXrayWrapper(&obj.toObject());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::WaiveXrays(HandleValue aVal, JSContext* aCx, MutableHandleValue aRetval)
+{
+ RootedValue value(aCx, aVal);
+ if (!xpc::WrapperFactory::WaiveXrayAndWrap(aCx, &value))
+ return NS_ERROR_FAILURE;
+ aRetval.set(value);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::UnwaiveXrays(HandleValue aVal, JSContext* aCx, MutableHandleValue aRetval)
+{
+ if (!aVal.isObject()) {
+ aRetval.set(aVal);
+ return NS_OK;
+ }
+
+ RootedObject obj(aCx, js::UncheckedUnwrap(&aVal.toObject()));
+ if (!JS_WrapObject(aCx, &obj))
+ return NS_ERROR_FAILURE;
+ aRetval.setObject(*obj);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetClassName(HandleValue aObj, bool aUnwrap, JSContext* aCx, char** aRv)
+{
+ if (!aObj.isObject())
+ return NS_ERROR_INVALID_ARG;
+ RootedObject obj(aCx, &aObj.toObject());
+ if (aUnwrap)
+ obj = js::UncheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
+ *aRv = NS_strdup(js::GetObjectClass(obj)->name);
+ NS_ENSURE_TRUE(*aRv, NS_ERROR_OUT_OF_MEMORY);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetDOMClassInfo(const nsAString& aClassName,
+ nsIClassInfo** aClassInfo)
+{
+ *aClassInfo = nullptr;
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetIncumbentGlobal(HandleValue aCallback,
+ JSContext* aCx, MutableHandleValue aOut)
+{
+ nsCOMPtr<nsIGlobalObject> global = mozilla::dom::GetIncumbentGlobal();
+ RootedValue globalVal(aCx);
+
+ if (!global) {
+ globalVal = NullValue();
+ } else {
+ // Note: We rely on the wrap call for outerization.
+ globalVal = ObjectValue(*global->GetGlobalJSObject());
+ if (!JS_WrapValue(aCx, &globalVal))
+ return NS_ERROR_FAILURE;
+ }
+
+ // Invoke the callback, if passed.
+ if (aCallback.isObject()) {
+ RootedValue ignored(aCx);
+ if (!JS_CallFunctionValue(aCx, nullptr, aCallback, JS::HandleValueArray(globalVal), &ignored))
+ return NS_ERROR_FAILURE;
+ }
+
+ aOut.set(globalVal);
+ return NS_OK;
+}
+
+/*
+ * Below is a bunch of awkward junk to allow JS test code to trigger the
+ * creation of an XPCWrappedJS, such that it ends up in the map. We need to
+ * hand the caller some sort of reference to hold onto (to prevent the
+ * refcount from dropping to zero as soon as the function returns), but trying
+ * to return a bonafide XPCWrappedJS to script causes all sorts of trouble. So
+ * we create a benign holder class instead, which acts as an opaque reference
+ * that script can use to keep the XPCWrappedJS alive and in the map.
+ */
+
+class WrappedJSHolder : public nsISupports
+{
+ NS_DECL_ISUPPORTS
+ WrappedJSHolder() {}
+
+ RefPtr<nsXPCWrappedJS> mWrappedJS;
+
+private:
+ virtual ~WrappedJSHolder() {}
+};
+NS_IMPL_ISUPPORTS0(WrappedJSHolder);
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GenerateXPCWrappedJS(HandleValue aObj, HandleValue aScope,
+ JSContext* aCx, nsISupports** aOut)
+{
+ if (!aObj.isObject())
+ return NS_ERROR_INVALID_ARG;
+ RootedObject obj(aCx, &aObj.toObject());
+ RootedObject scope(aCx, aScope.isObject() ? js::UncheckedUnwrap(&aScope.toObject())
+ : CurrentGlobalOrNull(aCx));
+ JSAutoCompartment ac(aCx, scope);
+ if (!JS_WrapObject(aCx, &obj))
+ return NS_ERROR_FAILURE;
+
+ RefPtr<WrappedJSHolder> holder = new WrappedJSHolder();
+ nsresult rv = nsXPCWrappedJS::GetNewOrUsed(obj, NS_GET_IID(nsISupports),
+ getter_AddRefs(holder->mWrappedJS));
+ holder.forget(aOut);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetWatchdogTimestamp(const nsAString& aCategory, PRTime* aOut)
+{
+ WatchdogTimestampCategory category;
+ if (aCategory.EqualsLiteral("ContextStateChange"))
+ category = TimestampContextStateChange;
+ else if (aCategory.EqualsLiteral("WatchdogWakeup"))
+ category = TimestampWatchdogWakeup;
+ else if (aCategory.EqualsLiteral("WatchdogHibernateStart"))
+ category = TimestampWatchdogHibernateStart;
+ else if (aCategory.EqualsLiteral("WatchdogHibernateStop"))
+ category = TimestampWatchdogHibernateStop;
+ else
+ return NS_ERROR_INVALID_ARG;
+ *aOut = XPCJSContext::Get()->GetWatchdogTimestamp(category);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetJSEngineTelemetryValue(JSContext* cx, MutableHandleValue rval)
+{
+ RootedObject obj(cx, JS_NewPlainObject(cx));
+ if (!obj)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ unsigned attrs = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
+
+ size_t i = JS_SetProtoCalled(cx);
+ RootedValue v(cx, DoubleValue(i));
+ if (!JS_DefineProperty(cx, obj, "setProto", v, attrs))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ i = JS_GetCustomIteratorCount(cx);
+ v.setDouble(i);
+ if (!JS_DefineProperty(cx, obj, "customIter", v, attrs))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rval.setObject(*obj);
+ return NS_OK;
+}
+
+bool
+xpc::CloneInto(JSContext* aCx, HandleValue aValue, HandleValue aScope,
+ HandleValue aOptions, MutableHandleValue aCloned)
+{
+ if (!aScope.isObject())
+ return false;
+
+ RootedObject scope(aCx, &aScope.toObject());
+ scope = js::CheckedUnwrap(scope);
+ if(!scope) {
+ JS_ReportErrorASCII(aCx, "Permission denied to clone object into scope");
+ return false;
+ }
+
+ if (!aOptions.isUndefined() && !aOptions.isObject()) {
+ JS_ReportErrorASCII(aCx, "Invalid argument");
+ return false;
+ }
+
+ RootedObject optionsObject(aCx, aOptions.isObject() ? &aOptions.toObject()
+ : nullptr);
+ StackScopedCloneOptions options(aCx, optionsObject);
+ if (aOptions.isObject() && !options.Parse())
+ return false;
+
+ {
+ JSAutoCompartment ac(aCx, scope);
+ aCloned.set(aValue);
+ if (!StackScopedClone(aCx, options, aCloned))
+ return false;
+ }
+
+ return JS_WrapValue(aCx, aCloned);
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::CloneInto(HandleValue aValue, HandleValue aScope,
+ HandleValue aOptions, JSContext* aCx,
+ MutableHandleValue aCloned)
+{
+ return xpc::CloneInto(aCx, aValue, aScope, aOptions, aCloned) ?
+ NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetWebIDLCallerPrincipal(nsIPrincipal** aResult)
+{
+ // This API may only be when the Entry Settings Object corresponds to a
+ // JS-implemented WebIDL call. In all other cases, the value will be null,
+ // and we throw.
+ nsCOMPtr<nsIPrincipal> callerPrin = mozilla::dom::GetWebIDLCallerPrincipal();
+ if (!callerPrin)
+ return NS_ERROR_NOT_AVAILABLE;
+ callerPrin.forget(aResult);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetObjectPrincipal(HandleValue val, JSContext* cx,
+ nsIPrincipal** result)
+{
+ if (!val.isObject())
+ return NS_ERROR_INVALID_ARG;
+ RootedObject obj(cx, &val.toObject());
+ obj = js::CheckedUnwrap(obj);
+ MOZ_ASSERT(obj);
+
+ nsCOMPtr<nsIPrincipal> prin = nsContentUtils::ObjectPrincipal(obj);
+ prin.forget(result);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::GetCompartmentLocation(HandleValue val,
+ JSContext* cx,
+ nsACString& result)
+{
+ if (!val.isObject())
+ return NS_ERROR_INVALID_ARG;
+ RootedObject obj(cx, &val.toObject());
+ obj = js::CheckedUnwrap(obj);
+ MOZ_ASSERT(obj);
+
+ result = xpc::CompartmentPrivate::Get(obj)->GetLocation();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::SetAddonInterposition(const nsACString& addonIdStr,
+ nsIAddonInterposition* interposition,
+ JSContext* cx)
+{
+ JSAddonId* addonId = xpc::NewAddonId(cx, addonIdStr);
+ if (!addonId)
+ return NS_ERROR_FAILURE;
+ if (!XPCWrappedNativeScope::SetAddonInterposition(cx, addonId, interposition))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::SetAddonCallInterposition(HandleValue target,
+ JSContext* cx)
+{
+ NS_ENSURE_TRUE(target.isObject(), NS_ERROR_INVALID_ARG);
+ RootedObject targetObj(cx, &target.toObject());
+ targetObj = js::CheckedUnwrap(targetObj);
+ NS_ENSURE_TRUE(targetObj, NS_ERROR_INVALID_ARG);
+ XPCWrappedNativeScope* xpcScope = ObjectScope(targetObj);
+ NS_ENSURE_TRUE(xpcScope, NS_ERROR_INVALID_ARG);
+
+ xpcScope->SetAddonCallInterposition();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::AllowCPOWsInAddon(const nsACString& addonIdStr,
+ bool allow,
+ JSContext* cx)
+{
+ JSAddonId* addonId = xpc::NewAddonId(cx, addonIdStr);
+ if (!addonId)
+ return NS_ERROR_FAILURE;
+ if (!XPCWrappedNativeScope::AllowCPOWsInAddon(cx, addonId, allow))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents_Utils::Now(double* aRetval)
+{
+ bool isInconsistent = false;
+ TimeStamp start = TimeStamp::ProcessCreation(isInconsistent);
+ *aRetval = (TimeStamp::Now() - start).ToMilliseconds();
+ return NS_OK;
+}
+
+/***************************************************************************/
+/***************************************************************************/
+/***************************************************************************/
+
+
+nsXPCComponentsBase::nsXPCComponentsBase(XPCWrappedNativeScope* aScope)
+ : mScope(aScope)
+{
+ MOZ_ASSERT(aScope, "aScope must not be null");
+}
+
+nsXPCComponents::nsXPCComponents(XPCWrappedNativeScope* aScope)
+ : nsXPCComponentsBase(aScope)
+{
+}
+
+nsXPCComponentsBase::~nsXPCComponentsBase()
+{
+}
+
+nsXPCComponents::~nsXPCComponents()
+{
+}
+
+void
+nsXPCComponentsBase::ClearMembers()
+{
+ mInterfaces = nullptr;
+ mInterfacesByID = nullptr;
+ mResults = nullptr;
+}
+
+void
+nsXPCComponents::ClearMembers()
+{
+ mClasses = nullptr;
+ mClassesByID = nullptr;
+ mID = nullptr;
+ mException = nullptr;
+ mConstructor = nullptr;
+ mUtils = nullptr;
+
+ nsXPCComponentsBase::ClearMembers();
+}
+
+/*******************************************/
+#define XPC_IMPL_GET_OBJ_METHOD(_class, _n) \
+NS_IMETHODIMP _class::Get##_n(nsIXPCComponents_##_n * *a##_n) { \
+ NS_ENSURE_ARG_POINTER(a##_n); \
+ if (!m##_n) \
+ m##_n = new nsXPCComponents_##_n(); \
+ RefPtr<nsXPCComponents_##_n> ret = m##_n; \
+ ret.forget(a##_n); \
+ return NS_OK; \
+}
+
+XPC_IMPL_GET_OBJ_METHOD(nsXPCComponentsBase, Interfaces)
+XPC_IMPL_GET_OBJ_METHOD(nsXPCComponentsBase, InterfacesByID)
+XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, Classes)
+XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, ClassesByID)
+XPC_IMPL_GET_OBJ_METHOD(nsXPCComponentsBase, Results)
+XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, ID)
+XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, Exception)
+XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, Constructor)
+XPC_IMPL_GET_OBJ_METHOD(nsXPCComponents, Utils)
+
+#undef XPC_IMPL_GET_OBJ_METHOD
+/*******************************************/
+
+NS_IMETHODIMP
+nsXPCComponentsBase::IsSuccessCode(nsresult result, bool* out)
+{
+ *out = NS_SUCCEEDED(result);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents::GetStack(nsIStackFrame * *aStack)
+{
+ nsresult rv;
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ rv = xpc->GetCurrentJSStack(aStack);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsXPCComponents::GetManager(nsIComponentManager * *aManager)
+{
+ MOZ_ASSERT(aManager, "bad param");
+ return NS_GetComponentManager(aManager);
+}
+
+NS_IMETHODIMP
+nsXPCComponents::GetReturnCode(JSContext* aCx, MutableHandleValue aOut)
+{
+ nsresult res = XPCJSContext::Get()->GetPendingResult();
+ aOut.setNumber(static_cast<uint32_t>(res));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCComponents::SetReturnCode(JSContext* aCx, HandleValue aCode)
+{
+ nsresult rv;
+ if (!ToUint32(aCx, aCode, (uint32_t*)&rv))
+ return NS_ERROR_FAILURE;
+ XPCJSContext::Get()->SetPendingResult(rv);
+ return NS_OK;
+}
+
+// static
+NS_IMETHODIMP nsXPCComponents::ReportError(HandleValue error, JSContext* cx)
+{
+ NS_WARNING("Components.reportError deprecated, use Components.utils.reportError");
+
+ nsCOMPtr<nsIXPCComponents_Utils> utils;
+ nsresult rv = GetUtils(getter_AddRefs(utils));
+ if (NS_FAILED(rv))
+ return rv;
+
+ return utils->ReportError(error, cx);
+}
+
+/**********************************************/
+
+class ComponentsSH : public nsIXPCScriptable
+{
+public:
+ explicit constexpr ComponentsSH(unsigned dummy)
+ {
+ }
+
+ // We don't actually inherit any ref counting infrastructure, but we don't
+ // need an nsAutoRefCnt member, so the _INHERITED macro is a hack to avoid
+ // having one.
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIXPCSCRIPTABLE
+ static nsresult Get(nsIXPCScriptable** helper)
+ {
+ *helper = &singleton;
+ return NS_OK;
+ }
+
+private:
+ static ComponentsSH singleton;
+};
+
+ComponentsSH ComponentsSH::singleton(0);
+
+// Singleton refcounting.
+NS_IMETHODIMP_(MozExternalRefCountType) ComponentsSH::AddRef(void) { return 1; }
+NS_IMETHODIMP_(MozExternalRefCountType) ComponentsSH::Release(void) { return 1; }
+
+NS_INTERFACE_MAP_BEGIN(ComponentsSH)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+#define NSXPCCOMPONENTSBASE_CID \
+{ 0xc62998e5, 0x95f1, 0x4058, \
+ { 0xa5, 0x09, 0xec, 0x21, 0x66, 0x18, 0x92, 0xb9 } }
+
+#define NSXPCCOMPONENTS_CID \
+{ 0x3649f405, 0xf0ec, 0x4c28, \
+ { 0xae, 0xb0, 0xaf, 0x9a, 0x51, 0xe4, 0x4c, 0x81 } }
+
+NS_IMPL_CLASSINFO(nsXPCComponentsBase, &ComponentsSH::Get, nsIClassInfo::DOM_OBJECT, NSXPCCOMPONENTSBASE_CID)
+NS_IMPL_ISUPPORTS_CI(nsXPCComponentsBase, nsIXPCComponentsBase)
+
+NS_IMPL_CLASSINFO(nsXPCComponents, &ComponentsSH::Get, nsIClassInfo::DOM_OBJECT, NSXPCCOMPONENTS_CID)
+// Below is more or less what NS_IMPL_ISUPPORTS_CI_INHERITED1 would look like
+// if it existed.
+NS_IMPL_ADDREF_INHERITED(nsXPCComponents, nsXPCComponentsBase)
+NS_IMPL_RELEASE_INHERITED(nsXPCComponents, nsXPCComponentsBase)
+NS_INTERFACE_MAP_BEGIN(nsXPCComponents)
+ NS_INTERFACE_MAP_ENTRY(nsIXPCComponents)
+ NS_IMPL_QUERY_CLASSINFO(nsXPCComponents)
+NS_INTERFACE_MAP_END_INHERITING(nsXPCComponentsBase)
+NS_IMPL_CI_INTERFACE_GETTER(nsXPCComponents, nsIXPCComponents)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us
+#define XPC_MAP_CLASSNAME ComponentsSH
+#define XPC_MAP_QUOTED_CLASSNAME "nsXPCComponents"
+#define XPC_MAP_WANT_PRECREATE
+#include "xpc_map_end.h" /* This will #undef the above */
+
+NS_IMETHODIMP
+ComponentsSH::PreCreate(nsISupports* nativeObj, JSContext* cx, JSObject* globalObj, JSObject** parentObj)
+{
+ nsXPCComponentsBase* self = static_cast<nsXPCComponentsBase*>(nativeObj);
+ // this should never happen
+ if (!self->GetScope()) {
+ NS_WARNING("mScope must not be null when nsXPCComponents::PreCreate is called");
+ return NS_ERROR_FAILURE;
+ }
+ *parentObj = self->GetScope()->GetGlobalJSObject();
+ return NS_OK;
+}
diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp
new file mode 100644
index 000000000..37932b452
--- /dev/null
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -0,0 +1,1799 @@
+/* -*- 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/. */
+
+/* Data conversion between native and JavaScript types. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Range.h"
+
+#include "xpcprivate.h"
+#include "nsIAtom.h"
+#include "nsWrapperCache.h"
+#include "nsJSUtils.h"
+#include "nsQueryObject.h"
+#include "WrapperFactory.h"
+
+#include "nsWrapperCacheInlines.h"
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "js/CharacterEncoding.h"
+#include "jsprf.h"
+
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/PrimitiveConversions.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+
+using namespace xpc;
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace JS;
+
+//#define STRICT_CHECK_OF_UNICODE
+#ifdef STRICT_CHECK_OF_UNICODE
+#define ILLEGAL_RANGE(c) (0!=((c) & 0xFF80))
+#else // STRICT_CHECK_OF_UNICODE
+#define ILLEGAL_RANGE(c) (0!=((c) & 0xFF00))
+#endif // STRICT_CHECK_OF_UNICODE
+
+#define ILLEGAL_CHAR_RANGE(c) (0!=((c) & 0x80))
+
+/***********************************************************/
+
+// static
+bool
+XPCConvert::IsMethodReflectable(const XPTMethodDescriptor& info)
+{
+ if (XPT_MD_IS_NOTXPCOM(info.flags) || XPT_MD_IS_HIDDEN(info.flags))
+ return false;
+
+ for (int i = info.num_args-1; i >= 0; i--) {
+ const nsXPTParamInfo& param = info.params[i];
+ const nsXPTType& type = param.GetType();
+
+ // Reflected methods can't use native types. All native types end up
+ // getting tagged as void*, so this check is easy.
+ if (type.TagPart() == nsXPTType::T_VOID)
+ return false;
+ }
+ return true;
+}
+
+static JSObject*
+UnwrapNativeCPOW(nsISupports* wrapper)
+{
+ nsCOMPtr<nsIXPConnectWrappedJS> underware = do_QueryInterface(wrapper);
+ if (underware) {
+ JSObject* mainObj = underware->GetJSObject();
+ if (mainObj && mozilla::jsipc::IsWrappedCPOW(mainObj))
+ return mainObj;
+ }
+ return nullptr;
+}
+
+/***************************************************************************/
+
+// static
+bool
+XPCConvert::GetISupportsFromJSObject(JSObject* obj, nsISupports** iface)
+{
+ const JSClass* jsclass = js::GetObjectJSClass(obj);
+ MOZ_ASSERT(jsclass, "obj has no class");
+ if (jsclass &&
+ (jsclass->flags & JSCLASS_HAS_PRIVATE) &&
+ (jsclass->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)) {
+ *iface = (nsISupports*) xpc_GetJSPrivate(obj);
+ return true;
+ }
+ *iface = UnwrapDOMObjectToISupports(obj);
+ return !!*iface;
+}
+
+/***************************************************************************/
+
+// static
+bool
+XPCConvert::NativeData2JS(MutableHandleValue d, const void* s,
+ const nsXPTType& type, const nsID* iid, nsresult* pErr)
+{
+ NS_PRECONDITION(s, "bad param");
+
+ AutoJSContext cx;
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
+
+ switch (type.TagPart()) {
+ case nsXPTType::T_I8 :
+ d.setInt32(*static_cast<const int8_t*>(s));
+ return true;
+ case nsXPTType::T_I16 :
+ d.setInt32(*static_cast<const int16_t*>(s));
+ return true;
+ case nsXPTType::T_I32 :
+ d.setInt32(*static_cast<const int32_t*>(s));
+ return true;
+ case nsXPTType::T_I64 :
+ d.setNumber(static_cast<double>(*static_cast<const int64_t*>(s)));
+ return true;
+ case nsXPTType::T_U8 :
+ d.setInt32(*static_cast<const uint8_t*>(s));
+ return true;
+ case nsXPTType::T_U16 :
+ d.setInt32(*static_cast<const uint16_t*>(s));
+ return true;
+ case nsXPTType::T_U32 :
+ d.setNumber(*static_cast<const uint32_t*>(s));
+ return true;
+ case nsXPTType::T_U64 :
+ d.setNumber(static_cast<double>(*static_cast<const uint64_t*>(s)));
+ return true;
+ case nsXPTType::T_FLOAT :
+ d.setNumber(*static_cast<const float*>(s));
+ return true;
+ case nsXPTType::T_DOUBLE:
+ d.setNumber(*static_cast<const double*>(s));
+ return true;
+ case nsXPTType::T_BOOL :
+ d.setBoolean(*static_cast<const bool*>(s));
+ return true;
+ case nsXPTType::T_CHAR :
+ {
+ char p = *static_cast<const char*>(s);
+
+#ifdef STRICT_CHECK_OF_UNICODE
+ MOZ_ASSERT(! ILLEGAL_CHAR_RANGE(p) , "passing non ASCII data");
+#endif // STRICT_CHECK_OF_UNICODE
+
+ JSString* str = JS_NewStringCopyN(cx, &p, 1);
+ if (!str)
+ return false;
+
+ d.setString(str);
+ return true;
+ }
+ case nsXPTType::T_WCHAR :
+ {
+ char16_t p = *static_cast<const char16_t*>(s);
+
+ JSString* str = JS_NewUCStringCopyN(cx, &p, 1);
+ if (!str)
+ return false;
+
+ d.setString(str);
+ return true;
+ }
+
+ case nsXPTType::T_JSVAL :
+ {
+ d.set(*static_cast<const Value*>(s));
+ return JS_WrapValue(cx, d);
+ }
+
+ case nsXPTType::T_VOID:
+ XPC_LOG_ERROR(("XPCConvert::NativeData2JS : void* params not supported"));
+ return false;
+
+ case nsXPTType::T_IID:
+ {
+ nsID* iid2 = *static_cast<nsID* const*>(s);
+ if (!iid2) {
+ d.setNull();
+ return true;
+ }
+
+ RootedObject scope(cx, JS::CurrentGlobalOrNull(cx));
+ JSObject* obj = xpc_NewIDObject(cx, scope, *iid2);
+ if (!obj)
+ return false;
+
+ d.setObject(*obj);
+ return true;
+ }
+
+ case nsXPTType::T_ASTRING:
+ // Fall through to T_DOMSTRING case
+
+ case nsXPTType::T_DOMSTRING:
+ {
+ const nsAString* p = *static_cast<const nsAString* const*>(s);
+ if (!p || p->IsVoid()) {
+ d.setNull();
+ return true;
+ }
+
+ nsStringBuffer* buf;
+ if (!XPCStringConvert::ReadableToJSVal(cx, *p, &buf, d))
+ return false;
+ if (buf)
+ buf->AddRef();
+ return true;
+ }
+
+ case nsXPTType::T_CHAR_STR:
+ {
+ const char* p = *static_cast<const char* const*>(s);
+ if (!p) {
+ d.setNull();
+ return true;
+ }
+
+#ifdef STRICT_CHECK_OF_UNICODE
+ bool isAscii = true;
+ for (char* t = p; *t && isAscii; t++) {
+ if (ILLEGAL_CHAR_RANGE(*t))
+ isAscii = false;
+ }
+ MOZ_ASSERT(isAscii, "passing non ASCII data");
+#endif // STRICT_CHECK_OF_UNICODE
+
+ JSString* str = JS_NewStringCopyZ(cx, p);
+ if (!str)
+ return false;
+
+ d.setString(str);
+ return true;
+ }
+
+ case nsXPTType::T_WCHAR_STR:
+ {
+ const char16_t* p = *static_cast<const char16_t* const*>(s);
+ if (!p) {
+ d.setNull();
+ return true;
+ }
+
+ JSString* str = JS_NewUCStringCopyZ(cx, p);
+ if (!str)
+ return false;
+
+ d.setString(str);
+ return true;
+ }
+ case nsXPTType::T_UTF8STRING:
+ {
+ const nsACString* utf8String = *static_cast<const nsACString* const*>(s);
+
+ if (!utf8String || utf8String->IsVoid()) {
+ d.setNull();
+ return true;
+ }
+
+ if (utf8String->IsEmpty()) {
+ d.set(JS_GetEmptyStringValue(cx));
+ return true;
+ }
+
+ const uint32_t len = CalcUTF8ToUnicodeLength(*utf8String);
+ // The cString is not empty at this point, but the calculated
+ // UTF-16 length is zero, meaning no valid conversion exists.
+ if (!len)
+ return false;
+
+ const size_t buffer_size = (len + 1) * sizeof(char16_t);
+ char16_t* buffer =
+ static_cast<char16_t*>(JS_malloc(cx, buffer_size));
+ if (!buffer)
+ return false;
+
+ uint32_t copied;
+ if (!UTF8ToUnicodeBuffer(*utf8String, buffer, &copied) ||
+ len != copied) {
+ // Copy or conversion during copy failed. Did not copy the
+ // whole string.
+ JS_free(cx, buffer);
+ return false;
+ }
+
+ // JS_NewUCString takes ownership on success, i.e. a
+ // successful call will make it the responsiblity of the JS VM
+ // to free the buffer.
+ JSString* str = JS_NewUCString(cx, buffer, len);
+ if (!str) {
+ JS_free(cx, buffer);
+ return false;
+ }
+
+ d.setString(str);
+ return true;
+ }
+ case nsXPTType::T_CSTRING:
+ {
+ const nsACString* cString = *static_cast<const nsACString* const*>(s);
+
+ if (!cString || cString->IsVoid()) {
+ d.setNull();
+ return true;
+ }
+
+ // c-strings (binary blobs) are deliberately not converted from
+ // UTF-8 to UTF-16. T_UTF8Sting is for UTF-8 encoded strings
+ // with automatic conversion.
+ JSString* str = JS_NewStringCopyN(cx, cString->Data(),
+ cString->Length());
+ if (!str)
+ return false;
+
+ d.setString(str);
+ return true;
+ }
+
+ case nsXPTType::T_INTERFACE:
+ case nsXPTType::T_INTERFACE_IS:
+ {
+ nsISupports* iface = *static_cast<nsISupports* const*>(s);
+ if (!iface) {
+ d.setNull();
+ return true;
+ }
+
+ if (iid->Equals(NS_GET_IID(nsIVariant))) {
+ nsCOMPtr<nsIVariant> variant = do_QueryInterface(iface);
+ if (!variant)
+ return false;
+
+ return XPCVariant::VariantDataToJS(variant,
+ pErr, d);
+ }
+
+ xpcObjectHelper helper(iface);
+ return NativeInterface2JSObject(d, nullptr, helper, iid, true, pErr);
+ }
+
+ default:
+ NS_ERROR("bad type");
+ return false;
+ }
+ return true;
+}
+
+/***************************************************************************/
+
+#ifdef DEBUG
+static bool
+CheckChar16InCharRange(char16_t c)
+{
+ if (ILLEGAL_RANGE(c)) {
+ /* U+0080/U+0100 - U+FFFF data lost. */
+ static const size_t MSG_BUF_SIZE = 64;
+ char msg[MSG_BUF_SIZE];
+ snprintf(msg, MSG_BUF_SIZE, "char16_t out of char range; high bits of data lost: 0x%x", int(c));
+ NS_WARNING(msg);
+ return false;
+ }
+
+ return true;
+}
+
+template<typename CharT>
+static void
+CheckCharsInCharRange(const CharT* chars, size_t len)
+{
+ for (size_t i = 0; i < len; i++) {
+ if (!CheckChar16InCharRange(chars[i]))
+ break;
+ }
+}
+#endif
+
+template<typename T>
+bool ConvertToPrimitive(JSContext* cx, HandleValue v, T* retval)
+{
+ return ValueToPrimitive<T, eDefault>(cx, v, retval);
+}
+
+// static
+bool
+XPCConvert::JSData2Native(void* d, HandleValue s,
+ const nsXPTType& type,
+ const nsID* iid,
+ nsresult* pErr)
+{
+ NS_PRECONDITION(d, "bad param");
+
+ AutoJSContext cx;
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+
+ switch (type.TagPart()) {
+ case nsXPTType::T_I8 :
+ return ConvertToPrimitive(cx, s, static_cast<int8_t*>(d));
+ case nsXPTType::T_I16 :
+ return ConvertToPrimitive(cx, s, static_cast<int16_t*>(d));
+ case nsXPTType::T_I32 :
+ return ConvertToPrimitive(cx, s, static_cast<int32_t*>(d));
+ case nsXPTType::T_I64 :
+ return ConvertToPrimitive(cx, s, static_cast<int64_t*>(d));
+ case nsXPTType::T_U8 :
+ return ConvertToPrimitive(cx, s, static_cast<uint8_t*>(d));
+ case nsXPTType::T_U16 :
+ return ConvertToPrimitive(cx, s, static_cast<uint16_t*>(d));
+ case nsXPTType::T_U32 :
+ return ConvertToPrimitive(cx, s, static_cast<uint32_t*>(d));
+ case nsXPTType::T_U64 :
+ return ConvertToPrimitive(cx, s, static_cast<uint64_t*>(d));
+ case nsXPTType::T_FLOAT :
+ return ConvertToPrimitive(cx, s, static_cast<float*>(d));
+ case nsXPTType::T_DOUBLE :
+ return ConvertToPrimitive(cx, s, static_cast<double*>(d));
+ case nsXPTType::T_BOOL :
+ return ConvertToPrimitive(cx, s, static_cast<bool*>(d));
+ case nsXPTType::T_CHAR :
+ {
+ JSString* str = ToString(cx, s);
+ if (!str) {
+ return false;
+ }
+
+ char16_t ch;
+ if (JS_GetStringLength(str) == 0) {
+ ch = 0;
+ } else {
+ if (!JS_GetStringCharAt(cx, str, 0, &ch))
+ return false;
+ }
+#ifdef DEBUG
+ CheckChar16InCharRange(ch);
+#endif
+ *((char*)d) = char(ch);
+ break;
+ }
+ case nsXPTType::T_WCHAR :
+ {
+ JSString* str;
+ if (!(str = ToString(cx, s))) {
+ return false;
+ }
+ size_t length = JS_GetStringLength(str);
+ if (length == 0) {
+ *((uint16_t*)d) = 0;
+ break;
+ }
+
+ char16_t ch;
+ if (!JS_GetStringCharAt(cx, str, 0, &ch))
+ return false;
+
+ *((uint16_t*)d) = uint16_t(ch);
+ break;
+ }
+ case nsXPTType::T_JSVAL :
+ *((Value*)d) = s;
+ break;
+ case nsXPTType::T_VOID:
+ XPC_LOG_ERROR(("XPCConvert::JSData2Native : void* params not supported"));
+ NS_ERROR("void* params not supported");
+ return false;
+ case nsXPTType::T_IID:
+ {
+ const nsID* pid = nullptr;
+
+ // There's no good reason to pass a null IID.
+ if (s.isNullOrUndefined()) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+ return false;
+ }
+
+ if (!s.isObject() ||
+ (!(pid = xpc_JSObjectToID(cx, &s.toObject()))) ||
+ (!(pid = (const nsID*) nsMemory::Clone(pid, sizeof(nsID))))) {
+ return false;
+ }
+ *((const nsID**)d) = pid;
+ return true;
+ }
+
+ case nsXPTType::T_ASTRING:
+ {
+ if (s.isUndefined()) {
+ (**((nsAString**)d)).SetIsVoid(true);
+ return true;
+ }
+ MOZ_FALLTHROUGH;
+ }
+ case nsXPTType::T_DOMSTRING:
+ {
+ if (s.isNull()) {
+ (**((nsAString**)d)).SetIsVoid(true);
+ return true;
+ }
+ size_t length = 0;
+ JSString* str = nullptr;
+ if (!s.isUndefined()) {
+ str = ToString(cx, s);
+ if (!str)
+ return false;
+
+ length = JS_GetStringLength(str);
+ if (!length) {
+ (**((nsAString**)d)).Truncate();
+ return true;
+ }
+ }
+
+ nsAString* ws = *((nsAString**)d);
+
+ if (!str) {
+ ws->AssignLiteral(u"undefined");
+ } else if (XPCStringConvert::IsDOMString(str)) {
+ // The characters represent an existing nsStringBuffer that
+ // was shared by XPCStringConvert::ReadableToJSVal.
+ const char16_t* chars = JS_GetTwoByteExternalStringChars(str);
+ if (chars[length] == '\0') {
+ // Safe to share the buffer.
+ nsStringBuffer::FromData((void*)chars)->ToString(length, *ws);
+ } else {
+ // We have to copy to ensure null-termination.
+ ws->Assign(chars, length);
+ }
+ } else if (XPCStringConvert::IsLiteral(str)) {
+ // The characters represent a literal char16_t string constant
+ // compiled into libxul, such as the string "undefined" above.
+ const char16_t* chars = JS_GetTwoByteExternalStringChars(str);
+ ws->AssignLiteral(chars, length);
+ } else {
+ if (!AssignJSString(cx, *ws, str))
+ return false;
+ }
+ return true;
+ }
+
+ case nsXPTType::T_CHAR_STR:
+ {
+ if (s.isUndefined() || s.isNull()) {
+ *((char**)d) = nullptr;
+ return true;
+ }
+
+ JSString* str = ToString(cx, s);
+ if (!str) {
+ return false;
+ }
+#ifdef DEBUG
+ if (JS_StringHasLatin1Chars(str)) {
+ size_t len;
+ AutoCheckCannotGC nogc;
+ const Latin1Char* chars = JS_GetLatin1StringCharsAndLength(cx, nogc, str, &len);
+ if (chars)
+ CheckCharsInCharRange(chars, len);
+ } else {
+ size_t len;
+ AutoCheckCannotGC nogc;
+ const char16_t* chars = JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &len);
+ if (chars)
+ CheckCharsInCharRange(chars, len);
+ }
+#endif // DEBUG
+ size_t length = JS_GetStringEncodingLength(cx, str);
+ if (length == size_t(-1)) {
+ return false;
+ }
+ char* buffer = static_cast<char*>(moz_xmalloc(length + 1));
+ if (!buffer) {
+ return false;
+ }
+ JS_EncodeStringToBuffer(cx, str, buffer, length);
+ buffer[length] = '\0';
+ *((void**)d) = buffer;
+ return true;
+ }
+
+ case nsXPTType::T_WCHAR_STR:
+ {
+ JSString* str;
+
+ if (s.isUndefined() || s.isNull()) {
+ *((char16_t**)d) = nullptr;
+ return true;
+ }
+
+ if (!(str = ToString(cx, s))) {
+ return false;
+ }
+ int len = JS_GetStringLength(str);
+ int byte_len = (len+1)*sizeof(char16_t);
+ if (!(*((void**)d) = moz_xmalloc(byte_len))) {
+ // XXX should report error
+ return false;
+ }
+ mozilla::Range<char16_t> destChars(*((char16_t**)d), len + 1);
+ if (!JS_CopyStringChars(cx, destChars, str))
+ return false;
+ destChars[len] = 0;
+
+ return true;
+ }
+
+ case nsXPTType::T_UTF8STRING:
+ {
+ if (s.isNull() || s.isUndefined()) {
+ nsCString* rs = *((nsCString**)d);
+ rs->SetIsVoid(true);
+ return true;
+ }
+
+ // The JS val is neither null nor void...
+ JSString* str = ToString(cx, s);
+ if (!str)
+ return false;
+
+ size_t length = JS_GetStringLength(str);
+ if (!length) {
+ nsCString* rs = *((nsCString**)d);
+ rs->Truncate();
+ return true;
+ }
+
+ JSFlatString* flat = JS_FlattenString(cx, str);
+ if (!flat)
+ return false;
+
+ size_t utf8Length = JS::GetDeflatedUTF8StringLength(flat);
+ nsACString* rs = *((nsACString**)d);
+ rs->SetLength(utf8Length);
+
+ JS::DeflateStringToUTF8Buffer(flat, mozilla::RangedPtr<char>(rs->BeginWriting(), utf8Length));
+
+ return true;
+ }
+
+ case nsXPTType::T_CSTRING:
+ {
+ if (s.isNull() || s.isUndefined()) {
+ nsACString* rs = *((nsACString**)d);
+ rs->SetIsVoid(true);
+ return true;
+ }
+
+ // The JS val is neither null nor void...
+ JSString* str = ToString(cx, s);
+ if (!str) {
+ return false;
+ }
+
+ size_t length = JS_GetStringEncodingLength(cx, str);
+ if (length == size_t(-1)) {
+ return false;
+ }
+
+ if (!length) {
+ nsCString* rs = *((nsCString**)d);
+ rs->Truncate();
+ return true;
+ }
+
+ nsACString* rs = *((nsACString**)d);
+ rs->SetLength(uint32_t(length));
+ if (rs->Length() != uint32_t(length)) {
+ return false;
+ }
+ JS_EncodeStringToBuffer(cx, str, rs->BeginWriting(), length);
+
+ return true;
+ }
+
+ case nsXPTType::T_INTERFACE:
+ case nsXPTType::T_INTERFACE_IS:
+ {
+ MOZ_ASSERT(iid,"can't do interface conversions without iid");
+
+ if (iid->Equals(NS_GET_IID(nsIVariant))) {
+ nsCOMPtr<nsIVariant> variant = XPCVariant::newVariant(cx, s);
+ if (!variant)
+ return false;
+
+ variant.forget(static_cast<nsISupports**>(d));
+ return true;
+ } else if (iid->Equals(NS_GET_IID(nsIAtom)) && s.isString()) {
+ // We're trying to pass a string as an nsIAtom. Let's atomize!
+ JSString* str = s.toString();
+ nsAutoJSString autoStr;
+ if (!autoStr.init(cx, str)) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_NULL_REF;
+ return false;
+ }
+ nsCOMPtr<nsIAtom> atom = NS_Atomize(autoStr);
+ atom.forget((nsISupports**)d);
+ return true;
+ }
+ //else ...
+
+ if (s.isNullOrUndefined()) {
+ *((nsISupports**)d) = nullptr;
+ return true;
+ }
+
+ // only wrap JSObjects
+ if (!s.isObject()) {
+ if (pErr && s.isInt32() && 0 == s.toInt32())
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_JS_ZERO_ISNOT_NULL;
+ return false;
+ }
+
+ RootedObject src(cx, &s.toObject());
+ return JSObject2NativeInterface((void**)d, src, iid, nullptr, pErr);
+ }
+ default:
+ NS_ERROR("bad type");
+ return false;
+ }
+ return true;
+}
+
+static inline bool
+CreateHolderIfNeeded(HandleObject obj, MutableHandleValue d,
+ nsIXPConnectJSObjectHolder** dest)
+{
+ if (dest) {
+ if (!obj)
+ return false;
+ RefPtr<XPCJSObjectHolder> objHolder = new XPCJSObjectHolder(obj);
+ objHolder.forget(dest);
+ }
+
+ d.setObjectOrNull(obj);
+
+ return true;
+}
+
+/***************************************************************************/
+// static
+bool
+XPCConvert::NativeInterface2JSObject(MutableHandleValue d,
+ nsIXPConnectJSObjectHolder** dest,
+ xpcObjectHelper& aHelper,
+ const nsID* iid,
+ bool allowNativeWrapper,
+ nsresult* pErr)
+{
+ if (!iid)
+ iid = &NS_GET_IID(nsISupports);
+
+ d.setNull();
+ if (dest)
+ *dest = nullptr;
+ if (!aHelper.Object())
+ return true;
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
+
+ // We used to have code here that unwrapped and simply exposed the
+ // underlying JSObject. That caused anomolies when JSComponents were
+ // accessed from other JS code - they didn't act like other xpconnect
+ // wrapped components. So, instead, we create "double wrapped" objects
+ // (that means an XPCWrappedNative around an nsXPCWrappedJS). This isn't
+ // optimal -- we could detect this and roll the functionality into a
+ // single wrapper, but the current solution is good enough for now.
+ AutoJSContext cx;
+ XPCWrappedNativeScope* xpcscope = ObjectScope(JS::CurrentGlobalOrNull(cx));
+ if (!xpcscope)
+ return false;
+
+ // First, see if this object supports the wrapper cache.
+ // Note: If |cache->IsDOMBinding()| is true, then it means that the object
+ // implementing it doesn't want a wrapped native as its JS Object, but
+ // instead it provides its own proxy object. In that case, the object
+ // to use is found as cache->GetWrapper(). If that is null, then the
+ // object will create (and fill the cache) from its WrapObject call.
+ nsWrapperCache* cache = aHelper.GetWrapperCache();
+
+ RootedObject flat(cx, cache ? cache->GetWrapper() : nullptr);
+ if (!flat && cache && cache->IsDOMBinding()) {
+ RootedObject global(cx, xpcscope->GetGlobalJSObject());
+ js::AssertSameCompartment(cx, global);
+ flat = cache->WrapObject(cx, nullptr);
+ if (!flat)
+ return false;
+ }
+ if (flat) {
+ if (allowNativeWrapper && !JS_WrapObject(cx, &flat))
+ return false;
+ return CreateHolderIfNeeded(flat, d, dest);
+ }
+
+#ifdef SPIDERMONKEY_PROMISE
+ if (iid->Equals(NS_GET_IID(nsISupports))) {
+ // Check for a Promise being returned via nsISupports. In that
+ // situation, we want to dig out its underlying JS object and return
+ // that.
+ RefPtr<Promise> promise = do_QueryObject(aHelper.Object());
+ if (promise) {
+ flat = promise->PromiseObj();
+ if (!JS_WrapObject(cx, &flat))
+ return false;
+ return CreateHolderIfNeeded(flat, d, dest);
+ }
+ }
+#endif // SPIDERMONKEY_PROMISE
+
+ // Don't double wrap CPOWs. This is a temporary measure for compatibility
+ // with objects that don't provide necessary QIs (such as objects under
+ // the new DOM bindings). We expect the other side of the CPOW to have
+ // the appropriate wrappers in place.
+ RootedObject cpow(cx, UnwrapNativeCPOW(aHelper.Object()));
+ if (cpow) {
+ if (!JS_WrapObject(cx, &cpow))
+ return false;
+ d.setObject(*cpow);
+ return true;
+ }
+
+ // Go ahead and create an XPCWrappedNative for this object.
+ RefPtr<XPCNativeInterface> iface =
+ XPCNativeInterface::GetNewOrUsed(iid);
+ if (!iface)
+ return false;
+
+ RefPtr<XPCWrappedNative> wrapper;
+ nsresult rv = XPCWrappedNative::GetNewOrUsed(aHelper, xpcscope, iface,
+ getter_AddRefs(wrapper));
+ if (NS_FAILED(rv) && pErr)
+ *pErr = rv;
+
+ // If creating the wrapped native failed, then return early.
+ if (NS_FAILED(rv) || !wrapper)
+ return false;
+
+ // If we're not creating security wrappers, we can return the
+ // XPCWrappedNative as-is here.
+ flat = wrapper->GetFlatJSObject();
+ if (!allowNativeWrapper) {
+ d.setObjectOrNull(flat);
+ if (dest)
+ wrapper.forget(dest);
+ if (pErr)
+ *pErr = NS_OK;
+ return true;
+ }
+
+ // The call to wrap here handles both cross-compartment and same-compartment
+ // security wrappers.
+ RootedObject original(cx, flat);
+ if (!JS_WrapObject(cx, &flat))
+ return false;
+
+ d.setObjectOrNull(flat);
+
+ if (dest) {
+ // The wrapper still holds the original flat object.
+ if (flat == original) {
+ wrapper.forget(dest);
+ } else {
+ if (!flat)
+ return false;
+ RefPtr<XPCJSObjectHolder> objHolder = new XPCJSObjectHolder(flat);
+ objHolder.forget(dest);
+ }
+ }
+
+ if (pErr)
+ *pErr = NS_OK;
+
+ return true;
+}
+
+/***************************************************************************/
+
+// static
+bool
+XPCConvert::JSObject2NativeInterface(void** dest, HandleObject src,
+ const nsID* iid,
+ nsISupports* aOuter,
+ nsresult* pErr)
+{
+ MOZ_ASSERT(dest, "bad param");
+ MOZ_ASSERT(src, "bad param");
+ MOZ_ASSERT(iid, "bad param");
+
+ AutoJSContext cx;
+ JSAutoCompartment ac(cx, src);
+
+ *dest = nullptr;
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+
+ nsISupports* iface;
+
+ if (!aOuter) {
+ // Note that if we have a non-null aOuter then it means that we are
+ // forcing the creation of a wrapper even if the object *is* a
+ // wrappedNative or other wise has 'nsISupportness'.
+ // This allows wrapJSAggregatedToNative to work.
+
+ // If we're looking at a security wrapper, see now if we're allowed to
+ // pass it to C++. If we are, then fall through to the code below. If
+ // we aren't, throw an exception eagerly.
+ //
+ // NB: It's very important that we _don't_ unwrap in the aOuter case,
+ // because the caller may explicitly want to create the XPCWrappedJS
+ // around a security wrapper. XBL does this with Xrays from the XBL
+ // scope - see nsBindingManager::GetBindingImplementation.
+ //
+ // It's also very important that "inner" be rooted here.
+ RootedObject inner(cx,
+ js::CheckedUnwrap(src,
+ /* stopAtWindowProxy = */ false));
+ if (!inner) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_SECURITY_MANAGER_VETO;
+ return false;
+ }
+
+ // Is this really a native xpcom object with a wrapper?
+ XPCWrappedNative* wrappedNative = nullptr;
+ if (IS_WN_REFLECTOR(inner))
+ wrappedNative = XPCWrappedNative::Get(inner);
+ if (wrappedNative) {
+ iface = wrappedNative->GetIdentityObject();
+ return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
+ }
+ // else...
+
+ // Deal with slim wrappers here.
+ if (GetISupportsFromJSObject(inner ? inner : src, &iface)) {
+ if (iface && NS_SUCCEEDED(iface->QueryInterface(*iid, dest))) {
+ return true;
+ }
+
+ // If that failed, and iid is for mozIDOMWindowProxy, we actually
+ // want the outer!
+ if (iid->Equals(NS_GET_IID(mozIDOMWindowProxy))) {
+ if (nsCOMPtr<mozIDOMWindow> inner = do_QueryInterface(iface)) {
+ iface = nsPIDOMWindowInner::From(inner)->GetOuterWindow();
+ return NS_SUCCEEDED(iface->QueryInterface(*iid, dest));
+ }
+ }
+
+ return false;
+ }
+
+#ifdef SPIDERMONKEY_PROMISE
+ // Deal with Promises being passed as nsISupports. In that situation we
+ // want to create a dom::Promise and use that.
+ if (iid->Equals(NS_GET_IID(nsISupports))) {
+ RootedObject innerObj(cx, inner);
+ if (IsPromiseObject(innerObj)) {
+ nsIGlobalObject* glob = NativeGlobal(innerObj);
+ RefPtr<Promise> p = Promise::CreateFromExisting(glob, innerObj);
+ return p && NS_SUCCEEDED(p->QueryInterface(*iid, dest));
+ }
+ }
+#endif // SPIDERMONKEY_PROMISE
+ }
+
+ RefPtr<nsXPCWrappedJS> wrapper;
+ nsresult rv = nsXPCWrappedJS::GetNewOrUsed(src, *iid, getter_AddRefs(wrapper));
+ if (pErr)
+ *pErr = rv;
+
+ if (NS_FAILED(rv) || !wrapper)
+ return false;
+
+ // If the caller wanted to aggregate this JS object to a native,
+ // attach it to the wrapper. Note that we allow a maximum of one
+ // aggregated native for a given XPCWrappedJS.
+ if (aOuter)
+ wrapper->SetAggregatedNativeObject(aOuter);
+
+ // We need to go through the QueryInterface logic to make this return
+ // the right thing for the various 'special' interfaces; e.g.
+ // nsIPropertyBag. We must use AggregatedQueryInterface in cases where
+ // there is an outer to avoid nasty recursion.
+ rv = aOuter ? wrapper->AggregatedQueryInterface(*iid, dest) :
+ wrapper->QueryInterface(*iid, dest);
+ if (pErr)
+ *pErr = rv;
+ return NS_SUCCEEDED(rv);
+}
+
+/***************************************************************************/
+/***************************************************************************/
+
+// static
+nsresult
+XPCConvert::ConstructException(nsresult rv, const char* message,
+ const char* ifaceName, const char* methodName,
+ nsISupports* data,
+ nsIException** exceptn,
+ JSContext* cx,
+ Value* jsExceptionPtr)
+{
+ MOZ_ASSERT(!cx == !jsExceptionPtr, "Expected cx and jsExceptionPtr to cooccur.");
+
+ static const char format[] = "\'%s\' when calling method: [%s::%s]";
+ const char * msg = message;
+ nsXPIDLString xmsg;
+ nsAutoCString sxmsg;
+
+ nsCOMPtr<nsIScriptError> errorObject = do_QueryInterface(data);
+ if (errorObject) {
+ if (NS_SUCCEEDED(errorObject->GetMessageMoz(getter_Copies(xmsg)))) {
+ CopyUTF16toUTF8(xmsg, sxmsg);
+ msg = sxmsg.get();
+ }
+ }
+ if (!msg)
+ if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &msg) || ! msg)
+ msg = "<error>";
+
+ nsCString msgStr(msg);
+ if (ifaceName && methodName)
+ msgStr.AppendPrintf(format, msg, ifaceName, methodName);
+
+ RefPtr<Exception> e = new Exception(msgStr, rv, EmptyCString(), nullptr, data);
+
+ if (cx && jsExceptionPtr) {
+ e->StowJSVal(*jsExceptionPtr);
+ }
+
+ e.forget(exceptn);
+ return NS_OK;
+}
+
+/********************************/
+
+class MOZ_STACK_CLASS AutoExceptionRestorer
+{
+public:
+ AutoExceptionRestorer(JSContext* cx, const Value& v)
+ : mContext(cx), tvr(cx, v)
+ {
+ JS_ClearPendingException(mContext);
+ }
+
+ ~AutoExceptionRestorer()
+ {
+ JS_SetPendingException(mContext, tvr);
+ }
+
+private:
+ JSContext * const mContext;
+ RootedValue tvr;
+};
+
+static nsresult
+JSErrorToXPCException(const char* toStringResult,
+ const char* ifaceName,
+ const char* methodName,
+ const JSErrorReport* report,
+ nsIException** exceptn)
+{
+ AutoJSContext cx;
+ nsresult rv = NS_ERROR_FAILURE;
+ RefPtr<nsScriptError> data;
+ if (report) {
+ nsAutoString bestMessage;
+ if (report && report->message()) {
+ CopyUTF8toUTF16(report->message().c_str(), bestMessage);
+ } else if (toStringResult) {
+ CopyUTF8toUTF16(toStringResult, bestMessage);
+ } else {
+ bestMessage.AssignLiteral("JavaScript Error");
+ }
+
+ const char16_t* linebuf = report->linebuf();
+
+ data = new nsScriptError();
+ data->InitWithWindowID(
+ bestMessage,
+ NS_ConvertASCIItoUTF16(report->filename),
+ linebuf ? nsDependentString(linebuf, report->linebufLength()) : EmptyString(),
+ report->lineno,
+ report->tokenOffset(), report->flags,
+ NS_LITERAL_CSTRING("XPConnect JavaScript"),
+ nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
+ }
+
+ if (data) {
+ nsAutoCString formattedMsg;
+ data->ToString(formattedMsg);
+
+ rv = XPCConvert::ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS,
+ formattedMsg.get(), ifaceName,
+ methodName,
+ static_cast<nsIScriptError*>(data.get()),
+ exceptn, nullptr, nullptr);
+ } else {
+ rv = XPCConvert::ConstructException(NS_ERROR_XPC_JAVASCRIPT_ERROR,
+ nullptr, ifaceName, methodName,
+ nullptr, exceptn, nullptr, nullptr);
+ }
+ return rv;
+}
+
+// static
+nsresult
+XPCConvert::JSValToXPCException(MutableHandleValue s,
+ const char* ifaceName,
+ const char* methodName,
+ nsIException** exceptn)
+{
+ AutoJSContext cx;
+ AutoExceptionRestorer aer(cx, s);
+
+ if (!s.isPrimitive()) {
+ // we have a JSObject
+ RootedObject obj(cx, s.toObjectOrNull());
+
+ if (!obj) {
+ NS_ERROR("when is an object not an object?");
+ return NS_ERROR_FAILURE;
+ }
+
+ // is this really a native xpcom object with a wrapper?
+ JSObject* unwrapped = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false);
+ if (!unwrapped)
+ return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
+ if (nsCOMPtr<nsISupports> supports = UnwrapReflectorToISupports(unwrapped)) {
+ nsCOMPtr<nsIException> iface = do_QueryInterface(supports);
+ if (iface) {
+ // just pass through the exception (with extra ref and all)
+ nsCOMPtr<nsIException> temp = iface;
+ temp.forget(exceptn);
+ return NS_OK;
+ } else {
+ // it is a wrapped native, but not an exception!
+ return ConstructException(NS_ERROR_XPC_JS_THREW_NATIVE_OBJECT,
+ nullptr, ifaceName, methodName, supports,
+ exceptn, nullptr, nullptr);
+ }
+ } else {
+ // It is a JSObject, but not a wrapped native...
+
+ // If it is an engine Error with an error report then let's
+ // extract the report and build an xpcexception from that
+ const JSErrorReport* report;
+ if (nullptr != (report = JS_ErrorFromException(cx, obj))) {
+ JSAutoByteString toStringResult;
+ RootedString str(cx, ToString(cx, s));
+ if (str)
+ toStringResult.encodeUtf8(cx, str);
+ return JSErrorToXPCException(toStringResult.ptr(), ifaceName,
+ methodName, report, exceptn);
+ }
+
+ // XXX we should do a check against 'js_ErrorClass' here and
+ // do the right thing - even though it has no JSErrorReport,
+ // The fact that it is a JSError exceptions means we can extract
+ // particular info and our 'result' should reflect that.
+
+ // otherwise we'll just try to convert it to a string
+
+ JSString* str = ToString(cx, s);
+ if (!str)
+ return NS_ERROR_FAILURE;
+
+ JSAutoByteString strBytes(cx, str);
+ if (!strBytes)
+ return NS_ERROR_FAILURE;
+
+ return ConstructException(NS_ERROR_XPC_JS_THREW_JS_OBJECT,
+ strBytes.ptr(), ifaceName, methodName,
+ nullptr, exceptn, cx, s.address());
+ }
+ }
+
+ if (s.isUndefined() || s.isNull()) {
+ return ConstructException(NS_ERROR_XPC_JS_THREW_NULL,
+ nullptr, ifaceName, methodName, nullptr,
+ exceptn, cx, s.address());
+ }
+
+ if (s.isNumber()) {
+ // lets see if it looks like an nsresult
+ nsresult rv;
+ double number;
+ bool isResult = false;
+
+ if (s.isInt32()) {
+ rv = (nsresult) s.toInt32();
+ if (NS_FAILED(rv))
+ isResult = true;
+ else
+ number = (double) s.toInt32();
+ } else {
+ number = s.toDouble();
+ if (number > 0.0 &&
+ number < (double)0xffffffff &&
+ 0.0 == fmod(number,1)) {
+ // Visual Studio 9 doesn't allow casting directly from a
+ // double to an enumeration type, contrary to 5.2.9(10) of
+ // C++11, so add an intermediate cast.
+ rv = (nsresult)(uint32_t) number;
+ if (NS_FAILED(rv))
+ isResult = true;
+ }
+ }
+
+ if (isResult)
+ return ConstructException(rv, nullptr, ifaceName, methodName,
+ nullptr, exceptn, cx, s.address());
+ else {
+ // XXX all this nsISupportsDouble code seems a little redundant
+ // now that we're storing the Value in the exception...
+ nsCOMPtr<nsISupportsDouble> data;
+ nsCOMPtr<nsIComponentManager> cm;
+ if (NS_FAILED(NS_GetComponentManager(getter_AddRefs(cm))) || !cm ||
+ NS_FAILED(cm->CreateInstanceByContractID(NS_SUPPORTS_DOUBLE_CONTRACTID,
+ nullptr,
+ NS_GET_IID(nsISupportsDouble),
+ getter_AddRefs(data))))
+ return NS_ERROR_FAILURE;
+ data->SetData(number);
+ rv = ConstructException(NS_ERROR_XPC_JS_THREW_NUMBER, nullptr,
+ ifaceName, methodName, data, exceptn, cx, s.address());
+ return rv;
+ }
+ }
+
+ // otherwise we'll just try to convert it to a string
+ // Note: e.g., bools get converted to JSStrings by this code.
+
+ JSString* str = ToString(cx, s);
+ if (str) {
+ JSAutoByteString strBytes(cx, str);
+ if (!!strBytes) {
+ return ConstructException(NS_ERROR_XPC_JS_THREW_STRING,
+ strBytes.ptr(), ifaceName, methodName,
+ nullptr, exceptn, cx, s.address());
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+/***************************************************************************/
+
+// array fun...
+
+#ifdef POPULATE
+#undef POPULATE
+#endif
+
+// static
+bool
+XPCConvert::NativeArray2JS(MutableHandleValue d, const void** s,
+ const nsXPTType& type, const nsID* iid,
+ uint32_t count, nsresult* pErr)
+{
+ NS_PRECONDITION(s, "bad param");
+
+ AutoJSContext cx;
+
+ // XXX add support for putting chars in a string rather than an array
+
+ // XXX add support to indicate *which* array element was not convertable
+
+ RootedObject array(cx, JS_NewArrayObject(cx, count));
+ if (!array)
+ return false;
+
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
+
+ uint32_t i;
+ RootedValue current(cx, JS::NullValue());
+
+#define POPULATE(_t) \
+ PR_BEGIN_MACRO \
+ for (i = 0; i < count; i++) { \
+ if (!NativeData2JS(&current, ((_t*)*s)+i, type, iid, pErr) || \
+ !JS_DefineElement(cx, array, i, current, JSPROP_ENUMERATE)) \
+ return false; \
+ } \
+ PR_END_MACRO
+
+ // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
+
+ switch (type.TagPart()) {
+ case nsXPTType::T_I8 : POPULATE(int8_t); break;
+ case nsXPTType::T_I16 : POPULATE(int16_t); break;
+ case nsXPTType::T_I32 : POPULATE(int32_t); break;
+ case nsXPTType::T_I64 : POPULATE(int64_t); break;
+ case nsXPTType::T_U8 : POPULATE(uint8_t); break;
+ case nsXPTType::T_U16 : POPULATE(uint16_t); break;
+ case nsXPTType::T_U32 : POPULATE(uint32_t); break;
+ case nsXPTType::T_U64 : POPULATE(uint64_t); break;
+ case nsXPTType::T_FLOAT : POPULATE(float); break;
+ case nsXPTType::T_DOUBLE : POPULATE(double); break;
+ case nsXPTType::T_BOOL : POPULATE(bool); break;
+ case nsXPTType::T_CHAR : POPULATE(char); break;
+ case nsXPTType::T_WCHAR : POPULATE(char16_t); break;
+ case nsXPTType::T_VOID : NS_ERROR("bad type"); return false;
+ case nsXPTType::T_IID : POPULATE(nsID*); break;
+ case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); return false;
+ case nsXPTType::T_CHAR_STR : POPULATE(char*); break;
+ case nsXPTType::T_WCHAR_STR : POPULATE(char16_t*); break;
+ case nsXPTType::T_INTERFACE : POPULATE(nsISupports*); break;
+ case nsXPTType::T_INTERFACE_IS : POPULATE(nsISupports*); break;
+ case nsXPTType::T_UTF8STRING : NS_ERROR("bad type"); return false;
+ case nsXPTType::T_CSTRING : NS_ERROR("bad type"); return false;
+ case nsXPTType::T_ASTRING : NS_ERROR("bad type"); return false;
+ default : NS_ERROR("bad type"); return false;
+ }
+
+ if (pErr)
+ *pErr = NS_OK;
+ d.setObject(*array);
+ return true;
+
+#undef POPULATE
+}
+
+
+
+// Check that the tag part of the type matches the type
+// of the array. If the check succeeds, check that the size
+// of the output does not exceed UINT32_MAX bytes. Allocate
+// the memory and copy the elements by memcpy.
+static bool
+CheckTargetAndPopulate(const nsXPTType& type,
+ uint8_t requiredType,
+ size_t typeSize,
+ uint32_t count,
+ JSObject* tArr,
+ void** output,
+ nsresult* pErr)
+{
+ // Check that the element type expected by the interface matches
+ // the type of the elements in the typed array exactly, including
+ // signedness.
+ if (type.TagPart() != requiredType) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+
+ return false;
+ }
+
+ // Calulate the maximum number of elements that can fit in
+ // UINT32_MAX bytes.
+ size_t max = UINT32_MAX / typeSize;
+
+ // This could overflow on 32-bit systems so check max first.
+ size_t byteSize = count * typeSize;
+ if (count > max || !(*output = moz_xmalloc(byteSize))) {
+ if (pErr)
+ *pErr = NS_ERROR_OUT_OF_MEMORY;
+
+ return false;
+ }
+
+ JS::AutoCheckCannotGC nogc;
+ bool isShared;
+ void* buf = JS_GetArrayBufferViewData(tArr, &isShared, nogc);
+
+ // Require opting in to shared memory - a future project.
+ if (isShared) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+
+ return false;
+ }
+
+ memcpy(*output, buf, byteSize);
+ return true;
+}
+
+// Fast conversion of typed arrays to native using memcpy.
+// No float or double canonicalization is done. Called by
+// JSarray2Native whenever a TypedArray is met. ArrayBuffers
+// are not accepted; create a properly typed array view on them
+// first. The element type of array must match the XPCOM
+// type in size, type and signedness exactly. As an exception,
+// Uint8ClampedArray is allowed for arrays of uint8_t. DataViews
+// are not supported.
+
+// static
+bool
+XPCConvert::JSTypedArray2Native(void** d,
+ JSObject* jsArray,
+ uint32_t count,
+ const nsXPTType& type,
+ nsresult* pErr)
+{
+ MOZ_ASSERT(jsArray, "bad param");
+ MOZ_ASSERT(d, "bad param");
+ MOZ_ASSERT(JS_IsTypedArrayObject(jsArray), "not a typed array");
+
+ // Check the actual length of the input array against the
+ // given size_is.
+ uint32_t len = JS_GetTypedArrayLength(jsArray);
+ if (len < count) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
+
+ return false;
+ }
+
+ void* output = nullptr;
+
+ switch (JS_GetArrayBufferViewType(jsArray)) {
+ case js::Scalar::Int8:
+ if (!CheckTargetAndPopulate(nsXPTType::T_I8, type,
+ sizeof(int8_t), count,
+ jsArray, &output, pErr)) {
+ return false;
+ }
+ break;
+
+ case js::Scalar::Uint8:
+ case js::Scalar::Uint8Clamped:
+ if (!CheckTargetAndPopulate(nsXPTType::T_U8, type,
+ sizeof(uint8_t), count,
+ jsArray, &output, pErr)) {
+ return false;
+ }
+ break;
+
+ case js::Scalar::Int16:
+ if (!CheckTargetAndPopulate(nsXPTType::T_I16, type,
+ sizeof(int16_t), count,
+ jsArray, &output, pErr)) {
+ return false;
+ }
+ break;
+
+ case js::Scalar::Uint16:
+ if (!CheckTargetAndPopulate(nsXPTType::T_U16, type,
+ sizeof(uint16_t), count,
+ jsArray, &output, pErr)) {
+ return false;
+ }
+ break;
+
+ case js::Scalar::Int32:
+ if (!CheckTargetAndPopulate(nsXPTType::T_I32, type,
+ sizeof(int32_t), count,
+ jsArray, &output, pErr)) {
+ return false;
+ }
+ break;
+
+ case js::Scalar::Uint32:
+ if (!CheckTargetAndPopulate(nsXPTType::T_U32, type,
+ sizeof(uint32_t), count,
+ jsArray, &output, pErr)) {
+ return false;
+ }
+ break;
+
+ case js::Scalar::Float32:
+ if (!CheckTargetAndPopulate(nsXPTType::T_FLOAT, type,
+ sizeof(float), count,
+ jsArray, &output, pErr)) {
+ return false;
+ }
+ break;
+
+ case js::Scalar::Float64:
+ if (!CheckTargetAndPopulate(nsXPTType::T_DOUBLE, type,
+ sizeof(double), count,
+ jsArray, &output, pErr)) {
+ return false;
+ }
+ break;
+
+ // Yet another array type was defined? It is not supported yet...
+ default:
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+
+ return false;
+ }
+
+ *d = output;
+ if (pErr)
+ *pErr = NS_OK;
+
+ return true;
+}
+
+// static
+bool
+XPCConvert::JSArray2Native(void** d, HandleValue s,
+ uint32_t count, const nsXPTType& type,
+ const nsID* iid, nsresult* pErr)
+{
+ MOZ_ASSERT(d, "bad param");
+
+ AutoJSContext cx;
+
+ // XXX add support for getting chars from strings
+
+ // XXX add support to indicate *which* array element was not convertable
+
+ if (s.isNullOrUndefined()) {
+ if (0 != count) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
+ return false;
+ }
+
+ *d = nullptr;
+ return true;
+ }
+
+ if (!s.isObject()) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY;
+ return false;
+ }
+
+ RootedObject jsarray(cx, &s.toObject());
+
+ // If this is a typed array, then try a fast conversion with memcpy.
+ if (JS_IsTypedArrayObject(jsarray)) {
+ return JSTypedArray2Native(d, jsarray, count, type, pErr);
+ }
+
+ bool isArray;
+ if (!JS_IsArrayObject(cx, jsarray, &isArray) || !isArray) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY;
+ return false;
+ }
+
+ uint32_t len;
+ if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_NOT_ENOUGH_ELEMENTS_IN_ARRAY;
+ return false;
+ }
+
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_JS;
+
+#define POPULATE(_mode, _t) \
+ PR_BEGIN_MACRO \
+ cleanupMode = _mode; \
+ size_t max = UINT32_MAX / sizeof(_t); \
+ if (count > max || \
+ nullptr == (array = moz_xmalloc(count * sizeof(_t)))) { \
+ if (pErr) \
+ *pErr = NS_ERROR_OUT_OF_MEMORY; \
+ goto failure; \
+ } \
+ for (initedCount = 0; initedCount < count; initedCount++) { \
+ if (!JS_GetElement(cx, jsarray, initedCount, &current) || \
+ !JSData2Native(((_t*)array)+initedCount, current, type, \
+ iid, pErr)) \
+ goto failure; \
+ } \
+ PR_END_MACRO
+
+ // No Action, FRee memory, RElease object
+ enum CleanupMode {na, fr, re};
+
+ CleanupMode cleanupMode;
+
+ void* array = nullptr;
+ uint32_t initedCount;
+ RootedValue current(cx);
+
+ // XXX check IsPtr - esp. to handle array of nsID (as opposed to nsID*)
+ // XXX make extra space at end of char* and wchar* and null termintate
+
+ switch (type.TagPart()) {
+ case nsXPTType::T_I8 : POPULATE(na, int8_t); break;
+ case nsXPTType::T_I16 : POPULATE(na, int16_t); break;
+ case nsXPTType::T_I32 : POPULATE(na, int32_t); break;
+ case nsXPTType::T_I64 : POPULATE(na, int64_t); break;
+ case nsXPTType::T_U8 : POPULATE(na, uint8_t); break;
+ case nsXPTType::T_U16 : POPULATE(na, uint16_t); break;
+ case nsXPTType::T_U32 : POPULATE(na, uint32_t); break;
+ case nsXPTType::T_U64 : POPULATE(na, uint64_t); break;
+ case nsXPTType::T_FLOAT : POPULATE(na, float); break;
+ case nsXPTType::T_DOUBLE : POPULATE(na, double); break;
+ case nsXPTType::T_BOOL : POPULATE(na, bool); break;
+ case nsXPTType::T_CHAR : POPULATE(na, char); break;
+ case nsXPTType::T_WCHAR : POPULATE(na, char16_t); break;
+ case nsXPTType::T_VOID : NS_ERROR("bad type"); goto failure;
+ case nsXPTType::T_IID : POPULATE(fr, nsID*); break;
+ case nsXPTType::T_DOMSTRING : NS_ERROR("bad type"); goto failure;
+ case nsXPTType::T_CHAR_STR : POPULATE(fr, char*); break;
+ case nsXPTType::T_WCHAR_STR : POPULATE(fr, char16_t*); break;
+ case nsXPTType::T_INTERFACE : POPULATE(re, nsISupports*); break;
+ case nsXPTType::T_INTERFACE_IS : POPULATE(re, nsISupports*); break;
+ case nsXPTType::T_UTF8STRING : NS_ERROR("bad type"); goto failure;
+ case nsXPTType::T_CSTRING : NS_ERROR("bad type"); goto failure;
+ case nsXPTType::T_ASTRING : NS_ERROR("bad type"); goto failure;
+ default : NS_ERROR("bad type"); goto failure;
+ }
+
+ *d = array;
+ if (pErr)
+ *pErr = NS_OK;
+ return true;
+
+failure:
+ // we may need to cleanup the partially filled array of converted stuff
+ if (array) {
+ if (cleanupMode == re) {
+ nsISupports** a = (nsISupports**) array;
+ for (uint32_t i = 0; i < initedCount; i++) {
+ nsISupports* p = a[i];
+ NS_IF_RELEASE(p);
+ }
+ } else if (cleanupMode == fr) {
+ void** a = (void**) array;
+ for (uint32_t i = 0; i < initedCount; i++) {
+ void* p = a[i];
+ if (p) free(p);
+ }
+ }
+ free(array);
+ }
+
+ return false;
+
+#undef POPULATE
+}
+
+// static
+bool
+XPCConvert::NativeStringWithSize2JS(MutableHandleValue d, const void* s,
+ const nsXPTType& type,
+ uint32_t count,
+ nsresult* pErr)
+{
+ NS_PRECONDITION(s, "bad param");
+
+ AutoJSContext cx;
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
+
+ switch (type.TagPart()) {
+ case nsXPTType::T_PSTRING_SIZE_IS:
+ {
+ char* p = *((char**)s);
+ if (!p)
+ break;
+ JSString* str;
+ if (!(str = JS_NewStringCopyN(cx, p, count)))
+ return false;
+ d.setString(str);
+ break;
+ }
+ case nsXPTType::T_PWSTRING_SIZE_IS:
+ {
+ char16_t* p = *((char16_t**)s);
+ if (!p)
+ break;
+ JSString* str;
+ if (!(str = JS_NewUCStringCopyN(cx, p, count)))
+ return false;
+ d.setString(str);
+ break;
+ }
+ default:
+ XPC_LOG_ERROR(("XPCConvert::NativeStringWithSize2JS : unsupported type"));
+ return false;
+ }
+ return true;
+}
+
+// static
+bool
+XPCConvert::JSStringWithSize2Native(void* d, HandleValue s,
+ uint32_t count, const nsXPTType& type,
+ nsresult* pErr)
+{
+ NS_PRECONDITION(!s.isNull(), "bad param");
+ NS_PRECONDITION(d, "bad param");
+
+ AutoJSContext cx;
+ uint32_t len;
+
+ if (pErr)
+ *pErr = NS_ERROR_XPC_BAD_CONVERT_NATIVE;
+
+ switch (type.TagPart()) {
+ case nsXPTType::T_PSTRING_SIZE_IS:
+ {
+ if (s.isUndefined() || s.isNull()) {
+ if (0 != count) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
+ return false;
+ }
+ if (0 != count) {
+ len = (count + 1) * sizeof(char);
+ if (!(*((void**)d) = moz_xmalloc(len)))
+ return false;
+ return true;
+ }
+ // else ...
+
+ *((char**)d) = nullptr;
+ return true;
+ }
+
+ JSString* str = ToString(cx, s);
+ if (!str) {
+ return false;
+ }
+
+ size_t length = JS_GetStringEncodingLength(cx, str);
+ if (length == size_t(-1)) {
+ return false;
+ }
+ if (length > count) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
+ return false;
+ }
+ len = uint32_t(length);
+
+ if (len < count)
+ len = count;
+
+ uint32_t alloc_len = (len + 1) * sizeof(char);
+ char* buffer = static_cast<char*>(moz_xmalloc(alloc_len));
+ if (!buffer) {
+ return false;
+ }
+ JS_EncodeStringToBuffer(cx, str, buffer, len);
+ buffer[len] = '\0';
+ *((char**)d) = buffer;
+
+ return true;
+ }
+
+ case nsXPTType::T_PWSTRING_SIZE_IS:
+ {
+ JSString* str;
+
+ if (s.isUndefined() || s.isNull()) {
+ if (0 != count) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
+ return false;
+ }
+
+ if (0 != count) {
+ len = (count + 1) * sizeof(char16_t);
+ if (!(*((void**)d) = moz_xmalloc(len)))
+ return false;
+ return true;
+ }
+
+ // else ...
+ *((const char16_t**)d) = nullptr;
+ return true;
+ }
+
+ if (!(str = ToString(cx, s))) {
+ return false;
+ }
+
+ len = JS_GetStringLength(str);
+ if (len > count) {
+ if (pErr)
+ *pErr = NS_ERROR_XPC_NOT_ENOUGH_CHARS_IN_STRING;
+ return false;
+ }
+
+ len = count;
+
+ uint32_t alloc_len = (len + 1) * sizeof(char16_t);
+ if (!(*((void**)d) = moz_xmalloc(alloc_len))) {
+ // XXX should report error
+ return false;
+ }
+ mozilla::Range<char16_t> destChars(*((char16_t**)d), len + 1);
+ if (!JS_CopyStringChars(cx, destChars, str))
+ return false;
+ destChars[count] = 0;
+
+ return true;
+ }
+ default:
+ XPC_LOG_ERROR(("XPCConvert::JSStringWithSize2Native : unsupported type"));
+ return false;
+ }
+}
diff --git a/js/xpconnect/src/XPCDebug.cpp b/js/xpconnect/src/XPCDebug.cpp
new file mode 100644
index 000000000..f8500ba10
--- /dev/null
+++ b/js/xpconnect/src/XPCDebug.cpp
@@ -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;
+}
diff --git a/js/xpconnect/src/XPCException.cpp b/js/xpconnect/src/XPCException.cpp
new file mode 100644
index 000000000..575cc4aa3
--- /dev/null
+++ b/js/xpconnect/src/XPCException.cpp
@@ -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;
+}
diff --git a/js/xpconnect/src/XPCForwards.h b/js/xpconnect/src/XPCForwards.h
new file mode 100644
index 000000000..c1259bb3a
--- /dev/null
+++ b/js/xpconnect/src/XPCForwards.h
@@ -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___ */
diff --git a/js/xpconnect/src/XPCInlines.h b/js/xpconnect/src/XPCInlines.h
new file mode 100644
index 000000000..20c63c972
--- /dev/null
+++ b/js/xpconnect/src/XPCInlines.h
@@ -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___ */
diff --git a/js/xpconnect/src/XPCJSContext.cpp b/js/xpconnect/src/XPCJSContext.cpp
new file mode 100644
index 000000000..6981b525c
--- /dev/null
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -0,0 +1,3771 @@
+/* -*- 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/. */
+
+/* Per JSContext object */
+
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
+
+#include "xpcprivate.h"
+#include "xpcpublic.h"
+#include "XPCWrapper.h"
+#include "XPCJSMemoryReporter.h"
+#include "WrapperFactory.h"
+#include "mozJSComponentLoader.h"
+#include "nsAutoPtr.h"
+#include "nsNetUtil.h"
+
+#include "nsIMemoryInfoDumper.h"
+#include "nsIMemoryReporter.h"
+#include "nsIObserverService.h"
+#include "nsIDebug2.h"
+#include "nsIDocShell.h"
+#include "nsIRunnable.h"
+#include "amIAddonManager.h"
+#include "nsPIDOMWindow.h"
+#include "nsPrintfCString.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Services.h"
+#include "mozilla/dom/ScriptSettings.h"
+
+#include "nsContentUtils.h"
+#include "nsCCUncollectableMarker.h"
+#include "nsCycleCollectionNoteRootCallback.h"
+#include "nsCycleCollector.h"
+#include "nsScriptLoader.h"
+#include "jsapi.h"
+#include "jsprf.h"
+#include "js/MemoryMetrics.h"
+#include "mozilla/dom/GeneratedAtomList.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/WindowBinding.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/Unused.h"
+#include "AccessCheck.h"
+#include "nsGlobalWindow.h"
+#include "nsAboutProtocolUtils.h"
+
+#include "GeckoProfiler.h"
+#include "nsIXULRuntime.h"
+#include "nsJSPrincipals.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+#if defined(MOZ_JEMALLOC4)
+#include "mozmemory.h"
+#endif
+
+#ifdef XP_WIN
+#include <windows.h>
+#endif
+
+using namespace mozilla;
+using namespace xpc;
+using namespace JS;
+using mozilla::dom::PerThreadAtomCache;
+using mozilla::dom::AutoEntryScript;
+
+/***************************************************************************/
+
+const char* const XPCJSContext::mStrings[] = {
+ "constructor", // IDX_CONSTRUCTOR
+ "toString", // IDX_TO_STRING
+ "toSource", // IDX_TO_SOURCE
+ "lastResult", // IDX_LAST_RESULT
+ "returnCode", // IDX_RETURN_CODE
+ "value", // IDX_VALUE
+ "QueryInterface", // IDX_QUERY_INTERFACE
+ "Components", // IDX_COMPONENTS
+ "wrappedJSObject", // IDX_WRAPPED_JSOBJECT
+ "Object", // IDX_OBJECT
+ "Function", // IDX_FUNCTION
+ "prototype", // IDX_PROTOTYPE
+ "createInstance", // IDX_CREATE_INSTANCE
+ "item", // IDX_ITEM
+ "__proto__", // IDX_PROTO
+ "__iterator__", // IDX_ITERATOR
+ "__exposedProps__", // IDX_EXPOSEDPROPS
+ "eval", // IDX_EVAL
+ "controllers", // IDX_CONTROLLERS
+ "realFrameElement", // IDX_REALFRAMEELEMENT
+ "length", // IDX_LENGTH
+ "name", // IDX_NAME
+ "undefined", // IDX_UNDEFINED
+ "", // IDX_EMPTYSTRING
+ "fileName", // IDX_FILENAME
+ "lineNumber", // IDX_LINENUMBER
+ "columnNumber", // IDX_COLUMNNUMBER
+ "stack", // IDX_STACK
+ "message", // IDX_MESSAGE
+ "lastIndex" // IDX_LASTINDEX
+};
+
+/***************************************************************************/
+
+static mozilla::Atomic<bool> sDiscardSystemSource(false);
+
+bool
+xpc::ShouldDiscardSystemSource() { return sDiscardSystemSource; }
+
+#ifdef DEBUG
+static mozilla::Atomic<bool> sExtraWarningsForSystemJS(false);
+bool xpc::ExtraWarningsForSystemJS() { return sExtraWarningsForSystemJS; }
+#else
+bool xpc::ExtraWarningsForSystemJS() { return false; }
+#endif
+
+static mozilla::Atomic<bool> sSharedMemoryEnabled(false);
+
+bool
+xpc::SharedMemoryEnabled() { return sSharedMemoryEnabled; }
+
+// *Some* NativeSets are referenced from mClassInfo2NativeSetMap.
+// *All* NativeSets are referenced from mNativeSetMap.
+// So, in mClassInfo2NativeSetMap we just clear references to the unmarked.
+// In mNativeSetMap we clear the references to the unmarked *and* delete them.
+
+class AsyncFreeSnowWhite : public Runnable
+{
+public:
+ NS_IMETHOD Run() override
+ {
+ TimeStamp start = TimeStamp::Now();
+ bool hadSnowWhiteObjects = nsCycleCollector_doDeferredDeletion();
+ Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_ASYNC_SNOW_WHITE_FREEING,
+ uint32_t((TimeStamp::Now() - start).ToMilliseconds()));
+ if (hadSnowWhiteObjects && !mContinuation) {
+ mContinuation = true;
+ if (NS_FAILED(NS_DispatchToCurrentThread(this))) {
+ mActive = false;
+ }
+ } else {
+#if defined(MOZ_JEMALLOC4)
+ if (mPurge) {
+ /* Jemalloc purges dirty pages regularly during free() when the
+ * ratio of dirty pages compared to active pages is higher than
+ * 1 << lg_dirty_mult. A high ratio can have an impact on
+ * performance, so we use the default ratio of 8, but force a
+ * regular purge of all remaining dirty pages, after cycle
+ * collection. */
+ Telemetry::AutoTimer<Telemetry::MEMORY_FREE_PURGED_PAGES_MS> timer;
+ jemalloc_free_dirty_pages();
+ }
+#endif
+ mActive = false;
+ }
+ return NS_OK;
+ }
+
+ void Dispatch(bool aContinuation = false, bool aPurge = false)
+ {
+ if (mContinuation) {
+ mContinuation = aContinuation;
+ }
+ mPurge = aPurge;
+ if (!mActive && NS_SUCCEEDED(NS_DispatchToCurrentThread(this))) {
+ mActive = true;
+ }
+ }
+
+ AsyncFreeSnowWhite() : mContinuation(false), mActive(false), mPurge(false) {}
+
+public:
+ bool mContinuation;
+ bool mActive;
+ bool mPurge;
+};
+
+namespace xpc {
+
+CompartmentPrivate::CompartmentPrivate(JSCompartment* c)
+ : wantXrays(false)
+ , allowWaivers(true)
+ , writeToGlobalPrototype(false)
+ , skipWriteToGlobalPrototype(false)
+ , isWebExtensionContentScript(false)
+ , waiveInterposition(false)
+ , allowCPOWs(false)
+ , universalXPConnectEnabled(false)
+ , forcePermissiveCOWs(false)
+ , scriptability(c)
+ , scope(nullptr)
+ , mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH))
+{
+ MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
+ mozilla::PodArrayZero(wrapperDenialWarnings);
+}
+
+CompartmentPrivate::~CompartmentPrivate()
+{
+ MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
+ mWrappedJSMap->ShutdownMarker();
+ delete mWrappedJSMap;
+}
+
+static bool
+TryParseLocationURICandidate(const nsACString& uristr,
+ CompartmentPrivate::LocationHint aLocationHint,
+ nsIURI** aURI)
+{
+ static NS_NAMED_LITERAL_CSTRING(kGRE, "resource://gre/");
+ static NS_NAMED_LITERAL_CSTRING(kToolkit, "chrome://global/");
+ static NS_NAMED_LITERAL_CSTRING(kBrowser, "chrome://browser/");
+
+ if (aLocationHint == CompartmentPrivate::LocationHintAddon) {
+ // Blacklist some known locations which are clearly not add-on related.
+ if (StringBeginsWith(uristr, kGRE) ||
+ StringBeginsWith(uristr, kToolkit) ||
+ StringBeginsWith(uristr, kBrowser))
+ return false;
+
+ // -- GROSS HACK ALERT --
+ // The Yandex Elements 8.10.2 extension implements its own "xb://" URL
+ // scheme. If we call NS_NewURI() on an "xb://..." URL, we'll end up
+ // calling into the extension's own JS-implemented nsIProtocolHandler
+ // object, which we can't allow while we're iterating over the JS heap.
+ // So just skip any such URL.
+ // -- GROSS HACK ALERT --
+ if (StringBeginsWith(uristr, NS_LITERAL_CSTRING("xb")))
+ return false;
+ }
+
+ nsCOMPtr<nsIURI> uri;
+ if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), uristr)))
+ return false;
+
+ nsAutoCString scheme;
+ if (NS_FAILED(uri->GetScheme(scheme)))
+ return false;
+
+ // Cannot really map data: and blob:.
+ // Also, data: URIs are pretty memory hungry, which is kinda bad
+ // for memory reporter use.
+ if (scheme.EqualsLiteral("data") || scheme.EqualsLiteral("blob"))
+ return false;
+
+ uri.forget(aURI);
+ return true;
+}
+
+bool CompartmentPrivate::TryParseLocationURI(CompartmentPrivate::LocationHint aLocationHint,
+ nsIURI** aURI)
+{
+ if (!aURI)
+ return false;
+
+ // Need to parse the URI.
+ if (location.IsEmpty())
+ return false;
+
+ // Handle Sandbox location strings.
+ // A sandbox string looks like this:
+ // <sandboxName> (from: <js-stack-frame-filename>:<lineno>)
+ // where <sandboxName> is user-provided via Cu.Sandbox()
+ // and <js-stack-frame-filename> and <lineno> is the stack frame location
+ // from where Cu.Sandbox was called.
+ // <js-stack-frame-filename> furthermore is "free form", often using a
+ // "uri -> uri -> ..." chain. The following code will and must handle this
+ // common case.
+ // It should be noted that other parts of the code may already rely on the
+ // "format" of these strings, such as the add-on SDK.
+
+ static const nsDependentCString from("(from: ");
+ static const nsDependentCString arrow(" -> ");
+ static const size_t fromLength = from.Length();
+ static const size_t arrowLength = arrow.Length();
+
+ // See: XPCComponents.cpp#AssembleSandboxMemoryReporterName
+ int32_t idx = location.Find(from);
+ if (idx < 0)
+ return TryParseLocationURICandidate(location, aLocationHint, aURI);
+
+
+ // When parsing we're looking for the right-most URI. This URI may be in
+ // <sandboxName>, so we try this first.
+ if (TryParseLocationURICandidate(Substring(location, 0, idx), aLocationHint,
+ aURI))
+ return true;
+
+ // Not in <sandboxName> so we need to inspect <js-stack-frame-filename> and
+ // the chain that is potentially contained within and grab the rightmost
+ // item that is actually a URI.
+
+ // First, hack off the :<lineno>) part as well
+ int32_t ridx = location.RFind(NS_LITERAL_CSTRING(":"));
+ nsAutoCString chain(Substring(location, idx + fromLength,
+ ridx - idx - fromLength));
+
+ // Loop over the "->" chain. This loop also works for non-chains, or more
+ // correctly chains with only one item.
+ for (;;) {
+ idx = chain.RFind(arrow);
+ if (idx < 0) {
+ // This is the last chain item. Try to parse what is left.
+ return TryParseLocationURICandidate(chain, aLocationHint, aURI);
+ }
+
+ // Try to parse current chain item
+ if (TryParseLocationURICandidate(Substring(chain, idx + arrowLength),
+ aLocationHint, aURI))
+ return true;
+
+ // Current chain item couldn't be parsed.
+ // Strip current item and continue.
+ chain = Substring(chain, 0, idx);
+ }
+
+ MOZ_CRASH("Chain parser loop does not terminate");
+}
+
+static bool
+PrincipalImmuneToScriptPolicy(nsIPrincipal* aPrincipal)
+{
+ // System principal gets a free pass.
+ if (nsXPConnect::SecurityManager()->IsSystemPrincipal(aPrincipal))
+ return true;
+
+ // nsExpandedPrincipal gets a free pass.
+ nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal);
+ if (ep)
+ return true;
+
+ // Check whether our URI is an "about:" URI that allows scripts. If it is,
+ // we need to allow JS to run.
+ nsCOMPtr<nsIURI> principalURI;
+ aPrincipal->GetURI(getter_AddRefs(principalURI));
+ MOZ_ASSERT(principalURI);
+ bool isAbout;
+ nsresult rv = principalURI->SchemeIs("about", &isAbout);
+ if (NS_SUCCEEDED(rv) && isAbout) {
+ nsCOMPtr<nsIAboutModule> module;
+ rv = NS_GetAboutModule(principalURI, getter_AddRefs(module));
+ if (NS_SUCCEEDED(rv)) {
+ uint32_t flags;
+ rv = module->GetURIFlags(principalURI, &flags);
+ if (NS_SUCCEEDED(rv) &&
+ (flags & nsIAboutModule::ALLOW_SCRIPT)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+Scriptability::Scriptability(JSCompartment* c) : mScriptBlocks(0)
+ , mDocShellAllowsScript(true)
+ , mScriptBlockedByPolicy(false)
+{
+ nsIPrincipal* prin = nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
+ mImmuneToScriptPolicy = PrincipalImmuneToScriptPolicy(prin);
+
+ // If we're not immune, we should have a real principal with a codebase URI.
+ // Check the URI against the new-style domain policy.
+ if (!mImmuneToScriptPolicy) {
+ nsCOMPtr<nsIURI> codebase;
+ nsresult rv = prin->GetURI(getter_AddRefs(codebase));
+ bool policyAllows;
+ if (NS_SUCCEEDED(rv) && codebase &&
+ NS_SUCCEEDED(nsXPConnect::SecurityManager()->PolicyAllowsScript(codebase, &policyAllows)))
+ {
+ mScriptBlockedByPolicy = !policyAllows;
+ } else {
+ // Something went wrong - be safe and block script.
+ mScriptBlockedByPolicy = true;
+ }
+ }
+}
+
+bool
+Scriptability::Allowed()
+{
+ return mDocShellAllowsScript && !mScriptBlockedByPolicy &&
+ mScriptBlocks == 0;
+}
+
+bool
+Scriptability::IsImmuneToScriptPolicy()
+{
+ return mImmuneToScriptPolicy;
+}
+
+void
+Scriptability::Block()
+{
+ ++mScriptBlocks;
+}
+
+void
+Scriptability::Unblock()
+{
+ MOZ_ASSERT(mScriptBlocks > 0);
+ --mScriptBlocks;
+}
+
+void
+Scriptability::SetDocShellAllowsScript(bool aAllowed)
+{
+ mDocShellAllowsScript = aAllowed || mImmuneToScriptPolicy;
+}
+
+/* static */
+Scriptability&
+Scriptability::Get(JSObject* aScope)
+{
+ return CompartmentPrivate::Get(aScope)->scriptability;
+}
+
+bool
+IsContentXBLScope(JSCompartment* compartment)
+{
+ // We always eagerly create compartment privates for XBL scopes.
+ CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
+ if (!priv || !priv->scope)
+ return false;
+ return priv->scope->IsContentXBLScope();
+}
+
+bool
+IsInContentXBLScope(JSObject* obj)
+{
+ return IsContentXBLScope(js::GetObjectCompartment(obj));
+}
+
+bool
+IsInAddonScope(JSObject* obj)
+{
+ return ObjectScope(obj)->IsAddonScope();
+}
+
+bool
+IsUniversalXPConnectEnabled(JSCompartment* compartment)
+{
+ CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
+ if (!priv)
+ return false;
+ return priv->universalXPConnectEnabled;
+}
+
+bool
+IsUniversalXPConnectEnabled(JSContext* cx)
+{
+ JSCompartment* compartment = js::GetContextCompartment(cx);
+ if (!compartment)
+ return false;
+ return IsUniversalXPConnectEnabled(compartment);
+}
+
+bool
+EnableUniversalXPConnect(JSContext* cx)
+{
+ JSCompartment* compartment = js::GetContextCompartment(cx);
+ if (!compartment)
+ return true;
+ // Never set universalXPConnectEnabled on a chrome compartment - it confuses
+ // the security wrapping code.
+ if (AccessCheck::isChrome(compartment))
+ return true;
+ CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
+ if (!priv)
+ return true;
+ if (priv->universalXPConnectEnabled)
+ return true;
+ priv->universalXPConnectEnabled = true;
+
+ // Recompute all the cross-compartment wrappers leaving the newly-privileged
+ // compartment.
+ bool ok = js::RecomputeWrappers(cx, js::SingleCompartment(compartment),
+ js::AllCompartments());
+ NS_ENSURE_TRUE(ok, false);
+
+ // The Components object normally isn't defined for unprivileged web content,
+ // but we define it when UniversalXPConnect is enabled to support legacy
+ // tests.
+ XPCWrappedNativeScope* scope = priv->scope;
+ if (!scope)
+ return true;
+ scope->ForcePrivilegedComponents();
+ return scope->AttachComponentsObject(cx);
+}
+
+JSObject*
+UnprivilegedJunkScope()
+{
+ return XPCJSContext::Get()->UnprivilegedJunkScope();
+}
+
+JSObject*
+PrivilegedJunkScope()
+{
+ return XPCJSContext::Get()->PrivilegedJunkScope();
+}
+
+JSObject*
+CompilationScope()
+{
+ return XPCJSContext::Get()->CompilationScope();
+}
+
+nsGlobalWindow*
+WindowOrNull(JSObject* aObj)
+{
+ MOZ_ASSERT(aObj);
+ MOZ_ASSERT(!js::IsWrapper(aObj));
+
+ nsGlobalWindow* win = nullptr;
+ UNWRAP_NON_WRAPPER_OBJECT(Window, aObj, win);
+ return win;
+}
+
+nsGlobalWindow*
+WindowGlobalOrNull(JSObject* aObj)
+{
+ MOZ_ASSERT(aObj);
+ JSObject* glob = js::GetGlobalForObjectCrossCompartment(aObj);
+
+ return WindowOrNull(glob);
+}
+
+nsGlobalWindow*
+AddonWindowOrNull(JSObject* aObj)
+{
+ if (!IsInAddonScope(aObj))
+ return nullptr;
+
+ JSObject* global = js::GetGlobalForObjectCrossCompartment(aObj);
+ JSObject* proto = js::GetPrototypeNoProxy(global);
+
+ // Addons could theoretically change the prototype of the addon scope, but
+ // we pretty much just want to crash if that happens so that we find out
+ // about it and get them to change their code.
+ MOZ_RELEASE_ASSERT(js::IsCrossCompartmentWrapper(proto) ||
+ xpc::IsSandboxPrototypeProxy(proto));
+ JSObject* mainGlobal = js::UncheckedUnwrap(proto, /* stopAtWindowProxy = */ false);
+ MOZ_RELEASE_ASSERT(JS_IsGlobalObject(mainGlobal));
+
+ return WindowOrNull(mainGlobal);
+}
+
+nsGlobalWindow*
+CurrentWindowOrNull(JSContext* cx)
+{
+ JSObject* glob = JS::CurrentGlobalOrNull(cx);
+ return glob ? WindowOrNull(glob) : nullptr;
+}
+
+} // namespace xpc
+
+static void
+CompartmentDestroyedCallback(JSFreeOp* fop, JSCompartment* compartment)
+{
+ // NB - This callback may be called in JS_DestroyContext, which happens
+ // after the XPCJSContext has been torn down.
+
+ // Get the current compartment private into an AutoPtr (which will do the
+ // cleanup for us), and null out the private (which may already be null).
+ nsAutoPtr<CompartmentPrivate> priv(CompartmentPrivate::Get(compartment));
+ JS_SetCompartmentPrivate(compartment, nullptr);
+}
+
+static size_t
+CompartmentSizeOfIncludingThisCallback(MallocSizeOf mallocSizeOf, JSCompartment* compartment)
+{
+ CompartmentPrivate* priv = CompartmentPrivate::Get(compartment);
+ return priv ? priv->SizeOfIncludingThis(mallocSizeOf) : 0;
+}
+
+/*
+ * Return true if there exists a non-system inner window which is a current
+ * inner window and whose reflector is gray. We don't merge system
+ * compartments, so we don't use them to trigger merging CCs.
+ */
+bool XPCJSContext::UsefulToMergeZones() const
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Turns out, actually making this return true often enough makes Windows
+ // mochitest-gl OOM a lot. Need to figure out what's going on there; see
+ // bug 1277036.
+
+ return false;
+}
+
+void XPCJSContext::TraceNativeBlackRoots(JSTracer* trc)
+{
+ if (AutoMarkingPtr* roots = Get()->mAutoRoots)
+ roots->TraceJSAll(trc);
+
+ // XPCJSObjectHolders don't participate in cycle collection, so always
+ // trace them here.
+ XPCRootSetElem* e;
+ for (e = mObjectHolderRoots; e; e = e->GetNextRoot())
+ static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
+
+ dom::TraceBlackJS(trc, JS_GetGCParameter(Context(), JSGC_NUMBER),
+ nsXPConnect::XPConnect()->IsShuttingDown());
+}
+
+void XPCJSContext::TraceAdditionalNativeGrayRoots(JSTracer* trc)
+{
+ XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(trc, this);
+
+ for (XPCRootSetElem* e = mVariantRoots; e ; e = e->GetNextRoot())
+ static_cast<XPCTraceableVariant*>(e)->TraceJS(trc);
+
+ for (XPCRootSetElem* e = mWrappedJSRoots; e ; e = e->GetNextRoot())
+ static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
+}
+
+void
+XPCJSContext::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb)
+{
+ XPCWrappedNativeScope::SuspectAllWrappers(this, cb);
+
+ for (XPCRootSetElem* e = mVariantRoots; e ; e = e->GetNextRoot()) {
+ XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(e);
+ if (nsCCUncollectableMarker::InGeneration(cb,
+ v->CCGeneration())) {
+ JS::Value val = v->GetJSValPreserveColor();
+ if (val.isObject() && !JS::ObjectIsMarkedGray(&val.toObject()))
+ continue;
+ }
+ cb.NoteXPCOMRoot(v);
+ }
+
+ for (XPCRootSetElem* e = mWrappedJSRoots; e ; e = e->GetNextRoot()) {
+ cb.NoteXPCOMRoot(ToSupports(static_cast<nsXPCWrappedJS*>(e)));
+ }
+}
+
+void
+XPCJSContext::UnmarkSkippableJSHolders()
+{
+ CycleCollectedJSContext::UnmarkSkippableJSHolders();
+}
+
+void
+XPCJSContext::PrepareForForgetSkippable()
+{
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "cycle-collector-forget-skippable", nullptr);
+ }
+}
+
+void
+XPCJSContext::BeginCycleCollectionCallback()
+{
+ nsJSContext::BeginCycleCollectionCallback();
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "cycle-collector-begin", nullptr);
+ }
+}
+
+void
+XPCJSContext::EndCycleCollectionCallback(CycleCollectorResults& aResults)
+{
+ nsJSContext::EndCycleCollectionCallback(aResults);
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "cycle-collector-end", nullptr);
+ }
+}
+
+void
+XPCJSContext::DispatchDeferredDeletion(bool aContinuation, bool aPurge)
+{
+ mAsyncSnowWhiteFreer->Dispatch(aContinuation, aPurge);
+}
+
+void
+xpc_UnmarkSkippableJSHolders()
+{
+ if (nsXPConnect::XPConnect()->GetContext()) {
+ nsXPConnect::XPConnect()->GetContext()->UnmarkSkippableJSHolders();
+ }
+}
+
+/* static */ void
+XPCJSContext::GCSliceCallback(JSContext* cx,
+ JS::GCProgress progress,
+ const JS::GCDescription& desc)
+{
+ XPCJSContext* self = nsXPConnect::GetContextInstance();
+ if (!self)
+ return;
+
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::SetGarbageCollecting(progress == JS::GC_CYCLE_BEGIN ||
+ progress == JS::GC_SLICE_BEGIN);
+#endif
+
+ if (self->mPrevGCSliceCallback)
+ (*self->mPrevGCSliceCallback)(cx, progress, desc);
+}
+
+/* static */ void
+XPCJSContext::DoCycleCollectionCallback(JSContext* cx)
+{
+ // The GC has detected that a CC at this point would collect a tremendous
+ // amount of garbage that is being revivified unnecessarily.
+ NS_DispatchToCurrentThread(
+ NS_NewRunnableFunction([](){nsJSContext::CycleCollectNow(nullptr);}));
+
+ XPCJSContext* self = nsXPConnect::GetContextInstance();
+ if (!self)
+ return;
+
+ if (self->mPrevDoCycleCollectionCallback)
+ (*self->mPrevDoCycleCollectionCallback)(cx);
+}
+
+void
+XPCJSContext::CustomGCCallback(JSGCStatus status)
+{
+ nsTArray<xpcGCCallback> callbacks(extraGCCallbacks);
+ for (uint32_t i = 0; i < callbacks.Length(); ++i)
+ callbacks[i](status);
+}
+
+/* static */ void
+XPCJSContext::FinalizeCallback(JSFreeOp* fop,
+ JSFinalizeStatus status,
+ bool isZoneGC,
+ void* data)
+{
+ XPCJSContext* self = nsXPConnect::GetContextInstance();
+ if (!self)
+ return;
+
+ switch (status) {
+ case JSFINALIZE_GROUP_START:
+ {
+ MOZ_ASSERT(!self->mDoingFinalization, "bad state");
+
+ MOZ_ASSERT(!self->mGCIsRunning, "bad state");
+ self->mGCIsRunning = true;
+
+ self->mDoingFinalization = true;
+ break;
+ }
+ case JSFINALIZE_GROUP_END:
+ {
+ MOZ_ASSERT(self->mDoingFinalization, "bad state");
+ self->mDoingFinalization = false;
+
+ // Sweep scopes needing cleanup
+ XPCWrappedNativeScope::KillDyingScopes();
+
+ MOZ_ASSERT(self->mGCIsRunning, "bad state");
+ self->mGCIsRunning = false;
+
+ break;
+ }
+ case JSFINALIZE_COLLECTION_END:
+ {
+ MOZ_ASSERT(!self->mGCIsRunning, "bad state");
+ self->mGCIsRunning = true;
+
+ if (AutoMarkingPtr* roots = Get()->mAutoRoots)
+ roots->MarkAfterJSFinalizeAll();
+
+ // Now we are going to recycle any unused WrappedNativeTearoffs.
+ // We do this by iterating all the live callcontexts
+ // and marking the tearoffs in use. And then we
+ // iterate over all the WrappedNative wrappers and sweep their
+ // tearoffs.
+ //
+ // This allows us to perhaps minimize the growth of the
+ // tearoffs. And also makes us not hold references to interfaces
+ // on our wrapped natives that we are not actually using.
+ //
+ // XXX We may decide to not do this on *every* gc cycle.
+
+ XPCCallContext* ccxp = XPCJSContext::Get()->GetCallContext();
+ while (ccxp) {
+ // Deal with the strictness of callcontext that
+ // complains if you ask for a tearoff when
+ // it is in a state where the tearoff could not
+ // possibly be valid.
+ if (ccxp->CanGetTearOff()) {
+ XPCWrappedNativeTearOff* to =
+ ccxp->GetTearOff();
+ if (to)
+ to->Mark();
+ }
+ ccxp = ccxp->GetPrevCallContext();
+ }
+
+ XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs();
+
+ // Now we need to kill the 'Dying' XPCWrappedNativeProtos.
+ // We transfered these native objects to this table when their
+ // JSObject's were finalized. We did not destroy them immediately
+ // at that point because the ordering of JS finalization is not
+ // deterministic and we did not yet know if any wrappers that
+ // might still be referencing the protos where still yet to be
+ // finalized and destroyed. We *do* know that the protos'
+ // JSObjects would not have been finalized if there were any
+ // wrappers that referenced the proto but where not themselves
+ // slated for finalization in this gc cycle. So... at this point
+ // we know that any and all wrappers that might have been
+ // referencing the protos in the dying list are themselves dead.
+ // So, we can safely delete all the protos in the list.
+
+ for (auto i = self->mDyingWrappedNativeProtoMap->Iter(); !i.Done(); i.Next()) {
+ auto entry = static_cast<XPCWrappedNativeProtoMap::Entry*>(i.Get());
+ delete static_cast<const XPCWrappedNativeProto*>(entry->key);
+ i.Remove();
+ }
+
+ MOZ_ASSERT(self->mGCIsRunning, "bad state");
+ self->mGCIsRunning = false;
+
+ break;
+ }
+ }
+}
+
+/* static */ void
+XPCJSContext::WeakPointerZoneGroupCallback(JSContext* cx, void* data)
+{
+ // Called before each sweeping slice -- after processing any final marking
+ // triggered by barriers -- to clear out any references to things that are
+ // about to be finalized and update any pointers to moved GC things.
+ XPCJSContext* self = static_cast<XPCJSContext*>(data);
+
+ self->mWrappedJSMap->UpdateWeakPointersAfterGC(self);
+
+ XPCWrappedNativeScope::UpdateWeakPointersAfterGC(self);
+}
+
+/* static */ void
+XPCJSContext::WeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data)
+{
+ // Called immediately after the ZoneGroup weak pointer callback, but only
+ // once for each compartment that is being swept.
+ XPCJSContext* self = static_cast<XPCJSContext*>(data);
+ CompartmentPrivate* xpcComp = CompartmentPrivate::Get(comp);
+ if (xpcComp)
+ xpcComp->UpdateWeakPointersAfterGC(self);
+}
+
+void
+CompartmentPrivate::UpdateWeakPointersAfterGC(XPCJSContext* context)
+{
+ mWrappedJSMap->UpdateWeakPointersAfterGC(context);
+}
+
+static void WatchdogMain(void* arg);
+class Watchdog;
+class WatchdogManager;
+class AutoLockWatchdog {
+ Watchdog* const mWatchdog;
+ public:
+ explicit AutoLockWatchdog(Watchdog* aWatchdog);
+ ~AutoLockWatchdog();
+};
+
+class Watchdog
+{
+ public:
+ explicit Watchdog(WatchdogManager* aManager)
+ : mManager(aManager)
+ , mLock(nullptr)
+ , mWakeup(nullptr)
+ , mThread(nullptr)
+ , mHibernating(false)
+ , mInitialized(false)
+ , mShuttingDown(false)
+ , mMinScriptRunTimeSeconds(1)
+ {}
+ ~Watchdog() { MOZ_ASSERT(!Initialized()); }
+
+ WatchdogManager* Manager() { return mManager; }
+ bool Initialized() { return mInitialized; }
+ bool ShuttingDown() { return mShuttingDown; }
+ PRLock* GetLock() { return mLock; }
+ bool Hibernating() { return mHibernating; }
+ void WakeUp()
+ {
+ MOZ_ASSERT(Initialized());
+ MOZ_ASSERT(Hibernating());
+ mHibernating = false;
+ PR_NotifyCondVar(mWakeup);
+ }
+
+ //
+ // Invoked by the main thread only.
+ //
+
+ void Init()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ mLock = PR_NewLock();
+ if (!mLock)
+ NS_RUNTIMEABORT("PR_NewLock failed.");
+ mWakeup = PR_NewCondVar(mLock);
+ if (!mWakeup)
+ NS_RUNTIMEABORT("PR_NewCondVar failed.");
+
+ {
+ AutoLockWatchdog lock(this);
+
+ // Gecko uses thread private for accounting and has to clean up at thread exit.
+ // Therefore, even though we don't have a return value from the watchdog, we need to
+ // join it on shutdown.
+ mThread = PR_CreateThread(PR_USER_THREAD, WatchdogMain, this,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD, 0);
+ if (!mThread)
+ NS_RUNTIMEABORT("PR_CreateThread failed!");
+
+ // WatchdogMain acquires the lock and then asserts mInitialized. So
+ // make sure to set mInitialized before releasing the lock here so
+ // that it's atomic with the creation of the thread.
+ mInitialized = true;
+ }
+ }
+
+ void Shutdown()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(Initialized());
+ { // Scoped lock.
+ AutoLockWatchdog lock(this);
+
+ // Signal to the watchdog thread that it's time to shut down.
+ mShuttingDown = true;
+
+ // Wake up the watchdog, and wait for it to call us back.
+ PR_NotifyCondVar(mWakeup);
+ }
+
+ PR_JoinThread(mThread);
+
+ // The thread sets mShuttingDown to false as it exits.
+ MOZ_ASSERT(!mShuttingDown);
+
+ // Destroy state.
+ mThread = nullptr;
+ PR_DestroyCondVar(mWakeup);
+ mWakeup = nullptr;
+ PR_DestroyLock(mLock);
+ mLock = nullptr;
+
+ // All done.
+ mInitialized = false;
+ }
+
+ void SetMinScriptRunTimeSeconds(int32_t seconds)
+ {
+ // This variable is atomic, and is set from the main thread without
+ // locking.
+ MOZ_ASSERT(seconds > 0);
+ mMinScriptRunTimeSeconds = seconds;
+ }
+
+ //
+ // Invoked by the watchdog thread only.
+ //
+
+ void Hibernate()
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+ mHibernating = true;
+ Sleep(PR_INTERVAL_NO_TIMEOUT);
+ }
+ void Sleep(PRIntervalTime timeout)
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ALWAYS_TRUE(PR_WaitCondVar(mWakeup, timeout) == PR_SUCCESS);
+ }
+ void Finished()
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+ mShuttingDown = false;
+ }
+
+ int32_t MinScriptRunTimeSeconds()
+ {
+ return mMinScriptRunTimeSeconds;
+ }
+
+ private:
+ WatchdogManager* mManager;
+
+ PRLock* mLock;
+ PRCondVar* mWakeup;
+ PRThread* mThread;
+ bool mHibernating;
+ bool mInitialized;
+ bool mShuttingDown;
+ mozilla::Atomic<int32_t> mMinScriptRunTimeSeconds;
+};
+
+#define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
+#define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
+
+class WatchdogManager : public nsIObserver
+{
+ public:
+
+ NS_DECL_ISUPPORTS
+ explicit WatchdogManager(XPCJSContext* aContext) : mContext(aContext)
+ , mContextState(CONTEXT_INACTIVE)
+ {
+ // All the timestamps start at zero except for context state change.
+ PodArrayZero(mTimestamps);
+ mTimestamps[TimestampContextStateChange] = PR_Now();
+
+ // Enable the watchdog, if appropriate.
+ RefreshWatchdog();
+
+ // Register ourselves as an observer to get updates on the pref.
+ mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog");
+ mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
+ mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
+ }
+
+ protected:
+
+ virtual ~WatchdogManager()
+ {
+ // Shutting down the watchdog requires context-switching to the watchdog
+ // thread, which isn't great to do in a destructor. So we require
+ // consumers to shut it down manually before releasing it.
+ MOZ_ASSERT(!mWatchdog);
+ mozilla::Preferences::RemoveObserver(this, "dom.use_watchdog");
+ mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
+ mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
+ }
+
+ public:
+
+ NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) override
+ {
+ RefreshWatchdog();
+ return NS_OK;
+ }
+
+ // Context statistics. These live on the watchdog manager, are written
+ // from the main thread, and are read from the watchdog thread (holding
+ // the lock in each case).
+ void
+ RecordContextActivity(bool active)
+ {
+ // The watchdog reads this state, so acquire the lock before writing it.
+ MOZ_ASSERT(NS_IsMainThread());
+ Maybe<AutoLockWatchdog> lock;
+ if (mWatchdog)
+ lock.emplace(mWatchdog);
+
+ // Write state.
+ mTimestamps[TimestampContextStateChange] = PR_Now();
+ mContextState = active ? CONTEXT_ACTIVE : CONTEXT_INACTIVE;
+
+ // The watchdog may be hibernating, waiting for the context to go
+ // active. Wake it up if necessary.
+ if (active && mWatchdog && mWatchdog->Hibernating())
+ mWatchdog->WakeUp();
+ }
+ bool IsContextActive() { return mContextState == CONTEXT_ACTIVE; }
+ PRTime TimeSinceLastContextStateChange()
+ {
+ return PR_Now() - GetTimestamp(TimestampContextStateChange);
+ }
+
+ // Note - Because of the context activity timestamp, these are read and
+ // written from both threads.
+ void RecordTimestamp(WatchdogTimestampCategory aCategory)
+ {
+ // The watchdog thread always holds the lock when it runs.
+ Maybe<AutoLockWatchdog> maybeLock;
+ if (NS_IsMainThread() && mWatchdog)
+ maybeLock.emplace(mWatchdog);
+ mTimestamps[aCategory] = PR_Now();
+ }
+ PRTime GetTimestamp(WatchdogTimestampCategory aCategory)
+ {
+ // The watchdog thread always holds the lock when it runs.
+ Maybe<AutoLockWatchdog> maybeLock;
+ if (NS_IsMainThread() && mWatchdog)
+ maybeLock.emplace(mWatchdog);
+ return mTimestamps[aCategory];
+ }
+
+ XPCJSContext* Context() { return mContext; }
+ Watchdog* GetWatchdog() { return mWatchdog; }
+
+ void RefreshWatchdog()
+ {
+ bool wantWatchdog = Preferences::GetBool("dom.use_watchdog", true);
+ if (wantWatchdog != !!mWatchdog) {
+ if (wantWatchdog)
+ StartWatchdog();
+ else
+ StopWatchdog();
+ }
+
+ if (mWatchdog) {
+ int32_t contentTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_CONTENT, 10);
+ if (contentTime <= 0)
+ contentTime = INT32_MAX;
+ int32_t chromeTime = Preferences::GetInt(PREF_MAX_SCRIPT_RUN_TIME_CHROME, 20);
+ if (chromeTime <= 0)
+ chromeTime = INT32_MAX;
+ mWatchdog->SetMinScriptRunTimeSeconds(std::min(contentTime, chromeTime));
+ }
+ }
+
+ void StartWatchdog()
+ {
+ MOZ_ASSERT(!mWatchdog);
+ mWatchdog = new Watchdog(this);
+ mWatchdog->Init();
+ }
+
+ void StopWatchdog()
+ {
+ MOZ_ASSERT(mWatchdog);
+ mWatchdog->Shutdown();
+ mWatchdog = nullptr;
+ }
+
+ private:
+ XPCJSContext* mContext;
+ nsAutoPtr<Watchdog> mWatchdog;
+
+ enum { CONTEXT_ACTIVE, CONTEXT_INACTIVE } mContextState;
+ PRTime mTimestamps[TimestampCount];
+};
+
+NS_IMPL_ISUPPORTS(WatchdogManager, nsIObserver)
+
+AutoLockWatchdog::AutoLockWatchdog(Watchdog* aWatchdog) : mWatchdog(aWatchdog)
+{
+ PR_Lock(mWatchdog->GetLock());
+}
+
+AutoLockWatchdog::~AutoLockWatchdog()
+{
+ PR_Unlock(mWatchdog->GetLock());
+}
+
+static void
+WatchdogMain(void* arg)
+{
+ PR_SetCurrentThreadName("JS Watchdog");
+
+ Watchdog* self = static_cast<Watchdog*>(arg);
+ WatchdogManager* manager = self->Manager();
+
+ // Lock lasts until we return
+ AutoLockWatchdog lock(self);
+
+ MOZ_ASSERT(self->Initialized());
+ MOZ_ASSERT(!self->ShuttingDown());
+ while (!self->ShuttingDown()) {
+ // Sleep only 1 second if recently (or currently) active; otherwise, hibernate
+ if (manager->IsContextActive() ||
+ manager->TimeSinceLastContextStateChange() <= PRTime(2*PR_USEC_PER_SEC))
+ {
+ self->Sleep(PR_TicksPerSecond());
+ } else {
+ manager->RecordTimestamp(TimestampWatchdogHibernateStart);
+ self->Hibernate();
+ manager->RecordTimestamp(TimestampWatchdogHibernateStop);
+ }
+
+ // Rise and shine.
+ manager->RecordTimestamp(TimestampWatchdogWakeup);
+
+ // Don't request an interrupt callback unless the current script has
+ // been running long enough that we might show the slow script dialog.
+ // Triggering the callback from off the main thread can be expensive.
+
+ // We want to avoid showing the slow script dialog if the user's laptop
+ // goes to sleep in the middle of running a script. To ensure this, we
+ // invoke the interrupt callback after only half the timeout has
+ // elapsed. The callback simply records the fact that it was called in
+ // the mSlowScriptSecondHalf flag. Then we wait another (timeout/2)
+ // seconds and invoke the callback again. This time around it sees
+ // mSlowScriptSecondHalf is set and so it shows the slow script
+ // dialog. If the computer is put to sleep during one of the (timeout/2)
+ // periods, the script still has the other (timeout/2) seconds to
+ // finish.
+ PRTime usecs = self->MinScriptRunTimeSeconds() * PR_USEC_PER_SEC / 2;
+ if (manager->IsContextActive() &&
+ manager->TimeSinceLastContextStateChange() >= usecs)
+ {
+ bool debuggerAttached = false;
+ nsCOMPtr<nsIDebug2> dbg = do_GetService("@mozilla.org/xpcom/debug;1");
+ if (dbg)
+ dbg->GetIsDebuggerAttached(&debuggerAttached);
+ if (!debuggerAttached)
+ JS_RequestInterruptCallback(manager->Context()->Context());
+ }
+ }
+
+ // Tell the manager that we've shut down.
+ self->Finished();
+}
+
+PRTime
+XPCJSContext::GetWatchdogTimestamp(WatchdogTimestampCategory aCategory)
+{
+ return mWatchdogManager->GetTimestamp(aCategory);
+}
+
+void
+xpc::SimulateActivityCallback(bool aActive)
+{
+ XPCJSContext::ActivityCallback(XPCJSContext::Get(), aActive);
+}
+
+// static
+void
+XPCJSContext::ActivityCallback(void* arg, bool active)
+{
+ if (!active) {
+ ProcessHangMonitor::ClearHang();
+ }
+
+ XPCJSContext* self = static_cast<XPCJSContext*>(arg);
+ self->mWatchdogManager->RecordContextActivity(active);
+}
+
+// static
+bool
+XPCJSContext::InterruptCallback(JSContext* cx)
+{
+ XPCJSContext* self = XPCJSContext::Get();
+
+ // Normally we record mSlowScriptCheckpoint when we start to process an
+ // event. However, we can run JS outside of event handlers. This code takes
+ // care of that case.
+ if (self->mSlowScriptCheckpoint.IsNull()) {
+ self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
+ self->mSlowScriptSecondHalf = false;
+ self->mSlowScriptActualWait = mozilla::TimeDuration();
+ self->mTimeoutAccumulated = false;
+ return true;
+ }
+
+ // Sometimes we get called back during XPConnect initialization, before Gecko
+ // has finished bootstrapping. Avoid crashing in nsContentUtils below.
+ if (!nsContentUtils::IsInitialized())
+ return true;
+
+ // This is at least the second interrupt callback we've received since
+ // returning to the event loop. See how long it's been, and what the limit
+ // is.
+ TimeDuration duration = TimeStamp::NowLoRes() - self->mSlowScriptCheckpoint;
+ bool chrome = nsContentUtils::IsCallerChrome();
+ const char* prefName = chrome ? PREF_MAX_SCRIPT_RUN_TIME_CHROME
+ : PREF_MAX_SCRIPT_RUN_TIME_CONTENT;
+ int32_t limit = Preferences::GetInt(prefName, chrome ? 20 : 10);
+
+ // If there's no limit, or we're within the limit, let it go.
+ if (limit == 0 || duration.ToSeconds() < limit / 2.0)
+ return true;
+
+ self->mSlowScriptActualWait += duration;
+
+ // In order to guard against time changes or laptops going to sleep, we
+ // don't trigger the slow script warning until (limit/2) seconds have
+ // elapsed twice.
+ if (!self->mSlowScriptSecondHalf) {
+ self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
+ self->mSlowScriptSecondHalf = true;
+ return true;
+ }
+
+ //
+ // This has gone on long enough! Time to take action. ;-)
+ //
+
+ // Get the DOM window associated with the running script. If the script is
+ // running in a non-DOM scope, we have to just let it keep running.
+ RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
+ RefPtr<nsGlobalWindow> win = WindowOrNull(global);
+ if (!win && IsSandbox(global)) {
+ // If this is a sandbox associated with a DOMWindow via a
+ // sandboxPrototype, use that DOMWindow. This supports GreaseMonkey
+ // and JetPack content scripts.
+ JS::Rooted<JSObject*> proto(cx);
+ if (!JS_GetPrototype(cx, global, &proto))
+ return false;
+ if (proto && IsSandboxPrototypeProxy(proto) &&
+ (proto = js::CheckedUnwrap(proto, /* stopAtWindowProxy = */ false)))
+ {
+ win = WindowGlobalOrNull(proto);
+ }
+ }
+
+ if (!win) {
+ NS_WARNING("No active window");
+ return true;
+ }
+
+ if (win->IsDying()) {
+ // The window is being torn down. When that happens we try to prevent
+ // the dispatch of new runnables, so it also makes sense to kill any
+ // long-running script. The user is primarily interested in this page
+ // going away.
+ return false;
+ }
+
+ if (win->GetIsPrerendered()) {
+ // We cannot display a dialog if the page is being prerendered, so
+ // just kill the page.
+ mozilla::dom::HandlePrerenderingViolation(win->AsInner());
+ return false;
+ }
+
+ // Accumulate slow script invokation delay.
+ if (!chrome && !self->mTimeoutAccumulated) {
+ uint32_t delay = uint32_t(self->mSlowScriptActualWait.ToMilliseconds() - (limit * 1000.0));
+ Telemetry::Accumulate(Telemetry::SLOW_SCRIPT_NOTIFY_DELAY, delay);
+ self->mTimeoutAccumulated = true;
+ }
+
+ // Show the prompt to the user, and kill if requested.
+ nsGlobalWindow::SlowScriptResponse response = win->ShowSlowScriptDialog();
+ if (response == nsGlobalWindow::KillSlowScript) {
+ if (Preferences::GetBool("dom.global_stop_script", true))
+ xpc::Scriptability::Get(global).Block();
+ return false;
+ }
+
+ // The user chose to continue the script. Reset the timer, and disable this
+ // machinery with a pref of the user opted out of future slow-script dialogs.
+ if (response != nsGlobalWindow::ContinueSlowScriptAndKeepNotifying)
+ self->mSlowScriptCheckpoint = TimeStamp::NowLoRes();
+
+ if (response == nsGlobalWindow::AlwaysContinueSlowScript)
+ Preferences::SetInt(prefName, 0);
+
+ return true;
+}
+
+void
+XPCJSContext::CustomOutOfMemoryCallback()
+{
+ if (!Preferences::GetBool("memory.dump_reports_on_oom")) {
+ return;
+ }
+
+ nsCOMPtr<nsIMemoryInfoDumper> dumper =
+ do_GetService("@mozilla.org/memory-info-dumper;1");
+ if (!dumper) {
+ return;
+ }
+
+ // If this fails, it fails silently.
+ dumper->DumpMemoryInfoToTempDir(NS_LITERAL_STRING("due-to-JS-OOM"),
+ /* anonymize = */ false,
+ /* minimizeMemoryUsage = */ false);
+}
+
+void
+XPCJSContext::CustomLargeAllocationFailureCallback()
+{
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
+ }
+}
+
+size_t
+XPCJSContext::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
+{
+ size_t n = 0;
+ n += mallocSizeOf(this);
+ n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
+ n += mIID2NativeInterfaceMap->SizeOfIncludingThis(mallocSizeOf);
+ n += mClassInfo2NativeSetMap->ShallowSizeOfIncludingThis(mallocSizeOf);
+ n += mNativeSetMap->SizeOfIncludingThis(mallocSizeOf);
+
+ n += CycleCollectedJSContext::SizeOfExcludingThis(mallocSizeOf);
+
+ // There are other XPCJSContext members that could be measured; the above
+ // ones have been seen by DMD to be worth measuring. More stuff may be
+ // added later.
+
+ return n;
+}
+
+size_t
+CompartmentPrivate::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
+{
+ size_t n = mallocSizeOf(this);
+ n += mWrappedJSMap->SizeOfIncludingThis(mallocSizeOf);
+ n += mWrappedJSMap->SizeOfWrappedJS(mallocSizeOf);
+ return n;
+}
+
+/***************************************************************************/
+
+#define JS_OPTIONS_DOT_STR "javascript.options."
+
+static void
+ReloadPrefsCallback(const char* pref, void* data)
+{
+ XPCJSContext* xpccx = reinterpret_cast<XPCJSContext*>(data);
+ JSContext* cx = xpccx->Context();
+
+ bool safeMode = false;
+ nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
+ if (xr) {
+ xr->GetInSafeMode(&safeMode);
+ }
+
+ bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit") && !safeMode;
+ bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion") && !safeMode;
+ bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs") && !safeMode;
+ bool useWasm = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm") && !safeMode;
+ bool useWasmBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_baselinejit") && !safeMode;
+ bool throwOnAsmJSValidationFailure = Preferences::GetBool(JS_OPTIONS_DOT_STR
+ "throw_on_asmjs_validation_failure");
+ bool useNativeRegExp = Preferences::GetBool(JS_OPTIONS_DOT_STR "native_regexp") && !safeMode;
+
+ bool parallelParsing = Preferences::GetBool(JS_OPTIONS_DOT_STR "parallel_parsing");
+ bool offthreadIonCompilation = Preferences::GetBool(JS_OPTIONS_DOT_STR
+ "ion.offthread_compilation");
+ bool useBaselineEager = Preferences::GetBool(JS_OPTIONS_DOT_STR
+ "baselinejit.unsafe_eager_compilation");
+ bool useIonEager = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion.unsafe_eager_compilation");
+
+ sDiscardSystemSource = Preferences::GetBool(JS_OPTIONS_DOT_STR "discardSystemSource");
+
+ bool useAsyncStack = Preferences::GetBool(JS_OPTIONS_DOT_STR "asyncstack");
+
+ bool throwOnDebuggeeWouldRun = Preferences::GetBool(JS_OPTIONS_DOT_STR
+ "throw_on_debuggee_would_run");
+
+ bool dumpStackOnDebuggeeWouldRun = Preferences::GetBool(JS_OPTIONS_DOT_STR
+ "dump_stack_on_debuggee_would_run");
+
+ bool werror = Preferences::GetBool(JS_OPTIONS_DOT_STR "werror");
+
+ bool extraWarnings = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict");
+
+ sSharedMemoryEnabled = Preferences::GetBool(JS_OPTIONS_DOT_STR "shared_memory");
+
+#ifdef DEBUG
+ sExtraWarningsForSystemJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict.debug");
+#endif
+
+#ifdef JS_GC_ZEAL
+ int32_t zeal = Preferences::GetInt(JS_OPTIONS_DOT_STR "gczeal", -1);
+ int32_t zeal_frequency =
+ Preferences::GetInt(JS_OPTIONS_DOT_STR "gczeal.frequency",
+ JS_DEFAULT_ZEAL_FREQ);
+ if (zeal >= 0) {
+ JS_SetGCZeal(cx, (uint8_t)zeal, zeal_frequency);
+ }
+#endif // JS_GC_ZEAL
+
+ JS::ContextOptionsRef(cx).setBaseline(useBaseline)
+ .setIon(useIon)
+ .setAsmJS(useAsmJS)
+ .setWasm(useWasm)
+ .setWasmAlwaysBaseline(useWasmBaseline)
+ .setThrowOnAsmJSValidationFailure(throwOnAsmJSValidationFailure)
+ .setNativeRegExp(useNativeRegExp)
+ .setAsyncStack(useAsyncStack)
+ .setThrowOnDebuggeeWouldRun(throwOnDebuggeeWouldRun)
+ .setDumpStackOnDebuggeeWouldRun(dumpStackOnDebuggeeWouldRun)
+ .setWerror(werror)
+ .setExtraWarnings(extraWarnings);
+
+ JS_SetParallelParsingEnabled(cx, parallelParsing);
+ JS_SetOffthreadIonCompilationEnabled(cx, offthreadIonCompilation);
+ JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_BASELINE_WARMUP_TRIGGER,
+ useBaselineEager ? 0 : -1);
+ JS_SetGlobalJitCompilerOption(cx, JSJITCOMPILER_ION_WARMUP_TRIGGER,
+ useIonEager ? 0 : -1);
+}
+
+XPCJSContext::~XPCJSContext()
+{
+ // Elsewhere we abort immediately if XPCJSContext initialization fails.
+ // Therefore the context must be non-null.
+ MOZ_ASSERT(MaybeContext());
+
+ // This destructor runs before ~CycleCollectedJSContext, which does the
+ // actual JS_DestroyContext() call. But destroying the context triggers
+ // one final GC, which can call back into the context with various
+ // callbacks if we aren't careful. Null out the relevant callbacks.
+ js::SetActivityCallback(Context(), nullptr, nullptr);
+ JS_RemoveFinalizeCallback(Context(), FinalizeCallback);
+ JS_RemoveWeakPointerZoneGroupCallback(Context(), WeakPointerZoneGroupCallback);
+ JS_RemoveWeakPointerCompartmentCallback(Context(), WeakPointerCompartmentCallback);
+
+ // Clear any pending exception. It might be an XPCWrappedJS, and if we try
+ // to destroy it later we will crash.
+ SetPendingException(nullptr);
+
+ JS::SetGCSliceCallback(Context(), mPrevGCSliceCallback);
+
+ xpc_DelocalizeContext(Context());
+
+ if (mWatchdogManager->GetWatchdog())
+ mWatchdogManager->StopWatchdog();
+
+ if (mCallContext)
+ mCallContext->SystemIsBeingShutDown();
+
+ auto rtPrivate = static_cast<PerThreadAtomCache*>(JS_GetContextPrivate(Context()));
+ delete rtPrivate;
+ JS_SetContextPrivate(Context(), nullptr);
+
+ // clean up and destroy maps...
+ mWrappedJSMap->ShutdownMarker();
+ delete mWrappedJSMap;
+ mWrappedJSMap = nullptr;
+
+ delete mWrappedJSClassMap;
+ mWrappedJSClassMap = nullptr;
+
+ delete mIID2NativeInterfaceMap;
+ mIID2NativeInterfaceMap = nullptr;
+
+ delete mClassInfo2NativeSetMap;
+ mClassInfo2NativeSetMap = nullptr;
+
+ delete mNativeSetMap;
+ mNativeSetMap = nullptr;
+
+ delete mThisTranslatorMap;
+ mThisTranslatorMap = nullptr;
+
+ delete mDyingWrappedNativeProtoMap;
+ mDyingWrappedNativeProtoMap = nullptr;
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ // Tell the profiler that the context is gone
+ if (PseudoStack* stack = mozilla_get_pseudo_stack())
+ stack->sampleContext(nullptr);
+#endif
+
+ Preferences::UnregisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
+}
+
+// If |*anonymizeID| is non-zero and this is a user compartment, the name will
+// be anonymized.
+static void
+GetCompartmentName(JSCompartment* c, nsCString& name, int* anonymizeID,
+ bool replaceSlashes)
+{
+ if (js::IsAtomsCompartment(c)) {
+ name.AssignLiteral("atoms");
+ } else if (*anonymizeID && !js::IsSystemCompartment(c)) {
+ name.AppendPrintf("<anonymized-%d>", *anonymizeID);
+ *anonymizeID += 1;
+ } else if (JSPrincipals* principals = JS_GetCompartmentPrincipals(c)) {
+ nsresult rv = nsJSPrincipals::get(principals)->GetScriptLocation(name);
+ if (NS_FAILED(rv)) {
+ name.AssignLiteral("(unknown)");
+ }
+
+ // If the compartment's location (name) differs from the principal's
+ // script location, append the compartment's location to allow
+ // differentiation of multiple compartments owned by the same principal
+ // (e.g. components owned by the system or null principal).
+ CompartmentPrivate* compartmentPrivate = CompartmentPrivate::Get(c);
+ if (compartmentPrivate) {
+ const nsACString& location = compartmentPrivate->GetLocation();
+ if (!location.IsEmpty() && !location.Equals(name)) {
+ name.AppendLiteral(", ");
+ name.Append(location);
+ }
+ }
+
+ if (*anonymizeID) {
+ // We might have a file:// URL that includes a path from the local
+ // filesystem, which should be omitted if we're anonymizing.
+ static const char* filePrefix = "file://";
+ int filePos = name.Find(filePrefix);
+ if (filePos >= 0) {
+ int pathPos = filePos + strlen(filePrefix);
+ int lastSlashPos = -1;
+ for (int i = pathPos; i < int(name.Length()); i++) {
+ if (name[i] == '/' || name[i] == '\\') {
+ lastSlashPos = i;
+ }
+ }
+ if (lastSlashPos != -1) {
+ name.ReplaceASCII(pathPos, lastSlashPos - pathPos,
+ "<anonymized>");
+ } else {
+ // Something went wrong. Anonymize the entire path to be
+ // safe.
+ name.Truncate(pathPos);
+ name += "<anonymized?!>";
+ }
+ }
+
+ // We might have a location like this:
+ // inProcessTabChildGlobal?ownedBy=http://www.example.com/
+ // The owner should be omitted if it's not a chrome: URI and we're
+ // anonymizing.
+ static const char* ownedByPrefix =
+ "inProcessTabChildGlobal?ownedBy=";
+ int ownedByPos = name.Find(ownedByPrefix);
+ if (ownedByPos >= 0) {
+ const char* chrome = "chrome:";
+ int ownerPos = ownedByPos + strlen(ownedByPrefix);
+ const nsDependentCSubstring& ownerFirstPart =
+ Substring(name, ownerPos, strlen(chrome));
+ if (!ownerFirstPart.EqualsASCII(chrome)) {
+ name.Truncate(ownerPos);
+ name += "<anonymized>";
+ }
+ }
+ }
+
+ // A hack: replace forward slashes with '\\' so they aren't
+ // treated as path separators. Users of the reporters
+ // (such as about:memory) have to undo this change.
+ if (replaceSlashes)
+ name.ReplaceChar('/', '\\');
+ } else {
+ name.AssignLiteral("null-principal");
+ }
+}
+
+extern void
+xpc::GetCurrentCompartmentName(JSContext* cx, nsCString& name)
+{
+ RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
+ if (!global) {
+ name.AssignLiteral("no global");
+ return;
+ }
+
+ JSCompartment* compartment = GetObjectCompartment(global);
+ int anonymizeID = 0;
+ GetCompartmentName(compartment, name, &anonymizeID, false);
+}
+
+void
+xpc::AddGCCallback(xpcGCCallback cb)
+{
+ XPCJSContext::Get()->AddGCCallback(cb);
+}
+
+void
+xpc::RemoveGCCallback(xpcGCCallback cb)
+{
+ XPCJSContext::Get()->RemoveGCCallback(cb);
+}
+
+static int64_t
+JSMainRuntimeGCHeapDistinguishedAmount()
+{
+ JSContext* cx = danger::GetJSContext();
+ return int64_t(JS_GetGCParameter(cx, JSGC_TOTAL_CHUNKS)) *
+ js::gc::ChunkSize;
+}
+
+static int64_t
+JSMainRuntimeTemporaryPeakDistinguishedAmount()
+{
+ JSContext* cx = danger::GetJSContext();
+ return JS::PeakSizeOfTemporary(cx);
+}
+
+static int64_t
+JSMainRuntimeCompartmentsSystemDistinguishedAmount()
+{
+ JSContext* cx = danger::GetJSContext();
+ return JS::SystemCompartmentCount(cx);
+}
+
+static int64_t
+JSMainRuntimeCompartmentsUserDistinguishedAmount()
+{
+ JSContext* cx = nsXPConnect::GetContextInstance()->Context();
+ return JS::UserCompartmentCount(cx);
+}
+
+class JSMainRuntimeTemporaryPeakReporter final : public nsIMemoryReporter
+{
+ ~JSMainRuntimeTemporaryPeakReporter() {}
+
+ public:
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
+ nsISupports* aData, bool aAnonymize) override
+ {
+ MOZ_COLLECT_REPORT(
+ "js-main-runtime-temporary-peak", KIND_OTHER, UNITS_BYTES,
+ JSMainRuntimeTemporaryPeakDistinguishedAmount(),
+ "Peak transient data size in the main JSRuntime (the current size "
+ "of which is reported as "
+ "'explicit/js-non-window/runtime/temporary').");
+
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(JSMainRuntimeTemporaryPeakReporter, nsIMemoryReporter)
+
+// The REPORT* macros do an unconditional report. The ZCREPORT* macros are for
+// compartments and zones; they aggregate any entries smaller than
+// SUNDRIES_THRESHOLD into the "sundries/gc-heap" and "sundries/malloc-heap"
+// entries for the compartment.
+
+#define SUNDRIES_THRESHOLD js::MemoryReportingSundriesThreshold()
+
+#define REPORT(_path, _kind, _units, _amount, _desc) \
+ handleReport->Callback(EmptyCString(), _path, \
+ nsIMemoryReporter::_kind, \
+ nsIMemoryReporter::_units, _amount, \
+ NS_LITERAL_CSTRING(_desc), data); \
+
+#define REPORT_BYTES(_path, _kind, _amount, _desc) \
+ REPORT(_path, _kind, UNITS_BYTES, _amount, _desc);
+
+#define REPORT_GC_BYTES(_path, _amount, _desc) \
+ do { \
+ size_t amount = _amount; /* evaluate _amount only once */ \
+ handleReport->Callback(EmptyCString(), _path, \
+ nsIMemoryReporter::KIND_NONHEAP, \
+ nsIMemoryReporter::UNITS_BYTES, amount, \
+ NS_LITERAL_CSTRING(_desc), data); \
+ gcTotal += amount; \
+ } while (0)
+
+// Report compartment/zone non-GC (KIND_HEAP) bytes.
+#define ZCREPORT_BYTES(_path, _amount, _desc) \
+ do { \
+ /* Assign _descLiteral plus "" into a char* to prove that it's */ \
+ /* actually a literal. */ \
+ size_t amount = _amount; /* evaluate _amount only once */ \
+ if (amount >= SUNDRIES_THRESHOLD) { \
+ handleReport->Callback(EmptyCString(), _path, \
+ nsIMemoryReporter::KIND_HEAP, \
+ nsIMemoryReporter::UNITS_BYTES, amount, \
+ NS_LITERAL_CSTRING(_desc), data); \
+ } else { \
+ sundriesMallocHeap += amount; \
+ } \
+ } while (0)
+
+// Report compartment/zone GC bytes.
+#define ZCREPORT_GC_BYTES(_path, _amount, _desc) \
+ do { \
+ size_t amount = _amount; /* evaluate _amount only once */ \
+ if (amount >= SUNDRIES_THRESHOLD) { \
+ handleReport->Callback(EmptyCString(), _path, \
+ nsIMemoryReporter::KIND_NONHEAP, \
+ nsIMemoryReporter::UNITS_BYTES, amount, \
+ NS_LITERAL_CSTRING(_desc), data); \
+ gcTotal += amount; \
+ } else { \
+ sundriesGCHeap += amount; \
+ } \
+ } while (0)
+
+// Report runtime bytes.
+#define RREPORT_BYTES(_path, _kind, _amount, _desc) \
+ do { \
+ size_t amount = _amount; /* evaluate _amount only once */ \
+ handleReport->Callback(EmptyCString(), _path, \
+ nsIMemoryReporter::_kind, \
+ nsIMemoryReporter::UNITS_BYTES, amount, \
+ NS_LITERAL_CSTRING(_desc), data); \
+ rtTotal += amount; \
+ } while (0)
+
+// Report GC thing bytes.
+#define MREPORT_BYTES(_path, _kind, _amount, _desc) \
+ do { \
+ size_t amount = _amount; /* evaluate _amount only once */ \
+ handleReport->Callback(EmptyCString(), _path, \
+ nsIMemoryReporter::_kind, \
+ nsIMemoryReporter::UNITS_BYTES, amount, \
+ NS_LITERAL_CSTRING(_desc), data); \
+ gcThingTotal += amount; \
+ } while (0)
+
+MOZ_DEFINE_MALLOC_SIZE_OF(JSMallocSizeOf)
+
+namespace xpc {
+
+static void
+ReportZoneStats(const JS::ZoneStats& zStats,
+ const xpc::ZoneStatsExtras& extras,
+ nsIHandleReportCallback* handleReport,
+ nsISupports* data,
+ bool anonymize,
+ size_t* gcTotalOut = nullptr)
+{
+ const nsCString& pathPrefix = extras.pathPrefix;
+ size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
+
+ MOZ_ASSERT(!gcTotalOut == zStats.isTotals);
+
+ ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("symbols/gc-heap"),
+ zStats.symbolsGCHeap,
+ "Symbols.");
+
+ ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("gc-heap-arena-admin"),
+ zStats.gcHeapArenaAdmin,
+ "Bookkeeping information and alignment padding within GC arenas.");
+
+ ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("unused-gc-things"),
+ zStats.unusedGCThings.totalSize(),
+ "Unused GC thing cells within non-empty arenas.");
+
+ ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("unique-id-map"),
+ zStats.uniqueIdMap,
+ "Address-independent cell identities.");
+
+ ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shape-tables"),
+ zStats.shapeTables,
+ "Tables storing shape information.");
+
+ ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"),
+ zStats.lazyScriptsGCHeap,
+ "Scripts that haven't executed yet.");
+
+ ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/malloc-heap"),
+ zStats.lazyScriptsMallocHeap,
+ "Lazy script tables containing closed-over bindings or inner functions.");
+
+ ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-codes-gc-heap"),
+ zStats.jitCodesGCHeap,
+ "References to executable code pools used by the JITs.");
+
+ ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("object-groups/gc-heap"),
+ zStats.objectGroupsGCHeap,
+ "Classification and type inference information about objects.");
+
+ ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("object-groups/malloc-heap"),
+ zStats.objectGroupsMallocHeap,
+ "Object group addenda.");
+
+ ZCREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("scopes/gc-heap"),
+ zStats.scopesGCHeap,
+ "Scope information for scripts.");
+
+ ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("scopes/malloc-heap"),
+ zStats.scopesMallocHeap,
+ "Arrays of binding names and other binding-related data.");
+
+ ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("type-pool"),
+ zStats.typePool,
+ "Type sets and related data.");
+
+ ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("baseline/optimized-stubs"),
+ zStats.baselineStubsOptimized,
+ "The Baseline JIT's optimized IC stubs (excluding code).");
+
+ size_t stringsNotableAboutMemoryGCHeap = 0;
+ size_t stringsNotableAboutMemoryMallocHeap = 0;
+
+ #define MAYBE_INLINE \
+ "The characters may be inline or on the malloc heap."
+ #define MAYBE_OVERALLOCATED \
+ "Sometimes over-allocated to simplify string concatenation."
+
+ for (size_t i = 0; i < zStats.notableStrings.length(); i++) {
+ const JS::NotableStringInfo& info = zStats.notableStrings[i];
+
+ MOZ_ASSERT(!zStats.isTotals);
+
+ // We don't do notable string detection when anonymizing, because
+ // there's a good chance its for crash submission, and the memory
+ // required for notable string detection is high.
+ MOZ_ASSERT(!anonymize);
+
+ nsDependentCString notableString(info.buffer);
+
+ // Viewing about:memory generates many notable strings which contain
+ // "string(length=". If we report these as notable, then we'll create
+ // even more notable strings the next time we open about:memory (unless
+ // there's a GC in the meantime), and so on ad infinitum.
+ //
+ // To avoid cluttering up about:memory like this, we stick notable
+ // strings which contain "string(length=" into their own bucket.
+# define STRING_LENGTH "string(length="
+ if (FindInReadable(NS_LITERAL_CSTRING(STRING_LENGTH), notableString)) {
+ stringsNotableAboutMemoryGCHeap += info.gcHeapLatin1;
+ stringsNotableAboutMemoryGCHeap += info.gcHeapTwoByte;
+ stringsNotableAboutMemoryMallocHeap += info.mallocHeapLatin1;
+ stringsNotableAboutMemoryMallocHeap += info.mallocHeapTwoByte;
+ continue;
+ }
+
+ // Escape / to \ before we put notableString into the memory reporter
+ // path, because we don't want any forward slashes in the string to
+ // count as path separators.
+ nsCString escapedString(notableString);
+ escapedString.ReplaceSubstring("/", "\\");
+
+ bool truncated = notableString.Length() < info.length;
+
+ nsCString path = pathPrefix +
+ nsPrintfCString("strings/" STRING_LENGTH "%d, copies=%d, \"%s\"%s)/",
+ info.length, info.numCopies, escapedString.get(),
+ truncated ? " (truncated)" : "");
+
+ if (info.gcHeapLatin1 > 0) {
+ REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap/latin1"),
+ info.gcHeapLatin1,
+ "Latin1 strings. " MAYBE_INLINE);
+ }
+
+ if (info.gcHeapTwoByte > 0) {
+ REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("gc-heap/two-byte"),
+ info.gcHeapTwoByte,
+ "TwoByte strings. " MAYBE_INLINE);
+ }
+
+ if (info.mallocHeapLatin1 > 0) {
+ REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap/latin1"),
+ KIND_HEAP, info.mallocHeapLatin1,
+ "Non-inline Latin1 string characters. " MAYBE_OVERALLOCATED);
+ }
+
+ if (info.mallocHeapTwoByte > 0) {
+ REPORT_BYTES(path + NS_LITERAL_CSTRING("malloc-heap/two-byte"),
+ KIND_HEAP, info.mallocHeapTwoByte,
+ "Non-inline TwoByte string characters. " MAYBE_OVERALLOCATED);
+ }
+ }
+
+ nsCString nonNotablePath = pathPrefix;
+ nonNotablePath += (zStats.isTotals || anonymize)
+ ? NS_LITERAL_CSTRING("strings/")
+ : NS_LITERAL_CSTRING("strings/string(<non-notable strings>)/");
+
+ if (zStats.stringInfo.gcHeapLatin1 > 0) {
+ REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap/latin1"),
+ zStats.stringInfo.gcHeapLatin1,
+ "Latin1 strings. " MAYBE_INLINE);
+ }
+
+ if (zStats.stringInfo.gcHeapTwoByte > 0) {
+ REPORT_GC_BYTES(nonNotablePath + NS_LITERAL_CSTRING("gc-heap/two-byte"),
+ zStats.stringInfo.gcHeapTwoByte,
+ "TwoByte strings. " MAYBE_INLINE);
+ }
+
+ if (zStats.stringInfo.mallocHeapLatin1 > 0) {
+ REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap/latin1"),
+ KIND_HEAP, zStats.stringInfo.mallocHeapLatin1,
+ "Non-inline Latin1 string characters. " MAYBE_OVERALLOCATED);
+ }
+
+ if (zStats.stringInfo.mallocHeapTwoByte > 0) {
+ REPORT_BYTES(nonNotablePath + NS_LITERAL_CSTRING("malloc-heap/two-byte"),
+ KIND_HEAP, zStats.stringInfo.mallocHeapTwoByte,
+ "Non-inline TwoByte string characters. " MAYBE_OVERALLOCATED);
+ }
+
+ if (stringsNotableAboutMemoryGCHeap > 0) {
+ MOZ_ASSERT(!zStats.isTotals);
+ REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/gc-heap"),
+ stringsNotableAboutMemoryGCHeap,
+ "Strings that contain the characters '" STRING_LENGTH "', which "
+ "are probably from about:memory itself." MAYBE_INLINE
+ " We filter them out rather than display them, because displaying "
+ "them would create even more such strings every time about:memory "
+ "is refreshed.");
+ }
+
+ if (stringsNotableAboutMemoryMallocHeap > 0) {
+ MOZ_ASSERT(!zStats.isTotals);
+ REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("strings/string(<about-memory>)/malloc-heap"),
+ KIND_HEAP, stringsNotableAboutMemoryMallocHeap,
+ "Non-inline string characters of strings that contain the "
+ "characters '" STRING_LENGTH "', which are probably from "
+ "about:memory itself. " MAYBE_OVERALLOCATED
+ " We filter them out rather than display them, because displaying "
+ "them would create even more such strings every time about:memory "
+ "is refreshed.");
+ }
+
+ const JS::ShapeInfo& shapeInfo = zStats.shapeInfo;
+ if (shapeInfo.shapesGCHeapTree > 0) {
+ REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/tree"),
+ shapeInfo.shapesGCHeapTree,
+ "Shapes in a property tree.");
+ }
+
+ if (shapeInfo.shapesGCHeapDict > 0) {
+ REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/dict"),
+ shapeInfo.shapesGCHeapDict,
+ "Shapes in dictionary mode.");
+ }
+
+ if (shapeInfo.shapesGCHeapBase > 0) {
+ REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/gc-heap/base"),
+ shapeInfo.shapesGCHeapBase,
+ "Base shapes, which collate data common to many shapes.");
+ }
+
+ if (shapeInfo.shapesMallocHeapTreeTables > 0) {
+ REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-tables"),
+ KIND_HEAP, shapeInfo.shapesMallocHeapTreeTables,
+ "Property tables of shapes in a property tree.");
+ }
+
+ if (shapeInfo.shapesMallocHeapDictTables > 0) {
+ REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/dict-tables"),
+ KIND_HEAP, shapeInfo.shapesMallocHeapDictTables,
+ "Property tables of shapes in dictionary mode.");
+ }
+
+ if (shapeInfo.shapesMallocHeapTreeKids > 0) {
+ REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("shapes/malloc-heap/tree-kids"),
+ KIND_HEAP, shapeInfo.shapesMallocHeapTreeKids,
+ "Kid hashes of shapes in a property tree.");
+ }
+
+ if (sundriesGCHeap > 0) {
+ // We deliberately don't use ZCREPORT_GC_BYTES here.
+ REPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
+ sundriesGCHeap,
+ "The sum of all 'gc-heap' measurements that are too small to be "
+ "worth showing individually.");
+ }
+
+ if (sundriesMallocHeap > 0) {
+ // We deliberately don't use ZCREPORT_BYTES here.
+ REPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
+ KIND_HEAP, sundriesMallocHeap,
+ "The sum of all 'malloc-heap' measurements that are too small to "
+ "be worth showing individually.");
+ }
+
+ if (gcTotalOut)
+ *gcTotalOut += gcTotal;
+
+# undef STRING_LENGTH
+}
+
+static void
+ReportClassStats(const ClassInfo& classInfo, const nsACString& path,
+ nsIHandleReportCallback* handleReport,
+ nsISupports* data, size_t& gcTotal)
+{
+ // We deliberately don't use ZCREPORT_BYTES, so that these per-class values
+ // don't go into sundries.
+
+ if (classInfo.objectsGCHeap > 0) {
+ REPORT_GC_BYTES(path + NS_LITERAL_CSTRING("objects/gc-heap"),
+ classInfo.objectsGCHeap,
+ "Objects, including fixed slots.");
+ }
+
+ if (classInfo.objectsMallocHeapSlots > 0) {
+ REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/slots"),
+ KIND_HEAP, classInfo.objectsMallocHeapSlots,
+ "Non-fixed object slots.");
+ }
+
+ if (classInfo.objectsMallocHeapElementsNormal > 0) {
+ REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/elements/normal"),
+ KIND_HEAP, classInfo.objectsMallocHeapElementsNormal,
+ "Normal (non-wasm) indexed elements.");
+ }
+
+ if (classInfo.objectsMallocHeapElementsAsmJS > 0) {
+ REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/elements/asm.js"),
+ KIND_HEAP, classInfo.objectsMallocHeapElementsAsmJS,
+ "asm.js array buffer elements allocated in the malloc heap.");
+ }
+
+ if (classInfo.objectsMallocHeapMisc > 0) {
+ REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/malloc-heap/misc"),
+ KIND_HEAP, classInfo.objectsMallocHeapMisc,
+ "Miscellaneous object data.");
+ }
+
+ if (classInfo.objectsNonHeapElementsNormal > 0) {
+ REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/normal"),
+ KIND_NONHEAP, classInfo.objectsNonHeapElementsNormal,
+ "Memory-mapped non-shared array buffer elements.");
+ }
+
+ if (classInfo.objectsNonHeapElementsShared > 0) {
+ REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/shared"),
+ KIND_NONHEAP, classInfo.objectsNonHeapElementsShared,
+ "Memory-mapped shared array buffer elements. These elements are "
+ "shared between one or more runtimes; the reported size is divided "
+ "by the buffer's refcount.");
+ }
+
+ // WebAssembly memories are always non-heap-allocated (mmap). We never put
+ // these under sundries, because (a) in practice they're almost always
+ // larger than the sundries threshold, and (b) we'd need a third category of
+ // sundries ("non-heap"), which would be a pain.
+ if (classInfo.objectsNonHeapElementsWasm > 0) {
+ REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/elements/wasm"),
+ KIND_NONHEAP, classInfo.objectsNonHeapElementsWasm,
+ "wasm/asm.js array buffer elements allocated outside both the "
+ "malloc heap and the GC heap.");
+ }
+
+ if (classInfo.objectsNonHeapCodeWasm > 0) {
+ REPORT_BYTES(path + NS_LITERAL_CSTRING("objects/non-heap/code/wasm"),
+ KIND_NONHEAP, classInfo.objectsNonHeapCodeWasm,
+ "AOT-compiled wasm/asm.js code.");
+ }
+
+ // Although wasm guard pages aren't committed in memory they can be very
+ // large and contribute greatly to vsize and so are worth reporting.
+ if (classInfo.wasmGuardPages > 0) {
+ REPORT_BYTES(NS_LITERAL_CSTRING("wasm-guard-pages"),
+ KIND_OTHER, classInfo.wasmGuardPages,
+ "Guard pages mapped after the end of wasm memories, reserved for "
+ "optimization tricks, but not committed and thus never contributing"
+ " to RSS, only vsize.");
+ }
+}
+
+static void
+ReportCompartmentStats(const JS::CompartmentStats& cStats,
+ const xpc::CompartmentStatsExtras& extras,
+ amIAddonManager* addonManager,
+ nsIHandleReportCallback* handleReport,
+ nsISupports* data, size_t* gcTotalOut = nullptr)
+{
+ static const nsDependentCString addonPrefix("explicit/add-ons/");
+
+ size_t gcTotal = 0, sundriesGCHeap = 0, sundriesMallocHeap = 0;
+ nsAutoCString cJSPathPrefix(extras.jsPathPrefix);
+ nsAutoCString cDOMPathPrefix(extras.domPathPrefix);
+
+ MOZ_ASSERT(!gcTotalOut == cStats.isTotals);
+
+ // Only attempt to prefix if we got a location and the path wasn't already
+ // prefixed.
+ if (extras.location && addonManager &&
+ cJSPathPrefix.Find(addonPrefix, false, 0, 0) != 0) {
+ nsAutoCString addonId;
+ bool ok;
+ if (NS_SUCCEEDED(addonManager->MapURIToAddonID(extras.location,
+ addonId, &ok))
+ && ok) {
+ // Insert the add-on id as "add-ons/@id@/" after "explicit/" to
+ // aggregate add-on compartments.
+ static const size_t explicitLength = strlen("explicit/");
+ addonId.Insert(NS_LITERAL_CSTRING("add-ons/"), 0);
+ addonId += "/";
+ cJSPathPrefix.Insert(addonId, explicitLength);
+ cDOMPathPrefix.Insert(addonId, explicitLength);
+ }
+ }
+
+ nsCString nonNotablePath = cJSPathPrefix;
+ nonNotablePath += cStats.isTotals
+ ? NS_LITERAL_CSTRING("classes/")
+ : NS_LITERAL_CSTRING("classes/class(<non-notable classes>)/");
+
+ ReportClassStats(cStats.classInfo, nonNotablePath, handleReport, data,
+ gcTotal);
+
+ for (size_t i = 0; i < cStats.notableClasses.length(); i++) {
+ MOZ_ASSERT(!cStats.isTotals);
+ const JS::NotableClassInfo& classInfo = cStats.notableClasses[i];
+
+ nsCString classPath = cJSPathPrefix +
+ nsPrintfCString("classes/class(%s)/", classInfo.className_);
+
+ ReportClassStats(classInfo, classPath, handleReport, data, gcTotal);
+ }
+
+ // Note that we use cDOMPathPrefix here. This is because we measure orphan
+ // DOM nodes in the JS reporter, but we want to report them in a "dom"
+ // sub-tree rather than a "js" sub-tree.
+ ZCREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("orphan-nodes"),
+ cStats.objectsPrivate,
+ "Orphan DOM nodes, i.e. those that are only reachable from JavaScript "
+ "objects.");
+
+ ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/gc-heap"),
+ cStats.scriptsGCHeap,
+ "JSScript instances. There is one per user-defined function in a "
+ "script, and one for the top-level code in a script.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/malloc-heap/data"),
+ cStats.scriptsMallocHeapData,
+ "Various variable-length tables in JSScripts.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/data"),
+ cStats.baselineData,
+ "The Baseline JIT's compilation data (BaselineScripts).");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("baseline/fallback-stubs"),
+ cStats.baselineStubsFallback,
+ "The Baseline JIT's fallback IC stubs (excluding code).");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("ion-data"),
+ cStats.ionData,
+ "The IonMonkey JIT's compilation data (IonScripts).");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/type-scripts"),
+ cStats.typeInferenceTypeScripts,
+ "Type sets associated with scripts.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/allocation-site-tables"),
+ cStats.typeInferenceAllocationSiteTables,
+ "Tables of type objects associated with allocation sites.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/array-type-tables"),
+ cStats.typeInferenceArrayTypeTables,
+ "Tables of type objects associated with array literals.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("type-inference/object-type-tables"),
+ cStats.typeInferenceObjectTypeTables,
+ "Tables of type objects associated with object literals.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("compartment-object"),
+ cStats.compartmentObject,
+ "The JSCompartment object itself.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("compartment-tables"),
+ cStats.compartmentTables,
+ "Compartment-wide tables storing object group information and wasm instances.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("inner-views"),
+ cStats.innerViewsTable,
+ "The table for array buffer inner views.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("lazy-array-buffers"),
+ cStats.lazyArrayBuffersTable,
+ "The table for typed object lazy array buffers.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("object-metadata"),
+ cStats.objectMetadataTable,
+ "The table used by debugging tools for tracking object metadata");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"),
+ cStats.crossCompartmentWrappersTable,
+ "The cross-compartment wrapper table.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("regexp-compartment"),
+ cStats.regexpCompartment,
+ "The regexp compartment and regexp data.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("saved-stacks-set"),
+ cStats.savedStacksSet,
+ "The saved stacks set.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("non-syntactic-lexical-scopes-table"),
+ cStats.nonSyntacticLexicalScopesTable,
+ "The non-syntactic lexical scopes table.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("jit-compartment"),
+ cStats.jitCompartment,
+ "The JIT compartment.");
+
+ ZCREPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("private-data"),
+ cStats.privateData,
+ "Extra data attached to the compartment by XPConnect, including "
+ "its wrapped-js.");
+
+ if (sundriesGCHeap > 0) {
+ // We deliberately don't use ZCREPORT_GC_BYTES here.
+ REPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/gc-heap"),
+ sundriesGCHeap,
+ "The sum of all 'gc-heap' measurements that are too small to be "
+ "worth showing individually.");
+ }
+
+ if (sundriesMallocHeap > 0) {
+ // We deliberately don't use ZCREPORT_BYTES here.
+ REPORT_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("sundries/malloc-heap"),
+ KIND_HEAP, sundriesMallocHeap,
+ "The sum of all 'malloc-heap' measurements that are too small to "
+ "be worth showing individually.");
+ }
+
+ if (gcTotalOut)
+ *gcTotalOut += gcTotal;
+}
+
+static void
+ReportScriptSourceStats(const ScriptSourceInfo& scriptSourceInfo,
+ const nsACString& path,
+ nsIHandleReportCallback* handleReport,
+ nsISupports* data, size_t& rtTotal)
+{
+ if (scriptSourceInfo.misc > 0) {
+ RREPORT_BYTES(path + NS_LITERAL_CSTRING("misc"),
+ KIND_HEAP, scriptSourceInfo.misc,
+ "Miscellaneous data relating to JavaScript source code.");
+ }
+}
+
+static void
+ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats,
+ const nsACString& rtPath,
+ amIAddonManager* addonManager,
+ nsIHandleReportCallback* handleReport,
+ nsISupports* data,
+ bool anonymize,
+ size_t* rtTotalOut)
+{
+ size_t gcTotal = 0;
+
+ for (size_t i = 0; i < rtStats.zoneStatsVector.length(); i++) {
+ const JS::ZoneStats& zStats = rtStats.zoneStatsVector[i];
+ const xpc::ZoneStatsExtras* extras =
+ static_cast<const xpc::ZoneStatsExtras*>(zStats.extra);
+ ReportZoneStats(zStats, *extras, handleReport, data, anonymize,
+ &gcTotal);
+ }
+
+ for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
+ const JS::CompartmentStats& cStats = rtStats.compartmentStatsVector[i];
+ const xpc::CompartmentStatsExtras* extras =
+ static_cast<const xpc::CompartmentStatsExtras*>(cStats.extra);
+
+ ReportCompartmentStats(cStats, *extras, addonManager, handleReport,
+ data, &gcTotal);
+ }
+
+ // Report the rtStats.runtime numbers under "runtime/", and compute their
+ // total for later.
+
+ size_t rtTotal = 0;
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/runtime-object"),
+ KIND_HEAP, rtStats.runtime.object,
+ "The JSRuntime object.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/atoms-table"),
+ KIND_HEAP, rtStats.runtime.atomsTable,
+ "The atoms table.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/contexts"),
+ KIND_HEAP, rtStats.runtime.contexts,
+ "JSContext objects and structures that belong to them.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/temporary"),
+ KIND_HEAP, rtStats.runtime.temporary,
+ "Transient data (mostly parse nodes) held by the JSRuntime during "
+ "compilation.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/interpreter-stack"),
+ KIND_HEAP, rtStats.runtime.interpreterStack,
+ "JS interpreter frames.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/math-cache"),
+ KIND_HEAP, rtStats.runtime.mathCache,
+ "The math cache.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/shared-immutable-strings-cache"),
+ KIND_HEAP, rtStats.runtime.sharedImmutableStringsCache,
+ "Immutable strings (such as JS scripts' source text) shared across all JSRuntimes.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/shared-intl-data"),
+ KIND_HEAP, rtStats.runtime.sharedIntlData,
+ "Shared internationalization data.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/uncompressed-source-cache"),
+ KIND_HEAP, rtStats.runtime.uncompressedSourceCache,
+ "The uncompressed source code cache.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/script-data"),
+ KIND_HEAP, rtStats.runtime.scriptData,
+ "The table holding script data shared in the runtime.");
+
+ nsCString nonNotablePath =
+ rtPath + nsPrintfCString("runtime/script-sources/source(scripts=%d, <non-notable files>)/",
+ rtStats.runtime.scriptSourceInfo.numScripts);
+
+ ReportScriptSourceStats(rtStats.runtime.scriptSourceInfo,
+ nonNotablePath, handleReport, data, rtTotal);
+
+ for (size_t i = 0; i < rtStats.runtime.notableScriptSources.length(); i++) {
+ const JS::NotableScriptSourceInfo& scriptSourceInfo =
+ rtStats.runtime.notableScriptSources[i];
+
+ // Escape / to \ before we put the filename into the memory reporter
+ // path, because we don't want any forward slashes in the string to
+ // count as path separators. Consumers of memory reporters (e.g.
+ // about:memory) will convert them back to / after doing path
+ // splitting.
+ nsCString escapedFilename;
+ if (anonymize) {
+ escapedFilename.AppendPrintf("<anonymized-source-%d>", int(i));
+ } else {
+ nsDependentCString filename(scriptSourceInfo.filename_);
+ escapedFilename.Append(filename);
+ escapedFilename.ReplaceSubstring("/", "\\");
+ }
+
+ nsCString notablePath = rtPath +
+ nsPrintfCString("runtime/script-sources/source(scripts=%d, %s)/",
+ scriptSourceInfo.numScripts, escapedFilename.get());
+
+ ReportScriptSourceStats(scriptSourceInfo, notablePath,
+ handleReport, data, rtTotal);
+ }
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/ion"),
+ KIND_NONHEAP, rtStats.runtime.code.ion,
+ "Code generated by the IonMonkey JIT.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/baseline"),
+ KIND_NONHEAP, rtStats.runtime.code.baseline,
+ "Code generated by the Baseline JIT.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/regexp"),
+ KIND_NONHEAP, rtStats.runtime.code.regexp,
+ "Code generated by the regexp JIT.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/other"),
+ KIND_NONHEAP, rtStats.runtime.code.other,
+ "Code generated by the JITs for wrappers and trampolines.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/code/unused"),
+ KIND_NONHEAP, rtStats.runtime.code.unused,
+ "Memory allocated by one of the JITs to hold code, but which is "
+ "currently unused.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/marker"),
+ KIND_HEAP, rtStats.runtime.gc.marker,
+ "The GC mark stack and gray roots.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-committed"),
+ KIND_NONHEAP, rtStats.runtime.gc.nurseryCommitted,
+ "Memory being used by the GC's nursery.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-malloced-buffers"),
+ KIND_HEAP, rtStats.runtime.gc.nurseryMallocedBuffers,
+ "Out-of-line slots and elements belonging to objects in the nursery.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/vals"),
+ KIND_HEAP, rtStats.runtime.gc.storeBufferVals,
+ "Values in the store buffer.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/cells"),
+ KIND_HEAP, rtStats.runtime.gc.storeBufferCells,
+ "Cells in the store buffer.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/slots"),
+ KIND_HEAP, rtStats.runtime.gc.storeBufferSlots,
+ "Slots in the store buffer.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/whole-cells"),
+ KIND_HEAP, rtStats.runtime.gc.storeBufferWholeCells,
+ "Whole cells in the store buffer.");
+
+ RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/generics"),
+ KIND_HEAP, rtStats.runtime.gc.storeBufferGenerics,
+ "Generic things in the store buffer.");
+
+ if (rtTotalOut)
+ *rtTotalOut = rtTotal;
+
+ // Report GC numbers that don't belong to a compartment.
+
+ // We don't want to report decommitted memory in "explicit", so we just
+ // change the leading "explicit/" to "decommitted/".
+ nsCString rtPath2(rtPath);
+ rtPath2.Replace(0, strlen("explicit"), NS_LITERAL_CSTRING("decommitted"));
+ REPORT_GC_BYTES(rtPath2 + NS_LITERAL_CSTRING("gc-heap/decommitted-arenas"),
+ rtStats.gcHeapDecommittedArenas,
+ "GC arenas in non-empty chunks that is decommitted, i.e. it takes up "
+ "address space but no physical memory or swap space.");
+
+ REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-chunks"),
+ rtStats.gcHeapUnusedChunks,
+ "Empty GC chunks which will soon be released unless claimed for new "
+ "allocations.");
+
+ REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/unused-arenas"),
+ rtStats.gcHeapUnusedArenas,
+ "Empty GC arenas within non-empty chunks.");
+
+ REPORT_GC_BYTES(rtPath + NS_LITERAL_CSTRING("gc-heap/chunk-admin"),
+ rtStats.gcHeapChunkAdmin,
+ "Bookkeeping information within GC chunks.");
+
+ // gcTotal is the sum of everything we've reported for the GC heap. It
+ // should equal rtStats.gcHeapChunkTotal.
+ MOZ_ASSERT(gcTotal == rtStats.gcHeapChunkTotal);
+}
+
+void
+ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats& rtStats,
+ const nsACString& rtPath,
+ nsIHandleReportCallback* handleReport,
+ nsISupports* data,
+ bool anonymize,
+ size_t* rtTotalOut)
+{
+ nsCOMPtr<amIAddonManager> am;
+ if (XRE_IsParentProcess()) {
+ // Only try to access the service from the main process.
+ am = do_GetService("@mozilla.org/addons/integration;1");
+ }
+ ReportJSRuntimeExplicitTreeStats(rtStats, rtPath, am.get(), handleReport,
+ data, anonymize, rtTotalOut);
+}
+
+
+} // namespace xpc
+
+class JSMainRuntimeCompartmentsReporter final : public nsIMemoryReporter
+{
+
+ ~JSMainRuntimeCompartmentsReporter() {}
+
+ public:
+ NS_DECL_ISUPPORTS
+
+ struct Data {
+ int anonymizeID;
+ js::Vector<nsCString, 0, js::SystemAllocPolicy> paths;
+ };
+
+ static void CompartmentCallback(JSContext* cx, void* vdata, JSCompartment* c) {
+ // silently ignore OOM errors
+ Data* data = static_cast<Data*>(vdata);
+ nsCString path;
+ GetCompartmentName(c, path, &data->anonymizeID, /* replaceSlashes = */ true);
+ path.Insert(js::IsSystemCompartment(c)
+ ? NS_LITERAL_CSTRING("js-main-runtime-compartments/system/")
+ : NS_LITERAL_CSTRING("js-main-runtime-compartments/user/"),
+ 0);
+ mozilla::Unused << data->paths.append(path);
+ }
+
+ NS_IMETHOD CollectReports(nsIHandleReportCallback* handleReport,
+ nsISupports* data, bool anonymize) override
+ {
+ // First we collect the compartment paths. Then we report them. Doing
+ // the two steps interleaved is a bad idea, because calling
+ // |handleReport| from within CompartmentCallback() leads to all manner
+ // of assertions.
+
+ Data d;
+ d.anonymizeID = anonymize ? 1 : 0;
+ JS_IterateCompartments(nsXPConnect::GetContextInstance()->Context(),
+ &d, CompartmentCallback);
+
+ for (size_t i = 0; i < d.paths.length(); i++)
+ REPORT(nsCString(d.paths[i]), KIND_OTHER, UNITS_COUNT, 1,
+ "A live compartment in the main JSRuntime.");
+
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS(JSMainRuntimeCompartmentsReporter, nsIMemoryReporter)
+
+MOZ_DEFINE_MALLOC_SIZE_OF(OrphanMallocSizeOf)
+
+namespace xpc {
+
+static size_t
+SizeOfTreeIncludingThis(nsINode* tree)
+{
+ size_t n = tree->SizeOfIncludingThis(OrphanMallocSizeOf);
+ for (nsIContent* child = tree->GetFirstChild(); child; child = child->GetNextNode(tree))
+ n += child->SizeOfIncludingThis(OrphanMallocSizeOf);
+
+ return n;
+}
+
+class OrphanReporter : public JS::ObjectPrivateVisitor
+{
+ public:
+ explicit OrphanReporter(GetISupportsFun aGetISupports)
+ : JS::ObjectPrivateVisitor(aGetISupports)
+ {
+ }
+
+ virtual size_t sizeOfIncludingThis(nsISupports* aSupports) override {
+ size_t n = 0;
+ nsCOMPtr<nsINode> node = do_QueryInterface(aSupports);
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains
+ // that we have to skip XBL elements because they violate certain
+ // assumptions. Yuk.
+ if (node && !node->IsInUncomposedDoc() &&
+ !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL)))
+ {
+ // This is an orphan node. If we haven't already handled the
+ // sub-tree that this node belongs to, measure the sub-tree's size
+ // and then record its root so we don't measure it again.
+ nsCOMPtr<nsINode> orphanTree = node->SubtreeRoot();
+ if (orphanTree &&
+ !mAlreadyMeasuredOrphanTrees.Contains(orphanTree)) {
+ // If PutEntry() fails we don't measure this tree, which could
+ // lead to under-measurement. But that's better than the
+ // alternatives, which are over-measurement or an OOM abort.
+ if (mAlreadyMeasuredOrphanTrees.PutEntry(orphanTree, fallible)) {
+ n += SizeOfTreeIncludingThis(orphanTree);
+ }
+ }
+ }
+ return n;
+ }
+
+ private:
+ nsTHashtable <nsISupportsHashKey> mAlreadyMeasuredOrphanTrees;
+};
+
+#ifdef DEBUG
+static bool
+StartsWithExplicit(nsACString& s)
+{
+ return StringBeginsWith(s, NS_LITERAL_CSTRING("explicit/"));
+}
+#endif
+
+class XPCJSContextStats : public JS::RuntimeStats
+{
+ WindowPaths* mWindowPaths;
+ WindowPaths* mTopWindowPaths;
+ bool mGetLocations;
+ int mAnonymizeID;
+
+ public:
+ XPCJSContextStats(WindowPaths* windowPaths, WindowPaths* topWindowPaths,
+ bool getLocations, bool anonymize)
+ : JS::RuntimeStats(JSMallocSizeOf),
+ mWindowPaths(windowPaths),
+ mTopWindowPaths(topWindowPaths),
+ mGetLocations(getLocations),
+ mAnonymizeID(anonymize ? 1 : 0)
+ {}
+
+ ~XPCJSContextStats() {
+ for (size_t i = 0; i != compartmentStatsVector.length(); ++i)
+ delete static_cast<xpc::CompartmentStatsExtras*>(compartmentStatsVector[i].extra);
+
+
+ for (size_t i = 0; i != zoneStatsVector.length(); ++i)
+ delete static_cast<xpc::ZoneStatsExtras*>(zoneStatsVector[i].extra);
+ }
+
+ virtual void initExtraZoneStats(JS::Zone* zone, JS::ZoneStats* zStats) override {
+ // Get the compartment's global.
+ AutoSafeJSContext cx;
+ JSCompartment* comp = js::GetAnyCompartmentInZone(zone);
+ xpc::ZoneStatsExtras* extras = new xpc::ZoneStatsExtras;
+ extras->pathPrefix.AssignLiteral("explicit/js-non-window/zones/");
+ RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, comp));
+ if (global) {
+ RefPtr<nsGlobalWindow> window;
+ if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) {
+ // The global is a |window| object. Use the path prefix that
+ // we should have already created for it.
+ if (mTopWindowPaths->Get(window->WindowID(),
+ &extras->pathPrefix))
+ extras->pathPrefix.AppendLiteral("/js-");
+ }
+ }
+
+ extras->pathPrefix += nsPrintfCString("zone(0x%p)/", (void*)zone);
+
+ MOZ_ASSERT(StartsWithExplicit(extras->pathPrefix));
+
+ zStats->extra = extras;
+ }
+
+ virtual void initExtraCompartmentStats(JSCompartment* c,
+ JS::CompartmentStats* cstats) override
+ {
+ xpc::CompartmentStatsExtras* extras = new xpc::CompartmentStatsExtras;
+ nsCString cName;
+ GetCompartmentName(c, cName, &mAnonymizeID, /* replaceSlashes = */ true);
+ CompartmentPrivate* cp = CompartmentPrivate::Get(c);
+ if (cp) {
+ if (mGetLocations) {
+ cp->GetLocationURI(CompartmentPrivate::LocationHintAddon,
+ getter_AddRefs(extras->location));
+ }
+ // Note: cannot use amIAddonManager implementation at this point,
+ // as it is a JS service and the JS heap is currently not idle.
+ // Otherwise, we could have computed the add-on id at this point.
+ }
+
+ // Get the compartment's global.
+ AutoSafeJSContext cx;
+ bool needZone = true;
+ RootedObject global(cx, JS_GetGlobalForCompartmentOrNull(cx, c));
+ if (global) {
+ RefPtr<nsGlobalWindow> window;
+ if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, window))) {
+ // The global is a |window| object. Use the path prefix that
+ // we should have already created for it.
+ if (mWindowPaths->Get(window->WindowID(),
+ &extras->jsPathPrefix)) {
+ extras->domPathPrefix.Assign(extras->jsPathPrefix);
+ extras->domPathPrefix.AppendLiteral("/dom/");
+ extras->jsPathPrefix.AppendLiteral("/js-");
+ needZone = false;
+ } else {
+ extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
+ extras->domPathPrefix.AssignLiteral("explicit/dom/unknown-window-global?!/");
+ }
+ } else {
+ extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
+ extras->domPathPrefix.AssignLiteral("explicit/dom/non-window-global?!/");
+ }
+ } else {
+ extras->jsPathPrefix.AssignLiteral("explicit/js-non-window/zones/");
+ extras->domPathPrefix.AssignLiteral("explicit/dom/no-global?!/");
+ }
+
+ if (needZone)
+ extras->jsPathPrefix += nsPrintfCString("zone(0x%p)/", (void*)js::GetCompartmentZone(c));
+
+ extras->jsPathPrefix += NS_LITERAL_CSTRING("compartment(") + cName + NS_LITERAL_CSTRING(")/");
+
+ // extras->jsPathPrefix is used for almost all the compartment-specific
+ // reports. At this point it has the form
+ // "<something>compartment(<cname>)/".
+ //
+ // extras->domPathPrefix is used for DOM orphan nodes, which are
+ // counted by the JS reporter but reported as part of the DOM
+ // measurements. At this point it has the form "<something>/dom/" if
+ // this compartment belongs to an nsGlobalWindow, and
+ // "explicit/dom/<something>?!/" otherwise (in which case it shouldn't
+ // be used, because non-nsGlobalWindow compartments shouldn't have
+ // orphan DOM nodes).
+
+ MOZ_ASSERT(StartsWithExplicit(extras->jsPathPrefix));
+ MOZ_ASSERT(StartsWithExplicit(extras->domPathPrefix));
+
+ cstats->extra = extras;
+ }
+};
+
+void
+JSReporter::CollectReports(WindowPaths* windowPaths,
+ WindowPaths* topWindowPaths,
+ nsIHandleReportCallback* handleReport,
+ nsISupports* data,
+ bool anonymize)
+{
+ XPCJSContext* xpccx = nsXPConnect::GetContextInstance();
+
+ // In the first step we get all the stats and stash them in a local
+ // data structure. In the second step we pass all the stashed stats to
+ // the callback. Separating these steps is important because the
+ // callback may be a JS function, and executing JS while getting these
+ // stats seems like a bad idea.
+
+ nsCOMPtr<amIAddonManager> addonManager;
+ if (XRE_IsParentProcess()) {
+ // Only try to access the service from the main process.
+ addonManager = do_GetService("@mozilla.org/addons/integration;1");
+ }
+ bool getLocations = !!addonManager;
+ XPCJSContextStats rtStats(windowPaths, topWindowPaths, getLocations,
+ anonymize);
+ OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
+ if (!JS::CollectRuntimeStats(xpccx->Context(), &rtStats, &orphanReporter,
+ anonymize))
+ {
+ return;
+ }
+
+ size_t xpcJSRuntimeSize = xpccx->SizeOfIncludingThis(JSMallocSizeOf);
+
+ size_t wrappedJSSize = xpccx->GetMultiCompartmentWrappedJSMap()->SizeOfWrappedJS(JSMallocSizeOf);
+
+ XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf);
+ XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo);
+
+ mozJSComponentLoader* loader = mozJSComponentLoader::Get();
+ size_t jsComponentLoaderSize = loader ? loader->SizeOfIncludingThis(JSMallocSizeOf) : 0;
+
+ // This is the second step (see above). First we report stuff in the
+ // "explicit" tree, then we report other stuff.
+
+ size_t rtTotal = 0;
+ xpc::ReportJSRuntimeExplicitTreeStats(rtStats,
+ NS_LITERAL_CSTRING("explicit/js-non-window/"),
+ addonManager, handleReport, data,
+ anonymize, &rtTotal);
+
+ // Report the sums of the compartment numbers.
+ xpc::CompartmentStatsExtras cExtrasTotal;
+ cExtrasTotal.jsPathPrefix.AssignLiteral("js-main-runtime/compartments/");
+ cExtrasTotal.domPathPrefix.AssignLiteral("window-objects/dom/");
+ ReportCompartmentStats(rtStats.cTotals, cExtrasTotal, addonManager,
+ handleReport, data);
+
+ xpc::ZoneStatsExtras zExtrasTotal;
+ zExtrasTotal.pathPrefix.AssignLiteral("js-main-runtime/zones/");
+ ReportZoneStats(rtStats.zTotals, zExtrasTotal, handleReport, data,
+ anonymize);
+
+ // Report the sum of the runtime/ numbers.
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/runtime"),
+ KIND_OTHER, rtTotal,
+ "The sum of all measurements under 'explicit/js-non-window/runtime/'.");
+
+ // Report the numbers for memory outside of compartments.
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"),
+ KIND_OTHER, rtStats.gcHeapUnusedChunks,
+ "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-arenas"),
+ KIND_OTHER, rtStats.gcHeapUnusedArenas,
+ "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/chunk-admin"),
+ KIND_OTHER, rtStats.gcHeapChunkAdmin,
+ "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
+
+ // Report a breakdown of the committed GC space.
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/chunks"),
+ KIND_OTHER, rtStats.gcHeapUnusedChunks,
+ "The same as 'explicit/js-non-window/gc-heap/unused-chunks'.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/arenas"),
+ KIND_OTHER, rtStats.gcHeapUnusedArenas,
+ "The same as 'explicit/js-non-window/gc-heap/unused-arenas'.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/objects"),
+ KIND_OTHER, rtStats.zTotals.unusedGCThings.object,
+ "Unused object cells within non-empty arenas.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/strings"),
+ KIND_OTHER, rtStats.zTotals.unusedGCThings.string,
+ "Unused string cells within non-empty arenas.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/symbols"),
+ KIND_OTHER, rtStats.zTotals.unusedGCThings.symbol,
+ "Unused symbol cells within non-empty arenas.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/shapes"),
+ KIND_OTHER, rtStats.zTotals.unusedGCThings.shape,
+ "Unused shape cells within non-empty arenas.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/base-shapes"),
+ KIND_OTHER, rtStats.zTotals.unusedGCThings.baseShape,
+ "Unused base shape cells within non-empty arenas.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/object-groups"),
+ KIND_OTHER, rtStats.zTotals.unusedGCThings.objectGroup,
+ "Unused object group cells within non-empty arenas.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/scopes"),
+ KIND_OTHER, rtStats.zTotals.unusedGCThings.scope,
+ "Unused scope cells within non-empty arenas.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/scripts"),
+ KIND_OTHER, rtStats.zTotals.unusedGCThings.script,
+ "Unused script cells within non-empty arenas.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/lazy-scripts"),
+ KIND_OTHER, rtStats.zTotals.unusedGCThings.lazyScript,
+ "Unused lazy script cells within non-empty arenas.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/unused/gc-things/jitcode"),
+ KIND_OTHER, rtStats.zTotals.unusedGCThings.jitcode,
+ "Unused jitcode cells within non-empty arenas.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/chunk-admin"),
+ KIND_OTHER, rtStats.gcHeapChunkAdmin,
+ "The same as 'explicit/js-non-window/gc-heap/chunk-admin'.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/arena-admin"),
+ KIND_OTHER, rtStats.zTotals.gcHeapArenaAdmin,
+ "The same as 'js-main-runtime/zones/gc-heap-arena-admin'.");
+
+ size_t gcThingTotal = 0;
+
+ MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/objects"),
+ KIND_OTHER, rtStats.cTotals.classInfo.objectsGCHeap,
+ "Used object cells.");
+
+ MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/strings"),
+ KIND_OTHER, rtStats.zTotals.stringInfo.sizeOfLiveGCThings(),
+ "Used string cells.");
+
+ MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/symbols"),
+ KIND_OTHER, rtStats.zTotals.symbolsGCHeap,
+ "Used symbol cells.");
+
+ MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/shapes"),
+ KIND_OTHER,
+ rtStats.zTotals.shapeInfo.shapesGCHeapTree + rtStats.zTotals.shapeInfo.shapesGCHeapDict,
+ "Used shape cells.");
+
+ MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/base-shapes"),
+ KIND_OTHER, rtStats.zTotals.shapeInfo.shapesGCHeapBase,
+ "Used base shape cells.");
+
+ MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/object-groups"),
+ KIND_OTHER, rtStats.zTotals.objectGroupsGCHeap,
+ "Used object group cells.");
+
+ MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/scopes"),
+ KIND_OTHER, rtStats.zTotals.scopesGCHeap,
+ "Used scope cells.");
+
+ MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/scripts"),
+ KIND_OTHER, rtStats.cTotals.scriptsGCHeap,
+ "Used script cells.");
+
+ MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/lazy-scripts"),
+ KIND_OTHER, rtStats.zTotals.lazyScriptsGCHeap,
+ "Used lazy script cells.");
+
+ MREPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime-gc-heap-committed/used/gc-things/jitcode"),
+ KIND_OTHER, rtStats.zTotals.jitCodesGCHeap,
+ "Used jitcode cells.");
+
+ MOZ_ASSERT(gcThingTotal == rtStats.gcHeapGCThings);
+
+ // Report xpconnect.
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/runtime"),
+ KIND_HEAP, xpcJSRuntimeSize,
+ "The XPConnect runtime.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/wrappedjs"),
+ KIND_HEAP, wrappedJSSize,
+ "Wrappers used to implement XPIDL interfaces with JS.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/scopes"),
+ KIND_HEAP, sizeInfo.mScopeAndMapSize,
+ "XPConnect scopes.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/proto-iface-cache"),
+ KIND_HEAP, sizeInfo.mProtoAndIfaceCacheSize,
+ "Prototype and interface binding caches.");
+
+ REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect/js-component-loader"),
+ KIND_HEAP, jsComponentLoaderSize,
+ "XPConnect's JS component loader.");
+}
+
+static nsresult
+JSSizeOfTab(JSObject* objArg, size_t* jsObjectsSize, size_t* jsStringsSize,
+ size_t* jsPrivateSize, size_t* jsOtherSize)
+{
+ JSContext* cx = nsXPConnect::GetContextInstance()->Context();
+ JS::RootedObject obj(cx, objArg);
+
+ TabSizes sizes;
+ OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject);
+ NS_ENSURE_TRUE(JS::AddSizeOfTab(cx, obj, moz_malloc_size_of,
+ &orphanReporter, &sizes),
+ NS_ERROR_OUT_OF_MEMORY);
+
+ *jsObjectsSize = sizes.objects;
+ *jsStringsSize = sizes.strings;
+ *jsPrivateSize = sizes.private_;
+ *jsOtherSize = sizes.other;
+ return NS_OK;
+}
+
+} // namespace xpc
+
+static void
+AccumulateTelemetryCallback(int id, uint32_t sample, const char* key)
+{
+ switch (id) {
+ case JS_TELEMETRY_GC_REASON:
+ Telemetry::Accumulate(Telemetry::GC_REASON_2, sample);
+ break;
+ case JS_TELEMETRY_GC_IS_ZONE_GC:
+ Telemetry::Accumulate(Telemetry::GC_IS_COMPARTMENTAL, sample);
+ break;
+ case JS_TELEMETRY_GC_MS:
+ Telemetry::Accumulate(Telemetry::GC_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_BUDGET_MS:
+ Telemetry::Accumulate(Telemetry::GC_BUDGET_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_ANIMATION_MS:
+ Telemetry::Accumulate(Telemetry::GC_ANIMATION_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_MAX_PAUSE_MS:
+ Telemetry::Accumulate(Telemetry::GC_MAX_PAUSE_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_MARK_MS:
+ Telemetry::Accumulate(Telemetry::GC_MARK_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_SWEEP_MS:
+ Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_COMPACT_MS:
+ Telemetry::Accumulate(Telemetry::GC_COMPACT_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_MARK_ROOTS_MS:
+ Telemetry::Accumulate(Telemetry::GC_MARK_ROOTS_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_MARK_GRAY_MS:
+ Telemetry::Accumulate(Telemetry::GC_MARK_GRAY_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_SLICE_MS:
+ Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_SLOW_PHASE:
+ Telemetry::Accumulate(Telemetry::GC_SLOW_PHASE, sample);
+ break;
+ case JS_TELEMETRY_GC_MMU_50:
+ Telemetry::Accumulate(Telemetry::GC_MMU_50, sample);
+ break;
+ case JS_TELEMETRY_GC_RESET:
+ Telemetry::Accumulate(Telemetry::GC_RESET, sample);
+ break;
+ case JS_TELEMETRY_GC_RESET_REASON:
+ Telemetry::Accumulate(Telemetry::GC_RESET_REASON, sample);
+ break;
+ case JS_TELEMETRY_GC_INCREMENTAL_DISABLED:
+ Telemetry::Accumulate(Telemetry::GC_INCREMENTAL_DISABLED, sample);
+ break;
+ case JS_TELEMETRY_GC_NON_INCREMENTAL:
+ Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL, sample);
+ break;
+ case JS_TELEMETRY_GC_NON_INCREMENTAL_REASON:
+ Telemetry::Accumulate(Telemetry::GC_NON_INCREMENTAL_REASON, sample);
+ break;
+ case JS_TELEMETRY_GC_SCC_SWEEP_TOTAL_MS:
+ Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_TOTAL_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_SCC_SWEEP_MAX_PAUSE_MS:
+ Telemetry::Accumulate(Telemetry::GC_SCC_SWEEP_MAX_PAUSE_MS, sample);
+ break;
+ case JS_TELEMETRY_GC_MINOR_REASON:
+ Telemetry::Accumulate(Telemetry::GC_MINOR_REASON, sample);
+ break;
+ case JS_TELEMETRY_GC_MINOR_REASON_LONG:
+ Telemetry::Accumulate(Telemetry::GC_MINOR_REASON_LONG, sample);
+ break;
+ case JS_TELEMETRY_GC_MINOR_US:
+ Telemetry::Accumulate(Telemetry::GC_MINOR_US, sample);
+ break;
+ case JS_TELEMETRY_GC_NURSERY_BYTES:
+ Telemetry::Accumulate(Telemetry::GC_NURSERY_BYTES, sample);
+ break;
+ case JS_TELEMETRY_GC_PRETENURE_COUNT:
+ Telemetry::Accumulate(Telemetry::GC_PRETENURE_COUNT, sample);
+ break;
+ case JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT:
+ Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_CONTENT, sample);
+ break;
+ case JS_TELEMETRY_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS:
+ Telemetry::Accumulate(Telemetry::JS_DEPRECATED_LANGUAGE_EXTENSIONS_IN_ADDONS, sample);
+ break;
+ case JS_TELEMETRY_ADDON_EXCEPTIONS:
+ Telemetry::Accumulate(Telemetry::JS_TELEMETRY_ADDON_EXCEPTIONS, nsDependentCString(key), sample);
+ break;
+ case JS_TELEMETRY_AOT_USAGE:
+ Telemetry::Accumulate(Telemetry::JS_AOT_USAGE, sample);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected JS_TELEMETRY id");
+ }
+}
+
+static void
+CompartmentNameCallback(JSContext* cx, JSCompartment* comp,
+ char* buf, size_t bufsize)
+{
+ nsCString name;
+ // This is called via the JSAPI and isn't involved in memory reporting, so
+ // we don't need to anonymize compartment names.
+ int anonymizeID = 0;
+ GetCompartmentName(comp, name, &anonymizeID, /* replaceSlashes = */ false);
+ if (name.Length() >= bufsize)
+ name.Truncate(bufsize - 1);
+ memcpy(buf, name.get(), name.Length() + 1);
+}
+
+static bool
+PreserveWrapper(JSContext* cx, JSObject* obj)
+{
+ MOZ_ASSERT(cx);
+ MOZ_ASSERT(obj);
+ MOZ_ASSERT(IS_WN_REFLECTOR(obj) || mozilla::dom::IsDOMObject(obj));
+
+ return mozilla::dom::IsDOMObject(obj) && mozilla::dom::TryPreserveWrapper(obj);
+}
+
+static nsresult
+ReadSourceFromFilename(JSContext* cx, const char* filename, char16_t** src, size_t* len)
+{
+ nsresult rv;
+
+ // mozJSSubScriptLoader prefixes the filenames of the scripts it loads with
+ // the filename of its caller. Axe that if present.
+ const char* arrow;
+ while ((arrow = strstr(filename, " -> ")))
+ filename = arrow + strlen(" -> ");
+
+ // Get the URI.
+ nsCOMPtr<nsIURI> uri;
+ rv = NS_NewURI(getter_AddRefs(uri), filename);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIChannel> scriptChannel;
+ rv = NS_NewChannel(getter_AddRefs(scriptChannel),
+ uri,
+ nsContentUtils::GetSystemPrincipal(),
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ nsIContentPolicy::TYPE_OTHER);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Only allow local reading.
+ nsCOMPtr<nsIURI> actualUri;
+ rv = scriptChannel->GetURI(getter_AddRefs(actualUri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsCString scheme;
+ rv = actualUri->GetScheme(scheme);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("jar"))
+ return NS_OK;
+
+ // Explicitly set the content type so that we don't load the
+ // exthandler to guess it.
+ scriptChannel->SetContentType(NS_LITERAL_CSTRING("text/plain"));
+
+ nsCOMPtr<nsIInputStream> scriptStream;
+ rv = scriptChannel->Open2(getter_AddRefs(scriptStream));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint64_t rawLen;
+ rv = scriptStream->Available(&rawLen);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!rawLen)
+ return NS_ERROR_FAILURE;
+
+ // Technically, this should be SIZE_MAX, but we don't run on machines
+ // where that would be less than UINT32_MAX, and the latter is already
+ // well beyond a reasonable limit.
+ if (rawLen > UINT32_MAX)
+ return NS_ERROR_FILE_TOO_BIG;
+
+ // Allocate an internal buf the size of the file.
+ auto buf = MakeUniqueFallible<unsigned char[]>(rawLen);
+ if (!buf)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ unsigned char* ptr = buf.get();
+ unsigned char* end = ptr + rawLen;
+ while (ptr < end) {
+ uint32_t bytesRead;
+ rv = scriptStream->Read(reinterpret_cast<char*>(ptr), end - ptr, &bytesRead);
+ if (NS_FAILED(rv))
+ return rv;
+ MOZ_ASSERT(bytesRead > 0, "stream promised more bytes before EOF");
+ ptr += bytesRead;
+ }
+
+ rv = nsScriptLoader::ConvertToUTF16(scriptChannel, buf.get(), rawLen, EmptyString(),
+ nullptr, *src, *len);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!*src)
+ return NS_ERROR_FAILURE;
+
+ // Historically this method used JS_malloc() which updates the GC memory
+ // accounting. Since ConvertToUTF16() now uses js_malloc() instead we
+ // update the accounting manually after the fact.
+ JS_updateMallocCounter(cx, *len);
+
+ return NS_OK;
+}
+
+// The JS engine calls this object's 'load' member function when it needs
+// the source for a chrome JS function. See the comment in the XPCJSContext
+// constructor.
+class XPCJSSourceHook: public js::SourceHook {
+ bool load(JSContext* cx, const char* filename, char16_t** src, size_t* length) {
+ *src = nullptr;
+ *length = 0;
+
+ if (!nsContentUtils::IsCallerChrome())
+ return true;
+
+ if (!filename)
+ return true;
+
+ nsresult rv = ReadSourceFromFilename(cx, filename, src, length);
+ if (NS_FAILED(rv)) {
+ xpc::Throw(cx, rv);
+ return false;
+ }
+
+ return true;
+ }
+};
+
+static const JSWrapObjectCallbacks WrapObjectCallbacks = {
+ xpc::WrapperFactory::Rewrap,
+ xpc::WrapperFactory::PrepareForWrapping
+};
+
+XPCJSContext::XPCJSContext()
+ : mCallContext(nullptr),
+ mAutoRoots(nullptr),
+ mResolveName(JSID_VOID),
+ mResolvingWrapper(nullptr),
+ mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH)),
+ mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_LENGTH)),
+ mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_LENGTH)),
+ mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_LENGTH)),
+ mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_LENGTH)),
+ mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_LENGTH)),
+ mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_LENGTH)),
+ mGCIsRunning(false),
+ mNativesToReleaseArray(),
+ mDoingFinalization(false),
+ mVariantRoots(nullptr),
+ mWrappedJSRoots(nullptr),
+ mObjectHolderRoots(nullptr),
+ mWatchdogManager(new WatchdogManager(this)),
+ mAsyncSnowWhiteFreer(new AsyncFreeSnowWhite()),
+ mSlowScriptSecondHalf(false),
+ mTimeoutAccumulated(false),
+ mPendingResult(NS_OK)
+{
+}
+
+#ifdef XP_WIN
+static size_t
+GetWindowsStackSize()
+{
+ // First, get the stack base. Because the stack grows down, this is the top
+ // of the stack.
+ const uint8_t* stackTop;
+#ifdef _WIN64
+ PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
+ stackTop = reinterpret_cast<const uint8_t*>(pTib->StackBase);
+#else
+ PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
+ stackTop = reinterpret_cast<const uint8_t*>(pTib->StackBase);
+#endif
+
+ // Now determine the stack bottom. Note that we can't use tib->StackLimit,
+ // because that's the size of the committed area and we're also interested
+ // in the reserved pages below that.
+ MEMORY_BASIC_INFORMATION mbi;
+ if (!VirtualQuery(&mbi, &mbi, sizeof(mbi)))
+ MOZ_CRASH("VirtualQuery failed");
+
+ const uint8_t* stackBottom = reinterpret_cast<const uint8_t*>(mbi.AllocationBase);
+
+ // Do some sanity checks.
+ size_t stackSize = size_t(stackTop - stackBottom);
+ MOZ_RELEASE_ASSERT(stackSize >= 1 * 1024 * 1024);
+ MOZ_RELEASE_ASSERT(stackSize <= 32 * 1024 * 1024);
+
+ // Subtract 40 KB (Win32) or 80 KB (Win64) to account for things like
+ // the guard page and large PGO stack frames.
+ return stackSize - 10 * sizeof(uintptr_t) * 1024;
+}
+#endif
+
+nsresult
+XPCJSContext::Initialize()
+{
+ nsresult rv = CycleCollectedJSContext::Initialize(nullptr,
+ JS::DefaultHeapMaxBytes,
+ JS::DefaultNurseryBytes);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_ASSERT(Context());
+ JSContext* cx = Context();
+
+ mUnprivilegedJunkScope.init(cx, nullptr);
+ mPrivilegedJunkScope.init(cx, nullptr);
+ mCompilationScope.init(cx, nullptr);
+
+ // these jsids filled in later when we have a JSContext to work with.
+ mStrIDs[0] = JSID_VOID;
+
+ auto cxPrivate = new PerThreadAtomCache();
+ memset(cxPrivate, 0, sizeof(PerThreadAtomCache));
+ JS_SetContextPrivate(cx, cxPrivate);
+
+ // Unconstrain the runtime's threshold on nominal heap size, to avoid
+ // triggering GC too often if operating continuously near an arbitrary
+ // finite threshold (0xffffffff is infinity for uint32_t parameters).
+ // This leaves the maximum-JS_malloc-bytes threshold still in effect
+ // to cause period, and we hope hygienic, last-ditch GCs from within
+ // the GC's allocator.
+ JS_SetGCParameter(cx, JSGC_MAX_BYTES, 0xffffffff);
+
+ // The JS engine permits us to set different stack limits for system code,
+ // trusted script, and untrusted script. We have tests that ensure that
+ // we can always execute 10 "heavy" (eval+with) stack frames deeper in
+ // privileged code. Our stack sizes vary greatly in different configurations,
+ // so satisfying those tests requires some care. Manual measurements of the
+ // number of heavy stack frames achievable gives us the following rough data,
+ // ordered by the effective categories in which they are grouped in the
+ // JS_SetNativeStackQuota call (which predates this analysis).
+ //
+ // (NB: These numbers may have drifted recently - see bug 938429)
+ // OSX 64-bit Debug: 7MB stack, 636 stack frames => ~11.3k per stack frame
+ // OSX64 Opt: 7MB stack, 2440 stack frames => ~3k per stack frame
+ //
+ // Linux 32-bit Debug: 2MB stack, 426 stack frames => ~4.8k per stack frame
+ // Linux 64-bit Debug: 4MB stack, 455 stack frames => ~9.0k per stack frame
+ //
+ // Windows (Opt+Debug): 900K stack, 235 stack frames => ~3.4k per stack frame
+ //
+ // Linux 32-bit Opt: 1MB stack, 272 stack frames => ~3.8k per stack frame
+ // Linux 64-bit Opt: 2MB stack, 316 stack frames => ~6.5k per stack frame
+ //
+ // We tune the trusted/untrusted quotas for each configuration to achieve our
+ // invariants while attempting to minimize overhead. In contrast, our buffer
+ // between system code and trusted script is a very unscientific 10k.
+ const size_t kSystemCodeBuffer = 10 * 1024;
+
+ // Our "default" stack is what we use in configurations where we don't have
+ // a compelling reason to do things differently. This is effectively 512KB
+ // on 32-bit platforms and 1MB on 64-bit platforms.
+ const size_t kDefaultStackQuota = 128 * sizeof(size_t) * 1024;
+
+ // Set stack sizes for different configurations. It's probably not great for
+ // the web to base this decision primarily on the default stack size that the
+ // underlying platform makes available, but that seems to be what we do. :-(
+
+#if defined(XP_MACOSX) || defined(DARWIN)
+ // MacOS has a gargantuan default stack size of 8MB. Go wild with 7MB,
+ // and give trusted script 180k extra. The stack is huge on mac anyway.
+ const size_t kStackQuota = 7 * 1024 * 1024;
+ const size_t kTrustedScriptBuffer = 180 * 1024;
+#elif defined(MOZ_ASAN)
+ // ASan requires more stack space due to red-zones, so give it double the
+ // default (1MB on 32-bit, 2MB on 64-bit). ASAN stack frame measurements
+ // were not taken at the time of this writing, so we hazard a guess that
+ // ASAN builds have roughly thrice the stack overhead as normal builds.
+ // On normal builds, the largest stack frame size we might encounter is
+ // 9.0k (see above), so let's use a buffer of 9.0 * 5 * 10 = 450k.
+ const size_t kStackQuota = 2 * kDefaultStackQuota;
+ const size_t kTrustedScriptBuffer = 450 * 1024;
+#elif defined(XP_WIN)
+ // 1MB is the default stack size on Windows. We use the /STACK linker flag
+ // to request a larger stack, so we determine the stack size at runtime.
+ const size_t kStackQuota = GetWindowsStackSize();
+ const size_t kTrustedScriptBuffer = (sizeof(size_t) == 8) ? 180 * 1024 //win64
+ : 120 * 1024; //win32
+ // The following two configurations are linux-only. Given the numbers above,
+ // we use 50k and 100k trusted buffers on 32-bit and 64-bit respectively.
+#elif defined(ANDROID)
+ // Android appears to have 1MB stacks. Allow the use of 3/4 of that size
+ // (768KB on 32-bit), since otherwise we can crash with a stack overflow
+ // when nearing the 1MB limit.
+ const size_t kStackQuota = kDefaultStackQuota + kDefaultStackQuota / 2;
+ const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
+#elif defined(DEBUG)
+ // Bug 803182: account for the 4x difference in the size of js::Interpret
+ // between optimized and debug builds.
+ // XXXbholley - Then why do we only account for 2x of difference?
+ const size_t kStackQuota = 2 * kDefaultStackQuota;
+ const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
+#else
+ const size_t kStackQuota = kDefaultStackQuota;
+ const size_t kTrustedScriptBuffer = sizeof(size_t) * 12800;
+#endif
+
+ // Avoid an unused variable warning on platforms where we don't use the
+ // default.
+ (void) kDefaultStackQuota;
+
+ JS_SetNativeStackQuota(cx,
+ kStackQuota,
+ kStackQuota - kSystemCodeBuffer,
+ kStackQuota - kSystemCodeBuffer - kTrustedScriptBuffer);
+
+ JS_SetDestroyCompartmentCallback(cx, CompartmentDestroyedCallback);
+ JS_SetSizeOfIncludingThisCompartmentCallback(cx, CompartmentSizeOfIncludingThisCallback);
+ JS_SetCompartmentNameCallback(cx, CompartmentNameCallback);
+ mPrevGCSliceCallback = JS::SetGCSliceCallback(cx, GCSliceCallback);
+ mPrevDoCycleCollectionCallback = JS::SetDoCycleCollectionCallback(cx,
+ DoCycleCollectionCallback);
+ JS_AddFinalizeCallback(cx, FinalizeCallback, nullptr);
+ JS_AddWeakPointerZoneGroupCallback(cx, WeakPointerZoneGroupCallback, this);
+ JS_AddWeakPointerCompartmentCallback(cx, WeakPointerCompartmentCallback, this);
+ JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
+ js::SetPreserveWrapperCallback(cx, PreserveWrapper);
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ if (PseudoStack* stack = mozilla_get_pseudo_stack())
+ stack->sampleContext(cx);
+#endif
+ JS_SetAccumulateTelemetryCallback(cx, AccumulateTelemetryCallback);
+ js::SetActivityCallback(cx, ActivityCallback, this);
+ JS_AddInterruptCallback(cx, InterruptCallback);
+ js::SetWindowProxyClass(cx, &OuterWindowProxyClass);
+
+ // The JS engine needs to keep the source code around in order to implement
+ // Function.prototype.toSource(). It'd be nice to not have to do this for
+ // chrome code and simply stub out requests for source on it. Life is not so
+ // easy, unfortunately. Nobody relies on chrome toSource() working in core
+ // browser code, but chrome tests use it. The worst offenders are addons,
+ // which like to monkeypatch chrome functions by calling toSource() on them
+ // and using regular expressions to modify them. We avoid keeping most browser
+ // JS source code in memory by setting LAZY_SOURCE on JS::CompileOptions when
+ // compiling some chrome code. This causes the JS engine not save the source
+ // code in memory. When the JS engine is asked to provide the source for a
+ // function compiled with LAZY_SOURCE, it calls SourceHook to load it.
+ ///
+ // Note we do have to retain the source code in memory for scripts compiled in
+ // isRunOnce mode and compiled function bodies (from
+ // JS::CompileFunction). In practice, this means content scripts and event
+ // handlers.
+ UniquePtr<XPCJSSourceHook> hook(new XPCJSSourceHook);
+ js::SetSourceHook(cx, Move(hook));
+
+ // Set up locale information and callbacks for the newly-created context so
+ // that the various toLocaleString() methods, localeCompare(), and other
+ // internationalization APIs work as desired.
+ if (!xpc_LocalizeContext(cx))
+ NS_RUNTIMEABORT("xpc_LocalizeContext failed.");
+
+ // Register memory reporters and distinguished amount functions.
+ RegisterStrongMemoryReporter(new JSMainRuntimeCompartmentsReporter());
+ RegisterStrongMemoryReporter(new JSMainRuntimeTemporaryPeakReporter());
+ RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount);
+ RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount);
+ RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount);
+ RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount);
+ mozilla::RegisterJSSizeOfTab(JSSizeOfTab);
+
+ // Watch for the JS boolean options.
+ ReloadPrefsCallback(nullptr, this);
+ Preferences::RegisterCallback(ReloadPrefsCallback, JS_OPTIONS_DOT_STR, this);
+
+ return NS_OK;
+}
+
+// static
+XPCJSContext*
+XPCJSContext::newXPCJSContext()
+{
+ XPCJSContext* self = new XPCJSContext();
+ nsresult rv = self->Initialize();
+ if (NS_FAILED(rv)) {
+ NS_RUNTIMEABORT("new XPCJSContext failed to initialize.");
+ delete self;
+ return nullptr;
+ }
+
+ if (self->Context() &&
+ self->GetMultiCompartmentWrappedJSMap() &&
+ self->GetWrappedJSClassMap() &&
+ self->GetIID2NativeInterfaceMap() &&
+ self->GetClassInfo2NativeSetMap() &&
+ self->GetNativeSetMap() &&
+ self->GetThisTranslatorMap() &&
+ self->GetDyingWrappedNativeProtoMap() &&
+ self->mWatchdogManager) {
+ return self;
+ }
+
+ NS_RUNTIMEABORT("new XPCJSContext failed to initialize.");
+
+ delete self;
+ return nullptr;
+}
+
+bool
+XPCJSContext::JSContextInitialized(JSContext* cx)
+{
+ JSAutoRequest ar(cx);
+
+ // if it is our first context then we need to generate our string ids
+ if (JSID_IS_VOID(mStrIDs[0])) {
+ RootedString str(cx);
+ for (unsigned i = 0; i < IDX_TOTAL_COUNT; i++) {
+ str = JS_AtomizeAndPinString(cx, mStrings[i]);
+ if (!str) {
+ mStrIDs[0] = JSID_VOID;
+ return false;
+ }
+ mStrIDs[i] = INTERNED_STRING_TO_JSID(cx, str);
+ mStrJSVals[i].setString(str);
+ }
+
+ if (!mozilla::dom::DefineStaticJSVals(cx)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+XPCJSContext::DescribeCustomObjects(JSObject* obj, const js::Class* clasp,
+ char (&name)[72]) const
+{
+ XPCNativeScriptableInfo* si = nullptr;
+
+ if (!IS_PROTO_CLASS(clasp)) {
+ return false;
+ }
+
+ XPCWrappedNativeProto* p =
+ static_cast<XPCWrappedNativeProto*>(xpc_GetJSPrivate(obj));
+ si = p->GetScriptableInfo();
+
+ if (!si) {
+ return false;
+ }
+
+ SprintfLiteral(name, "JS Object (%s - %s)", clasp->name, si->GetJSClass()->name);
+ return true;
+}
+
+bool
+XPCJSContext::NoteCustomGCThingXPCOMChildren(const js::Class* clasp, JSObject* obj,
+ nsCycleCollectionTraversalCallback& cb) const
+{
+ if (clasp != &XPC_WN_Tearoff_JSClass) {
+ return false;
+ }
+
+ // A tearoff holds a strong reference to its native object
+ // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative
+ // will be held alive through the parent of the JSObject of the tearoff.
+ XPCWrappedNativeTearOff* to =
+ static_cast<XPCWrappedNativeTearOff*>(xpc_GetJSPrivate(obj));
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative");
+ cb.NoteXPCOMChild(to->GetNative());
+ return true;
+}
+
+void
+XPCJSContext::BeforeProcessTask(bool aMightBlock)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // If ProcessNextEvent was called during a Promise "then" callback, we
+ // must process any pending microtasks before blocking in the event loop,
+ // otherwise we may deadlock until an event enters the queue later.
+ if (aMightBlock) {
+ if (Promise::PerformMicroTaskCheckpoint()) {
+ // If any microtask was processed, we post a dummy event in order to
+ // force the ProcessNextEvent call not to block. This is required
+ // to support nested event loops implemented using a pattern like
+ // "while (condition) thread.processNextEvent(true)", in case the
+ // condition is triggered here by a Promise "then" callback.
+
+ NS_DispatchToMainThread(new Runnable());
+ }
+ }
+
+ // Start the slow script timer.
+ mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
+ mSlowScriptSecondHalf = false;
+ mSlowScriptActualWait = mozilla::TimeDuration();
+ mTimeoutAccumulated = false;
+
+ // As we may be entering a nested event loop, we need to
+ // cancel any ongoing performance measurement.
+ js::ResetPerformanceMonitoring(Get()->Context());
+
+ CycleCollectedJSContext::BeforeProcessTask(aMightBlock);
+}
+
+void
+XPCJSContext::AfterProcessTask(uint32_t aNewRecursionDepth)
+{
+ // Now that we're back to the event loop, reset the slow script checkpoint.
+ mSlowScriptCheckpoint = mozilla::TimeStamp();
+ mSlowScriptSecondHalf = false;
+
+ // Call cycle collector occasionally.
+ MOZ_ASSERT(NS_IsMainThread());
+ nsJSContext::MaybePokeCC();
+
+ CycleCollectedJSContext::AfterProcessTask(aNewRecursionDepth);
+
+ // Now that we are certain that the event is complete,
+ // we can flush any ongoing performance measurement.
+ js::FlushPerformanceMonitoring(Get()->Context());
+
+ mozilla::jsipc::AfterProcessTask();
+}
+
+/***************************************************************************/
+
+void
+XPCJSContext::DebugDump(int16_t depth)
+{
+#ifdef DEBUG
+ depth--;
+ XPC_LOG_ALWAYS(("XPCJSContext @ %x", this));
+ XPC_LOG_INDENT();
+ XPC_LOG_ALWAYS(("mJSContext @ %x", Context()));
+
+ XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %x with %d wrapperclasses(s)",
+ mWrappedJSClassMap, mWrappedJSClassMap->Count()));
+ // iterate wrappersclasses...
+ if (depth && mWrappedJSClassMap->Count()) {
+ XPC_LOG_INDENT();
+ for (auto i = mWrappedJSClassMap->Iter(); !i.Done(); i.Next()) {
+ auto entry = static_cast<IID2WrappedJSClassMap::Entry*>(i.Get());
+ entry->value->DebugDump(depth);
+ }
+ XPC_LOG_OUTDENT();
+ }
+
+ // iterate wrappers...
+ XPC_LOG_ALWAYS(("mWrappedJSMap @ %x with %d wrappers(s)",
+ mWrappedJSMap, mWrappedJSMap->Count()));
+ if (depth && mWrappedJSMap->Count()) {
+ XPC_LOG_INDENT();
+ mWrappedJSMap->Dump(depth);
+ XPC_LOG_OUTDENT();
+ }
+
+ XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %x with %d interface(s)",
+ mIID2NativeInterfaceMap,
+ mIID2NativeInterfaceMap->Count()));
+
+ XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %x with %d sets(s)",
+ mClassInfo2NativeSetMap,
+ mClassInfo2NativeSetMap->Count()));
+
+ XPC_LOG_ALWAYS(("mThisTranslatorMap @ %x with %d translator(s)",
+ mThisTranslatorMap, mThisTranslatorMap->Count()));
+
+ XPC_LOG_ALWAYS(("mNativeSetMap @ %x with %d sets(s)",
+ mNativeSetMap, mNativeSetMap->Count()));
+
+ // iterate sets...
+ if (depth && mNativeSetMap->Count()) {
+ XPC_LOG_INDENT();
+ for (auto i = mNativeSetMap->Iter(); !i.Done(); i.Next()) {
+ auto entry = static_cast<NativeSetMap::Entry*>(i.Get());
+ entry->key_value->DebugDump(depth);
+ }
+ XPC_LOG_OUTDENT();
+ }
+
+ XPC_LOG_ALWAYS(("mPendingResult of %x", mPendingResult));
+
+ XPC_LOG_OUTDENT();
+#endif
+}
+
+/***************************************************************************/
+
+void
+XPCRootSetElem::AddToRootSet(XPCRootSetElem** listHead)
+{
+ MOZ_ASSERT(!mSelfp, "Must be not linked");
+
+ mSelfp = listHead;
+ mNext = *listHead;
+ if (mNext) {
+ MOZ_ASSERT(mNext->mSelfp == listHead, "Must be list start");
+ mNext->mSelfp = &mNext;
+ }
+ *listHead = this;
+}
+
+void
+XPCRootSetElem::RemoveFromRootSet()
+{
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ JS::PokeGC(xpc->GetContext()->Context());
+
+ MOZ_ASSERT(mSelfp, "Must be linked");
+
+ MOZ_ASSERT(*mSelfp == this, "Link invariant");
+ *mSelfp = mNext;
+ if (mNext)
+ mNext->mSelfp = mSelfp;
+#ifdef DEBUG
+ mSelfp = nullptr;
+ mNext = nullptr;
+#endif
+}
+
+void
+XPCJSContext::AddGCCallback(xpcGCCallback cb)
+{
+ MOZ_ASSERT(cb, "null callback");
+ extraGCCallbacks.AppendElement(cb);
+}
+
+void
+XPCJSContext::RemoveGCCallback(xpcGCCallback cb)
+{
+ MOZ_ASSERT(cb, "null callback");
+ bool found = extraGCCallbacks.RemoveElement(cb);
+ if (!found) {
+ NS_ERROR("Removing a callback which was never added.");
+ }
+}
+
+void
+XPCJSContext::InitSingletonScopes()
+{
+ // This all happens very early, so we don't bother with cx pushing.
+ JSContext* cx = Context();
+ JSAutoRequest ar(cx);
+ RootedValue v(cx);
+ nsresult rv;
+
+ // Create the Unprivileged Junk Scope.
+ SandboxOptions unprivilegedJunkScopeOptions;
+ unprivilegedJunkScopeOptions.sandboxName.AssignLiteral("XPConnect Junk Compartment");
+ unprivilegedJunkScopeOptions.invisibleToDebugger = true;
+ rv = CreateSandboxObject(cx, &v, nullptr, unprivilegedJunkScopeOptions);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ mUnprivilegedJunkScope = js::UncheckedUnwrap(&v.toObject());
+
+ // Create the Privileged Junk Scope.
+ SandboxOptions privilegedJunkScopeOptions;
+ privilegedJunkScopeOptions.sandboxName.AssignLiteral("XPConnect Privileged Junk Compartment");
+ privilegedJunkScopeOptions.invisibleToDebugger = true;
+ privilegedJunkScopeOptions.wantComponents = false;
+ rv = CreateSandboxObject(cx, &v, nsXPConnect::SystemPrincipal(), privilegedJunkScopeOptions);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ mPrivilegedJunkScope = js::UncheckedUnwrap(&v.toObject());
+
+ // Create the Compilation Scope.
+ SandboxOptions compilationScopeOptions;
+ compilationScopeOptions.sandboxName.AssignLiteral("XPConnect Compilation Compartment");
+ compilationScopeOptions.invisibleToDebugger = true;
+ compilationScopeOptions.discardSource = ShouldDiscardSystemSource();
+ rv = CreateSandboxObject(cx, &v, /* principal = */ nullptr, compilationScopeOptions);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ mCompilationScope = js::UncheckedUnwrap(&v.toObject());
+}
+
+void
+XPCJSContext::DeleteSingletonScopes()
+{
+ mUnprivilegedJunkScope = nullptr;
+ mPrivilegedJunkScope = nullptr;
+ mCompilationScope = nullptr;
+}
diff --git a/js/xpconnect/src/XPCJSID.cpp b/js/xpconnect/src/XPCJSID.cpp
new file mode 100644
index 000000000..b9cbee7be
--- /dev/null
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -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)));
+}
+
+
diff --git a/js/xpconnect/src/XPCJSMemoryReporter.h b/js/xpconnect/src/XPCJSMemoryReporter.h
new file mode 100644
index 000000000..7881ac0f5
--- /dev/null
+++ b/js/xpconnect/src/XPCJSMemoryReporter.h
@@ -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
diff --git a/js/xpconnect/src/XPCJSWeakReference.cpp b/js/xpconnect/src/XPCJSWeakReference.cpp
new file mode 100644
index 000000000..bd7c80bc0
--- /dev/null
+++ b/js/xpconnect/src/XPCJSWeakReference.cpp
@@ -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;
+}
diff --git a/js/xpconnect/src/XPCJSWeakReference.h b/js/xpconnect/src/XPCJSWeakReference.h
new file mode 100644
index 000000000..ba802044f
--- /dev/null
+++ b/js/xpconnect/src/XPCJSWeakReference.h
@@ -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___
diff --git a/js/xpconnect/src/XPCLocale.cpp b/js/xpconnect/src/XPCLocale.cpp
new file mode 100644
index 000000000..2fe78cb75
--- /dev/null
+++ b/js/xpconnect/src/XPCLocale.cpp
@@ -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;
+}
diff --git a/js/xpconnect/src/XPCLog.cpp b/js/xpconnect/src/XPCLog.cpp
new file mode 100644
index 000000000..515af2a47
--- /dev/null
+++ b/js/xpconnect/src/XPCLog.cpp
@@ -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
diff --git a/js/xpconnect/src/XPCLog.h b/js/xpconnect/src/XPCLog.h
new file mode 100644
index 000000000..e5f5a2cf0
--- /dev/null
+++ b/js/xpconnect/src/XPCLog.h
@@ -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___ */
diff --git a/js/xpconnect/src/XPCMaps.cpp b/js/xpconnect/src/XPCMaps.cpp
new file mode 100644
index 000000000..0d728d7de
--- /dev/null
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -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)
+{
+}
+
+/***************************************************************************/
diff --git a/js/xpconnect/src/XPCMaps.h b/js/xpconnect/src/XPCMaps.h
new file mode 100644
index 000000000..80c51d477
--- /dev/null
+++ b/js/xpconnect/src/XPCMaps.h
@@ -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___ */
diff --git a/js/xpconnect/src/XPCModule.cpp b/js/xpconnect/src/XPCModule.cpp
new file mode 100644
index 000000000..52100b39a
--- /dev/null
+++ b/js/xpconnect/src/XPCModule.cpp
@@ -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();
+}
diff --git a/js/xpconnect/src/XPCModule.h b/js/xpconnect/src/XPCModule.h
new file mode 100644
index 000000000..d62764625
--- /dev/null
+++ b/js/xpconnect/src/XPCModule.h
@@ -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();
diff --git a/js/xpconnect/src/XPCRuntimeService.cpp b/js/xpconnect/src/XPCRuntimeService.cpp
new file mode 100644
index 000000000..ce79dcc86
--- /dev/null
+++ b/js/xpconnect/src/XPCRuntimeService.cpp
@@ -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;
+}
diff --git a/js/xpconnect/src/XPCShellImpl.cpp b/js/xpconnect/src/XPCShellImpl.cpp
new file mode 100644
index 000000000..d86b5c5d3
--- /dev/null
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -0,0 +1,1765 @@
+/* -*- 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 "nsXULAppAPI.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "jsprf.h"
+#include "mozilla/ChaosMode.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/Preferences.h"
+#include "nsServiceManagerUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIXPConnect.h"
+#include "nsIServiceManager.h"
+#include "nsIFile.h"
+#include "nsString.h"
+#include "nsIDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nscore.h"
+#include "nsArrayEnumerator.h"
+#include "nsCOMArray.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsCOMPtr.h"
+#include "nsJSPrincipals.h"
+#include "xpcpublic.h"
+#include "xpcprivate.h"
+#include "BackstagePass.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIPrincipal.h"
+#include "nsJSUtils.h"
+#include "gfxPrefs.h"
+#include "nsIXULRuntime.h"
+
+#include "base/histogram.h"
+
+#ifdef ANDROID
+#include <android/log.h>
+#endif
+
+#ifdef XP_WIN
+#include "mozilla/widget/AudioSession.h"
+#include <windows.h>
+#if defined(MOZ_SANDBOX)
+#include "SandboxBroker.h"
+#endif
+#endif
+
+// all this crap is needed to do the interactive shell stuff
+#include <stdlib.h>
+#include <errno.h>
+#ifdef HAVE_IO_H
+#include <io.h> /* for isatty() */
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h> /* for isatty() */
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#include "nsICrashReporter.h"
+#endif
+
+using namespace mozilla;
+using namespace JS;
+using mozilla::dom::AutoJSAPI;
+using mozilla::dom::AutoEntryScript;
+
+class XPCShellDirProvider : public nsIDirectoryServiceProvider2
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
+
+ XPCShellDirProvider() { }
+ ~XPCShellDirProvider() { }
+
+ // The platform resource folder
+ void SetGREDirs(nsIFile* greDir);
+ void ClearGREDirs() { mGREDir = nullptr;
+ mGREBinDir = nullptr; }
+ // The application resource folder
+ void SetAppDir(nsIFile* appFile);
+ void ClearAppDir() { mAppDir = nullptr; }
+ // The app executable
+ void SetAppFile(nsIFile* appFile);
+ void ClearAppFile() { mAppFile = nullptr; }
+ // An additional custom plugin dir if specified
+ void SetPluginDir(nsIFile* pluginDir);
+ void ClearPluginDir() { mPluginDir = nullptr; }
+
+private:
+ nsCOMPtr<nsIFile> mGREDir;
+ nsCOMPtr<nsIFile> mGREBinDir;
+ nsCOMPtr<nsIFile> mAppDir;
+ nsCOMPtr<nsIFile> mPluginDir;
+ nsCOMPtr<nsIFile> mAppFile;
+};
+
+#ifdef XP_WIN
+class MOZ_STACK_CLASS AutoAudioSession
+{
+public:
+ AutoAudioSession() {
+ widget::StartAudioSession();
+ }
+
+ ~AutoAudioSession() {
+ widget::StopAudioSession();
+ }
+};
+#endif
+
+static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
+
+#define EXITCODE_RUNTIME_ERROR 3
+#define EXITCODE_FILE_NOT_FOUND 4
+
+static FILE* gOutFile = nullptr;
+static FILE* gErrFile = nullptr;
+static FILE* gInFile = nullptr;
+
+static int gExitCode = 0;
+static bool gQuitting = false;
+static bool reportWarnings = true;
+static bool compileOnly = false;
+
+static JSPrincipals* gJSPrincipals = nullptr;
+static nsAutoString* gWorkingDirectory = nullptr;
+
+static bool
+GetLocationProperty(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (!args.thisv().isObject()) {
+ JS_ReportErrorASCII(cx, "Unexpected this value for GetLocationProperty");
+ return false;
+ }
+#if !defined(XP_WIN) && !defined(XP_UNIX)
+ //XXX: your platform should really implement this
+ return false;
+#else
+ JS::AutoFilename filename;
+ if (JS::DescribeScriptedCaller(cx, &filename) && filename.get()) {
+ nsresult rv;
+ nsCOMPtr<nsIXPConnect> xpc =
+ do_GetService(kXPConnectServiceContractID, &rv);
+
+#if defined(XP_WIN)
+ // convert from the system codepage to UTF-16
+ int bufferSize = MultiByteToWideChar(CP_ACP, 0, filename.get(),
+ -1, nullptr, 0);
+ nsAutoString filenameString;
+ filenameString.SetLength(bufferSize);
+ MultiByteToWideChar(CP_ACP, 0, filename.get(),
+ -1, (LPWSTR)filenameString.BeginWriting(),
+ filenameString.Length());
+ // remove the null terminator
+ filenameString.SetLength(bufferSize - 1);
+
+ // replace forward slashes with backslashes,
+ // since nsLocalFileWin chokes on them
+ char16_t* start = filenameString.BeginWriting();
+ char16_t* end = filenameString.EndWriting();
+
+ while (start != end) {
+ if (*start == L'/')
+ *start = L'\\';
+ start++;
+ }
+#elif defined(XP_UNIX)
+ NS_ConvertUTF8toUTF16 filenameString(filename.get());
+#endif
+
+ nsCOMPtr<nsIFile> location;
+ if (NS_SUCCEEDED(rv)) {
+ rv = NS_NewLocalFile(filenameString,
+ false, getter_AddRefs(location));
+ }
+
+ if (!location && gWorkingDirectory) {
+ // could be a relative path, try appending it to the cwd
+ // and then normalize
+ nsAutoString absolutePath(*gWorkingDirectory);
+ absolutePath.Append(filenameString);
+
+ rv = NS_NewLocalFile(absolutePath,
+ false, getter_AddRefs(location));
+ }
+
+ if (location) {
+ bool symlink;
+ // don't normalize symlinks, because that's kind of confusing
+ if (NS_SUCCEEDED(location->IsSymlink(&symlink)) &&
+ !symlink)
+ location->Normalize();
+ RootedObject locationObj(cx);
+ rv = xpc->WrapNative(cx, &args.thisv().toObject(), location,
+ NS_GET_IID(nsIFile), locationObj.address());
+ if (NS_SUCCEEDED(rv) && locationObj) {
+ args.rval().setObject(*locationObj);
+ }
+ }
+ }
+
+ return true;
+#endif
+}
+
+static bool
+GetLine(JSContext* cx, char* bufp, FILE* file, const char* prompt)
+{
+ fputs(prompt, gOutFile);
+ fflush(gOutFile);
+
+ char line[4096] = { '\0' };
+ while (true) {
+ if (fgets(line, sizeof line, file)) {
+ strcpy(bufp, line);
+ return true;
+ }
+ if (errno != EINTR) {
+ return false;
+ }
+ }
+}
+
+static bool
+ReadLine(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ // While 4096 might be quite arbitrary, this is something to be fixed in
+ // bug 105707. It is also the same limit as in ProcessFile.
+ char buf[4096];
+ RootedString str(cx);
+
+ /* If a prompt was specified, construct the string */
+ if (args.length() > 0) {
+ str = JS::ToString(cx, args[0]);
+ if (!str)
+ return false;
+ } else {
+ str = JS_GetEmptyString(cx);
+ }
+
+ /* Get a line from the infile */
+ JSAutoByteString strBytes(cx, str);
+ if (!strBytes || !GetLine(cx, buf, gInFile, strBytes.ptr()))
+ return false;
+
+ /* Strip newline character added by GetLine() */
+ unsigned int buflen = strlen(buf);
+ if (buflen == 0) {
+ if (feof(gInFile)) {
+ args.rval().setNull();
+ return true;
+ }
+ } else if (buf[buflen - 1] == '\n') {
+ --buflen;
+ }
+
+ /* Turn buf into a JSString */
+ str = JS_NewStringCopyN(cx, buf, buflen);
+ if (!str)
+ return false;
+
+ args.rval().setString(str);
+ return true;
+}
+
+static bool
+Print(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ args.rval().setUndefined();
+
+ RootedString str(cx);
+ nsAutoCString utf8output;
+
+ for (unsigned i = 0; i < args.length(); i++) {
+ str = ToString(cx, args[i]);
+ if (!str)
+ return false;
+
+ JSAutoByteString utf8str;
+ if (!utf8str.encodeUtf8(cx, str))
+ return false;
+
+ if (i)
+ utf8output.Append(' ');
+ utf8output.Append(utf8str.ptr(), utf8str.length());
+ }
+ utf8output.Append('\n');
+ fputs(utf8output.get(), gOutFile);
+ fflush(gOutFile);
+ return true;
+}
+
+static bool
+Dump(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ args.rval().setUndefined();
+
+ if (!args.length())
+ return true;
+
+ RootedString str(cx, ToString(cx, args[0]));
+ if (!str)
+ return false;
+
+ JSAutoByteString utf8str;
+ if (!utf8str.encodeUtf8(cx, str))
+ return false;
+
+#ifdef ANDROID
+ __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.ptr());
+#endif
+#ifdef XP_WIN
+ if (IsDebuggerPresent()) {
+ nsAutoJSString wstr;
+ if (!wstr.init(cx, str))
+ return false;
+ OutputDebugStringW(wstr.get());
+ }
+#endif
+ fputs(utf8str.ptr(), gOutFile);
+ fflush(gOutFile);
+ return true;
+}
+
+static bool
+Load(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
+ if (!obj)
+ return false;
+
+ if (!JS_IsGlobalObject(obj)) {
+ JS_ReportErrorASCII(cx, "Trying to load() into a non-global object");
+ return false;
+ }
+
+ RootedString str(cx);
+ for (unsigned i = 0; i < args.length(); i++) {
+ str = ToString(cx, args[i]);
+ if (!str)
+ return false;
+ JSAutoByteString filename(cx, str);
+ if (!filename)
+ return false;
+ FILE* file = fopen(filename.ptr(), "r");
+ if (!file) {
+ filename.clear();
+ if (!filename.encodeUtf8(cx, str))
+ return false;
+ JS_ReportErrorUTF8(cx, "cannot open file '%s' for reading",
+ filename.ptr());
+ return false;
+ }
+ JS::CompileOptions options(cx);
+ options.setUTF8(true)
+ .setFileAndLine(filename.ptr(), 1)
+ .setIsRunOnce(true);
+ JS::Rooted<JSScript*> script(cx);
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ JS::Compile(cx, options, file, &script);
+ fclose(file);
+ if (!script)
+ return false;
+
+ if (!compileOnly) {
+ if (!JS_ExecuteScript(cx, script)) {
+ return false;
+ }
+ }
+ }
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool
+Version(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ args.rval().setInt32(JS_GetVersion(cx));
+ if (args.get(0).isInt32())
+ JS_SetVersionForCompartment(js::GetContextCompartment(cx),
+ JSVersion(args[0].toInt32()));
+ return true;
+}
+
+static bool
+Quit(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ gExitCode = 0;
+ if (!ToInt32(cx, args.get(0), &gExitCode))
+ return false;
+
+ gQuitting = true;
+// exit(0);
+ return false;
+}
+
+static bool
+DumpXPC(JSContext* cx, unsigned argc, Value* vp)
+{
+ JS::CallArgs args = CallArgsFromVp(argc, vp);
+
+ uint16_t depth = 2;
+ if (args.length() > 0) {
+ if (!JS::ToUint16(cx, args[0], &depth))
+ return false;
+ }
+
+ nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
+ if (xpc)
+ xpc->DebugDump(int16_t(depth));
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool
+GC(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ JS_GC(cx);
+
+ args.rval().setUndefined();
+ return true;
+}
+
+#ifdef JS_GC_ZEAL
+static bool
+GCZeal(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ uint32_t zeal;
+ if (!ToUint32(cx, args.get(0), &zeal))
+ return false;
+
+ JS_SetGCZeal(cx, uint8_t(zeal), JS_DEFAULT_ZEAL_FREQ);
+ args.rval().setUndefined();
+ return true;
+}
+#endif
+
+static bool
+SendCommand(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ if (args.length() == 0) {
+ JS_ReportErrorASCII(cx, "Function takes at least one argument!");
+ return false;
+ }
+
+ RootedString str(cx, ToString(cx, args[0]));
+ if (!str) {
+ JS_ReportErrorASCII(cx, "Could not convert argument 1 to string!");
+ return false;
+ }
+
+ if (args.length() > 1 && JS_TypeOfValue(cx, args[1]) != JSTYPE_FUNCTION) {
+ JS_ReportErrorASCII(cx, "Could not convert argument 2 to function!");
+ return false;
+ }
+
+ if (!XRE_SendTestShellCommand(cx, str, args.length() > 1 ? args[1].address() : nullptr)) {
+ JS_ReportErrorASCII(cx, "Couldn't send command!");
+ return false;
+ }
+
+ args.rval().setUndefined();
+ return true;
+}
+
+static bool
+Options(JSContext* cx, unsigned argc, Value* vp)
+{
+ JS::CallArgs args = CallArgsFromVp(argc, vp);
+ ContextOptions oldContextOptions = ContextOptionsRef(cx);
+
+ RootedString str(cx);
+ JSAutoByteString opt;
+ for (unsigned i = 0; i < args.length(); ++i) {
+ str = ToString(cx, args[i]);
+ if (!str)
+ return false;
+
+ opt.clear();
+ if (!opt.encodeUtf8(cx, str))
+ return false;
+
+ if (strcmp(opt.ptr(), "strict") == 0)
+ ContextOptionsRef(cx).toggleExtraWarnings();
+ else if (strcmp(opt.ptr(), "werror") == 0)
+ ContextOptionsRef(cx).toggleWerror();
+ else if (strcmp(opt.ptr(), "strict_mode") == 0)
+ ContextOptionsRef(cx).toggleStrictMode();
+ else {
+ JS_ReportErrorUTF8(cx, "unknown option name '%s'. The valid names are "
+ "strict, werror, and strict_mode.", opt.ptr());
+ return false;
+ }
+ }
+
+ char* names = nullptr;
+ if (oldContextOptions.extraWarnings()) {
+ names = JS_sprintf_append(names, "%s", "strict");
+ if (!names) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+ if (oldContextOptions.werror()) {
+ names = JS_sprintf_append(names, "%s%s", names ? "," : "", "werror");
+ if (!names) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+ if (names && oldContextOptions.strictMode()) {
+ names = JS_sprintf_append(names, "%s%s", names ? "," : "", "strict_mode");
+ if (!names) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+ }
+
+ str = JS_NewStringCopyZ(cx, names);
+ free(names);
+ if (!str)
+ return false;
+
+ args.rval().setString(str);
+ return true;
+}
+
+static PersistentRootedValue *sScriptedInterruptCallback = nullptr;
+
+static bool
+XPCShellInterruptCallback(JSContext* cx)
+{
+ MOZ_ASSERT(sScriptedInterruptCallback->initialized());
+ RootedValue callback(cx, *sScriptedInterruptCallback);
+
+ // If no interrupt callback was set by script, no-op.
+ if (callback.isUndefined())
+ return true;
+
+ JSAutoCompartment ac(cx, &callback.toObject());
+ RootedValue rv(cx);
+ if (!JS_CallFunctionValue(cx, nullptr, callback, JS::HandleValueArray::empty(), &rv) ||
+ !rv.isBoolean())
+ {
+ NS_WARNING("Scripted interrupt callback failed! Terminating script.");
+ JS_ClearPendingException(cx);
+ return false;
+ }
+
+ return rv.toBoolean();
+}
+
+static bool
+SetInterruptCallback(JSContext* cx, unsigned argc, Value* vp)
+{
+ MOZ_ASSERT(sScriptedInterruptCallback->initialized());
+
+ // Sanity-check args.
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ if (args.length() != 1) {
+ JS_ReportErrorASCII(cx, "Wrong number of arguments");
+ return false;
+ }
+
+ // Allow callers to remove the interrupt callback by passing undefined.
+ if (args[0].isUndefined()) {
+ *sScriptedInterruptCallback = UndefinedValue();
+ return true;
+ }
+
+ // Otherwise, we should have a callable object.
+ if (!args[0].isObject() || !JS::IsCallable(&args[0].toObject())) {
+ JS_ReportErrorASCII(cx, "Argument must be callable");
+ return false;
+ }
+
+ *sScriptedInterruptCallback = args[0];
+
+ return true;
+}
+
+static bool
+SimulateActivityCallback(JSContext* cx, unsigned argc, Value* vp)
+{
+ // Sanity-check args.
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ if (args.length() != 1 || !args[0].isBoolean()) {
+ JS_ReportErrorASCII(cx, "Wrong number of arguments");
+ return false;
+ }
+ xpc::SimulateActivityCallback(args[0].toBoolean());
+ return true;
+}
+
+static bool
+RegisterAppManifest(JSContext* cx, unsigned argc, Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ if (args.length() != 1) {
+ JS_ReportErrorASCII(cx, "Wrong number of arguments");
+ return false;
+ }
+ if (!args[0].isObject()) {
+ JS_ReportErrorASCII(cx, "Expected object as argument 1 to registerAppManifest");
+ return false;
+ }
+
+ Rooted<JSObject*> arg1(cx, &args[0].toObject());
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = nsXPConnect::XPConnect()->
+ WrapJS(cx, arg1, NS_GET_IID(nsIFile), getter_AddRefs(file));
+ if (NS_FAILED(rv)) {
+ XPCThrower::Throw(rv, cx);
+ return false;
+ }
+ rv = XRE_AddManifestLocation(NS_APP_LOCATION, file);
+ if (NS_FAILED(rv)) {
+ XPCThrower::Throw(rv, cx);
+ return false;
+ }
+ return true;
+}
+
+static const JSFunctionSpec glob_functions[] = {
+ JS_FS("print", Print, 0,0),
+ JS_FS("readline", ReadLine, 1,0),
+ JS_FS("load", Load, 1,0),
+ JS_FS("quit", Quit, 0,0),
+ JS_FS("version", Version, 1,0),
+ JS_FS("dumpXPC", DumpXPC, 1,0),
+ JS_FS("dump", Dump, 1,0),
+ JS_FS("gc", GC, 0,0),
+#ifdef JS_GC_ZEAL
+ JS_FS("gczeal", GCZeal, 1,0),
+#endif
+ JS_FS("options", Options, 0,0),
+ JS_FS("sendCommand", SendCommand, 1,0),
+ JS_FS("atob", xpc::Atob, 1,0),
+ JS_FS("btoa", xpc::Btoa, 1,0),
+ JS_FS("setInterruptCallback", SetInterruptCallback, 1,0),
+ JS_FS("simulateActivityCallback", SimulateActivityCallback, 1,0),
+ JS_FS("registerAppManifest", RegisterAppManifest, 1, 0),
+ JS_FS_END
+};
+
+static bool
+env_setProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
+ ObjectOpResult& result)
+{
+/* XXX porting may be easy, but these don't seem to supply setenv by default */
+#if !defined SOLARIS
+ RootedString valstr(cx);
+ RootedString idstr(cx);
+ int rv;
+
+ RootedValue idval(cx);
+ if (!JS_IdToValue(cx, id, &idval))
+ return false;
+
+ idstr = ToString(cx, idval);
+ valstr = ToString(cx, vp);
+ if (!idstr || !valstr)
+ return false;
+ JSAutoByteString name(cx, idstr);
+ if (!name)
+ return false;
+ JSAutoByteString value(cx, valstr);
+ if (!value)
+ return false;
+#if defined XP_WIN || defined HPUX || defined OSF1 || defined SCO
+ {
+ char* waste = JS_smprintf("%s=%s", name.ptr(), value.ptr());
+ if (!waste) {
+ JS_ReportOutOfMemory(cx);
+ return false;
+ }
+ rv = putenv(waste);
+#ifdef XP_WIN
+ /*
+ * HPUX9 at least still has the bad old non-copying putenv.
+ *
+ * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
+ * that will crash if you pass it an auto char array (so it must place
+ * its argument directly in the char* environ[] array).
+ */
+ free(waste);
+#endif
+ }
+#else
+ rv = setenv(name.ptr(), value.ptr(), 1);
+#endif
+ if (rv < 0) {
+ name.clear();
+ value.clear();
+ if (!name.encodeUtf8(cx, idstr))
+ return false;
+ if (!value.encodeUtf8(cx, valstr))
+ return false;
+ JS_ReportErrorUTF8(cx, "can't set envariable %s to %s", name.ptr(), value.ptr());
+ return false;
+ }
+ vp.setString(valstr);
+#endif /* !defined SOLARIS */
+ return result.succeed();
+}
+
+static bool
+env_enumerate(JSContext* cx, HandleObject obj)
+{
+ static bool reflected;
+ char** evp;
+ char* name;
+ char* value;
+ RootedString valstr(cx);
+ bool ok;
+
+ if (reflected)
+ return true;
+
+ for (evp = (char**)JS_GetPrivate(obj); (name = *evp) != nullptr; evp++) {
+ value = strchr(name, '=');
+ if (!value)
+ continue;
+ *value++ = '\0';
+ valstr = JS_NewStringCopyZ(cx, value);
+ ok = valstr ? JS_DefineProperty(cx, obj, name, valstr, JSPROP_ENUMERATE) : false;
+ value[-1] = '=';
+ if (!ok)
+ return false;
+ }
+
+ reflected = true;
+ return true;
+}
+
+static bool
+env_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+{
+ JSString* idstr;
+
+ RootedValue idval(cx);
+ if (!JS_IdToValue(cx, id, &idval))
+ return false;
+
+ idstr = ToString(cx, idval);
+ if (!idstr)
+ return false;
+ JSAutoByteString name(cx, idstr);
+ if (!name)
+ return false;
+ const char* value = getenv(name.ptr());
+ if (value) {
+ RootedString valstr(cx, JS_NewStringCopyZ(cx, value));
+ if (!valstr)
+ return false;
+ if (!JS_DefinePropertyById(cx, obj, id, valstr, JSPROP_ENUMERATE)) {
+ return false;
+ }
+ *resolvedp = true;
+ }
+ return true;
+}
+
+static const JSClassOps env_classOps = {
+ nullptr, nullptr, nullptr, env_setProperty,
+ env_enumerate, env_resolve
+};
+
+static const JSClass env_class = {
+ "environment", JSCLASS_HAS_PRIVATE,
+ &env_classOps
+};
+
+/***************************************************************************/
+
+typedef enum JSShellErrNum {
+#define MSG_DEF(name, number, count, exception, format) \
+ name = number,
+#include "jsshell.msg"
+#undef MSG_DEF
+ JSShellErr_Limit
+} JSShellErrNum;
+
+static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = {
+#define MSG_DEF(name, number, count, exception, format) \
+ { #name, format, count } ,
+#include "jsshell.msg"
+#undef MSG_DEF
+};
+
+static const JSErrorFormatString*
+my_GetErrorMessage(void* userRef, const unsigned errorNumber)
+{
+ if (errorNumber == 0 || errorNumber >= JSShellErr_Limit)
+ return nullptr;
+
+ return &jsShell_ErrorFormatString[errorNumber];
+}
+
+static bool
+ProcessLine(AutoJSAPI& jsapi, const char* buffer, int startline)
+{
+ JSContext* cx = jsapi.cx();
+ JS::RootedScript script(cx);
+ JS::RootedValue result(cx);
+ JS::CompileOptions options(cx);
+ options.setFileAndLine("typein", startline)
+ .setIsRunOnce(true);
+ if (!JS_CompileScript(cx, buffer, strlen(buffer), options, &script))
+ return false;
+ if (compileOnly)
+ return true;
+ if (!JS_ExecuteScript(cx, script, &result))
+ return false;
+
+ if (result.isUndefined())
+ return true;
+ RootedString str(cx);
+ if (!(str = ToString(cx, result)))
+ return false;
+ JSAutoByteString bytes;
+ if (!bytes.encodeLatin1(cx, str))
+ return false;
+
+ fprintf(gOutFile, "%s\n", bytes.ptr());
+ return true;
+}
+
+static bool
+ProcessFile(AutoJSAPI& jsapi, const char* filename, FILE* file, bool forceTTY)
+{
+ JSContext* cx = jsapi.cx();
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+ MOZ_ASSERT(global);
+
+ if (forceTTY) {
+ file = stdin;
+ } else if (!isatty(fileno(file))) {
+ /*
+ * It's not interactive - just execute it.
+ *
+ * Support the UNIX #! shell hack; gobble the first line if it starts
+ * with '#'. TODO - this isn't quite compatible with sharp variables,
+ * as a legal js program (using sharp variables) might start with '#'.
+ * But that would require multi-character lookahead.
+ */
+ int ch = fgetc(file);
+ if (ch == '#') {
+ while ((ch = fgetc(file)) != EOF) {
+ if (ch == '\n' || ch == '\r')
+ break;
+ }
+ }
+ ungetc(ch, file);
+
+ JS::RootedScript script(cx);
+ JS::RootedValue unused(cx);
+ JS::CompileOptions options(cx);
+ options.setUTF8(true)
+ .setFileAndLine(filename, 1)
+ .setIsRunOnce(true)
+ .setNoScriptRval(true);
+ if (!JS::Compile(cx, options, file, &script))
+ return false;
+ return compileOnly || JS_ExecuteScript(cx, script, &unused);
+ }
+
+ /* It's an interactive filehandle; drop into read-eval-print loop. */
+ int lineno = 1;
+ bool hitEOF = false;
+ do {
+ char buffer[4096];
+ char* bufp = buffer;
+ *bufp = '\0';
+
+ /*
+ * Accumulate lines until we get a 'compilable unit' - one that either
+ * generates an error (before running out of source) or that compiles
+ * cleanly. This should be whenever we get a complete statement that
+ * coincides with the end of a line.
+ */
+ int startline = lineno;
+ do {
+ if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
+ hitEOF = true;
+ break;
+ }
+ bufp += strlen(bufp);
+ lineno++;
+ } while (!JS_BufferIsCompilableUnit(cx, global, buffer, strlen(buffer)));
+
+ if (!ProcessLine(jsapi, buffer, startline))
+ jsapi.ReportException();
+ } while (!hitEOF && !gQuitting);
+
+ fprintf(gOutFile, "\n");
+ return true;
+}
+
+static bool
+Process(AutoJSAPI& jsapi, const char* filename, bool forceTTY)
+{
+ FILE* file;
+
+ if (forceTTY || !filename || strcmp(filename, "-") == 0) {
+ file = stdin;
+ } else {
+ file = fopen(filename, "r");
+ if (!file) {
+ /*
+ * Use Latin1 variant here because the encoding of the return value
+ * of strerror function can be non-UTF-8.
+ */
+ JS_ReportErrorNumberLatin1(jsapi.cx(), my_GetErrorMessage, nullptr,
+ JSSMSG_CANT_OPEN,
+ filename, strerror(errno));
+ gExitCode = EXITCODE_FILE_NOT_FOUND;
+ return false;
+ }
+ }
+
+ bool ok = ProcessFile(jsapi, filename, file, forceTTY);
+ if (file != stdin)
+ fclose(file);
+ return ok;
+}
+
+static int
+usage()
+{
+ fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
+ fprintf(gErrFile, "usage: xpcshell [-g gredir] [-a appdir] [-r manifest]... [-WwxiCSsmIp] [-v version] [-f scriptfile] [-e script] [scriptfile] [scriptarg...]\n");
+ return 2;
+}
+
+static bool
+printUsageAndSetExitCode()
+{
+ gExitCode = usage();
+ return false;
+}
+
+static void
+ProcessArgsForCompartment(JSContext* cx, char** argv, int argc)
+{
+ for (int i = 0; i < argc; i++) {
+ if (argv[i][0] != '-' || argv[i][1] == '\0')
+ break;
+
+ switch (argv[i][1]) {
+ case 'v':
+ case 'f':
+ case 'e':
+ if (++i == argc)
+ return;
+ break;
+ case 'S':
+ ContextOptionsRef(cx).toggleWerror();
+ MOZ_FALLTHROUGH; // because -S implies -s
+ case 's':
+ ContextOptionsRef(cx).toggleExtraWarnings();
+ break;
+ case 'I':
+ ContextOptionsRef(cx).toggleIon()
+ .toggleAsmJS()
+ .toggleWasm();
+ break;
+ }
+ }
+}
+
+static bool
+ProcessArgs(AutoJSAPI& jsapi, char** argv, int argc, XPCShellDirProvider* aDirProvider)
+{
+ JSContext* cx = jsapi.cx();
+ const char rcfilename[] = "xpcshell.js";
+ FILE* rcfile;
+ int rootPosition;
+ JS::Rooted<JSObject*> argsObj(cx);
+ char* filename = nullptr;
+ bool isInteractive = true;
+ bool forceTTY = false;
+
+ rcfile = fopen(rcfilename, "r");
+ if (rcfile) {
+ printf("[loading '%s'...]\n", rcfilename);
+ bool ok = ProcessFile(jsapi, rcfilename, rcfile, false);
+ fclose(rcfile);
+ if (!ok) {
+ return false;
+ }
+ }
+
+ JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
+
+ /*
+ * Scan past all optional arguments so we can create the arguments object
+ * before processing any -f options, which must interleave properly with
+ * -v and -w options. This requires two passes, and without getopt, we'll
+ * have to keep the option logic here and in the second for loop in sync.
+ * First of all, find out the first argument position which will be passed
+ * as a script file to be executed.
+ */
+ for (rootPosition = 0; rootPosition < argc; rootPosition++) {
+ if (argv[rootPosition][0] != '-' || argv[rootPosition][1] == '\0') {
+ ++rootPosition;
+ break;
+ }
+
+ bool isPairedFlag =
+ argv[rootPosition][0] != '\0' &&
+ (argv[rootPosition][1] == 'v' ||
+ argv[rootPosition][1] == 'f' ||
+ argv[rootPosition][1] == 'e');
+ if (isPairedFlag && rootPosition < argc - 1) {
+ ++rootPosition; // Skip over the 'foo' portion of |-v foo|, |-f foo|, or |-e foo|.
+ }
+ }
+
+ /*
+ * Create arguments early and define it to root it, so it's safe from any
+ * GC calls nested below, and so it is available to -f <file> arguments.
+ */
+ argsObj = JS_NewArrayObject(cx, 0);
+ if (!argsObj)
+ return 1;
+ if (!JS_DefineProperty(cx, global, "arguments", argsObj, 0))
+ return 1;
+
+ for (int j = 0, length = argc - rootPosition; j < length; j++) {
+ RootedString str(cx, JS_NewStringCopyZ(cx, argv[rootPosition++]));
+ if (!str ||
+ !JS_DefineElement(cx, argsObj, j, str, JSPROP_ENUMERATE)) {
+ return 1;
+ }
+ }
+
+ for (int i = 0; i < argc; i++) {
+ if (argv[i][0] != '-' || argv[i][1] == '\0') {
+ filename = argv[i++];
+ isInteractive = false;
+ break;
+ }
+ switch (argv[i][1]) {
+ case 'v':
+ if (++i == argc) {
+ return printUsageAndSetExitCode();
+ }
+ JS_SetVersionForCompartment(js::GetContextCompartment(cx),
+ JSVersion(atoi(argv[i])));
+ break;
+ case 'W':
+ reportWarnings = false;
+ break;
+ case 'w':
+ reportWarnings = true;
+ break;
+ case 'x':
+ break;
+ case 'd':
+ /* This used to try to turn on the debugger. */
+ break;
+ case 'm':
+ break;
+ case 'f':
+ if (++i == argc) {
+ return printUsageAndSetExitCode();
+ }
+ if (!Process(jsapi, argv[i], false))
+ return false;
+ /*
+ * XXX: js -f foo.js should interpret foo.js and then
+ * drop into interactive mode, but that breaks test
+ * harness. Just execute foo.js for now.
+ */
+ isInteractive = false;
+ break;
+ case 'i':
+ isInteractive = forceTTY = true;
+ break;
+ case 'e':
+ {
+ RootedValue rval(cx);
+
+ if (++i == argc) {
+ return printUsageAndSetExitCode();
+ }
+
+ JS::CompileOptions opts(cx);
+ opts.setFileAndLine("-e", 1);
+ JS::Evaluate(cx, opts, argv[i], strlen(argv[i]), &rval);
+
+ isInteractive = false;
+ break;
+ }
+ case 'C':
+ compileOnly = true;
+ isInteractive = false;
+ break;
+ case 'S':
+ case 's':
+ case 'I':
+ // These options are processed in ProcessArgsForCompartment.
+ break;
+ case 'p':
+ {
+ // plugins path
+ char* pluginPath = argv[++i];
+ nsCOMPtr<nsIFile> pluginsDir;
+ if (NS_FAILED(XRE_GetFileFromPath(pluginPath, getter_AddRefs(pluginsDir)))) {
+ fprintf(gErrFile, "Couldn't use given plugins dir.\n");
+ return printUsageAndSetExitCode();
+ }
+ aDirProvider->SetPluginDir(pluginsDir);
+ break;
+ }
+ default:
+ return printUsageAndSetExitCode();
+ }
+ }
+
+ if (filename || isInteractive)
+ return Process(jsapi, filename, forceTTY);
+ return true;
+}
+
+/***************************************************************************/
+
+// #define TEST_InitClassesWithNewWrappedGlobal
+
+#ifdef TEST_InitClassesWithNewWrappedGlobal
+// XXX hacky test code...
+#include "xpctest.h"
+
+class TestGlobal : public nsIXPCTestNoisy, public nsIXPCScriptable
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCTESTNOISY
+ NS_DECL_NSIXPCSCRIPTABLE
+
+ TestGlobal(){}
+};
+
+NS_IMPL_ISUPPORTS(TestGlobal, nsIXPCTestNoisy, nsIXPCScriptable)
+
+// The nsIXPCScriptable map declaration that will generate stubs for us...
+#define XPC_MAP_CLASSNAME TestGlobal
+#define XPC_MAP_QUOTED_CLASSNAME "TestGlobal"
+#define XPC_MAP_FLAGS nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY |\
+ nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY |\
+ nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY
+#include "xpc_map_end.h" /* This will #undef the above */
+
+NS_IMETHODIMP TestGlobal::Squawk() {return NS_OK;}
+
+#endif
+
+// uncomment to install the test 'this' translator
+// #define TEST_TranslateThis
+
+#ifdef TEST_TranslateThis
+
+#include "xpctest.h"
+
+class nsXPCFunctionThisTranslator : public nsIXPCFunctionThisTranslator
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR
+
+ nsXPCFunctionThisTranslator();
+ virtual ~nsXPCFunctionThisTranslator();
+ /* additional members */
+};
+
+/* Implementation file */
+NS_IMPL_ISUPPORTS(nsXPCFunctionThisTranslator, nsIXPCFunctionThisTranslator)
+
+nsXPCFunctionThisTranslator::nsXPCFunctionThisTranslator()
+{
+}
+
+nsXPCFunctionThisTranslator::~nsXPCFunctionThisTranslator()
+{
+}
+
+NS_IMETHODIMP
+nsXPCFunctionThisTranslator::TranslateThis(nsISupports* aInitialThis,
+ nsISupports** _retval)
+{
+ nsCOMPtr<nsISupports> temp = aInitialThis;
+ temp.forget(_retval);
+ return NS_OK;
+}
+
+#endif
+
+static bool
+GetCurrentWorkingDirectory(nsAString& workingDirectory)
+{
+#if !defined(XP_WIN) && !defined(XP_UNIX)
+ //XXX: your platform should really implement this
+ return false;
+#elif XP_WIN
+ DWORD requiredLength = GetCurrentDirectoryW(0, nullptr);
+ workingDirectory.SetLength(requiredLength);
+ GetCurrentDirectoryW(workingDirectory.Length(),
+ (LPWSTR)workingDirectory.BeginWriting());
+ // we got a trailing null there
+ workingDirectory.SetLength(requiredLength);
+ workingDirectory.Replace(workingDirectory.Length() - 1, 1, L'\\');
+#elif defined(XP_UNIX)
+ nsAutoCString cwd;
+ // 1024 is just a guess at a sane starting value
+ size_t bufsize = 1024;
+ char* result = nullptr;
+ while (result == nullptr) {
+ cwd.SetLength(bufsize);
+ result = getcwd(cwd.BeginWriting(), cwd.Length());
+ if (!result) {
+ if (errno != ERANGE)
+ return false;
+ // need to make the buffer bigger
+ bufsize *= 2;
+ }
+ }
+ // size back down to the actual string length
+ cwd.SetLength(strlen(result) + 1);
+ cwd.Replace(cwd.Length() - 1, 1, '/');
+ workingDirectory = NS_ConvertUTF8toUTF16(cwd);
+#endif
+ return true;
+}
+
+static JSSecurityCallbacks shellSecurityCallbacks;
+
+int
+XRE_XPCShellMain(int argc, char** argv, char** envp,
+ const XREShellData* aShellData)
+{
+ MOZ_ASSERT(aShellData);
+
+ JSContext* cx;
+ int result = 0;
+ nsresult rv;
+
+ gErrFile = stderr;
+ gOutFile = stdout;
+ gInFile = stdin;
+
+ NS_LogInit();
+
+ mozilla::LogModule::Init();
+
+ // A initializer to initialize histogram collection
+ // used by telemetry.
+ UniquePtr<base::StatisticsRecorder> telStats =
+ MakeUnique<base::StatisticsRecorder>();
+
+ if (PR_GetEnv("MOZ_CHAOSMODE")) {
+ ChaosFeature feature = ChaosFeature::Any;
+ long featureInt = strtol(PR_GetEnv("MOZ_CHAOSMODE"), nullptr, 16);
+ if (featureInt) {
+ // NOTE: MOZ_CHAOSMODE=0 or a non-hex value maps to Any feature.
+ feature = static_cast<ChaosFeature>(featureInt);
+ }
+ ChaosMode::SetChaosFeature(feature);
+ }
+
+ if (ChaosMode::isActive(ChaosFeature::Any)) {
+ printf_stderr("*** You are running in chaos test mode. See ChaosMode.h. ***\n");
+ }
+
+ nsCOMPtr<nsIFile> appFile;
+ rv = XRE_GetBinaryPath(argv[0], getter_AddRefs(appFile));
+ if (NS_FAILED(rv)) {
+ printf("Couldn't find application file.\n");
+ return 1;
+ }
+ nsCOMPtr<nsIFile> appDir;
+ rv = appFile->GetParent(getter_AddRefs(appDir));
+ if (NS_FAILED(rv)) {
+ printf("Couldn't get application directory.\n");
+ return 1;
+ }
+
+ XPCShellDirProvider dirprovider;
+
+ dirprovider.SetAppFile(appFile);
+
+ nsCOMPtr<nsIFile> greDir;
+ if (argc > 1 && !strcmp(argv[1], "-g")) {
+ if (argc < 3)
+ return usage();
+
+ rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(greDir));
+ if (NS_FAILED(rv)) {
+ printf("Couldn't use given GRE dir.\n");
+ return 1;
+ }
+
+ dirprovider.SetGREDirs(greDir);
+
+ argc -= 2;
+ argv += 2;
+ } else {
+#ifdef XP_MACOSX
+ // On OSX, the GreD needs to point to Contents/Resources in the .app
+ // bundle. Libraries will be loaded at a relative path to GreD, i.e.
+ // ../MacOS.
+ nsCOMPtr<nsIFile> tmpDir;
+ XRE_GetFileFromPath(argv[0], getter_AddRefs(greDir));
+ greDir->GetParent(getter_AddRefs(tmpDir));
+ tmpDir->Clone(getter_AddRefs(greDir));
+ tmpDir->SetNativeLeafName(NS_LITERAL_CSTRING("Resources"));
+ bool dirExists = false;
+ tmpDir->Exists(&dirExists);
+ if (dirExists) {
+ greDir = tmpDir.forget();
+ }
+ dirprovider.SetGREDirs(greDir);
+#else
+ nsAutoString workingDir;
+ if (!GetCurrentWorkingDirectory(workingDir)) {
+ printf("GetCurrentWorkingDirectory failed.\n");
+ return 1;
+ }
+ rv = NS_NewLocalFile(workingDir, true, getter_AddRefs(greDir));
+ if (NS_FAILED(rv)) {
+ printf("NS_NewLocalFile failed.\n");
+ return 1;
+ }
+#endif
+ }
+
+ if (argc > 1 && !strcmp(argv[1], "-a")) {
+ if (argc < 3)
+ return usage();
+
+ nsCOMPtr<nsIFile> dir;
+ rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(dir));
+ if (NS_SUCCEEDED(rv)) {
+ appDir = do_QueryInterface(dir, &rv);
+ dirprovider.SetAppDir(appDir);
+ }
+ if (NS_FAILED(rv)) {
+ printf("Couldn't use given appdir.\n");
+ return 1;
+ }
+ argc -= 2;
+ argv += 2;
+ }
+
+ while (argc > 1 && !strcmp(argv[1], "-r")) {
+ if (argc < 3)
+ return usage();
+
+ nsCOMPtr<nsIFile> lf;
+ rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(lf));
+ if (NS_FAILED(rv)) {
+ printf("Couldn't get manifest file.\n");
+ return 1;
+ }
+ XRE_AddManifestLocation(NS_APP_LOCATION, lf);
+
+ argc -= 2;
+ argv += 2;
+ }
+
+#ifdef MOZ_CRASHREPORTER
+ const char* val = getenv("MOZ_CRASHREPORTER");
+ if (val && *val) {
+ rv = CrashReporter::SetExceptionHandler(greDir, true);
+ if (NS_FAILED(rv)) {
+ printf("CrashReporter::SetExceptionHandler failed!\n");
+ return 1;
+ }
+ MOZ_ASSERT(CrashReporter::GetEnabled());
+ }
+#endif
+
+ {
+ if (argc > 1 && !strcmp(argv[1], "--greomni")) {
+ nsCOMPtr<nsIFile> greOmni;
+ nsCOMPtr<nsIFile> appOmni;
+ XRE_GetFileFromPath(argv[2], getter_AddRefs(greOmni));
+ if (argc > 3 && !strcmp(argv[3], "--appomni")) {
+ XRE_GetFileFromPath(argv[4], getter_AddRefs(appOmni));
+ argc-=2;
+ argv+=2;
+ } else {
+ appOmni = greOmni;
+ }
+
+ XRE_InitOmnijar(greOmni, appOmni);
+ argc-=2;
+ argv+=2;
+ }
+
+ nsCOMPtr<nsIServiceManager> servMan;
+ rv = NS_InitXPCOM2(getter_AddRefs(servMan), appDir, &dirprovider);
+ if (NS_FAILED(rv)) {
+ printf("NS_InitXPCOM2 failed!\n");
+ return 1;
+ }
+
+ // xpc::ErrorReport::LogToConsoleWithStack needs this to print errors
+ // to stderr.
+ Preferences::SetBool("browser.dom.window.dump.enabled", true);
+
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ cx = jsapi.cx();
+
+ // Override the default XPConnect interrupt callback. We could store the
+ // old one and restore it before shutting down, but there's not really a
+ // reason to bother.
+ sScriptedInterruptCallback = new PersistentRootedValue;
+ sScriptedInterruptCallback->init(cx, UndefinedValue());
+
+ JS_AddInterruptCallback(cx, XPCShellInterruptCallback);
+
+ argc--;
+ argv++;
+ ProcessArgsForCompartment(cx, argv, argc);
+
+ nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
+ if (!xpc) {
+ printf("failed to get nsXPConnect service!\n");
+ return 1;
+ }
+
+ nsCOMPtr<nsIPrincipal> systemprincipal;
+ // Fetch the system principal and store it away in a global, to use for
+ // script compilation in Load() and ProcessFile() (including interactive
+ // eval loop)
+ {
+
+ nsCOMPtr<nsIScriptSecurityManager> securityManager =
+ do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv) && securityManager) {
+ rv = securityManager->GetSystemPrincipal(getter_AddRefs(systemprincipal));
+ if (NS_FAILED(rv)) {
+ fprintf(gErrFile, "+++ Failed to obtain SystemPrincipal from ScriptSecurityManager service.\n");
+ } else {
+ // fetch the JS principals and stick in a global
+ gJSPrincipals = nsJSPrincipals::get(systemprincipal);
+ JS_HoldPrincipals(gJSPrincipals);
+ }
+ } else {
+ fprintf(gErrFile, "+++ Failed to get ScriptSecurityManager service, running without principals");
+ }
+ }
+
+ const JSSecurityCallbacks* scb = JS_GetSecurityCallbacks(cx);
+ MOZ_ASSERT(scb, "We are assuming that nsScriptSecurityManager::Init() has been run");
+ shellSecurityCallbacks = *scb;
+ JS_SetSecurityCallbacks(cx, &shellSecurityCallbacks);
+
+#ifdef TEST_TranslateThis
+ nsCOMPtr<nsIXPCFunctionThisTranslator>
+ translator(new nsXPCFunctionThisTranslator);
+ xpc->SetFunctionThisTranslator(NS_GET_IID(nsITestXPCFunctionCallback), translator);
+#endif
+
+ RefPtr<BackstagePass> backstagePass;
+ rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
+ if (NS_FAILED(rv)) {
+ fprintf(gErrFile, "+++ Failed to create BackstagePass: %8x\n",
+ static_cast<uint32_t>(rv));
+ return 1;
+ }
+
+ // Make the default XPCShell global use a fresh zone (rather than the
+ // System Zone) to improve cross-zone test coverage.
+ JS::CompartmentOptions options;
+ options.creationOptions().setZone(JS::FreshZone);
+ if (xpc::SharedMemoryEnabled())
+ options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
+ options.behaviors().setVersion(JSVERSION_LATEST);
+ nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
+ rv = xpc->InitClassesWithNewWrappedGlobal(cx,
+ static_cast<nsIGlobalObject*>(backstagePass),
+ systemprincipal,
+ 0,
+ options,
+ getter_AddRefs(holder));
+ if (NS_FAILED(rv))
+ return 1;
+
+ // Initialize graphics prefs on the main thread, if not already done
+ gfxPrefs::GetSingleton();
+ // Initialize e10s check on the main thread, if not already done
+ BrowserTabsRemoteAutostart();
+#ifdef XP_WIN
+ // Plugin may require audio session if installed plugin can initialize
+ // asynchronized.
+ AutoAudioSession audioSession;
+
+#if defined(MOZ_SANDBOX)
+ // Required for sandboxed child processes.
+ if (aShellData->sandboxBrokerServices) {
+ SandboxBroker::Initialize(aShellData->sandboxBrokerServices);
+ } else {
+ NS_WARNING("Failed to initialize broker services, sandboxed "
+ "processes will fail to start.");
+ }
+#endif
+#endif
+
+ {
+ JS::Rooted<JSObject*> glob(cx, holder->GetJSObject());
+ if (!glob) {
+ return 1;
+ }
+
+ // Even if we're building in a configuration where source is
+ // discarded, there's no reason to do that on XPCShell, and doing so
+ // might break various automation scripts.
+ JS::CompartmentBehaviorsRef(glob).setDiscardSource(false);
+
+ backstagePass->SetGlobalObject(glob);
+
+ JSAutoCompartment ac(cx, glob);
+
+ if (!JS_InitReflectParse(cx, glob)) {
+ return 1;
+ }
+
+ if (!JS_DefineFunctions(cx, glob, glob_functions) ||
+ !JS_DefineProfilingFunctions(cx, glob)) {
+ return 1;
+ }
+
+ JS::Rooted<JSObject*> envobj(cx);
+ envobj = JS_DefineObject(cx, glob, "environment", &env_class);
+ if (!envobj) {
+ return 1;
+ }
+
+ JS_SetPrivate(envobj, envp);
+
+ nsAutoString workingDirectory;
+ if (GetCurrentWorkingDirectory(workingDirectory))
+ gWorkingDirectory = &workingDirectory;
+
+ JS_DefineProperty(cx, glob, "__LOCATION__", JS::UndefinedHandleValue,
+ JSPROP_SHARED,
+ GetLocationProperty,
+ nullptr);
+
+ {
+ // We are almost certainly going to run script here, so we need an
+ // AutoEntryScript. This is Gecko-specific and not in any spec.
+ AutoEntryScript aes(backstagePass, "xpcshell argument processing");
+
+ // If an exception is thrown, we'll set our return code
+ // appropriately, and then let the AutoEntryScript destructor report
+ // the error to the console.
+ if (!ProcessArgs(aes, argv, argc, &dirprovider)) {
+ if (gExitCode) {
+ result = gExitCode;
+ } else if (gQuitting) {
+ result = 0;
+ } else {
+ result = EXITCODE_RUNTIME_ERROR;
+ }
+ }
+ }
+
+ JS_DropPrincipals(cx, gJSPrincipals);
+ JS_SetAllNonReservedSlotsToUndefined(cx, glob);
+ JS_SetAllNonReservedSlotsToUndefined(cx, JS_GlobalLexicalEnvironment(glob));
+ JS_GC(cx);
+ }
+ JS_GC(cx);
+ } // this scopes the nsCOMPtrs
+
+ if (!XRE_ShutdownTestShell())
+ NS_ERROR("problem shutting down testshell");
+
+ // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
+ rv = NS_ShutdownXPCOM( nullptr );
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
+
+#ifdef TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN
+ // test of late call and release (see above)
+ JSContext* bogusCX;
+ bogus->Peek(&bogusCX);
+ bogus = nullptr;
+#endif
+
+ telStats = nullptr;
+ appDir = nullptr;
+ appFile = nullptr;
+ dirprovider.ClearGREDirs();
+ dirprovider.ClearAppDir();
+ dirprovider.ClearPluginDir();
+ dirprovider.ClearAppFile();
+
+#ifdef MOZ_CRASHREPORTER
+ // Shut down the crashreporter service to prevent leaking some strings it holds.
+ if (CrashReporter::GetEnabled())
+ CrashReporter::UnsetExceptionHandler();
+#endif
+
+ NS_LogTerm();
+
+ return result;
+}
+
+void
+XPCShellDirProvider::SetGREDirs(nsIFile* greDir)
+{
+ mGREDir = greDir;
+ mGREDir->Clone(getter_AddRefs(mGREBinDir));
+#ifdef XP_MACOSX
+ nsAutoCString leafName;
+ mGREDir->GetNativeLeafName(leafName);
+ if (leafName.Equals("Resources")) {
+ mGREBinDir->SetNativeLeafName(NS_LITERAL_CSTRING("MacOS"));
+ }
+#endif
+}
+
+void
+XPCShellDirProvider::SetAppFile(nsIFile* appFile)
+{
+ mAppFile = appFile;
+}
+
+void
+XPCShellDirProvider::SetAppDir(nsIFile* appDir)
+{
+ mAppDir = appDir;
+}
+
+void
+XPCShellDirProvider::SetPluginDir(nsIFile* pluginDir)
+{
+ mPluginDir = pluginDir;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+XPCShellDirProvider::AddRef()
+{
+ return 2;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+XPCShellDirProvider::Release()
+{
+ return 1;
+}
+
+NS_IMPL_QUERY_INTERFACE(XPCShellDirProvider,
+ nsIDirectoryServiceProvider,
+ nsIDirectoryServiceProvider2)
+
+NS_IMETHODIMP
+XPCShellDirProvider::GetFile(const char* prop, bool* persistent,
+ nsIFile* *result)
+{
+ if (mGREDir && !strcmp(prop, NS_GRE_DIR)) {
+ *persistent = true;
+ return mGREDir->Clone(result);
+ } else if (mGREBinDir && !strcmp(prop, NS_GRE_BIN_DIR)) {
+ *persistent = true;
+ return mGREBinDir->Clone(result);
+ } else if (mAppFile && !strcmp(prop, XRE_EXECUTABLE_FILE)) {
+ *persistent = true;
+ return mAppFile->Clone(result);
+ } else if (mGREDir && !strcmp(prop, NS_APP_PREF_DEFAULTS_50_DIR)) {
+ nsCOMPtr<nsIFile> file;
+ *persistent = true;
+ if (NS_FAILED(mGREDir->Clone(getter_AddRefs(file))) ||
+ NS_FAILED(file->AppendNative(NS_LITERAL_CSTRING("defaults"))) ||
+ NS_FAILED(file->AppendNative(NS_LITERAL_CSTRING("pref"))))
+ return NS_ERROR_FAILURE;
+ file.forget(result);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+XPCShellDirProvider::GetFiles(const char* prop, nsISimpleEnumerator* *result)
+{
+ if (mGREDir && !strcmp(prop, "ChromeML")) {
+ nsCOMArray<nsIFile> dirs;
+
+ nsCOMPtr<nsIFile> file;
+ mGREDir->Clone(getter_AddRefs(file));
+ file->AppendNative(NS_LITERAL_CSTRING("chrome"));
+ dirs.AppendObject(file);
+
+ nsresult rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR,
+ getter_AddRefs(file));
+ if (NS_SUCCEEDED(rv))
+ dirs.AppendObject(file);
+
+ return NS_NewArrayEnumerator(result, dirs);
+ } else if (!strcmp(prop, NS_APP_PREFS_DEFAULTS_DIR_LIST)) {
+ nsCOMArray<nsIFile> dirs;
+ nsCOMPtr<nsIFile> appDir;
+ bool exists;
+ if (mAppDir &&
+ NS_SUCCEEDED(mAppDir->Clone(getter_AddRefs(appDir))) &&
+ NS_SUCCEEDED(appDir->AppendNative(NS_LITERAL_CSTRING("defaults"))) &&
+ NS_SUCCEEDED(appDir->AppendNative(NS_LITERAL_CSTRING("preferences"))) &&
+ NS_SUCCEEDED(appDir->Exists(&exists)) && exists) {
+ dirs.AppendObject(appDir);
+ return NS_NewArrayEnumerator(result, dirs);
+ }
+ return NS_ERROR_FAILURE;
+ } else if (!strcmp(prop, NS_APP_PLUGINS_DIR_LIST)) {
+ nsCOMArray<nsIFile> dirs;
+ // Add the test plugin location passed in by the caller or through
+ // runxpcshelltests.
+ if (mPluginDir) {
+ dirs.AppendObject(mPluginDir);
+ // If there was no path specified, default to the one set up by automation
+ } else {
+ nsCOMPtr<nsIFile> file;
+ bool exists;
+ // We have to add this path, buildbot copies the test plugin directory
+ // to (app)/bin when unpacking test zips.
+ if (mGREDir) {
+ mGREDir->Clone(getter_AddRefs(file));
+ if (NS_SUCCEEDED(mGREDir->Clone(getter_AddRefs(file)))) {
+ file->AppendNative(NS_LITERAL_CSTRING("plugins"));
+ if (NS_SUCCEEDED(file->Exists(&exists)) && exists) {
+ dirs.AppendObject(file);
+ }
+ }
+ }
+ }
+ return NS_NewArrayEnumerator(result, dirs);
+ }
+ return NS_ERROR_FAILURE;
+}
diff --git a/js/xpconnect/src/XPCString.cpp b/js/xpconnect/src/XPCString.cpp
new file mode 100644
index 000000000..42a57744e
--- /dev/null
+++ b/js/xpconnect/src/XPCString.cpp
@@ -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
diff --git a/js/xpconnect/src/XPCThrower.cpp b/js/xpconnect/src/XPCThrower.cpp
new file mode 100644
index 000000000..4a37e1554
--- /dev/null
+++ b/js/xpconnect/src/XPCThrower.cpp
@@ -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;
+ }
+}
diff --git a/js/xpconnect/src/XPCVariant.cpp b/js/xpconnect/src/XPCVariant.cpp
new file mode 100644
index 000000000..a3d2b88c5
--- /dev/null
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -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);
+}
diff --git a/js/xpconnect/src/XPCWrappedJS.cpp b/js/xpconnect/src/XPCWrappedJS.cpp
new file mode 100644
index 000000000..ebcfe6590
--- /dev/null
+++ b/js/xpconnect/src/XPCWrappedJS.cpp
@@ -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;
+}
diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp
new file mode 100644
index 000000000..2c9fd66bc
--- /dev/null
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -0,0 +1,1457 @@
+/* -*- 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/. */
+
+/* Sharable code and data for wrapper around JSObjects. */
+
+#include "xpcprivate.h"
+#include "jsprf.h"
+#include "nsArrayEnumerator.h"
+#include "nsContentUtils.h"
+#include "nsWrapperCache.h"
+#include "AccessCheck.h"
+#include "nsJSUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/DOMExceptionBinding.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+using namespace xpc;
+using namespace JS;
+using namespace mozilla;
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS(nsXPCWrappedJSClass, nsIXPCWrappedJSClass)
+
+// the value of this variable is never used - we use its address as a sentinel
+static uint32_t zero_methods_descriptor;
+
+bool AutoScriptEvaluate::StartEvaluating(HandleObject scope)
+{
+ NS_PRECONDITION(!mEvaluated, "AutoScriptEvaluate::Evaluate should only be called once");
+
+ if (!mJSContext)
+ return true;
+
+ mEvaluated = true;
+
+ JS_BeginRequest(mJSContext);
+ mAutoCompartment.emplace(mJSContext, scope);
+
+ // Saving the exception state keeps us from interfering with another script
+ // that may also be running on this context. This occurred first with the
+ // js debugger, as described in
+ // http://bugzilla.mozilla.org/show_bug.cgi?id=88130 but presumably could
+ // show up in any situation where a script calls into a wrapped js component
+ // on the same context, while the context has a nonzero exception state.
+ mState.emplace(mJSContext);
+
+ return true;
+}
+
+AutoScriptEvaluate::~AutoScriptEvaluate()
+{
+ if (!mJSContext || !mEvaluated)
+ return;
+ mState->restore();
+
+ JS_EndRequest(mJSContext);
+}
+
+// It turns out that some errors may be not worth reporting. So, this
+// function is factored out to manage that.
+bool xpc_IsReportableErrorCode(nsresult code)
+{
+ if (NS_SUCCEEDED(code))
+ return false;
+
+ switch (code) {
+ // Error codes that we don't want to report as errors...
+ // These generally indicate bad interface design AFAIC.
+ case NS_ERROR_FACTORY_REGISTER_AGAIN:
+ case NS_BASE_STREAM_WOULD_BLOCK:
+ return false;
+ default:
+ return true;
+ }
+}
+
+// A little stack-based RAII class to help management of the XPCJSContext
+// PendingResult.
+class MOZ_STACK_CLASS AutoSavePendingResult {
+public:
+ explicit AutoSavePendingResult(XPCJSContext* xpccx) :
+ mXPCContext(xpccx)
+ {
+ // Save any existing pending result and reset to NS_OK for this invocation.
+ mSavedResult = xpccx->GetPendingResult();
+ xpccx->SetPendingResult(NS_OK);
+ }
+ ~AutoSavePendingResult() {
+ mXPCContext->SetPendingResult(mSavedResult);
+ }
+private:
+ XPCJSContext* mXPCContext;
+ nsresult mSavedResult;
+};
+
+// static
+already_AddRefed<nsXPCWrappedJSClass>
+nsXPCWrappedJSClass::GetNewOrUsed(JSContext* cx, REFNSIID aIID, bool allowNonScriptable)
+{
+ XPCJSContext* xpccx = nsXPConnect::GetContextInstance();
+ IID2WrappedJSClassMap* map = xpccx->GetWrappedJSClassMap();
+ RefPtr<nsXPCWrappedJSClass> clasp = map->Find(aIID);
+
+ if (!clasp) {
+ nsCOMPtr<nsIInterfaceInfo> info;
+ nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
+ if (info) {
+ bool canScript, isBuiltin;
+ if (NS_SUCCEEDED(info->IsScriptable(&canScript)) && (canScript || allowNonScriptable) &&
+ NS_SUCCEEDED(info->IsBuiltinClass(&isBuiltin)) && !isBuiltin &&
+ nsXPConnect::IsISupportsDescendant(info))
+ {
+ clasp = new nsXPCWrappedJSClass(cx, aIID, info);
+ if (!clasp->mDescriptors)
+ clasp = nullptr;
+ }
+ }
+ }
+ return clasp.forget();
+}
+
+nsXPCWrappedJSClass::nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID,
+ nsIInterfaceInfo* aInfo)
+ : mContext(nsXPConnect::GetContextInstance()),
+ mInfo(aInfo),
+ mName(nullptr),
+ mIID(aIID),
+ mDescriptors(nullptr)
+{
+ mContext->GetWrappedJSClassMap()->Add(this);
+
+ uint16_t methodCount;
+ if (NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount))) {
+ if (methodCount) {
+ int wordCount = (methodCount/32)+1;
+ if (nullptr != (mDescriptors = new uint32_t[wordCount])) {
+ int i;
+ // init flags to 0;
+ for (i = wordCount-1; i >= 0; i--)
+ mDescriptors[i] = 0;
+
+ for (i = 0; i < methodCount; i++) {
+ const nsXPTMethodInfo* info;
+ if (NS_SUCCEEDED(mInfo->GetMethodInfo(i, &info)))
+ SetReflectable(i, XPCConvert::IsMethodReflectable(*info));
+ else {
+ delete [] mDescriptors;
+ mDescriptors = nullptr;
+ break;
+ }
+ }
+ }
+ } else {
+ mDescriptors = &zero_methods_descriptor;
+ }
+ }
+}
+
+nsXPCWrappedJSClass::~nsXPCWrappedJSClass()
+{
+ if (mDescriptors && mDescriptors != &zero_methods_descriptor)
+ delete [] mDescriptors;
+ if (mContext)
+ mContext->GetWrappedJSClassMap()->Remove(this);
+
+ if (mName)
+ free(mName);
+}
+
+JSObject*
+nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx,
+ JSObject* jsobjArg,
+ REFNSIID aIID)
+{
+ RootedObject jsobj(cx, jsobjArg);
+ JSObject* id;
+ RootedValue retval(cx);
+ RootedObject retObj(cx);
+ bool success = false;
+ RootedValue fun(cx);
+
+ // In bug 503926, we added a security check to make sure that we don't
+ // invoke content QI functions. In the modern world, this is probably
+ // unnecessary, because invoking QI involves passing an IID object to
+ // content, which will fail. But we do a belt-and-suspenders check to
+ // make sure that content can never trigger the rat's nest of code below.
+ // Once we completely turn off XPConnect for the web, this can definitely
+ // go away.
+ if (!AccessCheck::isChrome(jsobj) ||
+ !AccessCheck::isChrome(js::UncheckedUnwrap(jsobj)))
+ {
+ return nullptr;
+ }
+
+ // OK, it looks like we'll be calling into JS code.
+ AutoScriptEvaluate scriptEval(cx);
+
+ // XXX we should install an error reporter that will send reports to
+ // the JS error console service.
+ if (!scriptEval.StartEvaluating(jsobj))
+ return nullptr;
+
+ // check upfront for the existence of the function property
+ HandleId funid = mContext->GetStringID(XPCJSContext::IDX_QUERY_INTERFACE);
+ if (!JS_GetPropertyById(cx, jsobj, funid, &fun) || fun.isPrimitive())
+ return nullptr;
+
+ // Ensure that we are asking for a scriptable interface.
+ // NB: It's important for security that this check is here rather
+ // than later, since it prevents untrusted objects from implementing
+ // some interfaces in JS and aggregating a trusted object to
+ // implement intentionally (for security) unscriptable interfaces.
+ // We so often ask for nsISupports that we can short-circuit the test...
+ if (!aIID.Equals(NS_GET_IID(nsISupports))) {
+ bool allowNonScriptable = mozilla::jsipc::IsWrappedCPOW(jsobj);
+
+ nsCOMPtr<nsIInterfaceInfo> info;
+ nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
+ if (!info)
+ return nullptr;
+ bool canScript, isBuiltin;
+ if (NS_FAILED(info->IsScriptable(&canScript)) || (!canScript && !allowNonScriptable) ||
+ NS_FAILED(info->IsBuiltinClass(&isBuiltin)) || isBuiltin)
+ return nullptr;
+ }
+
+ id = xpc_NewIDObject(cx, jsobj, aIID);
+ if (id) {
+ // Throwing NS_NOINTERFACE is the prescribed way to fail QI from JS. It
+ // is not an exception that is ever worth reporting, but we don't want
+ // to eat all exceptions either.
+
+ {
+ RootedValue arg(cx, JS::ObjectValue(*id));
+ success = JS_CallFunctionValue(cx, jsobj, fun, HandleValueArray(arg), &retval);
+ }
+
+ if (!success && JS_IsExceptionPending(cx)) {
+ RootedValue jsexception(cx, NullValue());
+
+ if (JS_GetPendingException(cx, &jsexception)) {
+ nsresult rv;
+ if (jsexception.isObject()) {
+ // XPConnect may have constructed an object to represent a
+ // C++ QI failure. See if that is the case.
+ JS::Rooted<JSObject*> exceptionObj(cx, &jsexception.toObject());
+ Exception* e = nullptr;
+ UNWRAP_OBJECT(Exception, &exceptionObj, e);
+
+ if (e &&
+ NS_SUCCEEDED(e->GetResult(&rv)) &&
+ rv == NS_NOINTERFACE) {
+ JS_ClearPendingException(cx);
+ }
+ } else if (jsexception.isNumber()) {
+ // JS often throws an nsresult.
+ if (jsexception.isDouble())
+ // Visual Studio 9 doesn't allow casting directly from
+ // a double to an enumeration type, contrary to
+ // 5.2.9(10) of C++11, so add an intermediate cast.
+ rv = (nsresult)(uint32_t)(jsexception.toDouble());
+ else
+ rv = (nsresult)(jsexception.toInt32());
+
+ if (rv == NS_NOINTERFACE)
+ JS_ClearPendingException(cx);
+ }
+ }
+ } else if (!success) {
+ NS_WARNING("QI hook ran OOMed - this is probably a bug!");
+ }
+ }
+
+ if (success)
+ success = JS_ValueToObject(cx, retval, &retObj);
+
+ return success ? retObj.get() : nullptr;
+}
+
+/***************************************************************************/
+
+static bool
+GetNamedPropertyAsVariantRaw(XPCCallContext& ccx,
+ HandleObject aJSObj,
+ HandleId aName,
+ nsIVariant** aResult,
+ nsresult* pErr)
+{
+ nsXPTType type = nsXPTType((uint8_t)TD_INTERFACE_TYPE);
+ RootedValue val(ccx);
+
+ return JS_GetPropertyById(ccx, aJSObj, aName, &val) &&
+ XPCConvert::JSData2Native(aResult, val, type,
+ &NS_GET_IID(nsIVariant), pErr);
+}
+
+// static
+nsresult
+nsXPCWrappedJSClass::GetNamedPropertyAsVariant(XPCCallContext& ccx,
+ JSObject* aJSObjArg,
+ const nsAString& aName,
+ nsIVariant** aResult)
+{
+ JSContext* cx = ccx.GetJSContext();
+ RootedObject aJSObj(cx, aJSObjArg);
+
+ AutoScriptEvaluate scriptEval(cx);
+ if (!scriptEval.StartEvaluating(aJSObj))
+ return NS_ERROR_FAILURE;
+
+ // Wrap the string in a Value after the AutoScriptEvaluate, so that the
+ // resulting value ends up in the correct compartment.
+ nsStringBuffer* buf;
+ RootedValue value(cx);
+ if (!XPCStringConvert::ReadableToJSVal(ccx, aName, &buf, &value))
+ return NS_ERROR_OUT_OF_MEMORY;
+ if (buf)
+ buf->AddRef();
+
+ RootedId id(cx);
+ nsresult rv = NS_OK;
+ if (!JS_ValueToId(cx, value, &id) ||
+ !GetNamedPropertyAsVariantRaw(ccx, aJSObj, id, aResult, &rv)) {
+ if (NS_FAILED(rv))
+ return rv;
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+// static
+nsresult
+nsXPCWrappedJSClass::BuildPropertyEnumerator(XPCCallContext& ccx,
+ JSObject* aJSObjArg,
+ nsISimpleEnumerator** aEnumerate)
+{
+ JSContext* cx = ccx.GetJSContext();
+ RootedObject aJSObj(cx, aJSObjArg);
+
+ AutoScriptEvaluate scriptEval(cx);
+ if (!scriptEval.StartEvaluating(aJSObj))
+ return NS_ERROR_FAILURE;
+
+ Rooted<IdVector> idArray(cx, IdVector(cx));
+ if (!JS_Enumerate(cx, aJSObj, &idArray))
+ return NS_ERROR_FAILURE;
+
+ nsCOMArray<nsIProperty> propertyArray(idArray.length());
+ RootedId idName(cx);
+ for (size_t i = 0; i < idArray.length(); i++) {
+ idName = idArray[i];
+
+ nsCOMPtr<nsIVariant> value;
+ nsresult rv;
+ if (!GetNamedPropertyAsVariantRaw(ccx, aJSObj, idName,
+ getter_AddRefs(value), &rv)) {
+ if (NS_FAILED(rv))
+ return rv;
+ return NS_ERROR_FAILURE;
+ }
+
+ RootedValue jsvalName(cx);
+ if (!JS_IdToValue(cx, idName, &jsvalName))
+ return NS_ERROR_FAILURE;
+
+ JSString* name = ToString(cx, jsvalName);
+ if (!name)
+ return NS_ERROR_FAILURE;
+
+ nsAutoJSString autoStr;
+ if (!autoStr.init(cx, name))
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIProperty> property =
+ new xpcProperty(autoStr.get(), (uint32_t)autoStr.Length(), value);
+
+ if (!propertyArray.AppendObject(property))
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_NewArrayEnumerator(aEnumerate, propertyArray);
+}
+
+/***************************************************************************/
+
+NS_IMPL_ISUPPORTS(xpcProperty, nsIProperty)
+
+xpcProperty::xpcProperty(const char16_t* aName, uint32_t aNameLen,
+ nsIVariant* aValue)
+ : mName(aName, aNameLen), mValue(aValue)
+{
+}
+
+NS_IMETHODIMP xpcProperty::GetName(nsAString & aName)
+{
+ aName.Assign(mName);
+ return NS_OK;
+}
+
+NS_IMETHODIMP xpcProperty::GetValue(nsIVariant * *aValue)
+{
+ nsCOMPtr<nsIVariant> rval = mValue;
+ rval.forget(aValue);
+ return NS_OK;
+}
+
+/***************************************************************************/
+// This 'WrappedJSIdentity' class and singleton allow us to figure out if
+// any given nsISupports* is implemented by a WrappedJS object. This is done
+// using a QueryInterface call on the interface pointer with our ID. If
+// that call returns NS_OK and the pointer is to our singleton, then the
+// interface must be implemented by a WrappedJS object. NOTE: the
+// 'WrappedJSIdentity' object is not a real XPCOM object and should not be
+// used for anything else (hence it is declared in this implementation file).
+
+// {5C5C3BB0-A9BA-11d2-BA64-00805F8A5DD7}
+#define NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID \
+{ 0x5c5c3bb0, 0xa9ba, 0x11d2, \
+ { 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
+
+class WrappedJSIdentity
+{
+ // no instance methods...
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID)
+
+ static void* GetSingleton()
+ {
+ static WrappedJSIdentity* singleton = nullptr;
+ if (!singleton)
+ singleton = new WrappedJSIdentity();
+ return (void*) singleton;
+ }
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(WrappedJSIdentity,
+ NS_IXPCONNECT_WRAPPED_JS_IDENTITY_CLASS_IID)
+
+/***************************************************************************/
+
+// static
+bool
+nsXPCWrappedJSClass::IsWrappedJS(nsISupports* aPtr)
+{
+ void* result;
+ NS_PRECONDITION(aPtr, "null pointer");
+ return aPtr &&
+ NS_OK == aPtr->QueryInterface(NS_GET_IID(WrappedJSIdentity), &result) &&
+ result == WrappedJSIdentity::GetSingleton();
+}
+
+NS_IMETHODIMP
+nsXPCWrappedJSClass::DelegatedQueryInterface(nsXPCWrappedJS* self,
+ REFNSIID aIID,
+ void** aInstancePtr)
+{
+ if (aIID.Equals(NS_GET_IID(nsIXPConnectJSObjectHolder))) {
+ NS_ADDREF(self);
+ *aInstancePtr = (void*) static_cast<nsIXPConnectJSObjectHolder*>(self);
+ return NS_OK;
+ }
+
+ // Objects internal to xpconnect are the only objects that even know *how*
+ // to ask for this iid. And none of them bother refcounting the thing.
+ if (aIID.Equals(NS_GET_IID(WrappedJSIdentity))) {
+ // asking to find out if this is a wrapper object
+ *aInstancePtr = WrappedJSIdentity::GetSingleton();
+ return NS_OK;
+ }
+
+ if (aIID.Equals(NS_GET_IID(nsIPropertyBag))) {
+ // We only want to expose one implementation from our aggregate.
+ nsXPCWrappedJS* root = self->GetRootWrapper();
+
+ if (!root->IsValid()) {
+ *aInstancePtr = nullptr;
+ return NS_NOINTERFACE;
+ }
+
+ NS_ADDREF(root);
+ *aInstancePtr = (void*) static_cast<nsIPropertyBag*>(root);
+ return NS_OK;
+ }
+
+ // We can't have a cached wrapper.
+ if (aIID.Equals(NS_GET_IID(nsWrapperCache))) {
+ *aInstancePtr = nullptr;
+ return NS_NOINTERFACE;
+ }
+
+ // QI on an XPCWrappedJS can run script, so we need an AutoEntryScript.
+ // This is inherently Gecko-specific.
+ // We check both nativeGlobal and nativeGlobal->GetGlobalJSObject() even
+ // though we have derived nativeGlobal from the JS global, because we know
+ // there are cases where this can happen. See bug 1094953.
+ nsIGlobalObject* nativeGlobal =
+ NativeGlobal(js::GetGlobalForObjectCrossCompartment(self->GetJSObject()));
+ NS_ENSURE_TRUE(nativeGlobal, NS_ERROR_FAILURE);
+ NS_ENSURE_TRUE(nativeGlobal->GetGlobalJSObject(), NS_ERROR_FAILURE);
+ AutoEntryScript aes(nativeGlobal, "XPCWrappedJS QueryInterface",
+ /* aIsMainThread = */ true);
+ XPCCallContext ccx(aes.cx());
+ if (!ccx.IsValid()) {
+ *aInstancePtr = nullptr;
+ return NS_NOINTERFACE;
+ }
+
+ // We support nsISupportsWeakReference iff the root wrapped JSObject
+ // claims to support it in its QueryInterface implementation.
+ if (aIID.Equals(NS_GET_IID(nsISupportsWeakReference))) {
+ // We only want to expose one implementation from our aggregate.
+ nsXPCWrappedJS* root = self->GetRootWrapper();
+
+ // Fail if JSObject doesn't claim support for nsISupportsWeakReference
+ if (!root->IsValid() ||
+ !CallQueryInterfaceOnJSObject(ccx, root->GetJSObject(), aIID)) {
+ *aInstancePtr = nullptr;
+ return NS_NOINTERFACE;
+ }
+
+ NS_ADDREF(root);
+ *aInstancePtr = (void*) static_cast<nsISupportsWeakReference*>(root);
+ return NS_OK;
+ }
+
+ // Checks for any existing wrapper explicitly constructed for this iid.
+ // This includes the current 'self' wrapper. This also deals with the
+ // nsISupports case (for which it returns mRoot).
+ // Also check if asking for an interface from which one of our wrappers inherits.
+ if (nsXPCWrappedJS* sibling = self->FindOrFindInherited(aIID)) {
+ NS_ADDREF(sibling);
+ *aInstancePtr = sibling->GetXPTCStub();
+ return NS_OK;
+ }
+
+ // Check if the desired interface is a function interface. If so, we don't
+ // want to QI, because the function almost certainly doesn't have a QueryInterface
+ // property, and doesn't need one.
+ bool isFunc = false;
+ nsCOMPtr<nsIInterfaceInfo> info;
+ nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
+ if (info && NS_SUCCEEDED(info->IsFunction(&isFunc)) && isFunc) {
+ RefPtr<nsXPCWrappedJS> wrapper;
+ RootedObject obj(RootingCx(), self->GetJSObject());
+ nsresult rv = nsXPCWrappedJS::GetNewOrUsed(obj, aIID, getter_AddRefs(wrapper));
+
+ // Do the same thing we do for the "check for any existing wrapper" case above.
+ if (NS_SUCCEEDED(rv) && wrapper) {
+ *aInstancePtr = wrapper.forget().take()->GetXPTCStub();
+ }
+ return rv;
+ }
+
+ // else we do the more expensive stuff...
+
+ // check if the JSObject claims to implement this interface
+ RootedObject jsobj(ccx, CallQueryInterfaceOnJSObject(ccx, self->GetJSObject(),
+ aIID));
+ if (jsobj) {
+ // We can't use XPConvert::JSObject2NativeInterface() here
+ // since that can find a XPCWrappedNative directly on the
+ // proto chain, and we don't want that here. We need to find
+ // the actual JS object that claimed it supports the interface
+ // we're looking for or we'll potentially bypass security
+ // checks etc by calling directly through to a native found on
+ // the prototype chain.
+ //
+ // Instead, simply do the nsXPCWrappedJS part of
+ // XPConvert::JSObject2NativeInterface() here to make sure we
+ // get a new (or used) nsXPCWrappedJS.
+ RefPtr<nsXPCWrappedJS> wrapper;
+ nsresult rv = nsXPCWrappedJS::GetNewOrUsed(jsobj, aIID, getter_AddRefs(wrapper));
+ if (NS_SUCCEEDED(rv) && wrapper) {
+ // We need to go through the QueryInterface logic to make
+ // this return the right thing for the various 'special'
+ // interfaces; e.g. nsIPropertyBag.
+ rv = wrapper->QueryInterface(aIID, aInstancePtr);
+ return rv;
+ }
+ }
+
+ // else...
+ // no can do
+ *aInstancePtr = nullptr;
+ return NS_NOINTERFACE;
+}
+
+JSObject*
+nsXPCWrappedJSClass::GetRootJSObject(JSContext* cx, JSObject* aJSObjArg)
+{
+ RootedObject aJSObj(cx, aJSObjArg);
+ JSObject* result = CallQueryInterfaceOnJSObject(cx, aJSObj,
+ NS_GET_IID(nsISupports));
+ if (!result)
+ result = aJSObj;
+ JSObject* inner = js::UncheckedUnwrap(result);
+ if (inner)
+ return inner;
+ return result;
+}
+
+bool
+nsXPCWrappedJSClass::GetArraySizeFromParam(JSContext* cx,
+ const XPTMethodDescriptor* method,
+ const nsXPTParamInfo& param,
+ uint16_t methodIndex,
+ uint8_t paramIndex,
+ nsXPTCMiniVariant* nativeParams,
+ uint32_t* result) const
+{
+ uint8_t argnum;
+ nsresult rv;
+
+ rv = mInfo->GetSizeIsArgNumberForParam(methodIndex, &param, 0, &argnum);
+ if (NS_FAILED(rv))
+ return false;
+
+ const nsXPTParamInfo& arg_param = method->params[argnum];
+
+ // This should be enforced by the xpidl compiler, but it's not.
+ // See bug 695235.
+ MOZ_ASSERT(arg_param.GetType().TagPart() == nsXPTType::T_U32,
+ "size_is references parameter of invalid type.");
+
+ if (arg_param.IsIndirect())
+ *result = *(uint32_t*)nativeParams[argnum].val.p;
+ else
+ *result = nativeParams[argnum].val.u32;
+
+ return true;
+}
+
+bool
+nsXPCWrappedJSClass::GetInterfaceTypeFromParam(JSContext* cx,
+ const XPTMethodDescriptor* method,
+ const nsXPTParamInfo& param,
+ uint16_t methodIndex,
+ const nsXPTType& type,
+ nsXPTCMiniVariant* nativeParams,
+ nsID* result) const
+{
+ uint8_t type_tag = type.TagPart();
+
+ if (type_tag == nsXPTType::T_INTERFACE) {
+ if (NS_SUCCEEDED(GetInterfaceInfo()->
+ GetIIDForParamNoAlloc(methodIndex, &param, result))) {
+ return true;
+ }
+ } else if (type_tag == nsXPTType::T_INTERFACE_IS) {
+ uint8_t argnum;
+ nsresult rv;
+ rv = mInfo->GetInterfaceIsArgNumberForParam(methodIndex,
+ &param, &argnum);
+ if (NS_FAILED(rv))
+ return false;
+
+ const nsXPTParamInfo& arg_param = method->params[argnum];
+ const nsXPTType& arg_type = arg_param.GetType();
+
+ if (arg_type.TagPart() == nsXPTType::T_IID) {
+ if (arg_param.IsIndirect()) {
+ nsID** p = (nsID**) nativeParams[argnum].val.p;
+ if (!p || !*p)
+ return false;
+ *result = **p;
+ } else {
+ nsID* p = (nsID*) nativeParams[argnum].val.p;
+ if (!p)
+ return false;
+ *result = *p;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/* static */ void
+nsXPCWrappedJSClass::CleanupPointerArray(const nsXPTType& datum_type,
+ uint32_t array_count,
+ void** arrayp)
+{
+ if (datum_type.IsInterfacePointer()) {
+ nsISupports** pp = (nsISupports**) arrayp;
+ for (uint32_t k = 0; k < array_count; k++) {
+ nsISupports* p = pp[k];
+ NS_IF_RELEASE(p);
+ }
+ } else {
+ void** pp = (void**) arrayp;
+ for (uint32_t k = 0; k < array_count; k++) {
+ void* p = pp[k];
+ if (p) free(p);
+ }
+ }
+}
+
+/* static */ void
+nsXPCWrappedJSClass::CleanupPointerTypeObject(const nsXPTType& type,
+ void** pp)
+{
+ MOZ_ASSERT(pp,"null pointer");
+ if (type.IsInterfacePointer()) {
+ nsISupports* p = *((nsISupports**)pp);
+ if (p) p->Release();
+ } else {
+ void* p = *((void**)pp);
+ if (p) free(p);
+ }
+}
+
+void
+nsXPCWrappedJSClass::CleanupOutparams(JSContext* cx, uint16_t methodIndex,
+ const nsXPTMethodInfo* info, nsXPTCMiniVariant* nativeParams,
+ bool inOutOnly, uint8_t n) const
+{
+ // clean up any 'out' params handed in
+ for (uint8_t i = 0; i < n; i++) {
+ const nsXPTParamInfo& param = info->params[i];
+ if (!param.IsOut())
+ continue;
+
+ const nsXPTType& type = param.GetType();
+ if (!type.deprecated_IsPointer())
+ continue;
+ void* p = nativeParams[i].val.p;
+ if (!p)
+ continue;
+
+ // The inOutOnly flag was introduced when consolidating two very
+ // similar code paths in CallMethod in bug 1175513. I don't know
+ // if and why the difference is necessary.
+ if (!inOutOnly || param.IsIn()) {
+ if (type.IsArray()) {
+ void** pp = *static_cast<void***>(p);
+ if (pp) {
+ // we need to get the array length and iterate the items
+ uint32_t array_count;
+ nsXPTType datum_type;
+
+ if (NS_SUCCEEDED(mInfo->GetTypeForParam(methodIndex, &param,
+ 1, &datum_type)) &&
+ datum_type.deprecated_IsPointer() &&
+ GetArraySizeFromParam(cx, info, param, methodIndex,
+ i, nativeParams, &array_count) &&
+ array_count) {
+
+ CleanupPointerArray(datum_type, array_count, pp);
+ }
+
+ // always release the array if it is inout
+ free(pp);
+ }
+ } else {
+ CleanupPointerTypeObject(type, static_cast<void**>(p));
+ }
+ }
+ *static_cast<void**>(p) = nullptr;
+ }
+}
+
+nsresult
+nsXPCWrappedJSClass::CheckForException(XPCCallContext & ccx,
+ AutoEntryScript& aes,
+ const char * aPropertyName,
+ const char * anInterfaceName,
+ nsIException* aSyntheticException)
+{
+ JSContext * cx = ccx.GetJSContext();
+ MOZ_ASSERT(cx == aes.cx());
+ nsCOMPtr<nsIException> xpc_exception = aSyntheticException;
+ /* this one would be set by our error reporter */
+
+ XPCJSContext* xpccx = XPCJSContext::Get();
+
+ // Get this right away in case we do something below to cause JS code
+ // to run.
+ nsresult pending_result = xpccx->GetPendingResult();
+
+ RootedValue js_exception(cx);
+ bool is_js_exception = JS_GetPendingException(cx, &js_exception);
+
+ /* JS might throw an expection whether the reporter was called or not */
+ if (is_js_exception) {
+ if (!xpc_exception)
+ XPCConvert::JSValToXPCException(&js_exception, anInterfaceName,
+ aPropertyName,
+ getter_AddRefs(xpc_exception));
+
+ /* cleanup and set failed even if we can't build an exception */
+ if (!xpc_exception) {
+ xpccx->SetPendingException(nullptr); // XXX necessary?
+ }
+ }
+
+ // Clear the pending exception now, because xpc_exception might be JS-
+ // implemented, so invoking methods on it might re-enter JS, which we can't
+ // do with an exception on the stack.
+ aes.ClearException();
+
+ if (xpc_exception) {
+ nsresult e_result;
+ if (NS_SUCCEEDED(xpc_exception->GetResult(&e_result))) {
+ // Figure out whether or not we should report this exception.
+ bool reportable = xpc_IsReportableErrorCode(e_result);
+ if (reportable) {
+ // Ugly special case for GetInterface. It's "special" in the
+ // same way as QueryInterface in that a failure is not
+ // exceptional and shouldn't be reported. We have to do this
+ // check here instead of in xpcwrappedjs (like we do for QI) to
+ // avoid adding extra code to all xpcwrappedjs objects.
+ if (e_result == NS_ERROR_NO_INTERFACE &&
+ !strcmp(anInterfaceName, "nsIInterfaceRequestor") &&
+ !strcmp(aPropertyName, "getInterface")) {
+ reportable = false;
+ }
+
+ // More special case, see bug 877760.
+ if (e_result == NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED) {
+ reportable = false;
+ }
+ }
+
+ // Try to use the error reporter set on the context to handle this
+ // error if it came from a JS exception.
+ if (reportable && is_js_exception)
+ {
+ // Note that we cleared the exception above, so we need to set it again,
+ // just so that we can tell the JS engine to pass it back to us via the
+ // error reporting callback. This is all very dumb.
+ JS_SetPendingException(cx, js_exception);
+ aes.ReportException();
+ reportable = false;
+ }
+
+ if (reportable) {
+ if (nsContentUtils::DOMWindowDumpEnabled()) {
+ static const char line[] =
+ "************************************************************\n";
+ static const char preamble[] =
+ "* Call to xpconnect wrapped JSObject produced this error: *\n";
+ static const char cant_get_text[] =
+ "FAILED TO GET TEXT FROM EXCEPTION\n";
+
+ fputs(line, stdout);
+ fputs(preamble, stdout);
+ nsCString text;
+ if (NS_SUCCEEDED(xpc_exception->ToString(cx, text)) &&
+ !text.IsEmpty()) {
+ fputs(text.get(), stdout);
+ fputs("\n", stdout);
+ } else
+ fputs(cant_get_text, stdout);
+ fputs(line, stdout);
+ }
+
+ // Log the exception to the JS Console, so that users can do
+ // something with it.
+ nsCOMPtr<nsIConsoleService> consoleService
+ (do_GetService(XPC_CONSOLE_CONTRACTID));
+ if (nullptr != consoleService) {
+ nsresult rv;
+ nsCOMPtr<nsIScriptError> scriptError;
+ nsCOMPtr<nsISupports> errorData;
+ rv = xpc_exception->GetData(getter_AddRefs(errorData));
+ if (NS_SUCCEEDED(rv))
+ scriptError = do_QueryInterface(errorData);
+
+ if (nullptr == scriptError) {
+ // No luck getting one from the exception, so
+ // try to cook one up.
+ scriptError = do_CreateInstance(XPC_SCRIPT_ERROR_CONTRACTID);
+ if (nullptr != scriptError) {
+ nsCString newMessage;
+ rv = xpc_exception->ToString(cx, newMessage);
+ if (NS_SUCCEEDED(rv)) {
+ // try to get filename, lineno from the first
+ // stack frame location.
+ int32_t lineNumber = 0;
+ nsString sourceName;
+
+ nsCOMPtr<nsIStackFrame> location;
+ xpc_exception->
+ GetLocation(getter_AddRefs(location));
+ if (location) {
+ // Get line number w/o checking; 0 is ok.
+ location->GetLineNumber(cx, &lineNumber);
+
+ // get a filename.
+ rv = location->GetFilename(cx, sourceName);
+ }
+
+ rv = scriptError->InitWithWindowID(NS_ConvertUTF8toUTF16(newMessage),
+ sourceName,
+ EmptyString(),
+ lineNumber, 0, 0,
+ "XPConnect JavaScript",
+ nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx));
+ if (NS_FAILED(rv))
+ scriptError = nullptr;
+ }
+ }
+ }
+ if (nullptr != scriptError)
+ consoleService->LogMessage(scriptError);
+ }
+ }
+ // Whether or not it passes the 'reportable' test, it might
+ // still be an error and we have to do the right thing here...
+ if (NS_FAILED(e_result)) {
+ xpccx->SetPendingException(xpc_exception);
+ return e_result;
+ }
+ }
+ } else {
+ // see if JS code signaled failure result without throwing exception
+ if (NS_FAILED(pending_result)) {
+ return pending_result;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsXPCWrappedJSClass::CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
+ const XPTMethodDescriptor* info_,
+ nsXPTCMiniVariant* nativeParams)
+{
+ Value* sp = nullptr;
+ Value* argv = nullptr;
+ uint8_t i;
+ nsresult retval = NS_ERROR_FAILURE;
+ bool success;
+ bool readyToDoTheCall = false;
+ nsID param_iid;
+ const nsXPTMethodInfo* info = static_cast<const nsXPTMethodInfo*>(info_);
+ const char* name = info->name;
+ bool foundDependentParam;
+
+ // Make sure not to set the callee on ccx until after we've gone through
+ // the whole nsIXPCFunctionThisTranslator bit. That code uses ccx to
+ // convert natives to JSObjects, but we do NOT plan to pass those JSObjects
+ // to our real callee.
+ //
+ // We're about to call into script via an XPCWrappedJS, so we need an
+ // AutoEntryScript. This is probably Gecko-specific at this point, and
+ // definitely will be when we turn off XPConnect for the web.
+ nsIGlobalObject* nativeGlobal =
+ NativeGlobal(js::GetGlobalForObjectCrossCompartment(wrapper->GetJSObject()));
+ AutoEntryScript aes(nativeGlobal, "XPCWrappedJS method call",
+ /* aIsMainThread = */ true);
+ XPCCallContext ccx(aes.cx());
+ if (!ccx.IsValid())
+ return retval;
+
+ JSContext* cx = ccx.GetJSContext();
+
+ if (!cx || !IsReflectable(methodIndex))
+ return NS_ERROR_FAILURE;
+
+ // [implicit_jscontext] and [optional_argc] have a different calling
+ // convention, which we don't support for JS-implemented components.
+ if (info->WantsOptArgc() || info->WantsContext()) {
+ const char* str = "IDL methods marked with [implicit_jscontext] "
+ "or [optional_argc] may not be implemented in JS";
+ // Throw and warn for good measure.
+ JS_ReportErrorASCII(cx, "%s", str);
+ NS_WARNING(str);
+ return CheckForException(ccx, aes, name, GetInterfaceName());
+ }
+
+ RootedValue fval(cx);
+ RootedObject obj(cx, wrapper->GetJSObject());
+ RootedObject thisObj(cx, obj);
+
+ JSAutoCompartment ac(cx, obj);
+
+ AutoValueVector args(cx);
+ AutoScriptEvaluate scriptEval(cx);
+
+ XPCJSContext* xpccx = XPCJSContext::Get();
+ AutoSavePendingResult apr(xpccx);
+
+ // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
+ uint8_t paramCount = info->num_args;
+ uint8_t argc = paramCount -
+ (paramCount && XPT_PD_IS_RETVAL(info->params[paramCount-1].flags) ? 1 : 0);
+
+ if (!scriptEval.StartEvaluating(obj))
+ goto pre_call_clean_up;
+
+ xpccx->SetPendingException(nullptr);
+
+ // We use js_Invoke so that the gcthings we use as args will be rooted by
+ // the engine as we do conversions and prepare to do the function call.
+
+ // setup stack
+
+ // if this isn't a function call then we don't need to push extra stuff
+ if (!(XPT_MD_IS_SETTER(info->flags) || XPT_MD_IS_GETTER(info->flags))) {
+ // We get fval before allocating the stack to avoid gc badness that can
+ // happen if the GetProperty call leaves our request and the gc runs
+ // while the stack we allocate contains garbage.
+
+ // If the interface is marked as a [function] then we will assume that
+ // our JSObject is a function and not an object with a named method.
+
+ bool isFunction;
+ if (NS_FAILED(mInfo->IsFunction(&isFunction)))
+ goto pre_call_clean_up;
+
+ // In the xpidl [function] case we are making sure now that the
+ // JSObject is callable. If it is *not* callable then we silently
+ // fallback to looking up the named property...
+ // (because jst says he thinks this fallback is 'The Right Thing'.)
+ //
+ // In the normal (non-function) case we just lookup the property by
+ // name and as long as the object has such a named property we go ahead
+ // and try to make the call. If it turns out the named property is not
+ // a callable object then the JS engine will throw an error and we'll
+ // pass this along to the caller as an exception/result code.
+
+ fval = ObjectValue(*obj);
+ if (isFunction &&
+ JS_TypeOfValue(ccx, fval) == JSTYPE_FUNCTION) {
+
+ // We may need to translate the 'this' for the function object.
+
+ if (paramCount) {
+ const nsXPTParamInfo& firstParam = info->params[0];
+ if (firstParam.IsIn()) {
+ const nsXPTType& firstType = firstParam.GetType();
+
+ if (firstType.IsInterfacePointer()) {
+ nsIXPCFunctionThisTranslator* translator;
+
+ IID2ThisTranslatorMap* map =
+ mContext->GetThisTranslatorMap();
+
+ translator = map->Find(mIID);
+
+ if (translator) {
+ nsCOMPtr<nsISupports> newThis;
+ if (NS_FAILED(translator->
+ TranslateThis((nsISupports*)nativeParams[0].val.p,
+ getter_AddRefs(newThis)))) {
+ goto pre_call_clean_up;
+ }
+ if (newThis) {
+ RootedValue v(cx);
+ xpcObjectHelper helper(newThis);
+ bool ok =
+ XPCConvert::NativeInterface2JSObject(
+ &v, nullptr, helper, nullptr,
+ false, nullptr);
+ if (!ok) {
+ goto pre_call_clean_up;
+ }
+ thisObj = v.toObjectOrNull();
+ if (!JS_WrapObject(cx, &thisObj))
+ goto pre_call_clean_up;
+ }
+ }
+ }
+ }
+ }
+ } else {
+ if (!JS_GetProperty(cx, obj, name, &fval))
+ goto pre_call_clean_up;
+ // XXX We really want to factor out the error reporting better and
+ // specifically report the failure to find a function with this name.
+ // This is what we do below if the property is found but is not a
+ // function. We just need to factor better so we can get to that
+ // reporting path from here.
+
+ thisObj = obj;
+ }
+ }
+
+ if (!args.resize(argc)) {
+ retval = NS_ERROR_OUT_OF_MEMORY;
+ goto pre_call_clean_up;
+ }
+
+ argv = args.begin();
+ sp = argv;
+
+ // build the args
+ // NB: This assignment *looks* wrong because we haven't yet called our
+ // function. However, we *have* already entered the compartmen that we're
+ // about to call, and that's the global that we want here. In other words:
+ // we're trusting the JS engine to come up with a good global to use for
+ // our object (whatever it was).
+ for (i = 0; i < argc; i++) {
+ const nsXPTParamInfo& param = info->params[i];
+ const nsXPTType& type = param.GetType();
+ nsXPTType datum_type;
+ uint32_t array_count;
+ bool isArray = type.IsArray();
+ RootedValue val(cx, NullValue());
+ bool isSizedString = isArray ?
+ false :
+ type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
+ type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
+
+
+ // verify that null was not passed for 'out' param
+ if (param.IsOut() && !nativeParams[i].val.p) {
+ retval = NS_ERROR_INVALID_ARG;
+ goto pre_call_clean_up;
+ }
+
+ if (isArray) {
+ if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, &param, 1,
+ &datum_type)))
+ goto pre_call_clean_up;
+ } else
+ datum_type = type;
+
+ if (param.IsIn()) {
+ nsXPTCMiniVariant* pv;
+
+ if (param.IsIndirect())
+ pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
+ else
+ pv = &nativeParams[i];
+
+ if (datum_type.IsInterfacePointer() &&
+ !GetInterfaceTypeFromParam(cx, info, param, methodIndex,
+ datum_type, nativeParams,
+ &param_iid))
+ goto pre_call_clean_up;
+
+ if (isArray || isSizedString) {
+ if (!GetArraySizeFromParam(cx, info, param, methodIndex,
+ i, nativeParams, &array_count))
+ goto pre_call_clean_up;
+ }
+
+ if (isArray) {
+ if (!XPCConvert::NativeArray2JS(&val,
+ (const void**)&pv->val,
+ datum_type, &param_iid,
+ array_count, nullptr))
+ goto pre_call_clean_up;
+ } else if (isSizedString) {
+ if (!XPCConvert::NativeStringWithSize2JS(&val,
+ (const void*)&pv->val,
+ datum_type,
+ array_count, nullptr))
+ goto pre_call_clean_up;
+ } else {
+ if (!XPCConvert::NativeData2JS(&val, &pv->val, type,
+ &param_iid, nullptr))
+ goto pre_call_clean_up;
+ }
+ }
+
+ if (param.IsOut() || param.IsDipper()) {
+ // create an 'out' object
+ RootedObject out_obj(cx, NewOutObject(cx));
+ if (!out_obj) {
+ retval = NS_ERROR_OUT_OF_MEMORY;
+ goto pre_call_clean_up;
+ }
+
+ if (param.IsIn()) {
+ if (!JS_SetPropertyById(cx, out_obj,
+ mContext->GetStringID(XPCJSContext::IDX_VALUE),
+ val)) {
+ goto pre_call_clean_up;
+ }
+ }
+ *sp++ = JS::ObjectValue(*out_obj);
+ } else
+ *sp++ = val;
+ }
+
+ readyToDoTheCall = true;
+
+pre_call_clean_up:
+ // clean up any 'out' params handed in
+ CleanupOutparams(cx, methodIndex, info, nativeParams, /* inOutOnly = */ true, paramCount);
+
+ // Make sure "this" doesn't get deleted during this call.
+ nsCOMPtr<nsIXPCWrappedJSClass> kungFuDeathGrip(this);
+
+ if (!readyToDoTheCall)
+ return retval;
+
+ // do the deed - note exceptions
+
+ MOZ_ASSERT(!aes.HasException());
+
+ nsCOMPtr<nsIException> syntheticException;
+ RootedValue rval(cx);
+ if (XPT_MD_IS_GETTER(info->flags)) {
+ success = JS_GetProperty(cx, obj, name, &rval);
+ } else if (XPT_MD_IS_SETTER(info->flags)) {
+ rval = *argv;
+ success = JS_SetProperty(cx, obj, name, rval);
+ } else {
+ if (!fval.isPrimitive()) {
+ success = JS_CallFunctionValue(cx, thisObj, fval, args, &rval);
+ } else {
+ // The property was not an object so can't be a function.
+ // Let's build and 'throw' an exception.
+
+ static const nsresult code =
+ NS_ERROR_XPC_JSOBJECT_HAS_NO_FUNCTION_NAMED;
+ static const char format[] = "%s \"%s\"";
+ const char * msg;
+ char* sz = nullptr;
+
+ if (nsXPCException::NameAndFormatForNSResult(code, nullptr, &msg) && msg)
+ sz = JS_smprintf(format, msg, name);
+
+ XPCConvert::ConstructException(code, sz, GetInterfaceName(), name,
+ nullptr, getter_AddRefs(syntheticException),
+ nullptr, nullptr);
+ if (sz)
+ JS_smprintf_free(sz);
+ success = false;
+ }
+ }
+
+ if (!success)
+ return CheckForException(ccx, aes, name, GetInterfaceName(),
+ syntheticException);
+
+ XPCJSContext::Get()->SetPendingException(nullptr); // XXX necessary?
+
+ // convert out args and result
+ // NOTE: this is the total number of native params, not just the args
+ // Convert independent params only.
+ // When we later convert the dependent params (if any) we will know that
+ // the params upon which they depend will have already been converted -
+ // regardless of ordering.
+
+ foundDependentParam = false;
+ for (i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& param = info->params[i];
+ MOZ_ASSERT(!param.IsShared(), "[shared] implies [noscript]!");
+ if (!param.IsOut() && !param.IsDipper())
+ continue;
+
+ const nsXPTType& type = param.GetType();
+ if (type.IsDependent()) {
+ foundDependentParam = true;
+ continue;
+ }
+
+ RootedValue val(cx);
+ uint8_t type_tag = type.TagPart();
+ nsXPTCMiniVariant* pv;
+
+ if (param.IsDipper())
+ pv = (nsXPTCMiniVariant*) &nativeParams[i].val.p;
+ else
+ pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
+
+ if (param.IsRetval())
+ val = rval;
+ else if (argv[i].isPrimitive())
+ break;
+ else {
+ RootedObject obj(cx, &argv[i].toObject());
+ if (!JS_GetPropertyById(cx, obj,
+ mContext->GetStringID(XPCJSContext::IDX_VALUE),
+ &val))
+ break;
+ }
+
+ // setup allocator and/or iid
+
+ if (type_tag == nsXPTType::T_INTERFACE) {
+ if (NS_FAILED(GetInterfaceInfo()->
+ GetIIDForParamNoAlloc(methodIndex, &param,
+ &param_iid)))
+ break;
+ }
+
+ if (!XPCConvert::JSData2Native(&pv->val, val, type,
+ &param_iid, nullptr))
+ break;
+ }
+
+ // if any params were dependent, then we must iterate again to convert them.
+ if (foundDependentParam && i == paramCount) {
+ for (i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& param = info->params[i];
+ if (!param.IsOut())
+ continue;
+
+ const nsXPTType& type = param.GetType();
+ if (!type.IsDependent())
+ continue;
+
+ RootedValue val(cx);
+ nsXPTCMiniVariant* pv;
+ nsXPTType datum_type;
+ uint32_t array_count;
+ bool isArray = type.IsArray();
+ bool isSizedString = isArray ?
+ false :
+ type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
+ type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
+
+ pv = (nsXPTCMiniVariant*) nativeParams[i].val.p;
+
+ if (param.IsRetval())
+ val = rval;
+ else {
+ RootedObject obj(cx, &argv[i].toObject());
+ if (!JS_GetPropertyById(cx, obj,
+ mContext->GetStringID(XPCJSContext::IDX_VALUE),
+ &val))
+ break;
+ }
+
+ // setup allocator and/or iid
+
+ if (isArray) {
+ if (NS_FAILED(mInfo->GetTypeForParam(methodIndex, &param, 1,
+ &datum_type)))
+ break;
+ } else
+ datum_type = type;
+
+ if (datum_type.IsInterfacePointer()) {
+ if (!GetInterfaceTypeFromParam(cx, info, param, methodIndex,
+ datum_type, nativeParams,
+ &param_iid))
+ break;
+ }
+
+ if (isArray || isSizedString) {
+ if (!GetArraySizeFromParam(cx, info, param, methodIndex,
+ i, nativeParams, &array_count))
+ break;
+ }
+
+ if (isArray) {
+ if (array_count &&
+ !XPCConvert::JSArray2Native((void**)&pv->val, val,
+ array_count, datum_type,
+ &param_iid, nullptr))
+ break;
+ } else if (isSizedString) {
+ if (!XPCConvert::JSStringWithSize2Native((void*)&pv->val, val,
+ array_count, datum_type,
+ nullptr))
+ break;
+ } else {
+ if (!XPCConvert::JSData2Native(&pv->val, val, type,
+ &param_iid,
+ nullptr))
+ break;
+ }
+ }
+ }
+
+ if (i != paramCount) {
+ // We didn't manage all the result conversions!
+ // We have to cleanup any junk that *did* get converted.
+ CleanupOutparams(cx, methodIndex, info, nativeParams, /* inOutOnly = */ false, i);
+ } else {
+ // set to whatever the JS code might have set as the result
+ retval = xpccx->GetPendingResult();
+ }
+
+ return retval;
+}
+
+const char*
+nsXPCWrappedJSClass::GetInterfaceName()
+{
+ if (!mName)
+ mInfo->GetName(&mName);
+ return mName;
+}
+
+static const JSClass XPCOutParamClass = {
+ "XPCOutParam",
+ 0,
+ JS_NULL_CLASS_OPS
+};
+
+bool
+xpc::IsOutObject(JSContext* cx, JSObject* obj)
+{
+ return js::GetObjectJSClass(obj) == &XPCOutParamClass;
+}
+
+JSObject*
+xpc::NewOutObject(JSContext* cx)
+{
+ return JS_NewObject(cx, &XPCOutParamClass);
+}
+
+
+NS_IMETHODIMP
+nsXPCWrappedJSClass::DebugDump(int16_t depth)
+{
+#ifdef DEBUG
+ depth-- ;
+ XPC_LOG_ALWAYS(("nsXPCWrappedJSClass @ %x with mRefCnt = %d", this, mRefCnt.get()));
+ XPC_LOG_INDENT();
+ char* name;
+ mInfo->GetName(&name);
+ XPC_LOG_ALWAYS(("interface name is %s", name));
+ if (name)
+ free(name);
+ char * iid = mIID.ToString();
+ XPC_LOG_ALWAYS(("IID number is %s", iid ? iid : "invalid"));
+ if (iid)
+ free(iid);
+ XPC_LOG_ALWAYS(("InterfaceInfo @ %x", mInfo.get()));
+ uint16_t methodCount = 0;
+ if (depth) {
+ uint16_t i;
+ nsCOMPtr<nsIInterfaceInfo> parent;
+ XPC_LOG_INDENT();
+ mInfo->GetParent(getter_AddRefs(parent));
+ XPC_LOG_ALWAYS(("parent @ %x", parent.get()));
+ mInfo->GetMethodCount(&methodCount);
+ XPC_LOG_ALWAYS(("MethodCount = %d", methodCount));
+ mInfo->GetConstantCount(&i);
+ XPC_LOG_ALWAYS(("ConstantCount = %d", i));
+ XPC_LOG_OUTDENT();
+ }
+ XPC_LOG_ALWAYS(("mContext @ %x", mContext));
+ XPC_LOG_ALWAYS(("mDescriptors @ %x count = %d", mDescriptors, methodCount));
+ if (depth && mDescriptors && methodCount) {
+ depth--;
+ XPC_LOG_INDENT();
+ for (uint16_t i = 0; i < methodCount; i++) {
+ XPC_LOG_ALWAYS(("Method %d is %s%s", \
+ i, IsReflectable(i) ? "":" NOT ","reflectable"));
+ }
+ XPC_LOG_OUTDENT();
+ depth++;
+ }
+ XPC_LOG_OUTDENT();
+#endif
+ return NS_OK;
+}
diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp
new file mode 100644
index 000000000..acf92f3c3
--- /dev/null
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -0,0 +1,2325 @@
+/* -*- 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/. */
+
+/* Wrapper object for reflecting native xpcom objects into JavaScript. */
+
+#include "xpcprivate.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "nsWrapperCacheInlines.h"
+#include "XPCLog.h"
+#include "jsprf.h"
+#include "jsfriendapi.h"
+#include "AccessCheck.h"
+#include "WrapperFactory.h"
+#include "XrayWrapper.h"
+
+#include "nsContentUtils.h"
+#include "nsCycleCollectionNoteRootCallback.h"
+
+#include <stdint.h>
+#include "mozilla/DeferredFinalize.h"
+#include "mozilla/Likely.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/dom/BindingUtils.h"
+#include <algorithm>
+
+using namespace xpc;
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace JS;
+
+/***************************************************************************/
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
+
+// No need to unlink the JS objects: if the XPCWrappedNative is cycle
+// collected then its mFlatJSObject will be cycle collected too and
+// finalization of the mFlatJSObject will unlink the JS objects (see
+// XPC_WN_NoHelper_Finalize and FlatJSObjectFinalized).
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPCWrappedNative)
+ tmp->ExpireWrapper();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(XPCWrappedNative)
+ if (!tmp->IsValid())
+ return NS_OK;
+
+ if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
+ char name[72];
+ XPCNativeScriptableInfo* si = tmp->GetScriptableInfo();
+ if (si)
+ SprintfLiteral(name, "XPCWrappedNative (%s)", si->GetJSClass()->name);
+ else
+ SprintfLiteral(name, "XPCWrappedNative");
+
+ cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
+ } else {
+ NS_IMPL_CYCLE_COLLECTION_DESCRIBE(XPCWrappedNative, tmp->mRefCnt.get())
+ }
+
+ if (tmp->HasExternalReference()) {
+
+ // If our refcount is > 1, our reference to the flat JS object is
+ // considered "strong", and we're going to traverse it.
+ //
+ // If our refcount is <= 1, our reference to the flat JS object is
+ // considered "weak", and we're *not* going to traverse it.
+ //
+ // This reasoning is in line with the slightly confusing lifecycle rules
+ // for XPCWrappedNatives, described in a larger comment below and also
+ // on our wiki at http://wiki.mozilla.org/XPConnect_object_wrapping
+
+ JSObject* obj = tmp->GetFlatJSObjectPreserveColor();
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFlatJSObject");
+ cb.NoteJSChild(JS::GCCellPtr(obj));
+ }
+
+ // XPCWrappedNative keeps its native object alive.
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mIdentity");
+ cb.NoteXPCOMChild(tmp->GetIdentityObject());
+
+ tmp->NoteTearoffs(cb);
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+void
+XPCWrappedNative::Suspect(nsCycleCollectionNoteRootCallback& cb)
+{
+ if (!IsValid() || IsWrapperExpired())
+ return;
+
+ MOZ_ASSERT(NS_IsMainThread(),
+ "Suspecting wrapped natives from non-main thread");
+
+ // Only record objects that might be part of a cycle as roots, unless
+ // the callback wants all traces (a debug feature). Do this even if
+ // the XPCWN doesn't own the JS reflector object in case the reflector
+ // keeps alive other C++ things. This is safe because if the reflector
+ // had died the reference from the XPCWN to it would have been cleared.
+ JSObject* obj = GetFlatJSObjectPreserveColor();
+ if (JS::ObjectIsMarkedGray(obj) || cb.WantAllTraces())
+ cb.NoteJSRoot(obj);
+}
+
+void
+XPCWrappedNative::NoteTearoffs(nsCycleCollectionTraversalCallback& cb)
+{
+ // Tearoffs hold their native object alive. If their JS object hasn't been
+ // finalized yet we'll note the edge between the JS object and the native
+ // (see nsXPConnect::Traverse), but if their JS object has been finalized
+ // then the tearoff is only reachable through the XPCWrappedNative, so we
+ // record an edge here.
+ for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
+ JSObject* jso = to->GetJSObjectPreserveColor();
+ if (!jso) {
+ NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "tearoff's mNative");
+ cb.NoteXPCOMChild(to->GetNative());
+ }
+ }
+}
+
+#ifdef XPC_CHECK_CLASSINFO_CLAIMS
+static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper);
+#else
+#define DEBUG_CheckClassInfoClaims(wrapper) ((void)0)
+#endif
+
+/***************************************************************************/
+static nsresult
+FinishCreate(XPCWrappedNativeScope* Scope,
+ XPCNativeInterface* Interface,
+ nsWrapperCache* cache,
+ XPCWrappedNative* inWrapper,
+ XPCWrappedNative** resultWrapper);
+
+// static
+//
+// This method handles the special case of wrapping a new global object.
+//
+// The normal code path for wrapping natives goes through
+// XPCConvert::NativeInterface2JSObject, XPCWrappedNative::GetNewOrUsed,
+// and finally into XPCWrappedNative::Init. Unfortunately, this path assumes
+// very early on that we have an XPCWrappedNativeScope and corresponding global
+// JS object, which are the very things we need to create here. So we special-
+// case the logic and do some things in a different order.
+nsresult
+XPCWrappedNative::WrapNewGlobal(xpcObjectHelper& nativeHelper,
+ nsIPrincipal* principal,
+ bool initStandardClasses,
+ JS::CompartmentOptions& aOptions,
+ XPCWrappedNative** wrappedGlobal)
+{
+ AutoJSContext cx;
+ nsISupports* identity = nativeHelper.GetCanonical();
+
+ // The object should specify that it's meant to be global.
+ MOZ_ASSERT(nativeHelper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
+
+ // We shouldn't be reusing globals.
+ MOZ_ASSERT(!nativeHelper.GetWrapperCache() ||
+ !nativeHelper.GetWrapperCache()->GetWrapperPreserveColor());
+
+ // Put together the ScriptableCreateInfo...
+ XPCNativeScriptableCreateInfo sciProto;
+ XPCNativeScriptableCreateInfo sciMaybe;
+ const XPCNativeScriptableCreateInfo& sciWrapper =
+ GatherScriptableCreateInfo(identity, nativeHelper.GetClassInfo(),
+ sciProto, sciMaybe);
+
+ // ...and then ScriptableInfo. We need all this stuff now because it's going
+ // to tell us the JSClass of the object we're going to create.
+ XPCNativeScriptableInfo* si = XPCNativeScriptableInfo::Construct(&sciWrapper);
+ MOZ_ASSERT(si);
+
+ // Finally, we get to the JSClass.
+ const JSClass* clasp = si->GetJSClass();
+ MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
+
+ // Create the global.
+ aOptions.creationOptions().setTrace(XPCWrappedNative::Trace);
+ if (xpc::SharedMemoryEnabled())
+ aOptions.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
+ RootedObject global(cx, xpc::CreateGlobalObject(cx, clasp, principal, aOptions));
+ if (!global)
+ return NS_ERROR_FAILURE;
+ XPCWrappedNativeScope* scope = CompartmentPrivate::Get(global)->scope;
+
+ // Immediately enter the global's compartment, so that everything else we
+ // create ends up there.
+ JSAutoCompartment ac(cx, global);
+
+ // If requested, initialize the standard classes on the global.
+ if (initStandardClasses && ! JS_InitStandardClasses(cx, global))
+ return NS_ERROR_FAILURE;
+
+ // Make a proto.
+ XPCWrappedNativeProto* proto =
+ XPCWrappedNativeProto::GetNewOrUsed(scope,
+ nativeHelper.GetClassInfo(), &sciProto,
+ /* callPostCreatePrototype = */ false);
+ if (!proto)
+ return NS_ERROR_FAILURE;
+
+ // Set up the prototype on the global.
+ MOZ_ASSERT(proto->GetJSProtoObject());
+ RootedObject protoObj(cx, proto->GetJSProtoObject());
+ bool success = JS_SplicePrototype(cx, global, protoObj);
+ if (!success)
+ return NS_ERROR_FAILURE;
+
+ // Construct the wrapper, which takes over the strong reference to the
+ // native object.
+ RefPtr<XPCWrappedNative> wrapper =
+ new XPCWrappedNative(nativeHelper.forgetCanonical(), proto);
+
+ //
+ // We don't call ::Init() on this wrapper, because our setup requirements
+ // are different for globals. We do our setup inline here, instead.
+ //
+
+ // Share mScriptableInfo with the proto.
+ //
+ // This is probably more trouble than it's worth, since we've already
+ // created an XPCNativeScriptableInfo for ourselves. Nevertheless, this is
+ // what ::Init() does, and we want to be as consistent as possible with
+ // that code.
+ XPCNativeScriptableInfo* siProto = proto->GetScriptableInfo();
+ if (siProto && siProto->GetCallback() == sciWrapper.GetCallback()) {
+ wrapper->mScriptableInfo = siProto;
+ // XPCNativeScriptableInfo uses manual memory management. If we're
+ // switching over to that of the proto, we need to destroy the one
+ // we've allocated.
+ delete si;
+ si = nullptr;
+ } else {
+ wrapper->mScriptableInfo = si;
+ }
+
+ // Set the JS object to the global we already created.
+ wrapper->mFlatJSObject = global;
+ wrapper->mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
+
+ // Set the private to the XPCWrappedNative.
+ JS_SetPrivate(global, wrapper);
+
+ // There are dire comments elsewhere in the code about how a GC can
+ // happen somewhere after wrapper initialization but before the wrapper is
+ // added to the hashtable in FinishCreate(). It's not clear if that can
+ // happen here, but let's just be safe for now.
+ AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper);
+
+ // Call the common Init finish routine. This mainly just does an AddRef
+ // on behalf of XPConnect (the corresponding Release is in the finalizer
+ // hook), but it does some other miscellaneous things too, so we don't
+ // inline it.
+ success = wrapper->FinishInit();
+ MOZ_ASSERT(success);
+
+ // Go through some extra work to find the tearoff. This is kind of silly
+ // on a conceptual level: the point of tearoffs is to cache the results
+ // of QI-ing mIdentity to different interfaces, and we don't need that
+ // since we're dealing with nsISupports. But lots of code expects tearoffs
+ // to exist for everything, so we just follow along.
+ RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(&NS_GET_IID(nsISupports));
+ MOZ_ASSERT(iface);
+ nsresult status;
+ success = wrapper->FindTearOff(iface, false, &status);
+ if (!success)
+ return status;
+
+ // Call the common creation finish routine. This does all of the bookkeeping
+ // like inserting the wrapper into the wrapper map and setting up the wrapper
+ // cache.
+ nsresult rv = FinishCreate(scope, iface, nativeHelper.GetWrapperCache(),
+ wrapper, wrappedGlobal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+// static
+nsresult
+XPCWrappedNative::GetNewOrUsed(xpcObjectHelper& helper,
+ XPCWrappedNativeScope* Scope,
+ XPCNativeInterface* Interface,
+ XPCWrappedNative** resultWrapper)
+{
+ MOZ_ASSERT(Interface);
+ AutoJSContext cx;
+ nsWrapperCache* cache = helper.GetWrapperCache();
+
+ MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor(),
+ "We assume the caller already checked if it could get the "
+ "wrapper from the cache.");
+
+ nsresult rv;
+
+ MOZ_ASSERT(!Scope->GetContext()->GCIsRunning(),
+ "XPCWrappedNative::GetNewOrUsed called during GC");
+
+ nsISupports* identity = helper.GetCanonical();
+
+ if (!identity) {
+ NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<XPCWrappedNative> wrapper;
+
+ Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
+ // Some things are nsWrapperCache subclasses but never use the cache, so go
+ // ahead and check our map even if we have a cache and it has no existing
+ // wrapper: we might have an XPCWrappedNative anyway.
+ wrapper = map->Find(identity);
+
+ if (wrapper) {
+ if (!wrapper->FindTearOff(Interface, false, &rv)) {
+ MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
+ return rv;
+ }
+ wrapper.forget(resultWrapper);
+ return NS_OK;
+ }
+
+ // There is a chance that the object wants to have the self-same JSObject
+ // reflection regardless of the scope into which we are reflecting it.
+ // Many DOM objects require this. The scriptable helper specifies this
+ // in preCreate by indicating a 'parent' of a particular scope.
+ //
+ // To handle this we need to get the scriptable helper early and ask it.
+ // It is possible that we will then end up forwarding this entire call
+ // to this same function but with a different scope.
+
+ // If we are making a wrapper for an nsIClassInfo singleton then
+ // We *don't* want to have it use the prototype meant for instances
+ // of that class.
+ uint32_t classInfoFlags;
+ bool isClassInfoSingleton = helper.GetClassInfo() == helper.Object() &&
+ NS_SUCCEEDED(helper.GetClassInfo()
+ ->GetFlags(&classInfoFlags)) &&
+ (classInfoFlags & nsIClassInfo::SINGLETON_CLASSINFO);
+
+ nsIClassInfo* info = helper.GetClassInfo();
+
+ XPCNativeScriptableCreateInfo sciProto;
+ XPCNativeScriptableCreateInfo sci;
+
+ // Gather scriptable create info if we are wrapping something
+ // other than an nsIClassInfo object. We need to not do this for
+ // nsIClassInfo objects because often nsIClassInfo implementations
+ // are also nsIXPCScriptable helper implementations, but the helper
+ // code is obviously intended for the implementation of the class
+ // described by the nsIClassInfo, not for the class info object
+ // itself.
+ const XPCNativeScriptableCreateInfo& sciWrapper =
+ isClassInfoSingleton ? sci :
+ GatherScriptableCreateInfo(identity, info, sciProto, sci);
+
+ RootedObject parent(cx, Scope->GetGlobalJSObject());
+
+ mozilla::Maybe<JSAutoCompartment> ac;
+
+ if (sciWrapper.GetFlags().WantPreCreate()) {
+ RootedObject plannedParent(cx, parent);
+ nsresult rv = sciWrapper.GetCallback()->PreCreate(identity, cx,
+ parent, parent.address());
+ if (NS_FAILED(rv))
+ return rv;
+ rv = NS_OK;
+
+ MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
+ "Xray wrapper being used to parent XPCWrappedNative?");
+
+ MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(parent) == parent,
+ "Non-global being used to parent XPCWrappedNative?");
+
+ ac.emplace(static_cast<JSContext*>(cx), parent);
+
+ if (parent != plannedParent) {
+ XPCWrappedNativeScope* betterScope = ObjectScope(parent);
+ MOZ_ASSERT(betterScope != Scope,
+ "How can we have the same scope for two different globals?");
+ return GetNewOrUsed(helper, betterScope, Interface, resultWrapper);
+ }
+
+ // Take the performance hit of checking the hashtable again in case
+ // the preCreate call caused the wrapper to get created through some
+ // interesting path (the DOM code tends to make this happen sometimes).
+
+ if (cache) {
+ RootedObject cached(cx, cache->GetWrapper());
+ if (cached)
+ wrapper = XPCWrappedNative::Get(cached);
+ } else {
+ wrapper = map->Find(identity);
+ }
+
+ if (wrapper) {
+ if (wrapper->FindTearOff(Interface, false, &rv)) {
+ MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
+ return rv;
+ }
+ wrapper.forget(resultWrapper);
+ return NS_OK;
+ }
+ } else {
+ ac.emplace(static_cast<JSContext*>(cx), parent);
+ }
+
+ AutoMarkingWrappedNativeProtoPtr proto(cx);
+
+ // If there is ClassInfo (and we are not building a wrapper for the
+ // nsIClassInfo interface) then we use a wrapper that needs a prototype.
+
+ // Note that the security check happens inside FindTearOff - after the
+ // wrapper is actually created, but before JS code can see it.
+
+ if (info && !isClassInfoSingleton) {
+ proto = XPCWrappedNativeProto::GetNewOrUsed(Scope, info, &sciProto);
+ if (!proto)
+ return NS_ERROR_FAILURE;
+
+ wrapper = new XPCWrappedNative(helper.forgetCanonical(), proto);
+ } else {
+ RefPtr<XPCNativeInterface> iface = Interface;
+ if (!iface)
+ iface = XPCNativeInterface::GetISupports();
+
+ XPCNativeSetKey key(iface);
+ RefPtr<XPCNativeSet> set =
+ XPCNativeSet::GetNewOrUsed(&key);
+
+ if (!set)
+ return NS_ERROR_FAILURE;
+
+ wrapper = new XPCWrappedNative(helper.forgetCanonical(), Scope,
+ set.forget());
+ }
+
+ MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(parent),
+ "Xray wrapper being used to parent XPCWrappedNative?");
+
+ // We use an AutoMarkingPtr here because it is possible for JS gc to happen
+ // after we have Init'd the wrapper but *before* we add it to the hashtable.
+ // This would cause the mSet to get collected and we'd later crash. I've
+ // *seen* this happen.
+ AutoMarkingWrappedNativePtr wrapperMarker(cx, wrapper);
+
+ if (!wrapper->Init(&sciWrapper))
+ return NS_ERROR_FAILURE;
+
+ if (!wrapper->FindTearOff(Interface, false, &rv)) {
+ MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
+ return rv;
+ }
+
+ return FinishCreate(Scope, Interface, cache, wrapper, resultWrapper);
+}
+
+static nsresult
+FinishCreate(XPCWrappedNativeScope* Scope,
+ XPCNativeInterface* Interface,
+ nsWrapperCache* cache,
+ XPCWrappedNative* inWrapper,
+ XPCWrappedNative** resultWrapper)
+{
+ AutoJSContext cx;
+ MOZ_ASSERT(inWrapper);
+
+ Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
+
+ RefPtr<XPCWrappedNative> wrapper;
+ // Deal with the case where the wrapper got created as a side effect
+ // of one of our calls out of this code. Add() returns the (possibly
+ // pre-existing) wrapper that ultimately ends up in the map, which is
+ // what we want.
+ wrapper = map->Add(inWrapper);
+ if (!wrapper)
+ return NS_ERROR_FAILURE;
+
+ if (wrapper == inWrapper) {
+ JSObject* flat = wrapper->GetFlatJSObject();
+ MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor() ||
+ flat == cache->GetWrapperPreserveColor(),
+ "This object has a cached wrapper that's different from "
+ "the JSObject held by its native wrapper?");
+
+ if (cache && !cache->GetWrapperPreserveColor())
+ cache->SetWrapper(flat);
+ }
+
+ DEBUG_CheckClassInfoClaims(wrapper);
+ wrapper.forget(resultWrapper);
+ return NS_OK;
+}
+
+// static
+nsresult
+XPCWrappedNative::GetUsedOnly(nsISupports* Object,
+ XPCWrappedNativeScope* Scope,
+ XPCNativeInterface* Interface,
+ XPCWrappedNative** resultWrapper)
+{
+ AutoJSContext cx;
+ MOZ_ASSERT(Object, "XPCWrappedNative::GetUsedOnly was called with a null Object");
+ MOZ_ASSERT(Interface);
+
+ RefPtr<XPCWrappedNative> wrapper;
+ nsWrapperCache* cache = nullptr;
+ CallQueryInterface(Object, &cache);
+ if (cache) {
+ RootedObject flat(cx, cache->GetWrapper());
+ if (!flat) {
+ *resultWrapper = nullptr;
+ return NS_OK;
+ }
+ wrapper = XPCWrappedNative::Get(flat);
+ } else {
+ nsCOMPtr<nsISupports> identity = do_QueryInterface(Object);
+
+ if (!identity) {
+ NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
+ return NS_ERROR_FAILURE;
+ }
+
+ Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
+
+ wrapper = map->Find(identity);
+ if (!wrapper) {
+ *resultWrapper = nullptr;
+ return NS_OK;
+ }
+ }
+
+ nsresult rv;
+ if (!wrapper->FindTearOff(Interface, false, &rv)) {
+ MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
+ return rv;
+ }
+
+ wrapper.forget(resultWrapper);
+ return NS_OK;
+}
+
+// This ctor is used if this object will have a proto.
+XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
+ XPCWrappedNativeProto* aProto)
+ : mMaybeProto(aProto),
+ mSet(aProto->GetSet()),
+ mScriptableInfo(nullptr)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mIdentity = aIdentity;
+ mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
+
+ MOZ_ASSERT(mMaybeProto, "bad ctor param");
+ MOZ_ASSERT(mSet, "bad ctor param");
+}
+
+// This ctor is used if this object will NOT have a proto.
+XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
+ XPCWrappedNativeScope* aScope,
+ already_AddRefed<XPCNativeSet>&& aSet)
+
+ : mMaybeScope(TagScope(aScope)),
+ mSet(aSet),
+ mScriptableInfo(nullptr)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mIdentity = aIdentity;
+ mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
+
+ MOZ_ASSERT(aScope, "bad ctor param");
+ MOZ_ASSERT(mSet, "bad ctor param");
+}
+
+XPCWrappedNative::~XPCWrappedNative()
+{
+ Destroy();
+}
+
+void
+XPCWrappedNative::Destroy()
+{
+ XPCWrappedNativeProto* proto = GetProto();
+
+ if (mScriptableInfo &&
+ (!HasProto() ||
+ (proto && proto->GetScriptableInfo() != mScriptableInfo))) {
+ delete mScriptableInfo;
+ mScriptableInfo = nullptr;
+ }
+
+ XPCWrappedNativeScope* scope = GetScope();
+ if (scope) {
+ Native2WrappedNativeMap* map = scope->GetWrappedNativeMap();
+
+ // Post-1.9 we should not remove this wrapper from the map if it is
+ // uninitialized.
+ map->Remove(this);
+ }
+
+ if (mIdentity) {
+ XPCJSContext* cx = GetContext();
+ if (cx && cx->GetDoingFinalization()) {
+ DeferredFinalize(mIdentity.forget().take());
+ } else {
+ mIdentity = nullptr;
+ }
+ }
+
+ mMaybeScope = nullptr;
+}
+
+void
+XPCWrappedNative::UpdateScriptableInfo(XPCNativeScriptableInfo* si)
+{
+ MOZ_ASSERT(mScriptableInfo, "UpdateScriptableInfo expects an existing scriptable info");
+ mScriptableInfo = si;
+}
+
+void
+XPCWrappedNative::SetProto(XPCWrappedNativeProto* p)
+{
+ MOZ_ASSERT(!IsWrapperExpired(), "bad ptr!");
+
+ MOZ_ASSERT(HasProto());
+
+ // Write barrier for incremental GC.
+ JSContext* cx = GetContext()->Context();
+ GetProto()->WriteBarrierPre(cx);
+
+ mMaybeProto = p;
+}
+
+// This is factored out so that it can be called publicly
+// static
+void
+XPCWrappedNative::GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo,
+ XPCNativeScriptableCreateInfo& sciProto)
+{
+ MOZ_ASSERT(classInfo, "bad param");
+ MOZ_ASSERT(!sciProto.GetCallback(), "bad param");
+
+ nsXPCClassInfo* classInfoHelper = nullptr;
+ CallQueryInterface(classInfo, &classInfoHelper);
+ if (classInfoHelper) {
+ nsCOMPtr<nsIXPCScriptable> helper =
+ dont_AddRef(static_cast<nsIXPCScriptable*>(classInfoHelper));
+ uint32_t flags = classInfoHelper->GetScriptableFlags();
+ sciProto.SetCallback(helper.forget());
+ sciProto.SetFlags(XPCNativeScriptableFlags(flags));
+
+ return;
+ }
+
+ nsCOMPtr<nsIXPCScriptable> helper;
+ nsresult rv = classInfo->GetScriptableHelper(getter_AddRefs(helper));
+ if (NS_SUCCEEDED(rv) && helper) {
+ uint32_t flags = helper->GetScriptableFlags();
+ sciProto.SetCallback(helper.forget());
+ sciProto.SetFlags(XPCNativeScriptableFlags(flags));
+ }
+}
+
+// static
+const XPCNativeScriptableCreateInfo&
+XPCWrappedNative::GatherScriptableCreateInfo(nsISupports* obj,
+ nsIClassInfo* classInfo,
+ XPCNativeScriptableCreateInfo& sciProto,
+ XPCNativeScriptableCreateInfo& sciWrapper)
+{
+ MOZ_ASSERT(!sciWrapper.GetCallback(), "bad param");
+
+ // Get the class scriptable helper (if present)
+ if (classInfo) {
+ GatherProtoScriptableCreateInfo(classInfo, sciProto);
+
+ if (sciProto.GetFlags().DontAskInstanceForScriptable())
+ return sciProto;
+ }
+
+ // Do the same for the wrapper specific scriptable
+ nsCOMPtr<nsIXPCScriptable> helper(do_QueryInterface(obj));
+ if (helper) {
+ uint32_t flags = helper->GetScriptableFlags();
+ sciWrapper.SetCallback(helper.forget());
+ sciWrapper.SetFlags(XPCNativeScriptableFlags(flags));
+
+ // A whole series of assertions to catch bad uses of scriptable flags on
+ // the siWrapper...
+
+ MOZ_ASSERT(!(sciWrapper.GetFlags().WantPreCreate() &&
+ !sciProto.GetFlags().WantPreCreate()),
+ "Can't set WANT_PRECREATE on an instance scriptable "
+ "without also setting it on the class scriptable");
+
+ MOZ_ASSERT(!(sciWrapper.GetFlags().DontEnumQueryInterface() &&
+ !sciProto.GetFlags().DontEnumQueryInterface() &&
+ sciProto.GetCallback()),
+ "Can't set DONT_ENUM_QUERY_INTERFACE on an instance scriptable "
+ "without also setting it on the class scriptable (if present and shared)");
+
+ MOZ_ASSERT(!(sciWrapper.GetFlags().DontAskInstanceForScriptable() &&
+ !sciProto.GetFlags().DontAskInstanceForScriptable()),
+ "Can't set DONT_ASK_INSTANCE_FOR_SCRIPTABLE on an instance scriptable "
+ "without also setting it on the class scriptable");
+
+ MOZ_ASSERT(!(sciWrapper.GetFlags().ClassInfoInterfacesOnly() &&
+ !sciProto.GetFlags().ClassInfoInterfacesOnly() &&
+ sciProto.GetCallback()),
+ "Can't set CLASSINFO_INTERFACES_ONLY on an instance scriptable "
+ "without also setting it on the class scriptable (if present and shared)");
+
+ MOZ_ASSERT(!(sciWrapper.GetFlags().AllowPropModsDuringResolve() &&
+ !sciProto.GetFlags().AllowPropModsDuringResolve() &&
+ sciProto.GetCallback()),
+ "Can't set ALLOW_PROP_MODS_DURING_RESOLVE on an instance scriptable "
+ "without also setting it on the class scriptable (if present and shared)");
+
+ MOZ_ASSERT(!(sciWrapper.GetFlags().AllowPropModsToPrototype() &&
+ !sciProto.GetFlags().AllowPropModsToPrototype() &&
+ sciProto.GetCallback()),
+ "Can't set ALLOW_PROP_MODS_TO_PROTOTYPE on an instance scriptable "
+ "without also setting it on the class scriptable (if present and shared)");
+
+ return sciWrapper;
+ }
+
+ return sciProto;
+}
+
+bool
+XPCWrappedNative::Init(const XPCNativeScriptableCreateInfo* sci)
+{
+ AutoJSContext cx;
+ // setup our scriptable info...
+
+ if (sci->GetCallback()) {
+ if (HasProto()) {
+ XPCNativeScriptableInfo* siProto = GetProto()->GetScriptableInfo();
+ if (siProto && siProto->GetCallback() == sci->GetCallback())
+ mScriptableInfo = siProto;
+ }
+ if (!mScriptableInfo) {
+ mScriptableInfo = XPCNativeScriptableInfo::Construct(sci);
+
+ if (!mScriptableInfo)
+ return false;
+ }
+ }
+ XPCNativeScriptableInfo* si = mScriptableInfo;
+
+ // create our flatJSObject
+
+ const JSClass* jsclazz = si ? si->GetJSClass() : Jsvalify(&XPC_WN_NoHelper_JSClass);
+
+ // We should have the global jsclass flag if and only if we're a global.
+ MOZ_ASSERT_IF(si, !!si->GetFlags().IsGlobalObject() == !!(jsclazz->flags & JSCLASS_IS_GLOBAL));
+
+ MOZ_ASSERT(jsclazz &&
+ jsclazz->name &&
+ jsclazz->flags &&
+ jsclazz->getResolve() &&
+ jsclazz->hasFinalize(), "bad class");
+
+ // XXXbz JS_GetObjectPrototype wants an object, even though it then asserts
+ // that this object is same-compartment with cx, which means it could just
+ // use the cx global...
+ RootedObject global(cx, CurrentGlobalOrNull(cx));
+ RootedObject protoJSObject(cx, HasProto() ?
+ GetProto()->GetJSProtoObject() :
+ JS_GetObjectPrototype(cx, global));
+ if (!protoJSObject) {
+ return false;
+ }
+
+ mFlatJSObject = JS_NewObjectWithGivenProto(cx, jsclazz, protoJSObject);
+ if (!mFlatJSObject) {
+ mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
+ return false;
+ }
+
+ mFlatJSObject.setFlags(FLAT_JS_OBJECT_VALID);
+ JS_SetPrivate(mFlatJSObject, this);
+
+ return FinishInit();
+}
+
+bool
+XPCWrappedNative::FinishInit()
+{
+ AutoJSContext cx;
+
+ // This reference will be released when mFlatJSObject is finalized.
+ // Since this reference will push the refcount to 2 it will also root
+ // mFlatJSObject;
+ MOZ_ASSERT(1 == mRefCnt, "unexpected refcount value");
+ NS_ADDREF(this);
+
+ // A hack for bug 517665, increase the probability for GC.
+ JS_updateMallocCounter(cx, 2 * sizeof(XPCWrappedNative));
+
+ return true;
+}
+
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPCWrappedNative)
+ NS_INTERFACE_MAP_ENTRY(nsIXPConnectWrappedNative)
+ NS_INTERFACE_MAP_ENTRY(nsIXPConnectJSObjectHolder)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPConnectWrappedNative)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCWrappedNative)
+
+// Release calls Destroy() immediately when the refcount drops to 0 to
+// clear the weak references nsXPConnect has to XPCWNs and to ensure there
+// are no pointers to dying protos.
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(XPCWrappedNative, Destroy())
+
+/*
+ * Wrapped Native lifetime management is messy!
+ *
+ * - At creation we push the refcount to 2 (only one of which is owned by
+ * the native caller that caused the wrapper creation).
+ * - During the JS GC Mark phase we mark any wrapper with a refcount > 1.
+ * - The *only* thing that can make the wrapper get destroyed is the
+ * finalization of mFlatJSObject. And *that* should only happen if the only
+ * reference is the single extra (internal) reference we hold.
+ *
+ * - The wrapper has a pointer to the nsISupports 'view' of the wrapped native
+ * object i.e... mIdentity. This is held until the wrapper's refcount goes
+ * to zero and the wrapper is released, or until an expired wrapper (i.e.,
+ * one unlinked by the cycle collector) has had its JS object finalized.
+ *
+ * - The wrapper also has 'tearoffs'. It has one tearoff for each interface
+ * that is actually used on the native object. 'Used' means we have either
+ * needed to QueryInterface to verify the availability of that interface
+ * of that we've had to QueryInterface in order to actually make a call
+ * into the wrapped object via the pointer for the given interface.
+ *
+ * - Each tearoff's 'mNative' member (if non-null) indicates one reference
+ * held by our wrapper on the wrapped native for the given interface
+ * associated with the tearoff. If we release that reference then we set
+ * the tearoff's 'mNative' to null.
+ *
+ * - We use the occasion of the JavaScript GCCallback for the JSGC_MARK_END
+ * event to scan the tearoffs of all wrappers for non-null mNative members
+ * that represent unused references. We can tell that a given tearoff's
+ * mNative is unused by noting that no live XPCCallContexts hold a pointer
+ * to the tearoff.
+ *
+ * - As a time/space tradeoff we may decide to not do this scanning on
+ * *every* JavaScript GC. We *do* want to do this *sometimes* because
+ * we want to allow for wrapped native's to do their own tearoff patterns.
+ * So, we want to avoid holding references to interfaces that we don't need.
+ * At the same time, we don't want to be bracketing every call into a
+ * wrapped native object with a QueryInterface/Release pair. And we *never*
+ * make a call into the object except via the correct interface for which
+ * we've QI'd.
+ *
+ * - Each tearoff *can* have a mJSObject whose lazily resolved properties
+ * represent the methods/attributes/constants of that specific interface.
+ * This is optionally reflected into JavaScript as "foo.nsIFoo" when "foo"
+ * is the name of mFlatJSObject and "nsIFoo" is the name of the given
+ * interface associated with the tearoff. When we create the tearoff's
+ * mJSObject we set it's parent to be mFlatJSObject. This way we know that
+ * when mFlatJSObject get's collected there are no outstanding reachable
+ * tearoff mJSObjects. Note that we must clear the private of any lingering
+ * mJSObjects at this point because we have no guarentee of the *order* of
+ * finalization within a given gc cycle.
+ */
+
+void
+XPCWrappedNative::FlatJSObjectFinalized()
+{
+ if (!IsValid())
+ return;
+
+ // Iterate the tearoffs and null out each of their JSObject's privates.
+ // This will keep them from trying to access their pointers to the
+ // dying tearoff object. We can safely assume that those remaining
+ // JSObjects are about to be finalized too.
+
+ for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
+ JSObject* jso = to->GetJSObjectPreserveColor();
+ if (jso) {
+ JS_SetPrivate(jso, nullptr);
+#ifdef DEBUG
+ JS_UpdateWeakPointerAfterGCUnbarriered(&jso);
+ MOZ_ASSERT(!jso);
+#endif
+ to->JSObjectFinalized();
+ }
+
+ // We also need to release any native pointers held...
+ RefPtr<nsISupports> native = to->TakeNative();
+ if (native && GetContext()) {
+ DeferredFinalize(native.forget().take());
+ }
+
+ to->SetInterface(nullptr);
+ }
+
+ nsWrapperCache* cache = nullptr;
+ CallQueryInterface(mIdentity, &cache);
+ if (cache)
+ cache->ClearWrapper();
+
+ mFlatJSObject = nullptr;
+ mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
+
+ MOZ_ASSERT(mIdentity, "bad pointer!");
+#ifdef XP_WIN
+ // Try to detect free'd pointer
+ MOZ_ASSERT(*(int*)mIdentity.get() != 0xdddddddd, "bad pointer!");
+ MOZ_ASSERT(*(int*)mIdentity.get() != 0, "bad pointer!");
+#endif
+
+ if (IsWrapperExpired()) {
+ Destroy();
+ }
+
+ // Note that it's not safe to touch mNativeWrapper here since it's
+ // likely that it has already been finalized.
+
+ Release();
+}
+
+void
+XPCWrappedNative::FlatJSObjectMoved(JSObject* obj, const JSObject* old)
+{
+ JS::AutoAssertGCCallback inCallback(obj);
+ MOZ_ASSERT(mFlatJSObject.unbarrieredGetPtr() == old);
+
+ nsWrapperCache* cache = nullptr;
+ CallQueryInterface(mIdentity, &cache);
+ if (cache)
+ cache->UpdateWrapper(obj, old);
+
+ mFlatJSObject = obj;
+}
+
+void
+XPCWrappedNative::SystemIsBeingShutDown()
+{
+ if (!IsValid())
+ return;
+
+ // The long standing strategy is to leak some objects still held at shutdown.
+ // The general problem is that propagating release out of xpconnect at
+ // shutdown time causes a world of problems.
+
+ // We leak mIdentity (see above).
+
+ // Short circuit future finalization.
+ JS_SetPrivate(mFlatJSObject, nullptr);
+ mFlatJSObject = nullptr;
+ mFlatJSObject.unsetFlags(FLAT_JS_OBJECT_VALID);
+
+ XPCWrappedNativeProto* proto = GetProto();
+
+ if (HasProto())
+ proto->SystemIsBeingShutDown();
+
+ // We don't destroy mScriptableInfo here. The destructor will do it.
+
+ // Cleanup the tearoffs.
+ for (XPCWrappedNativeTearOff* to = &mFirstTearOff; to; to = to->GetNextTearOff()) {
+ if (JSObject* jso = to->GetJSObjectPreserveColor()) {
+ JS_SetPrivate(jso, nullptr);
+ to->SetJSObject(nullptr);
+ }
+ // We leak the tearoff mNative
+ // (for the same reason we leak mIdentity - see above).
+ Unused << to->TakeNative().take();
+ to->SetInterface(nullptr);
+ }
+}
+
+/***************************************************************************/
+
+// Dynamically ensure that two objects don't end up with the same private.
+class MOZ_STACK_CLASS AutoClonePrivateGuard {
+public:
+ AutoClonePrivateGuard(JSContext* cx, JSObject* aOld, JSObject* aNew)
+ : mOldReflector(cx, aOld), mNewReflector(cx, aNew)
+ {
+ MOZ_ASSERT(JS_GetPrivate(aOld) == JS_GetPrivate(aNew));
+ }
+
+ ~AutoClonePrivateGuard()
+ {
+ if (JS_GetPrivate(mOldReflector)) {
+ JS_SetPrivate(mNewReflector, nullptr);
+ }
+ }
+
+private:
+ RootedObject mOldReflector;
+ RootedObject mNewReflector;
+};
+
+bool
+XPCWrappedNative::ExtendSet(XPCNativeInterface* aInterface)
+{
+ if (!mSet->HasInterface(aInterface)) {
+ XPCNativeSetKey key(mSet, aInterface);
+ RefPtr<XPCNativeSet> newSet =
+ XPCNativeSet::GetNewOrUsed(&key);
+ if (!newSet)
+ return false;
+
+ mSet = newSet.forget();
+ }
+ return true;
+}
+
+XPCWrappedNativeTearOff*
+XPCWrappedNative::FindTearOff(XPCNativeInterface* aInterface,
+ bool needJSObject /* = false */,
+ nsresult* pError /* = nullptr */)
+{
+ AutoJSContext cx;
+ nsresult rv = NS_OK;
+ XPCWrappedNativeTearOff* to;
+ XPCWrappedNativeTearOff* firstAvailable = nullptr;
+
+ XPCWrappedNativeTearOff* lastTearOff;
+ for (lastTearOff = to = &mFirstTearOff;
+ to;
+ lastTearOff = to, to = to->GetNextTearOff()) {
+ if (to->GetInterface() == aInterface) {
+ if (needJSObject && !to->GetJSObjectPreserveColor()) {
+ AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
+ bool ok = InitTearOffJSObject(to);
+ // During shutdown, we don't sweep tearoffs. So make sure
+ // to unmark manually in case the auto-marker marked us.
+ // We shouldn't ever be getting here _during_ our
+ // Mark/Sweep cycle, so this should be safe.
+ to->Unmark();
+ if (!ok) {
+ to = nullptr;
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ if (pError)
+ *pError = rv;
+ return to;
+ }
+ if (!firstAvailable && to->IsAvailable())
+ firstAvailable = to;
+ }
+
+ to = firstAvailable;
+
+ if (!to) {
+ to = lastTearOff->AddTearOff();
+ }
+
+ {
+ // Scope keeps |tearoff| from leaking across the rest of the function.
+ AutoMarkingWrappedNativeTearOffPtr tearoff(cx, to);
+ rv = InitTearOff(to, aInterface, needJSObject);
+ // During shutdown, we don't sweep tearoffs. So make sure to unmark
+ // manually in case the auto-marker marked us. We shouldn't ever be
+ // getting here _during_ our Mark/Sweep cycle, so this should be safe.
+ to->Unmark();
+ if (NS_FAILED(rv))
+ to = nullptr;
+ }
+
+ if (pError)
+ *pError = rv;
+ return to;
+}
+
+XPCWrappedNativeTearOff*
+XPCWrappedNative::FindTearOff(const nsIID& iid) {
+ RefPtr<XPCNativeInterface> iface = XPCNativeInterface::GetNewOrUsed(&iid);
+ return iface ? FindTearOff(iface) : nullptr;
+}
+
+nsresult
+XPCWrappedNative::InitTearOff(XPCWrappedNativeTearOff* aTearOff,
+ XPCNativeInterface* aInterface,
+ bool needJSObject)
+{
+ AutoJSContext cx;
+
+ // Determine if the object really does this interface...
+
+ const nsIID* iid = aInterface->GetIID();
+ nsISupports* identity = GetIdentityObject();
+
+ // This is an nsRefPtr instead of an nsCOMPtr because it may not be the
+ // canonical nsISupports for this object.
+ RefPtr<nsISupports> qiResult;
+
+ // If the scriptable helper forbids us from reflecting additional
+ // interfaces, then don't even try the QI, just fail.
+ if (mScriptableInfo &&
+ mScriptableInfo->GetFlags().ClassInfoInterfacesOnly() &&
+ !mSet->HasInterface(aInterface) &&
+ !mSet->HasInterfaceWithAncestor(aInterface)) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ // We are about to call out to other code.
+ // So protect our intended tearoff.
+
+ aTearOff->SetReserved();
+
+ if (NS_FAILED(identity->QueryInterface(*iid, getter_AddRefs(qiResult))) || !qiResult) {
+ aTearOff->SetInterface(nullptr);
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ // Guard against trying to build a tearoff for a shared nsIClassInfo.
+ if (iid->Equals(NS_GET_IID(nsIClassInfo))) {
+ nsCOMPtr<nsISupports> alternate_identity(do_QueryInterface(qiResult));
+ if (alternate_identity.get() != identity) {
+ aTearOff->SetInterface(nullptr);
+ return NS_ERROR_NO_INTERFACE;
+ }
+ }
+
+ // Guard against trying to build a tearoff for an interface that is
+ // aggregated and is implemented as a nsIXPConnectWrappedJS using this
+ // self-same JSObject. The XBL system does this. If we mutate the set
+ // of this wrapper then we will shadow the method that XBL has added to
+ // the JSObject that it has inserted in the JS proto chain between our
+ // JSObject and our XPCWrappedNativeProto's JSObject. If we let this
+ // set mutation happen then the interface's methods will be added to
+ // our JSObject, but calls on those methods will get routed up to
+ // native code and into the wrappedJS - which will do a method lookup
+ // on *our* JSObject and find the same method and make another call
+ // into an infinite loop.
+ // see: http://bugzilla.mozilla.org/show_bug.cgi?id=96725
+
+ // The code in this block also does a check for the double wrapped
+ // nsIPropertyBag case.
+
+ nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(qiResult));
+ if (wrappedJS) {
+ RootedObject jso(cx, wrappedJS->GetJSObject());
+ if (jso == mFlatJSObject) {
+ // The implementing JSObject is the same as ours! Just say OK
+ // without actually extending the set.
+ //
+ // XXX It is a little cheesy to have FindTearOff return an
+ // 'empty' tearoff. But this is the centralized place to do the
+ // QI activities on the underlying object. *And* most caller to
+ // FindTearOff only look for a non-null result and ignore the
+ // actual tearoff returned. The only callers that do use the
+ // returned tearoff make sure to check for either a non-null
+ // JSObject or a matching Interface before proceeding.
+ // I think we can get away with this bit of ugliness.
+
+ aTearOff->SetInterface(nullptr);
+ return NS_OK;
+ }
+
+ // Decide whether or not to expose nsIPropertyBag to calling
+ // JS code in the double wrapped case.
+ //
+ // Our rule here is that when JSObjects are double wrapped and
+ // exposed to other JSObjects then the nsIPropertyBag interface
+ // is only exposed on an 'opt-in' basis; i.e. if the underlying
+ // JSObject wants other JSObjects to be able to see this interface
+ // then it must implement QueryInterface and not throw an exception
+ // when asked for nsIPropertyBag. It need not actually *implement*
+ // nsIPropertyBag - xpconnect will do that work.
+
+ if (iid->Equals(NS_GET_IID(nsIPropertyBag)) && jso) {
+ RefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, *iid);
+ if (clasp) {
+ RootedObject answer(cx, clasp->CallQueryInterfaceOnJSObject(cx, jso, *iid));
+
+ if (!answer) {
+ aTearOff->SetInterface(nullptr);
+ return NS_ERROR_NO_INTERFACE;
+ }
+ }
+ }
+ }
+
+ if (NS_FAILED(nsXPConnect::SecurityManager()->CanCreateWrapper(cx, *iid, identity,
+ GetClassInfo()))) {
+ // the security manager vetoed. It should have set an exception.
+ aTearOff->SetInterface(nullptr);
+ return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
+ }
+
+ // If this is not already in our set we need to extend our set.
+ // Note: we do not cache the result of the previous call to HasInterface()
+ // because we unlocked and called out in the interim and the result of the
+ // previous call might not be correct anymore.
+
+ if (!mSet->HasInterface(aInterface) && !ExtendSet(aInterface)) {
+ aTearOff->SetInterface(nullptr);
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ aTearOff->SetInterface(aInterface);
+ aTearOff->SetNative(qiResult);
+ if (needJSObject && !InitTearOffJSObject(aTearOff))
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ return NS_OK;
+}
+
+bool
+XPCWrappedNative::InitTearOffJSObject(XPCWrappedNativeTearOff* to)
+{
+ AutoJSContext cx;
+
+ JSObject* obj = JS_NewObject(cx, Jsvalify(&XPC_WN_Tearoff_JSClass));
+ if (!obj)
+ return false;
+
+ JS_SetPrivate(obj, to);
+ to->SetJSObject(obj);
+
+ js::SetReservedSlot(obj, XPC_WN_TEAROFF_FLAT_OBJECT_SLOT,
+ JS::ObjectValue(*mFlatJSObject));
+ return true;
+}
+
+/***************************************************************************/
+
+static bool Throw(nsresult errNum, XPCCallContext& ccx)
+{
+ XPCThrower::Throw(errNum, ccx);
+ return false;
+}
+
+/***************************************************************************/
+
+class MOZ_STACK_CLASS CallMethodHelper
+{
+ XPCCallContext& mCallContext;
+ nsresult mInvokeResult;
+ nsIInterfaceInfo* const mIFaceInfo;
+ const nsXPTMethodInfo* mMethodInfo;
+ nsISupports* const mCallee;
+ const uint16_t mVTableIndex;
+ HandleId mIdxValueId;
+
+ AutoTArray<nsXPTCVariant, 8> mDispatchParams;
+ uint8_t mJSContextIndex; // TODO make const
+ uint8_t mOptArgcIndex; // TODO make const
+
+ Value* const mArgv;
+ const uint32_t mArgc;
+
+ MOZ_ALWAYS_INLINE bool
+ GetArraySizeFromParam(uint8_t paramIndex, HandleValue maybeArray, uint32_t* result);
+
+ MOZ_ALWAYS_INLINE bool
+ GetInterfaceTypeFromParam(uint8_t paramIndex,
+ const nsXPTType& datum_type,
+ nsID* result) const;
+
+ MOZ_ALWAYS_INLINE bool
+ GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const;
+
+ MOZ_ALWAYS_INLINE bool
+ GatherAndConvertResults();
+
+ MOZ_ALWAYS_INLINE bool
+ QueryInterfaceFastPath();
+
+ nsXPTCVariant*
+ GetDispatchParam(uint8_t paramIndex)
+ {
+ if (paramIndex >= mJSContextIndex)
+ paramIndex += 1;
+ if (paramIndex >= mOptArgcIndex)
+ paramIndex += 1;
+ return &mDispatchParams[paramIndex];
+ }
+ const nsXPTCVariant*
+ GetDispatchParam(uint8_t paramIndex) const
+ {
+ return const_cast<CallMethodHelper*>(this)->GetDispatchParam(paramIndex);
+ }
+
+ MOZ_ALWAYS_INLINE bool InitializeDispatchParams();
+
+ MOZ_ALWAYS_INLINE bool ConvertIndependentParams(bool* foundDependentParam);
+ MOZ_ALWAYS_INLINE bool ConvertIndependentParam(uint8_t i);
+ MOZ_ALWAYS_INLINE bool ConvertDependentParams();
+ MOZ_ALWAYS_INLINE bool ConvertDependentParam(uint8_t i);
+
+ MOZ_ALWAYS_INLINE void CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type);
+
+ MOZ_ALWAYS_INLINE bool AllocateStringClass(nsXPTCVariant* dp,
+ const nsXPTParamInfo& paramInfo);
+
+ MOZ_ALWAYS_INLINE nsresult Invoke();
+
+public:
+
+ explicit CallMethodHelper(XPCCallContext& ccx)
+ : mCallContext(ccx)
+ , mInvokeResult(NS_ERROR_UNEXPECTED)
+ , mIFaceInfo(ccx.GetInterface()->GetInterfaceInfo())
+ , mMethodInfo(nullptr)
+ , mCallee(ccx.GetTearOff()->GetNative())
+ , mVTableIndex(ccx.GetMethodIndex())
+ , mIdxValueId(ccx.GetContext()->GetStringID(XPCJSContext::IDX_VALUE))
+ , mJSContextIndex(UINT8_MAX)
+ , mOptArgcIndex(UINT8_MAX)
+ , mArgv(ccx.GetArgv())
+ , mArgc(ccx.GetArgc())
+
+ {
+ // Success checked later.
+ mIFaceInfo->GetMethodInfo(mVTableIndex, &mMethodInfo);
+ }
+
+ ~CallMethodHelper();
+
+ MOZ_ALWAYS_INLINE bool Call();
+
+};
+
+// static
+bool
+XPCWrappedNative::CallMethod(XPCCallContext& ccx,
+ CallMode mode /*= CALL_METHOD */)
+{
+ nsresult rv = ccx.CanCallNow();
+ if (NS_FAILED(rv)) {
+ return Throw(rv, ccx);
+ }
+
+ return CallMethodHelper(ccx).Call();
+}
+
+bool
+CallMethodHelper::Call()
+{
+ mCallContext.SetRetVal(JS::UndefinedValue());
+
+ XPCJSContext::Get()->SetPendingException(nullptr);
+
+ if (mVTableIndex == 0) {
+ return QueryInterfaceFastPath();
+ }
+
+ if (!mMethodInfo) {
+ Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, mCallContext);
+ return false;
+ }
+
+ if (!InitializeDispatchParams())
+ return false;
+
+ // Iterate through the params doing conversions of independent params only.
+ // When we later convert the dependent params (if any) we will know that
+ // the params upon which they depend will have already been converted -
+ // regardless of ordering.
+ bool foundDependentParam = false;
+ if (!ConvertIndependentParams(&foundDependentParam))
+ return false;
+
+ if (foundDependentParam && !ConvertDependentParams())
+ return false;
+
+ mInvokeResult = Invoke();
+
+ if (JS_IsExceptionPending(mCallContext)) {
+ return false;
+ }
+
+ if (NS_FAILED(mInvokeResult)) {
+ ThrowBadResult(mInvokeResult, mCallContext);
+ return false;
+ }
+
+ return GatherAndConvertResults();
+}
+
+CallMethodHelper::~CallMethodHelper()
+{
+ uint8_t paramCount = mMethodInfo->GetParamCount();
+ if (mDispatchParams.Length()) {
+ for (uint8_t i = 0; i < paramCount; i++) {
+ nsXPTCVariant* dp = GetDispatchParam(i);
+ const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
+
+ if (paramInfo.GetType().IsArray()) {
+ void* p = dp->val.p;
+ if (!p)
+ continue;
+
+ // Clean up the array contents if necessary.
+ if (dp->DoesValNeedCleanup()) {
+ // We need some basic information to properly destroy the array.
+ uint32_t array_count = 0;
+ nsXPTType datum_type;
+ if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count) ||
+ !NS_SUCCEEDED(mIFaceInfo->GetTypeForParam(mVTableIndex,
+ &paramInfo,
+ 1, &datum_type))) {
+ // XXXbholley - I'm not convinced that the above calls will
+ // ever fail.
+ NS_ERROR("failed to get array information, we'll leak here");
+ continue;
+ }
+
+ // Loop over the array contents. For each one, we create a
+ // dummy 'val' and pass it to the cleanup helper.
+ for (uint32_t k = 0; k < array_count; k++) {
+ nsXPTCMiniVariant v;
+ v.val.p = static_cast<void**>(p)[k];
+ CleanupParam(v, datum_type);
+ }
+ }
+
+ // always free the array itself
+ free(p);
+ } else {
+ // Clean up single parameters (if requested).
+ if (dp->DoesValNeedCleanup())
+ CleanupParam(*dp, dp->type);
+ }
+ }
+ }
+}
+
+bool
+CallMethodHelper::GetArraySizeFromParam(uint8_t paramIndex,
+ HandleValue maybeArray,
+ uint32_t* result)
+{
+ nsresult rv;
+ const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
+
+ // TODO fixup the various exceptions that are thrown
+
+ rv = mIFaceInfo->GetSizeIsArgNumberForParam(mVTableIndex, &paramInfo, 0, &paramIndex);
+ if (NS_FAILED(rv))
+ return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
+
+ // If the array length wasn't passed, it might have been listed as optional.
+ // When converting arguments from JS to C++, we pass the array as |maybeArray|,
+ // and give ourselves the chance to infer the length. Once we have it, we stick
+ // it in the right slot so that we can find it again when cleaning up the params.
+ // from the array.
+ if (paramIndex >= mArgc && maybeArray.isObject()) {
+ MOZ_ASSERT(mMethodInfo->GetParam(paramIndex).IsOptional());
+ RootedObject arrayOrNull(mCallContext, maybeArray.isObject() ? &maybeArray.toObject()
+ : nullptr);
+
+ bool isArray;
+ if (!JS_IsArrayObject(mCallContext, maybeArray, &isArray) ||
+ !isArray ||
+ !JS_GetArrayLength(mCallContext, arrayOrNull, &GetDispatchParam(paramIndex)->val.u32))
+ {
+ return Throw(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY, mCallContext);
+ }
+ }
+
+ *result = GetDispatchParam(paramIndex)->val.u32;
+
+ return true;
+}
+
+bool
+CallMethodHelper::GetInterfaceTypeFromParam(uint8_t paramIndex,
+ const nsXPTType& datum_type,
+ nsID* result) const
+{
+ nsresult rv;
+ const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
+ uint8_t tag = datum_type.TagPart();
+
+ // TODO fixup the various exceptions that are thrown
+
+ if (tag == nsXPTType::T_INTERFACE) {
+ rv = mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, &paramInfo, result);
+ if (NS_FAILED(rv))
+ return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
+ paramIndex, mCallContext);
+ } else if (tag == nsXPTType::T_INTERFACE_IS) {
+ rv = mIFaceInfo->GetInterfaceIsArgNumberForParam(mVTableIndex, &paramInfo,
+ &paramIndex);
+ if (NS_FAILED(rv))
+ return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
+
+ nsID* p = (nsID*) GetDispatchParam(paramIndex)->val.p;
+ if (!p)
+ return ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO,
+ paramIndex, mCallContext);
+ *result = *p;
+ }
+ return true;
+}
+
+bool
+CallMethodHelper::GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const
+{
+ const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex);
+
+ MOZ_ASSERT(!paramInfo.IsDipper(), "Dipper params are handled separately");
+ if (paramInfo.IsOut() && !paramInfo.IsRetval()) {
+ MOZ_ASSERT(paramIndex < mArgc || paramInfo.IsOptional(),
+ "Expected either enough arguments or an optional argument");
+ Value arg = paramIndex < mArgc ? mArgv[paramIndex] : JS::NullValue();
+ if (paramIndex < mArgc) {
+ RootedObject obj(mCallContext);
+ if (!arg.isPrimitive())
+ obj = &arg.toObject();
+ if (!obj || !JS_GetPropertyById(mCallContext, obj, mIdxValueId, srcp)) {
+ // Explicitly passed in unusable value for out param. Note
+ // that if i >= mArgc we already know that |arg| is JS::NullValue(),
+ // and that's ok.
+ ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, paramIndex,
+ mCallContext);
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool
+CallMethodHelper::GatherAndConvertResults()
+{
+ // now we iterate through the native params to gather and convert results
+ uint8_t paramCount = mMethodInfo->GetParamCount();
+ for (uint8_t i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
+ if (!paramInfo.IsOut() && !paramInfo.IsDipper())
+ continue;
+
+ const nsXPTType& type = paramInfo.GetType();
+ nsXPTCVariant* dp = GetDispatchParam(i);
+ RootedValue v(mCallContext, NullValue());
+ uint32_t array_count = 0;
+ nsXPTType datum_type;
+ bool isArray = type.IsArray();
+ bool isSizedString = isArray ?
+ false :
+ type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
+ type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
+
+ if (isArray) {
+ if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, &paramInfo, 1,
+ &datum_type))) {
+ Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
+ return false;
+ }
+ } else
+ datum_type = type;
+
+ if (isArray || isSizedString) {
+ if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count))
+ return false;
+ }
+
+ nsID param_iid;
+ if (datum_type.IsInterfacePointer() &&
+ !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
+ return false;
+
+ nsresult err;
+ if (isArray) {
+ if (!XPCConvert::NativeArray2JS(&v, (const void**)&dp->val,
+ datum_type, &param_iid,
+ array_count, &err)) {
+ // XXX need exception scheme for arrays to indicate bad element
+ ThrowBadParam(err, i, mCallContext);
+ return false;
+ }
+ } else if (isSizedString) {
+ if (!XPCConvert::NativeStringWithSize2JS(&v,
+ (const void*)&dp->val,
+ datum_type,
+ array_count, &err)) {
+ ThrowBadParam(err, i, mCallContext);
+ return false;
+ }
+ } else {
+ if (!XPCConvert::NativeData2JS(&v, &dp->val, datum_type,
+ &param_iid, &err)) {
+ ThrowBadParam(err, i, mCallContext);
+ return false;
+ }
+ }
+
+ if (paramInfo.IsRetval()) {
+ mCallContext.SetRetVal(v);
+ } else if (i < mArgc) {
+ // we actually assured this before doing the invoke
+ MOZ_ASSERT(mArgv[i].isObject(), "out var is not object");
+ RootedObject obj(mCallContext, &mArgv[i].toObject());
+ if (!JS_SetPropertyById(mCallContext, obj, mIdxValueId, v)) {
+ ThrowBadParam(NS_ERROR_XPC_CANT_SET_OUT_VAL, i, mCallContext);
+ return false;
+ }
+ } else {
+ MOZ_ASSERT(paramInfo.IsOptional(),
+ "Expected either enough arguments or an optional argument");
+ }
+ }
+
+ return true;
+}
+
+bool
+CallMethodHelper::QueryInterfaceFastPath()
+{
+ MOZ_ASSERT(mVTableIndex == 0,
+ "Using the QI fast-path for a method other than QueryInterface");
+
+ if (mArgc < 1) {
+ Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
+ return false;
+ }
+
+ if (!mArgv[0].isObject()) {
+ ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext);
+ return false;
+ }
+
+ const nsID* iid = xpc_JSObjectToID(mCallContext, &mArgv[0].toObject());
+ if (!iid) {
+ ThrowBadParam(NS_ERROR_XPC_BAD_CONVERT_JS, 0, mCallContext);
+ return false;
+ }
+
+ nsISupports* qiresult = nullptr;
+ mInvokeResult = mCallee->QueryInterface(*iid, (void**) &qiresult);
+
+ if (NS_FAILED(mInvokeResult)) {
+ ThrowBadResult(mInvokeResult, mCallContext);
+ return false;
+ }
+
+ RootedValue v(mCallContext, NullValue());
+ nsresult err;
+ bool success =
+ XPCConvert::NativeData2JS(&v, &qiresult,
+ nsXPTType::T_INTERFACE_IS,
+ iid, &err);
+ NS_IF_RELEASE(qiresult);
+
+ if (!success) {
+ ThrowBadParam(err, 0, mCallContext);
+ return false;
+ }
+
+ mCallContext.SetRetVal(v);
+ return true;
+}
+
+bool
+CallMethodHelper::InitializeDispatchParams()
+{
+ const uint8_t wantsOptArgc = mMethodInfo->WantsOptArgc() ? 1 : 0;
+ const uint8_t wantsJSContext = mMethodInfo->WantsContext() ? 1 : 0;
+ const uint8_t paramCount = mMethodInfo->GetParamCount();
+ uint8_t requiredArgs = paramCount;
+ uint8_t hasRetval = 0;
+
+ // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
+ if (paramCount && mMethodInfo->GetParam(paramCount-1).IsRetval()) {
+ hasRetval = 1;
+ requiredArgs--;
+ }
+
+ if (mArgc < requiredArgs || wantsOptArgc) {
+ if (wantsOptArgc)
+ mOptArgcIndex = requiredArgs;
+
+ // skip over any optional arguments
+ while (requiredArgs && mMethodInfo->GetParam(requiredArgs-1).IsOptional())
+ requiredArgs--;
+
+ if (mArgc < requiredArgs) {
+ Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
+ return false;
+ }
+ }
+
+ if (wantsJSContext) {
+ if (wantsOptArgc)
+ // Need to bump mOptArgcIndex up one here.
+ mJSContextIndex = mOptArgcIndex++;
+ else if (mMethodInfo->IsSetter() || mMethodInfo->IsGetter())
+ // For attributes, we always put the JSContext* first.
+ mJSContextIndex = 0;
+ else
+ mJSContextIndex = paramCount - hasRetval;
+ }
+
+ // iterate through the params to clear flags (for safe cleanup later)
+ for (uint8_t i = 0; i < paramCount + wantsJSContext + wantsOptArgc; i++) {
+ nsXPTCVariant* dp = mDispatchParams.AppendElement();
+ dp->ClearFlags();
+ dp->val.p = nullptr;
+ }
+
+ // Fill in the JSContext argument
+ if (wantsJSContext) {
+ nsXPTCVariant* dp = &mDispatchParams[mJSContextIndex];
+ dp->type = nsXPTType::T_VOID;
+ dp->val.p = mCallContext;
+ }
+
+ // Fill in the optional_argc argument
+ if (wantsOptArgc) {
+ nsXPTCVariant* dp = &mDispatchParams[mOptArgcIndex];
+ dp->type = nsXPTType::T_U8;
+ dp->val.u8 = std::min<uint32_t>(mArgc, paramCount) - requiredArgs;
+ }
+
+ return true;
+}
+
+bool
+CallMethodHelper::ConvertIndependentParams(bool* foundDependentParam)
+{
+ const uint8_t paramCount = mMethodInfo->GetParamCount();
+ for (uint8_t i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
+
+ if (paramInfo.GetType().IsDependent())
+ *foundDependentParam = true;
+ else if (!ConvertIndependentParam(i))
+ return false;
+
+ }
+
+ return true;
+}
+
+bool
+CallMethodHelper::ConvertIndependentParam(uint8_t i)
+{
+ const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
+ const nsXPTType& type = paramInfo.GetType();
+ uint8_t type_tag = type.TagPart();
+ nsXPTCVariant* dp = GetDispatchParam(i);
+ dp->type = type;
+ MOZ_ASSERT(!paramInfo.IsShared(), "[shared] implies [noscript]!");
+
+ // String classes are always "in" - those that are marked "out" are converted
+ // by the XPIDL compiler to "in+dipper". See the note above IsDipper() in
+ // xptinfo.h.
+ //
+ // Also note that the fact that we bail out early for dipper parameters means
+ // that "inout" dipper parameters don't work - see bug 687612.
+ if (paramInfo.IsStringClass()) {
+ if (!AllocateStringClass(dp, paramInfo))
+ return false;
+ if (paramInfo.IsDipper()) {
+ // We've allocated our string class explicitly, so we don't need
+ // to do any conversions on the incoming argument. However, we still
+ // need to verify that it's an object, so that we don't get surprised
+ // later on when trying to assign the result to .value.
+ if (i < mArgc && !mArgv[i].isObject()) {
+ ThrowBadParam(NS_ERROR_XPC_NEED_OUT_OBJECT, i, mCallContext);
+ return false;
+ }
+ return true;
+ }
+ }
+
+ // Specify the correct storage/calling semantics.
+ if (paramInfo.IsIndirect())
+ dp->SetIndirect();
+
+ // The JSVal proper is always stored within the 'val' union and passed
+ // indirectly, regardless of in/out-ness.
+ if (type_tag == nsXPTType::T_JSVAL) {
+ // Root the value.
+ dp->val.j.setUndefined();
+ if (!js::AddRawValueRoot(mCallContext, &dp->val.j, "XPCWrappedNative::CallMethod param"))
+ return false;
+ }
+
+ // Flag cleanup for anything that isn't self-contained.
+ if (!type.IsArithmetic())
+ dp->SetValNeedsCleanup();
+
+ // Even if there's nothing to convert, we still need to examine the
+ // JSObject container for out-params. If it's null or otherwise invalid,
+ // we want to know before the call, rather than after.
+ //
+ // This is a no-op for 'in' params.
+ RootedValue src(mCallContext);
+ if (!GetOutParamSource(i, &src))
+ return false;
+
+ // All that's left to do is value conversion. Bail early if we don't need
+ // to do that.
+ if (!paramInfo.IsIn())
+ return true;
+
+ // We're definitely some variety of 'in' now, so there's something to
+ // convert. The source value for conversion depends on whether we're
+ // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
+ // so all that's left is 'in'.
+ if (!paramInfo.IsOut()) {
+ // Handle the 'in' case.
+ MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
+ "Expected either enough arguments or an optional argument");
+ if (i < mArgc)
+ src = mArgv[i];
+ else if (type_tag == nsXPTType::T_JSVAL)
+ src.setUndefined();
+ else
+ src.setNull();
+ }
+
+ nsID param_iid;
+ if (type_tag == nsXPTType::T_INTERFACE &&
+ NS_FAILED(mIFaceInfo->GetIIDForParamNoAlloc(mVTableIndex, &paramInfo,
+ &param_iid))) {
+ ThrowBadParam(NS_ERROR_XPC_CANT_GET_PARAM_IFACE_INFO, i, mCallContext);
+ return false;
+ }
+
+ // Don't allow CPOWs to be passed to native code (in case they try to cast
+ // to a concrete type).
+ if (src.isObject() &&
+ jsipc::IsWrappedCPOW(&src.toObject()) &&
+ type_tag == nsXPTType::T_INTERFACE &&
+ !param_iid.Equals(NS_GET_IID(nsISupports)))
+ {
+ // Allow passing CPOWs to XPCWrappedJS.
+ nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(mCallee));
+ if (!wrappedJS) {
+ ThrowBadParam(NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE, i, mCallContext);
+ return false;
+ }
+ }
+
+ nsresult err;
+ if (!XPCConvert::JSData2Native(&dp->val, src, type, &param_iid, &err)) {
+ ThrowBadParam(err, i, mCallContext);
+ return false;
+ }
+
+ return true;
+}
+
+bool
+CallMethodHelper::ConvertDependentParams()
+{
+ const uint8_t paramCount = mMethodInfo->GetParamCount();
+ for (uint8_t i = 0; i < paramCount; i++) {
+ const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
+
+ if (!paramInfo.GetType().IsDependent())
+ continue;
+ if (!ConvertDependentParam(i))
+ return false;
+ }
+
+ return true;
+}
+
+bool
+CallMethodHelper::ConvertDependentParam(uint8_t i)
+{
+ const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(i);
+ const nsXPTType& type = paramInfo.GetType();
+ nsXPTType datum_type;
+ uint32_t array_count = 0;
+ bool isArray = type.IsArray();
+
+ bool isSizedString = isArray ?
+ false :
+ type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
+ type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS;
+
+ nsXPTCVariant* dp = GetDispatchParam(i);
+ dp->type = type;
+
+ if (isArray) {
+ if (NS_FAILED(mIFaceInfo->GetTypeForParam(mVTableIndex, &paramInfo, 1,
+ &datum_type))) {
+ Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext);
+ return false;
+ }
+ MOZ_ASSERT(datum_type.TagPart() != nsXPTType::T_JSVAL,
+ "Arrays of JSVals not currently supported - see bug 693337.");
+ } else {
+ datum_type = type;
+ }
+
+ // Specify the correct storage/calling semantics.
+ if (paramInfo.IsIndirect())
+ dp->SetIndirect();
+
+ // We have 3 possible type of dependent parameters: Arrays, Sized Strings,
+ // and iid_is Interface pointers. The latter two always need cleanup, and
+ // arrays need cleanup for all non-arithmetic types. Since the latter two
+ // cases also happen to be non-arithmetic, we can just inspect datum_type
+ // here.
+ if (!datum_type.IsArithmetic())
+ dp->SetValNeedsCleanup();
+
+ // Even if there's nothing to convert, we still need to examine the
+ // JSObject container for out-params. If it's null or otherwise invalid,
+ // we want to know before the call, rather than after.
+ //
+ // This is a no-op for 'in' params.
+ RootedValue src(mCallContext);
+ if (!GetOutParamSource(i, &src))
+ return false;
+
+ // All that's left to do is value conversion. Bail early if we don't need
+ // to do that.
+ if (!paramInfo.IsIn())
+ return true;
+
+ // We're definitely some variety of 'in' now, so there's something to
+ // convert. The source value for conversion depends on whether we're
+ // dealing with an 'in' or an 'inout' parameter. 'inout' was handled above,
+ // so all that's left is 'in'.
+ if (!paramInfo.IsOut()) {
+ // Handle the 'in' case.
+ MOZ_ASSERT(i < mArgc || paramInfo.IsOptional(),
+ "Expected either enough arguments or an optional argument");
+ src = i < mArgc ? mArgv[i] : JS::NullValue();
+ }
+
+ nsID param_iid;
+ if (datum_type.IsInterfacePointer() &&
+ !GetInterfaceTypeFromParam(i, datum_type, &param_iid))
+ return false;
+
+ nsresult err;
+
+ if (isArray || isSizedString) {
+ if (!GetArraySizeFromParam(i, src, &array_count))
+ return false;
+
+ if (isArray) {
+ if (array_count &&
+ !XPCConvert::JSArray2Native((void**)&dp->val, src,
+ array_count, datum_type, &param_iid,
+ &err)) {
+ // XXX need exception scheme for arrays to indicate bad element
+ ThrowBadParam(err, i, mCallContext);
+ return false;
+ }
+ } else // if (isSizedString)
+ {
+ if (!XPCConvert::JSStringWithSize2Native((void*)&dp->val,
+ src, array_count,
+ datum_type, &err)) {
+ ThrowBadParam(err, i, mCallContext);
+ return false;
+ }
+ }
+ } else {
+ if (!XPCConvert::JSData2Native(&dp->val, src, type,
+ &param_iid, &err)) {
+ ThrowBadParam(err, i, mCallContext);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Performs all necessary teardown on a parameter after method invocation.
+//
+// This method should only be called if the value in question was flagged
+// for cleanup (ie, if dp->DoesValNeedCleanup()).
+void
+CallMethodHelper::CleanupParam(nsXPTCMiniVariant& param, nsXPTType& type)
+{
+ // We handle array elements, but not the arrays themselves.
+ MOZ_ASSERT(type.TagPart() != nsXPTType::T_ARRAY, "Can't handle arrays.");
+
+ // Pointers may sometimes be null even if cleanup was requested. Combine
+ // the null checking for all the different types into one check here.
+ if (type.TagPart() != nsXPTType::T_JSVAL && param.val.p == nullptr)
+ return;
+
+ switch (type.TagPart()) {
+ case nsXPTType::T_JSVAL:
+ js::RemoveRawValueRoot(mCallContext, (Value*)&param.val);
+ break;
+ case nsXPTType::T_INTERFACE:
+ case nsXPTType::T_INTERFACE_IS:
+ ((nsISupports*)param.val.p)->Release();
+ break;
+ case nsXPTType::T_ASTRING:
+ case nsXPTType::T_DOMSTRING:
+ nsXPConnect::GetContextInstance()->mScratchStrings.Destroy((nsString*)param.val.p);
+ break;
+ case nsXPTType::T_UTF8STRING:
+ case nsXPTType::T_CSTRING:
+ nsXPConnect::GetContextInstance()->mScratchCStrings.Destroy((nsCString*)param.val.p);
+ break;
+ default:
+ MOZ_ASSERT(!type.IsArithmetic(), "Cleanup requested on unexpected type.");
+ free(param.val.p);
+ break;
+ }
+}
+
+bool
+CallMethodHelper::AllocateStringClass(nsXPTCVariant* dp,
+ const nsXPTParamInfo& paramInfo)
+{
+ // Get something we can make comparisons with.
+ uint8_t type_tag = paramInfo.GetType().TagPart();
+
+ // There should be 4 cases, all strings. Verify that here.
+ MOZ_ASSERT(type_tag == nsXPTType::T_ASTRING ||
+ type_tag == nsXPTType::T_DOMSTRING ||
+ type_tag == nsXPTType::T_UTF8STRING ||
+ type_tag == nsXPTType::T_CSTRING,
+ "Unexpected string class type!");
+
+ // ASTRING and DOMSTRING are very similar, and both use nsString.
+ // UTF8_STRING and CSTRING are also quite similar, and both use nsCString.
+ if (type_tag == nsXPTType::T_ASTRING || type_tag == nsXPTType::T_DOMSTRING)
+ dp->val.p = nsXPConnect::GetContextInstance()->mScratchStrings.Create();
+ else
+ dp->val.p = nsXPConnect::GetContextInstance()->mScratchCStrings.Create();
+
+ // Check for OOM, in either case.
+ if (!dp->val.p) {
+ JS_ReportOutOfMemory(mCallContext);
+ return false;
+ }
+
+ // We allocated, so we need to deallocate after the method call completes.
+ dp->SetValNeedsCleanup();
+
+ return true;
+}
+
+nsresult
+CallMethodHelper::Invoke()
+{
+ uint32_t argc = mDispatchParams.Length();
+ nsXPTCVariant* argv = mDispatchParams.Elements();
+
+ return NS_InvokeByIndex(mCallee, mVTableIndex, argc, argv);
+}
+
+/***************************************************************************/
+// interface methods
+
+JSObject*
+XPCWrappedNative::GetJSObject()
+{
+ return GetFlatJSObject();
+}
+
+NS_IMETHODIMP XPCWrappedNative::GetNative(nsISupports * *aNative)
+{
+ // No need to QI here, we already have the correct nsISupports
+ // vtable.
+ nsCOMPtr<nsISupports> rval = mIdentity;
+ rval.forget(aNative);
+ return NS_OK;
+}
+
+NS_IMETHODIMP XPCWrappedNative::GetJSObjectPrototype(JSObject * *aJSObjectPrototype)
+{
+ *aJSObjectPrototype = HasProto() ?
+ GetProto()->GetJSProtoObject() : GetFlatJSObject();
+ return NS_OK;
+}
+
+nsIPrincipal*
+XPCWrappedNative::GetObjectPrincipal() const
+{
+ nsIPrincipal* principal = GetScope()->GetPrincipal();
+#ifdef DEBUG
+ // Because of inner window reuse, we can have objects with one principal
+ // living in a scope with a different (but same-origin) principal. So
+ // just check same-origin here.
+ nsCOMPtr<nsIScriptObjectPrincipal> objPrin(do_QueryInterface(mIdentity));
+ if (objPrin) {
+ bool equal;
+ if (!principal)
+ equal = !objPrin->GetPrincipal();
+ else
+ principal->Equals(objPrin->GetPrincipal(), &equal);
+ MOZ_ASSERT(equal, "Principal mismatch. Expect bad things to happen");
+ }
+#endif
+ return principal;
+}
+
+NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithMember(HandleId name,
+ nsIInterfaceInfo * *_retval)
+{
+ RefPtr<XPCNativeInterface> iface;
+ XPCNativeMember* member;
+
+ if (GetSet()->FindMember(name, &member, &iface) && iface) {
+ nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo();
+ temp.forget(_retval);
+ } else
+ *_retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP XPCWrappedNative::FindInterfaceWithName(HandleId name,
+ nsIInterfaceInfo * *_retval)
+{
+ XPCNativeInterface* iface = GetSet()->FindNamedInterface(name);
+ if (iface) {
+ nsCOMPtr<nsIInterfaceInfo> temp = iface->GetInterfaceInfo();
+ temp.forget(_retval);
+ } else
+ *_retval = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP_(bool)
+XPCWrappedNative::HasNativeMember(HandleId name)
+{
+ XPCNativeMember* member = nullptr;
+ uint16_t ignored;
+ return GetSet()->FindMember(name, &member, &ignored) && !!member;
+}
+
+NS_IMETHODIMP XPCWrappedNative::DebugDump(int16_t depth)
+{
+#ifdef DEBUG
+ depth-- ;
+ XPC_LOG_ALWAYS(("XPCWrappedNative @ %x with mRefCnt = %d", this, mRefCnt.get()));
+ XPC_LOG_INDENT();
+
+ if (HasProto()) {
+ XPCWrappedNativeProto* proto = GetProto();
+ if (depth && proto)
+ proto->DebugDump(depth);
+ else
+ XPC_LOG_ALWAYS(("mMaybeProto @ %x", proto));
+ } else
+ XPC_LOG_ALWAYS(("Scope @ %x", GetScope()));
+
+ if (depth && mSet)
+ mSet->DebugDump(depth);
+ else
+ XPC_LOG_ALWAYS(("mSet @ %x", mSet.get()));
+
+ XPC_LOG_ALWAYS(("mFlatJSObject of %x", mFlatJSObject.unbarrieredGetPtr()));
+ XPC_LOG_ALWAYS(("mIdentity of %x", mIdentity.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
+ return NS_OK;
+}
+
+/***************************************************************************/
+
+char*
+XPCWrappedNative::ToString(XPCWrappedNativeTearOff* to /* = nullptr */ ) const
+{
+#ifdef DEBUG
+# define FMT_ADDR " @ 0x%p"
+# define FMT_STR(str) str
+# define PARAM_ADDR(w) , w
+#else
+# define FMT_ADDR ""
+# define FMT_STR(str)
+# define PARAM_ADDR(w)
+#endif
+
+ char* sz = nullptr;
+ char* name = nullptr;
+
+ XPCNativeScriptableInfo* si = GetScriptableInfo();
+ if (si)
+ name = JS_smprintf("%s", si->GetJSClass()->name);
+ if (to) {
+ const char* fmt = name ? " (%s)" : "%s";
+ name = JS_sprintf_append(name, fmt,
+ to->GetInterface()->GetNameString());
+ } else if (!name) {
+ XPCNativeSet* set = GetSet();
+ XPCNativeInterface** array = set->GetInterfaceArray();
+ RefPtr<XPCNativeInterface> isupp = XPCNativeInterface::GetISupports();
+ uint16_t count = set->GetInterfaceCount();
+
+ if (count == 1)
+ name = JS_sprintf_append(name, "%s", array[0]->GetNameString());
+ else if (count == 2 && array[0] == isupp) {
+ name = JS_sprintf_append(name, "%s", array[1]->GetNameString());
+ } else {
+ for (uint16_t i = 0; i < count; i++) {
+ const char* fmt = (i == 0) ?
+ "(%s" : (i == count-1) ?
+ ", %s)" : ", %s";
+ name = JS_sprintf_append(name, fmt,
+ array[i]->GetNameString());
+ }
+ }
+ }
+
+ if (!name) {
+ return nullptr;
+ }
+ const char* fmt = "[xpconnect wrapped %s" FMT_ADDR FMT_STR(" (native")
+ FMT_ADDR FMT_STR(")") "]";
+ if (si) {
+ fmt = "[object %s" FMT_ADDR FMT_STR(" (native") FMT_ADDR FMT_STR(")") "]";
+ }
+ sz = JS_smprintf(fmt, name PARAM_ADDR(this) PARAM_ADDR(mIdentity.get()));
+
+ JS_smprintf_free(name);
+
+
+ return sz;
+
+#undef FMT_ADDR
+#undef PARAM_ADDR
+}
+
+/***************************************************************************/
+
+#ifdef XPC_CHECK_CLASSINFO_CLAIMS
+static void DEBUG_CheckClassInfoClaims(XPCWrappedNative* wrapper)
+{
+ if (!wrapper || !wrapper->GetClassInfo())
+ return;
+
+ nsISupports* obj = wrapper->GetIdentityObject();
+ XPCNativeSet* set = wrapper->GetSet();
+ uint16_t count = set->GetInterfaceCount();
+ for (uint16_t i = 0; i < count; i++) {
+ nsIClassInfo* clsInfo = wrapper->GetClassInfo();
+ XPCNativeInterface* iface = set->GetInterfaceAt(i);
+ nsIInterfaceInfo* info = iface->GetInterfaceInfo();
+ const nsIID* iid;
+ nsISupports* ptr;
+
+ info->GetIIDShared(&iid);
+ nsresult rv = obj->QueryInterface(*iid, (void**)&ptr);
+ if (NS_SUCCEEDED(rv)) {
+ NS_RELEASE(ptr);
+ continue;
+ }
+ if (rv == NS_ERROR_OUT_OF_MEMORY)
+ continue;
+
+ // Houston, We have a problem...
+
+ char* className = nullptr;
+ char* contractID = nullptr;
+ const char* interfaceName;
+
+ info->GetNameShared(&interfaceName);
+ clsInfo->GetContractID(&contractID);
+ if (wrapper->GetScriptableInfo()) {
+ wrapper->GetScriptableInfo()->GetCallback()->
+ GetClassName(&className);
+ }
+
+
+ printf("\n!!! Object's nsIClassInfo lies about its interfaces!!!\n"
+ " classname: %s \n"
+ " contractid: %s \n"
+ " unimplemented interface name: %s\n\n",
+ className ? className : "<unknown>",
+ contractID ? contractID : "<unknown>",
+ interfaceName);
+
+ if (className)
+ free(className);
+ if (contractID)
+ free(contractID);
+ }
+}
+#endif
+
+NS_IMPL_ISUPPORTS(XPCJSObjectHolder, nsIXPConnectJSObjectHolder)
+
+JSObject*
+XPCJSObjectHolder::GetJSObject()
+{
+ NS_PRECONDITION(mJSObj, "bad object state");
+ return mJSObj;
+}
+
+XPCJSObjectHolder::XPCJSObjectHolder(JSObject* obj)
+ : mJSObj(obj)
+{
+ MOZ_ASSERT(obj);
+ XPCJSContext::Get()->AddObjectHolderRoot(this);
+}
+
+XPCJSObjectHolder::~XPCJSObjectHolder()
+{
+ RemoveFromRootSet();
+}
+
+void
+XPCJSObjectHolder::TraceJS(JSTracer* trc)
+{
+ JS::TraceEdge(trc, &mJSObj, "XPCJSObjectHolder::mJSObj");
+}
diff --git a/js/xpconnect/src/XPCWrappedNativeInfo.cpp b/js/xpconnect/src/XPCWrappedNativeInfo.cpp
new file mode 100644
index 000000000..302454fb5
--- /dev/null
+++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp
@@ -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
+}
diff --git a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
new file mode 100644
index 000000000..12b203b70
--- /dev/null
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -0,0 +1,1331 @@
+/* -*- 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/. */
+
+/* JavaScript JSClasses and JSOps for our Wrapped Native JS Objects. */
+
+#include "xpcprivate.h"
+#include "xpc_make_class.h"
+#include "jsprf.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/Preferences.h"
+#include "nsIAddonInterposition.h"
+#include "AddonWrapper.h"
+#include "js/Class.h"
+
+using namespace mozilla;
+using namespace JS;
+
+/***************************************************************************/
+
+// All of the exceptions thrown into JS from this file go through here.
+// That makes this a nice place to set a breakpoint.
+
+static bool Throw(nsresult errNum, JSContext* cx)
+{
+ XPCThrower::Throw(errNum, cx);
+ return false;
+}
+
+// Handy macro used in many callback stub below.
+
+#define THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper) \
+ PR_BEGIN_MACRO \
+ if (!wrapper) \
+ return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \
+ if (!wrapper->IsValid()) \
+ return Throw(NS_ERROR_XPC_HAS_BEEN_SHUTDOWN, cx); \
+ PR_END_MACRO
+
+/***************************************************************************/
+
+static bool
+ToStringGuts(XPCCallContext& ccx)
+{
+ char* sz;
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+
+ if (wrapper)
+ sz = wrapper->ToString(ccx.GetTearOff());
+ else
+ sz = JS_smprintf("[xpconnect wrapped native prototype]");
+
+ if (!sz) {
+ JS_ReportOutOfMemory(ccx);
+ return false;
+ }
+
+ JSString* str = JS_NewStringCopyZ(ccx, sz);
+ JS_smprintf_free(sz);
+ if (!str)
+ return false;
+
+ ccx.SetRetVal(JS::StringValue(str));
+ return true;
+}
+
+/***************************************************************************/
+
+static bool
+XPC_WN_Shared_ToString(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+ if (!obj)
+ return false;
+
+ XPCCallContext ccx(cx, obj);
+ if (!ccx.IsValid())
+ return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
+ ccx.SetName(ccx.GetContext()->GetStringID(XPCJSContext::IDX_TO_STRING));
+ ccx.SetArgsAndResultPtr(args.length(), args.array(), vp);
+ return ToStringGuts(ccx);
+}
+
+static bool
+XPC_WN_Shared_ToSource(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ static const char empty[] = "({})";
+ JSString* str = JS_NewStringCopyN(cx, empty, sizeof(empty)-1);
+ if (!str)
+ return false;
+ args.rval().setString(str);
+
+ return true;
+}
+
+static bool
+XPC_WN_Shared_toPrimitive(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ RootedObject obj(cx);
+ if (!JS_ValueToObject(cx, args.thisv(), &obj))
+ return false;
+ XPCCallContext ccx(cx, obj);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ JSType hint;
+ if (!GetFirstArgumentAsTypeHint(cx, args, &hint))
+ return false;
+
+ if (hint == JSTYPE_NUMBER) {
+ args.rval().set(JS_GetNaNValue(cx));
+ return true;
+ }
+
+ MOZ_ASSERT(hint == JSTYPE_STRING || hint == JSTYPE_VOID);
+ ccx.SetName(ccx.GetContext()->GetStringID(XPCJSContext::IDX_TO_STRING));
+ ccx.SetArgsAndResultPtr(0, nullptr, args.rval().address());
+
+ XPCNativeMember* member = ccx.GetMember();
+ if (member && member->IsMethod()) {
+ if (!XPCWrappedNative::CallMethod(ccx))
+ return false;
+
+ if (args.rval().isPrimitive())
+ return true;
+ }
+
+ // else...
+ return ToStringGuts(ccx);
+}
+
+/***************************************************************************/
+
+// A "double wrapped object" is a user JSObject that has been wrapped as a
+// wrappedJS in order to be used by native code and then re-wrapped by a
+// wrappedNative wrapper to be used by JS code. One might think of it as:
+// wrappedNative(wrappedJS(underlying_JSObject))
+// This is done (as opposed to just unwrapping the wrapped JS and automatically
+// returning the underlying JSObject) so that JS callers will see what looks
+// Like any other xpcom object - and be limited to use its interfaces.
+//
+// See the comment preceding nsIXPCWrappedJSObjectGetter in nsIXPConnect.idl.
+
+static JSObject*
+GetDoubleWrappedJSObject(XPCCallContext& ccx, XPCWrappedNative* wrapper)
+{
+ RootedObject obj(ccx);
+ nsCOMPtr<nsIXPConnectWrappedJS>
+ underware = do_QueryInterface(wrapper->GetIdentityObject());
+ if (underware) {
+ RootedObject mainObj(ccx, underware->GetJSObject());
+ if (mainObj) {
+ RootedId id(ccx, ccx.GetContext()->
+ GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT));
+
+ JSAutoCompartment ac(ccx, mainObj);
+
+ RootedValue val(ccx);
+ if (JS_GetPropertyById(ccx, mainObj, id, &val) &&
+ !val.isPrimitive()) {
+ obj = val.toObjectOrNull();
+ }
+ }
+ }
+ return obj;
+}
+
+// This is the getter native function we use to handle 'wrappedJSObject' for
+// double wrapped JSObjects.
+
+static bool
+XPC_WN_DoubleWrappedGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+ if (!obj)
+ return false;
+
+ XPCCallContext ccx(cx, obj);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
+
+ RootedObject realObject(cx, GetDoubleWrappedJSObject(ccx, wrapper));
+ if (!realObject) {
+ // This is pretty unexpected at this point. The object originally
+ // responded to this get property call and now gives no object.
+ // XXX Should this throw something at the caller?
+ args.rval().setNull();
+ return true;
+ }
+
+ // It is a double wrapped object. This should really never appear in
+ // content these days, but addons still do it - see bug 965921.
+ if (MOZ_UNLIKELY(!nsContentUtils::IsCallerChrome())) {
+ JS_ReportErrorASCII(cx, "Attempt to use .wrappedJSObject in untrusted code");
+ return false;
+ }
+ args.rval().setObject(*realObject);
+ return JS_WrapValue(cx, args.rval());
+}
+
+/***************************************************************************/
+
+// This is our shared function to define properties on our JSObjects.
+
+/*
+ * NOTE:
+ * We *never* set the tearoff names (e.g. nsIFoo) as JS_ENUMERATE.
+ * We *never* set toString or toSource as JS_ENUMERATE.
+ */
+
+static bool
+DefinePropertyIfFound(XPCCallContext& ccx,
+ HandleObject obj,
+ HandleId idArg,
+ XPCNativeSet* set,
+ XPCNativeInterface* ifaceArg,
+ XPCNativeMember* member,
+ XPCWrappedNativeScope* scope,
+ bool reflectToStringAndToSource,
+ XPCWrappedNative* wrapperToReflectInterfaceNames,
+ XPCWrappedNative* wrapperToReflectDoubleWrap,
+ XPCNativeScriptableInfo* scriptableInfo,
+ unsigned propFlags,
+ bool* resolved)
+{
+ RootedId id(ccx, idArg);
+ RefPtr<XPCNativeInterface> iface = ifaceArg;
+ XPCJSContext* xpccx = ccx.GetContext();
+ bool found;
+ const char* name;
+
+ propFlags |= JSPROP_RESOLVING;
+
+ if (set) {
+ if (iface)
+ found = true;
+ else
+ found = set->FindMember(id, &member, &iface);
+ } else
+ found = (nullptr != (member = iface->FindMember(id)));
+
+ if (!found) {
+ if (reflectToStringAndToSource) {
+ JSNative call;
+ uint32_t flags = 0;
+
+ if (scriptableInfo) {
+ nsCOMPtr<nsIClassInfo> classInfo = do_QueryInterface(
+ scriptableInfo->GetCallback());
+
+ if (classInfo) {
+ nsresult rv = classInfo->GetFlags(&flags);
+ if (NS_FAILED(rv))
+ return Throw(rv, ccx);
+ }
+ }
+
+ bool overwriteToString = !(flags & nsIClassInfo::DOM_OBJECT)
+ || Preferences::GetBool("dom.XPCToStringForDOMClasses", false);
+
+ if(id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING)
+ && overwriteToString)
+ {
+ call = XPC_WN_Shared_ToString;
+ name = xpccx->GetStringName(XPCJSContext::IDX_TO_STRING);
+ } else if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE)) {
+ call = XPC_WN_Shared_ToSource;
+ name = xpccx->GetStringName(XPCJSContext::IDX_TO_SOURCE);
+ } else if (id == SYMBOL_TO_JSID(
+ JS::GetWellKnownSymbol(ccx, JS::SymbolCode::toPrimitive)))
+ {
+ call = XPC_WN_Shared_toPrimitive;
+ name = "[Symbol.toPrimitive]";
+ } else {
+ call = nullptr;
+ }
+
+ if (call) {
+ RootedFunction fun(ccx, JS_NewFunction(ccx, call, 0, 0, name));
+ if (!fun) {
+ JS_ReportOutOfMemory(ccx);
+ return false;
+ }
+
+ AutoResolveName arn(ccx, id);
+ if (resolved)
+ *resolved = true;
+ RootedObject value(ccx, JS_GetFunctionObject(fun));
+ return JS_DefinePropertyById(ccx, obj, id, value,
+ propFlags & ~JSPROP_ENUMERATE);
+ }
+ }
+ // This *might* be a tearoff name that is not yet part of our
+ // set. Let's lookup the name and see if it is the name of an
+ // interface. Then we'll see if the object actually *does* this
+ // interface and add a tearoff as necessary.
+
+ if (wrapperToReflectInterfaceNames) {
+ JSAutoByteString name;
+ RefPtr<XPCNativeInterface> iface2;
+ XPCWrappedNativeTearOff* to;
+ RootedObject jso(ccx);
+ nsresult rv = NS_OK;
+
+ if (JSID_IS_STRING(id) &&
+ name.encodeLatin1(ccx, JSID_TO_STRING(id)) &&
+ (iface2 = XPCNativeInterface::GetNewOrUsed(name.ptr()), iface2) &&
+ nullptr != (to = wrapperToReflectInterfaceNames->
+ FindTearOff(iface2, true, &rv)) &&
+ nullptr != (jso = to->GetJSObject()))
+
+ {
+ AutoResolveName arn(ccx, id);
+ if (resolved)
+ *resolved = true;
+ return JS_DefinePropertyById(ccx, obj, id, jso,
+ propFlags & ~JSPROP_ENUMERATE);
+ } else if (NS_FAILED(rv) && rv != NS_ERROR_NO_INTERFACE) {
+ return Throw(rv, ccx);
+ }
+ }
+
+ // This *might* be a double wrapped JSObject
+ if (wrapperToReflectDoubleWrap &&
+ id == xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT) &&
+ GetDoubleWrappedJSObject(ccx, wrapperToReflectDoubleWrap)) {
+ // We build and add a getter function.
+ // A security check is done on a per-get basis.
+
+ JSFunction* fun;
+
+ id = xpccx->GetStringID(XPCJSContext::IDX_WRAPPED_JSOBJECT);
+ name = xpccx->GetStringName(XPCJSContext::IDX_WRAPPED_JSOBJECT);
+
+ fun = JS_NewFunction(ccx, XPC_WN_DoubleWrappedGetter,
+ 0, 0, name);
+
+ if (!fun)
+ return false;
+
+ RootedObject funobj(ccx, JS_GetFunctionObject(fun));
+ if (!funobj)
+ return false;
+
+ propFlags |= JSPROP_GETTER | JSPROP_SHARED;
+ propFlags &= ~JSPROP_ENUMERATE;
+
+ AutoResolveName arn(ccx, id);
+ if (resolved)
+ *resolved = true;
+ return JS_DefinePropertyById(ccx, obj, id, UndefinedHandleValue, propFlags,
+ JS_DATA_TO_FUNC_PTR(JSNative, funobj.get()),
+ nullptr);
+ }
+
+ if (resolved)
+ *resolved = false;
+ return true;
+ }
+
+ if (!member) {
+ if (wrapperToReflectInterfaceNames) {
+ XPCWrappedNativeTearOff* to =
+ wrapperToReflectInterfaceNames->FindTearOff(iface, true);
+
+ if (!to)
+ return false;
+ RootedObject jso(ccx, to->GetJSObject());
+ if (!jso)
+ return false;
+
+ AutoResolveName arn(ccx, id);
+ if (resolved)
+ *resolved = true;
+ return JS_DefinePropertyById(ccx, obj, id, jso,
+ propFlags & ~JSPROP_ENUMERATE);
+ }
+ if (resolved)
+ *resolved = false;
+ return true;
+ }
+
+ if (member->IsConstant()) {
+ RootedValue val(ccx);
+ AutoResolveName arn(ccx, id);
+ if (resolved)
+ *resolved = true;
+ return member->GetConstantValue(ccx, iface, val.address()) &&
+ JS_DefinePropertyById(ccx, obj, id, val, propFlags);
+ }
+
+ if (scope->HasInterposition()) {
+ Rooted<PropertyDescriptor> desc(ccx);
+ if (!xpc::InterposeProperty(ccx, obj, iface->GetIID(), id, &desc))
+ return false;
+
+ if (desc.object()) {
+ AutoResolveName arn(ccx, id);
+ if (resolved)
+ *resolved = true;
+ desc.attributesRef() |= JSPROP_RESOLVING;
+ return JS_DefinePropertyById(ccx, obj, id, desc);
+ }
+ }
+
+ if (id == xpccx->GetStringID(XPCJSContext::IDX_TO_STRING) ||
+ id == xpccx->GetStringID(XPCJSContext::IDX_TO_SOURCE) ||
+ (scriptableInfo &&
+ scriptableInfo->GetFlags().DontEnumQueryInterface() &&
+ id == xpccx->GetStringID(XPCJSContext::IDX_QUERY_INTERFACE)))
+ propFlags &= ~JSPROP_ENUMERATE;
+
+ RootedValue funval(ccx);
+ if (!member->NewFunctionObject(ccx, iface, obj, funval.address()))
+ return false;
+
+ if (member->IsMethod()) {
+ AutoResolveName arn(ccx, id);
+ if (resolved)
+ *resolved = true;
+ return JS_DefinePropertyById(ccx, obj, id, funval, propFlags);
+ }
+
+ // else...
+
+ MOZ_ASSERT(member->IsAttribute(), "way broken!");
+
+ propFlags |= JSPROP_GETTER | JSPROP_SHARED;
+ propFlags &= ~JSPROP_READONLY;
+ JSObject* funobj = funval.toObjectOrNull();
+ JSNative getter = JS_DATA_TO_FUNC_PTR(JSNative, funobj);
+ JSNative setter;
+ if (member->IsWritableAttribute()) {
+ propFlags |= JSPROP_SETTER;
+ setter = JS_DATA_TO_FUNC_PTR(JSNative, funobj);
+ } else {
+ setter = nullptr;
+ }
+
+ AutoResolveName arn(ccx, id);
+ if (resolved)
+ *resolved = true;
+
+ return JS_DefinePropertyById(ccx, obj, id, UndefinedHandleValue, propFlags, getter, setter);
+}
+
+/***************************************************************************/
+/***************************************************************************/
+
+static bool
+XPC_WN_OnlyIWrite_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
+{
+ XPCCallContext ccx(cx, obj, nullptr, id);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ // Allow only XPConnect to add/set the property
+ if (ccx.GetResolveName() == id)
+ return true;
+
+ return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
+}
+
+bool
+XPC_WN_CannotModifyPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
+ HandleValue v)
+{
+ return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
+}
+
+bool
+XPC_WN_CantDeletePropertyStub(JSContext* cx, HandleObject obj, HandleId id,
+ ObjectOpResult& result)
+{
+ return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
+}
+
+bool
+XPC_WN_CannotModifySetPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
+ MutableHandleValue vp, ObjectOpResult& result)
+{
+ return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
+}
+
+bool
+XPC_WN_Shared_Enumerate(JSContext* cx, HandleObject obj)
+{
+ XPCCallContext ccx(cx, obj);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ // Since we aren't going to enumerate tearoff names and the prototype
+ // handles non-mutated members, we can do this potential short-circuit.
+ if (!wrapper->HasMutatedSet())
+ return true;
+
+ XPCNativeSet* set = wrapper->GetSet();
+ XPCNativeSet* protoSet = wrapper->HasProto() ?
+ wrapper->GetProto()->GetSet() : nullptr;
+
+ uint16_t interface_count = set->GetInterfaceCount();
+ XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
+ for (uint16_t i = 0; i < interface_count; i++) {
+ XPCNativeInterface* iface = interfaceArray[i];
+ uint16_t member_count = iface->GetMemberCount();
+ for (uint16_t k = 0; k < member_count; k++) {
+ XPCNativeMember* member = iface->GetMemberAt(k);
+ jsid name = member->GetName();
+
+ // Skip if this member is going to come from the proto.
+ uint16_t index;
+ if (protoSet &&
+ protoSet->FindMember(name, nullptr, &index) && index == i)
+ continue;
+ if (!xpc_ForcePropertyResolve(cx, obj, name))
+ return false;
+ }
+ }
+ return true;
+}
+
+/***************************************************************************/
+
+enum WNHelperType {
+ WN_NOHELPER,
+ WN_HELPER
+};
+
+static void
+WrappedNativeFinalize(js::FreeOp* fop, JSObject* obj, WNHelperType helperType)
+{
+ const js::Class* clazz = js::GetObjectClass(obj);
+ if (clazz->flags & JSCLASS_DOM_GLOBAL) {
+ mozilla::dom::DestroyProtoAndIfaceCache(obj);
+ }
+ nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
+ if (!p)
+ return;
+
+ XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
+ if (helperType == WN_HELPER)
+ wrapper->GetScriptableCallback()->Finalize(wrapper, js::CastToJSFreeOp(fop), obj);
+ wrapper->FlatJSObjectFinalized();
+}
+
+static void
+WrappedNativeObjectMoved(JSObject* obj, const JSObject* old)
+{
+ nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
+ if (!p)
+ return;
+
+ XPCWrappedNative* wrapper = static_cast<XPCWrappedNative*>(p);
+ wrapper->FlatJSObjectMoved(obj, old);
+}
+
+void
+XPC_WN_NoHelper_Finalize(js::FreeOp* fop, JSObject* obj)
+{
+ WrappedNativeFinalize(fop, obj, WN_NOHELPER);
+}
+
+/*
+ * General comment about XPConnect tracing: Given a C++ object |wrapper| and its
+ * corresponding JS object |obj|, calling |wrapper->TraceSelf| will ask the JS
+ * engine to mark |obj|. Eventually, this will lead to the trace hook being
+ * called for |obj|. The trace hook should call |wrapper->TraceInside|, which
+ * should mark any JS objects held by |wrapper| as members.
+ */
+
+/* static */ void
+XPCWrappedNative::Trace(JSTracer* trc, JSObject* obj)
+{
+ const js::Class* clazz = js::GetObjectClass(obj);
+ if (clazz->flags & JSCLASS_DOM_GLOBAL) {
+ mozilla::dom::TraceProtoAndIfaceCache(trc, obj);
+ }
+ MOZ_ASSERT(IS_WN_CLASS(clazz));
+
+ XPCWrappedNative* wrapper = XPCWrappedNative::Get(obj);
+ if (wrapper && wrapper->IsValid())
+ wrapper->TraceInside(trc);
+}
+
+void
+XPCWrappedNative_Trace(JSTracer* trc, JSObject* obj)
+{
+ XPCWrappedNative::Trace(trc, obj);
+}
+
+static bool
+XPC_WN_NoHelper_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+{
+ XPCCallContext ccx(cx, obj, nullptr, id);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ XPCNativeSet* set = ccx.GetSet();
+ if (!set)
+ return true;
+
+ // Don't resolve properties that are on our prototype.
+ if (ccx.GetInterface() && !ccx.GetStaticMemberIsLocal())
+ return true;
+
+ return DefinePropertyIfFound(ccx, obj, id,
+ set, nullptr, nullptr, wrapper->GetScope(),
+ true, wrapper, wrapper, nullptr,
+ JSPROP_ENUMERATE |
+ JSPROP_READONLY |
+ JSPROP_PERMANENT,
+ resolvedp);
+}
+
+static const js::ClassOps XPC_WN_NoHelper_JSClassOps = {
+ XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty
+ XPC_WN_CantDeletePropertyStub, // delProperty
+ nullptr, // getProperty
+ nullptr, // setProperty
+ XPC_WN_Shared_Enumerate, // enumerate
+ XPC_WN_NoHelper_Resolve, // resolve
+ nullptr, // mayResolve
+ XPC_WN_NoHelper_Finalize, // finalize
+ nullptr, // call
+ nullptr, // construct
+ nullptr, // hasInstance
+ XPCWrappedNative::Trace, // trace
+};
+
+const js::ClassExtension XPC_WN_JSClassExtension = {
+ nullptr, // weakmapKeyDelegateOp
+ WrappedNativeObjectMoved
+};
+
+const js::Class XPC_WN_NoHelper_JSClass = {
+ "XPCWrappedNative_NoHelper",
+ XPC_WRAPPER_FLAGS |
+ JSCLASS_IS_WRAPPED_NATIVE |
+ JSCLASS_PRIVATE_IS_NSISUPPORTS |
+ JSCLASS_FOREGROUND_FINALIZE,
+ &XPC_WN_NoHelper_JSClassOps,
+ JS_NULL_CLASS_SPEC,
+ &XPC_WN_JSClassExtension,
+ JS_NULL_OBJECT_OPS
+};
+
+
+/***************************************************************************/
+
+bool
+XPC_WN_MaybeResolvingPropertyStub(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
+{
+ XPCCallContext ccx(cx, obj);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ if (ccx.GetResolvingWrapper() == wrapper)
+ return true;
+ return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
+}
+
+bool
+XPC_WN_MaybeResolvingSetPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
+ MutableHandleValue vp, ObjectOpResult& result)
+{
+ result.succeed();
+ return XPC_WN_MaybeResolvingPropertyStub(cx, obj, id, vp);
+}
+
+bool
+XPC_WN_MaybeResolvingDeletePropertyStub(JSContext* cx, HandleObject obj, HandleId id,
+ ObjectOpResult& result)
+{
+ XPCCallContext ccx(cx, obj);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ if (ccx.GetResolvingWrapper() == wrapper) {
+ return result.succeed();
+ }
+ return Throw(NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN, cx);
+}
+
+// macro fun!
+#define PRE_HELPER_STUB \
+ /* It's very important for "unwrapped" to be rooted here. */ \
+ RootedObject unwrapped(cx, js::CheckedUnwrap(obj, false)); \
+ if (!unwrapped) { \
+ JS_ReportErrorASCII(cx, "Permission denied to operate on object."); \
+ return false; \
+ } \
+ if (!IS_WN_REFLECTOR(unwrapped)) { \
+ return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx); \
+ } \
+ XPCWrappedNative* wrapper = XPCWrappedNative::Get(unwrapped); \
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper); \
+ bool retval = true; \
+ nsresult rv = wrapper->GetScriptableCallback()->
+
+#define POST_HELPER_STUB \
+ if (NS_FAILED(rv)) \
+ return Throw(rv, cx); \
+ return retval;
+
+#define POST_HELPER_STUB_WITH_OBJECTOPRESULT(failMethod) \
+ if (NS_FAILED(rv)) \
+ return Throw(rv, cx); \
+ return retval ? result.succeed() : result.failMethod();
+
+bool
+XPC_WN_Helper_GetProperty(JSContext* cx, HandleObject obj, HandleId id,
+ MutableHandleValue vp)
+{
+ PRE_HELPER_STUB
+ GetProperty(wrapper, cx, obj, id, vp.address(), &retval);
+ POST_HELPER_STUB
+}
+
+bool
+XPC_WN_Helper_SetProperty(JSContext* cx, HandleObject obj, HandleId id,
+ MutableHandleValue vp, ObjectOpResult& result)
+{
+ PRE_HELPER_STUB
+ SetProperty(wrapper, cx, obj, id, vp.address(), &retval);
+ POST_HELPER_STUB_WITH_OBJECTOPRESULT(failReadOnly)
+}
+
+bool
+XPC_WN_Helper_Call(JSContext* cx, unsigned argc, Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ // N.B. we want obj to be the callee, not JS_THIS(cx, vp)
+ RootedObject obj(cx, &args.callee());
+
+ XPCCallContext ccx(cx, obj, nullptr, JSID_VOIDHANDLE, args.length(),
+ args.array(), args.rval().address());
+ if (!ccx.IsValid())
+ return false;
+
+ PRE_HELPER_STUB
+ Call(wrapper, cx, obj, args, &retval);
+ POST_HELPER_STUB
+}
+
+bool
+XPC_WN_Helper_Construct(JSContext* cx, unsigned argc, Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ RootedObject obj(cx, &args.callee());
+ if (!obj)
+ return false;
+
+ XPCCallContext ccx(cx, obj, nullptr, JSID_VOIDHANDLE, args.length(),
+ args.array(), args.rval().address());
+ if (!ccx.IsValid())
+ return false;
+
+ PRE_HELPER_STUB
+ Construct(wrapper, cx, obj, args, &retval);
+ POST_HELPER_STUB
+}
+
+bool
+XPC_WN_Helper_HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue valp, bool* bp)
+{
+ bool retval2;
+ PRE_HELPER_STUB
+ HasInstance(wrapper, cx, obj, valp, &retval2, &retval);
+ *bp = retval2;
+ POST_HELPER_STUB
+}
+
+void
+XPC_WN_Helper_Finalize(js::FreeOp* fop, JSObject* obj)
+{
+ WrappedNativeFinalize(fop, obj, WN_HELPER);
+}
+
+bool
+XPC_WN_Helper_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+{
+ nsresult rv = NS_OK;
+ bool retval = true;
+ bool resolved = false;
+ XPCCallContext ccx(cx, obj);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ RootedId old(cx, ccx.SetResolveName(id));
+
+ XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
+ if (si && si->GetFlags().WantResolve()) {
+ XPCWrappedNative* oldResolvingWrapper;
+ bool allowPropMods = si->GetFlags().AllowPropModsDuringResolve();
+
+ if (allowPropMods)
+ oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
+
+ rv = si->GetCallback()->Resolve(wrapper, cx, obj, id, &resolved, &retval);
+
+ if (allowPropMods)
+ (void)ccx.SetResolvingWrapper(oldResolvingWrapper);
+ }
+
+ old = ccx.SetResolveName(old);
+ MOZ_ASSERT(old == id, "bad nest");
+
+ if (NS_FAILED(rv)) {
+ return Throw(rv, cx);
+ }
+
+ if (resolved) {
+ *resolvedp = true;
+ } else if (wrapper->HasMutatedSet()) {
+ // We are here if scriptable did not resolve this property and
+ // it *might* be in the instance set but not the proto set.
+
+ XPCNativeSet* set = wrapper->GetSet();
+ XPCNativeSet* protoSet = wrapper->HasProto() ?
+ wrapper->GetProto()->GetSet() : nullptr;
+ XPCNativeMember* member = nullptr;
+ RefPtr<XPCNativeInterface> iface;
+ bool IsLocal = false;
+
+ if (set->FindMember(id, &member, &iface, protoSet, &IsLocal) &&
+ IsLocal) {
+ XPCWrappedNative* oldResolvingWrapper;
+
+ XPCNativeScriptableFlags siFlags(0);
+ if (si)
+ siFlags = si->GetFlags();
+
+ XPCWrappedNative* wrapperForInterfaceNames =
+ siFlags.DontReflectInterfaceNames() ? nullptr : wrapper;
+
+ oldResolvingWrapper = ccx.SetResolvingWrapper(wrapper);
+ retval = DefinePropertyIfFound(ccx, obj, id,
+ set, iface, member,
+ wrapper->GetScope(),
+ false,
+ wrapperForInterfaceNames,
+ nullptr, si,
+ JSPROP_ENUMERATE, resolvedp);
+ (void)ccx.SetResolvingWrapper(oldResolvingWrapper);
+ }
+ }
+
+ return retval;
+}
+
+bool
+XPC_WN_Helper_Enumerate(JSContext* cx, HandleObject obj)
+{
+ XPCCallContext ccx(cx, obj);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
+ if (!si || !si->GetFlags().WantEnumerate())
+ return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
+
+ if (!XPC_WN_Shared_Enumerate(cx, obj))
+ return false;
+
+ bool retval = true;
+ nsresult rv = si->GetCallback()->Enumerate(wrapper, cx, obj, &retval);
+ if (NS_FAILED(rv))
+ return Throw(rv, cx);
+ return retval;
+}
+
+/***************************************************************************/
+
+static bool
+XPC_WN_JSOp_Enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties,
+ bool enumerableOnly)
+{
+ XPCCallContext ccx(cx, obj);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
+ if (!si || !si->GetFlags().WantNewEnumerate())
+ return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
+
+ if (!XPC_WN_Shared_Enumerate(cx, obj))
+ return false;
+
+ bool retval = true;
+ nsresult rv = si->GetCallback()->NewEnumerate(wrapper, cx, obj, properties, &retval);
+ if (NS_FAILED(rv))
+ return Throw(rv, cx);
+ return retval;
+}
+
+/***************************************************************************/
+
+// static
+XPCNativeScriptableInfo*
+XPCNativeScriptableInfo::Construct(const XPCNativeScriptableCreateInfo* sci)
+{
+ MOZ_ASSERT(sci, "bad param");
+ nsCOMPtr<nsIXPCScriptable> callback = sci->GetCallback();
+ MOZ_ASSERT(callback);
+ MOZ_ASSERT(callback->GetScriptableFlags() == sci->GetFlags());
+ return new XPCNativeScriptableInfo(callback);
+}
+
+const js::ObjectOps XPC_WN_ObjectOpsWithEnumerate = {
+ nullptr, // lookupProperty
+ nullptr, // defineProperty
+ nullptr, // hasProperty
+ nullptr, // getProperty
+ nullptr, // setProperty
+ nullptr, // getOwnPropertyDescriptor
+ nullptr, // deleteProperty
+ nullptr, // watch
+ nullptr, // unwatch
+ nullptr, // getElements
+ XPC_WN_JSOp_Enumerate,
+ nullptr, // funToString
+};
+
+/***************************************************************************/
+/***************************************************************************/
+
+// Compatibility hack.
+//
+// XPConnect used to do all sorts of funny tricks to find the "correct"
+// |this| object for a given method (often to the detriment of proper
+// call/apply). When these tricks were removed, a fair amount of chrome
+// code broke, because it was relying on being able to grab methods off
+// some XPCOM object (like the nsITelemetry service) and invoke them without
+// a proper |this|. So, if it's quite clear that we're in this situation and
+// about to use a |this| argument that just won't work, fix things up.
+//
+// This hack is only useful for getters/setters if someone sets an XPCOM object
+// as the prototype for a vanilla JS object and expects the XPCOM attributes to
+// work on the derived object, which we really don't want to support. But we
+// handle it anyway, for now, to minimize regression risk on an already-risky
+// landing.
+//
+// This hack is mainly useful for the NoHelper JSClass. We also fix up
+// Components.utils because it implements nsIXPCScriptable (giving it a custom
+// JSClass) but not nsIClassInfo (which would put the methods on a prototype).
+
+#define IS_NOHELPER_CLASS(clasp) (clasp == &XPC_WN_NoHelper_JSClass)
+#define IS_CU_CLASS(clasp) (clasp->name[0] == 'n' && !strcmp(clasp->name, "nsXPCComponents_Utils"))
+
+MOZ_ALWAYS_INLINE JSObject*
+FixUpThisIfBroken(JSObject* obj, JSObject* funobj)
+{
+ if (funobj) {
+ JSObject* parentObj =
+ &js::GetFunctionNativeReserved(funobj,
+ XPC_FUNCTION_PARENT_OBJECT_SLOT).toObject();
+ const js::Class* parentClass = js::GetObjectClass(parentObj);
+ if (MOZ_UNLIKELY((IS_NOHELPER_CLASS(parentClass) || IS_CU_CLASS(parentClass)) &&
+ (js::GetObjectClass(obj) != parentClass)))
+ {
+ return parentObj;
+ }
+ }
+ return obj;
+}
+
+bool
+XPC_WN_CallMethod(JSContext* cx, unsigned argc, Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
+ RootedObject funobj(cx, &args.callee());
+
+ RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+ if (!obj)
+ return false;
+
+ obj = FixUpThisIfBroken(obj, funobj);
+ XPCCallContext ccx(cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
+ args.array(), vp);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ RefPtr<XPCNativeInterface> iface;
+ XPCNativeMember* member;
+
+ if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member))
+ return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx);
+ ccx.SetCallInfo(iface, member, false);
+ return XPCWrappedNative::CallMethod(ccx);
+}
+
+bool
+XPC_WN_GetterSetter(JSContext* cx, unsigned argc, Value* vp)
+{
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ MOZ_ASSERT(JS_TypeOfValue(cx, args.calleev()) == JSTYPE_FUNCTION, "bad function");
+ RootedObject funobj(cx, &args.callee());
+
+ RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+ if (!obj)
+ return false;
+
+ obj = FixUpThisIfBroken(obj, funobj);
+ XPCCallContext ccx(cx, obj, funobj, JSID_VOIDHANDLE, args.length(),
+ args.array(), vp);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ RefPtr<XPCNativeInterface> iface;
+ XPCNativeMember* member;
+
+ if (!XPCNativeMember::GetCallInfo(funobj, &iface, &member))
+ return Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, cx);
+
+ if (args.length() != 0 && member->IsWritableAttribute()) {
+ ccx.SetCallInfo(iface, member, true);
+ bool retval = XPCWrappedNative::SetAttribute(ccx);
+ if (retval)
+ args.rval().set(args[0]);
+ return retval;
+ }
+ // else...
+
+ ccx.SetCallInfo(iface, member, false);
+ return XPCWrappedNative::GetAttribute(ccx);
+}
+
+/***************************************************************************/
+
+static bool
+XPC_WN_Shared_Proto_Enumerate(JSContext* cx, HandleObject obj)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_Proto_JSClass ||
+ js::GetObjectClass(obj) == &XPC_WN_NoMods_Proto_JSClass,
+ "bad proto");
+ XPCWrappedNativeProto* self =
+ (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
+ if (!self)
+ return false;
+
+ XPCNativeSet* set = self->GetSet();
+ if (!set)
+ return false;
+
+ XPCCallContext ccx(cx);
+ if (!ccx.IsValid())
+ return false;
+
+ uint16_t interface_count = set->GetInterfaceCount();
+ XPCNativeInterface** interfaceArray = set->GetInterfaceArray();
+ for (uint16_t i = 0; i < interface_count; i++) {
+ XPCNativeInterface* iface = interfaceArray[i];
+ uint16_t member_count = iface->GetMemberCount();
+
+ for (uint16_t k = 0; k < member_count; k++) {
+ if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName()))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void
+XPC_WN_Shared_Proto_Finalize(js::FreeOp* fop, JSObject* obj)
+{
+ // This can be null if xpc shutdown has already happened
+ XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
+ if (p)
+ p->JSProtoObjectFinalized(fop, obj);
+}
+
+static void
+XPC_WN_Shared_Proto_ObjectMoved(JSObject* obj, const JSObject* old)
+{
+ // This can be null if xpc shutdown has already happened
+ XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
+ if (p)
+ p->JSProtoObjectMoved(obj, old);
+}
+
+static void
+XPC_WN_Shared_Proto_Trace(JSTracer* trc, JSObject* obj)
+{
+ // This can be null if xpc shutdown has already happened
+ XPCWrappedNativeProto* p =
+ (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
+ if (p)
+ p->TraceInside(trc);
+}
+
+/*****************************************************/
+
+static bool
+XPC_WN_ModsAllowed_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvep)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_ModsAllowed_Proto_JSClass,
+ "bad proto");
+
+ XPCWrappedNativeProto* self =
+ (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
+ if (!self)
+ return false;
+
+ XPCCallContext ccx(cx);
+ if (!ccx.IsValid())
+ return false;
+
+ XPCNativeScriptableInfo* si = self->GetScriptableInfo();
+ return DefinePropertyIfFound(ccx, obj, id,
+ self->GetSet(), nullptr, nullptr,
+ self->GetScope(),
+ true, nullptr, nullptr, si,
+ JSPROP_ENUMERATE, resolvep);
+}
+
+static const js::ClassOps XPC_WN_ModsAllowed_Proto_JSClassOps = {
+ nullptr, // addProperty
+ nullptr, // delProperty
+ nullptr, // getProperty
+ nullptr, // setProperty
+ XPC_WN_Shared_Proto_Enumerate, // enumerate
+ XPC_WN_ModsAllowed_Proto_Resolve, // resolve
+ nullptr, // mayResolve
+ XPC_WN_Shared_Proto_Finalize, // finalize
+ nullptr, // call
+ nullptr, // construct
+ nullptr, // hasInstance
+ XPC_WN_Shared_Proto_Trace, // trace
+};
+
+static const js::ClassExtension XPC_WN_Shared_Proto_ClassExtension = {
+ nullptr, /* weakmapKeyDelegateOp */
+ XPC_WN_Shared_Proto_ObjectMoved
+};
+
+const js::Class XPC_WN_ModsAllowed_Proto_JSClass = {
+ "XPC_WN_ModsAllowed_Proto_JSClass",
+ XPC_WRAPPER_FLAGS,
+ &XPC_WN_ModsAllowed_Proto_JSClassOps,
+ JS_NULL_CLASS_SPEC,
+ &XPC_WN_Shared_Proto_ClassExtension,
+ JS_NULL_OBJECT_OPS
+};
+
+/***************************************************************************/
+
+static bool
+XPC_WN_OnlyIWrite_Proto_AddPropertyStub(JSContext* cx, HandleObject obj, HandleId id,
+ HandleValue v)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_Proto_JSClass,
+ "bad proto");
+
+ XPCWrappedNativeProto* self =
+ (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
+ if (!self)
+ return false;
+
+ XPCCallContext ccx(cx);
+ if (!ccx.IsValid())
+ return false;
+
+ // Allow XPConnect to add the property only
+ if (ccx.GetResolveName() == id)
+ return true;
+
+ return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
+}
+
+static bool
+XPC_WN_NoMods_Proto_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+{
+ MOZ_ASSERT(js::GetObjectClass(obj) == &XPC_WN_NoMods_Proto_JSClass,
+ "bad proto");
+
+ XPCWrappedNativeProto* self =
+ (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
+ if (!self)
+ return false;
+
+ XPCCallContext ccx(cx);
+ if (!ccx.IsValid())
+ return false;
+
+ XPCNativeScriptableInfo* si = self->GetScriptableInfo();
+
+ return DefinePropertyIfFound(ccx, obj, id,
+ self->GetSet(), nullptr, nullptr,
+ self->GetScope(),
+ true, nullptr, nullptr, si,
+ JSPROP_READONLY |
+ JSPROP_PERMANENT |
+ JSPROP_ENUMERATE, resolvedp);
+}
+
+static const js::ClassOps XPC_WN_NoMods_Proto_JSClassOps = {
+ XPC_WN_OnlyIWrite_Proto_AddPropertyStub, // addProperty
+ XPC_WN_CantDeletePropertyStub, // delProperty
+ nullptr, // getProperty
+ nullptr, // setProperty
+ XPC_WN_Shared_Proto_Enumerate, // enumerate
+ XPC_WN_NoMods_Proto_Resolve, // resolve
+ nullptr, // mayResolve
+ XPC_WN_Shared_Proto_Finalize, // finalize
+ nullptr, // call
+ nullptr, // construct
+ nullptr, // hasInstance
+ XPC_WN_Shared_Proto_Trace, // trace
+};
+
+const js::Class XPC_WN_NoMods_Proto_JSClass = {
+ "XPC_WN_NoMods_Proto_JSClass",
+ XPC_WRAPPER_FLAGS,
+ &XPC_WN_NoMods_Proto_JSClassOps,
+ JS_NULL_CLASS_SPEC,
+ &XPC_WN_Shared_Proto_ClassExtension,
+ JS_NULL_OBJECT_OPS
+};
+
+/***************************************************************************/
+
+static bool
+XPC_WN_TearOff_Enumerate(JSContext* cx, HandleObject obj)
+{
+ XPCCallContext ccx(cx, obj);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ XPCWrappedNativeTearOff* to = ccx.GetTearOff();
+ XPCNativeInterface* iface;
+
+ if (!to || nullptr == (iface = to->GetInterface()))
+ return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
+
+ uint16_t member_count = iface->GetMemberCount();
+ for (uint16_t k = 0; k < member_count; k++) {
+ if (!xpc_ForcePropertyResolve(cx, obj, iface->GetMemberAt(k)->GetName()))
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+XPC_WN_TearOff_Resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
+{
+ XPCCallContext ccx(cx, obj);
+ XPCWrappedNative* wrapper = ccx.GetWrapper();
+ THROW_AND_RETURN_IF_BAD_WRAPPER(cx, wrapper);
+
+ XPCWrappedNativeTearOff* to = ccx.GetTearOff();
+ XPCNativeInterface* iface;
+
+ if (!to || nullptr == (iface = to->GetInterface()))
+ return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
+
+ return DefinePropertyIfFound(ccx, obj, id, nullptr, iface, nullptr,
+ wrapper->GetScope(),
+ true, nullptr, nullptr, nullptr,
+ JSPROP_READONLY |
+ JSPROP_PERMANENT |
+ JSPROP_ENUMERATE, resolvedp);
+}
+
+static void
+XPC_WN_TearOff_Finalize(js::FreeOp* fop, JSObject* obj)
+{
+ XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
+ xpc_GetJSPrivate(obj);
+ if (!p)
+ return;
+ p->JSObjectFinalized();
+}
+
+static void
+XPC_WN_TearOff_ObjectMoved(JSObject* obj, const JSObject* old)
+{
+ XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
+ xpc_GetJSPrivate(obj);
+ if (!p)
+ return;
+ p->JSObjectMoved(obj, old);
+}
+
+// Make sure XPC_WRAPPER_FLAGS has no reserved slots, so our
+// XPC_WN_TEAROFF_RESERVED_SLOTS value is OK.
+
+static_assert(((XPC_WRAPPER_FLAGS >> JSCLASS_RESERVED_SLOTS_SHIFT) &
+ JSCLASS_RESERVED_SLOTS_MASK) == 0,
+ "XPC_WRAPPER_FLAGS should not include any reserved slots");
+
+static const js::ClassOps XPC_WN_Tearoff_JSClassOps = {
+ XPC_WN_OnlyIWrite_AddPropertyStub, // addProperty
+ XPC_WN_CantDeletePropertyStub, // delProperty
+ nullptr, // getProperty
+ nullptr, // setProperty
+ XPC_WN_TearOff_Enumerate, // enumerate
+ XPC_WN_TearOff_Resolve, // resolve
+ nullptr, // mayResolve
+ XPC_WN_TearOff_Finalize, // finalize
+ nullptr, // call
+ nullptr, // construct
+ nullptr, // hasInstance
+ nullptr, // trace
+};
+
+static const js::ClassExtension XPC_WN_Tearoff_JSClassExtension = {
+ nullptr, // weakmapKeyDelegateOp
+ XPC_WN_TearOff_ObjectMoved
+};
+
+const js::Class XPC_WN_Tearoff_JSClass = {
+ "WrappedNative_TearOff",
+ XPC_WRAPPER_FLAGS |
+ JSCLASS_HAS_RESERVED_SLOTS(XPC_WN_TEAROFF_RESERVED_SLOTS),
+ &XPC_WN_Tearoff_JSClassOps,
+ JS_NULL_CLASS_SPEC,
+ &XPC_WN_Tearoff_JSClassExtension
+};
diff --git a/js/xpconnect/src/XPCWrappedNativeProto.cpp b/js/xpconnect/src/XPCWrappedNativeProto.cpp
new file mode 100644
index 000000000..d9c95a3e6
--- /dev/null
+++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -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
+}
+
+
diff --git a/js/xpconnect/src/XPCWrappedNativeScope.cpp b/js/xpconnect/src/XPCWrappedNativeScope.cpp
new file mode 100644
index 000000000..24f1067d0
--- /dev/null
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -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.
+}
diff --git a/js/xpconnect/src/XPCWrapper.cpp b/js/xpconnect/src/XPCWrapper.cpp
new file mode 100644
index 000000000..a6b331017
--- /dev/null
+++ b/js/xpconnect/src/XPCWrapper.cpp
@@ -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
diff --git a/js/xpconnect/src/XPCWrapper.h b/js/xpconnect/src/XPCWrapper.h
new file mode 100644
index 000000000..7e0ed8c1f
--- /dev/null
+++ b/js/xpconnect/src/XPCWrapper.h
@@ -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
diff --git a/js/xpconnect/src/jsshell.msg b/js/xpconnect/src/jsshell.msg
new file mode 100644
index 000000000..078d75e28
--- /dev/null
+++ b/js/xpconnect/src/jsshell.msg
@@ -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}")
diff --git a/js/xpconnect/src/moz.build b/js/xpconnect/src/moz.build
new file mode 100644
index 000000000..7e787bb56
--- /dev/null
+++ b/js/xpconnect/src/moz.build
@@ -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']
diff --git a/js/xpconnect/src/nsScriptError.cpp b/js/xpconnect/src/nsScriptError.cpp
new file mode 100644
index 000000000..ff687bc44
--- /dev/null
+++ b/js/xpconnect/src/nsScriptError.cpp
@@ -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)
diff --git a/js/xpconnect/src/nsScriptErrorWithStack.cpp b/js/xpconnect/src/nsScriptErrorWithStack.cpp
new file mode 100644
index 000000000..edc12fa76
--- /dev/null
+++ b/js/xpconnect/src/nsScriptErrorWithStack.cpp
@@ -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;
+}
diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp
new file mode 100644
index 000000000..0466175b1
--- /dev/null
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -0,0 +1,1336 @@
+/* -*- 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/. */
+
+/* High level class and public functions implementation. */
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Likely.h"
+#include "mozilla/Unused.h"
+
+#include "xpcprivate.h"
+#include "XPCWrapper.h"
+#include "jsfriendapi.h"
+#include "nsJSEnvironment.h"
+#include "nsThreadUtils.h"
+#include "nsDOMJSUtils.h"
+
+#include "WrapperFactory.h"
+#include "AccessCheck.h"
+
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/Exceptions.h"
+#include "mozilla/dom/Promise.h"
+
+#include "nsDOMMutationObserver.h"
+#include "nsICycleCollectorListener.h"
+#include "mozilla/XPTInterfaceInfoManager.h"
+#include "nsIObjectInputStream.h"
+#include "nsIObjectOutputStream.h"
+#include "nsScriptSecurityManager.h"
+#include "nsIPermissionManager.h"
+#include "nsContentUtils.h"
+#include "jsfriendapi.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace xpc;
+using namespace JS;
+
+NS_IMPL_ISUPPORTS(nsXPConnect, nsIXPConnect)
+
+nsXPConnect* nsXPConnect::gSelf = nullptr;
+bool nsXPConnect::gOnceAliveNowDead = false;
+
+// Global cache of the default script security manager (QI'd to
+// nsIScriptSecurityManager) and the system principal.
+nsIScriptSecurityManager* nsXPConnect::gScriptSecurityManager = nullptr;
+nsIPrincipal* nsXPConnect::gSystemPrincipal = nullptr;
+
+const char XPC_CONTEXT_STACK_CONTRACTID[] = "@mozilla.org/js/xpc/ContextStack;1";
+const char XPC_EXCEPTION_CONTRACTID[] = "@mozilla.org/js/xpc/Exception;1";
+const char XPC_CONSOLE_CONTRACTID[] = "@mozilla.org/consoleservice;1";
+const char XPC_SCRIPT_ERROR_CONTRACTID[] = "@mozilla.org/scripterror;1";
+const char XPC_ID_CONTRACTID[] = "@mozilla.org/js/xpc/ID;1";
+const char XPC_XPCONNECT_CONTRACTID[] = "@mozilla.org/js/xpc/XPConnect;1";
+
+/***************************************************************************/
+
+nsXPConnect::nsXPConnect()
+ : mContext(nullptr),
+ mShuttingDown(false)
+{
+ mContext = XPCJSContext::newXPCJSContext();
+ if (!mContext) {
+ NS_RUNTIMEABORT("Couldn't create XPCJSContext.");
+ }
+}
+
+nsXPConnect::~nsXPConnect()
+{
+ mContext->DeleteSingletonScopes();
+
+ // In order to clean up everything properly, we need to GC twice: once now,
+ // to clean anything that can go away on its own (like the Junk Scope, which
+ // we unrooted above), and once after forcing a bunch of shutdown in
+ // XPConnect, to clean the stuff we forcibly disconnected. The forced
+ // shutdown code defaults to leaking in a number of situations, so we can't
+ // get by with only the second GC. :-(
+ mContext->GarbageCollect(JS::gcreason::XPCONNECT_SHUTDOWN);
+
+ mShuttingDown = true;
+ XPCWrappedNativeScope::SystemIsBeingShutDown();
+
+ // The above causes us to clean up a bunch of XPConnect data structures,
+ // after which point we need to GC to clean everything up. We need to do
+ // this before deleting the XPCJSContext, because doing so destroys the
+ // maps that our finalize callback depends on.
+ mContext->GarbageCollect(JS::gcreason::XPCONNECT_SHUTDOWN);
+
+ NS_RELEASE(gSystemPrincipal);
+ gScriptSecurityManager = nullptr;
+
+ // shutdown the logging system
+ XPC_LOG_FINISH();
+
+ delete mContext;
+
+ gSelf = nullptr;
+ gOnceAliveNowDead = true;
+}
+
+// static
+void
+nsXPConnect::InitStatics()
+{
+ gSelf = new nsXPConnect();
+ gOnceAliveNowDead = false;
+ if (!gSelf->mContext) {
+ NS_RUNTIMEABORT("Couldn't create XPCJSContext.");
+ }
+
+ // Initial extra ref to keep the singleton alive
+ // balanced by explicit call to ReleaseXPConnectSingleton()
+ NS_ADDREF(gSelf);
+
+ // Fire up the SSM.
+ nsScriptSecurityManager::InitStatics();
+ gScriptSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
+ gScriptSecurityManager->GetSystemPrincipal(&gSystemPrincipal);
+ MOZ_RELEASE_ASSERT(gSystemPrincipal);
+
+ if (!JS::InitSelfHostedCode(gSelf->mContext->Context()))
+ MOZ_CRASH("InitSelfHostedCode failed");
+ if (!gSelf->mContext->JSContextInitialized(gSelf->mContext->Context()))
+ MOZ_CRASH("JSContextInitialized failed");
+
+ // Initialize our singleton scopes.
+ gSelf->mContext->InitSingletonScopes();
+}
+
+nsXPConnect*
+nsXPConnect::GetSingleton()
+{
+ nsXPConnect* xpc = nsXPConnect::XPConnect();
+ NS_IF_ADDREF(xpc);
+ return xpc;
+}
+
+// static
+void
+nsXPConnect::ReleaseXPConnectSingleton()
+{
+ nsXPConnect* xpc = gSelf;
+ if (xpc) {
+ nsrefcnt cnt;
+ NS_RELEASE2(xpc, cnt);
+ }
+}
+
+// static
+XPCJSContext*
+nsXPConnect::GetContextInstance()
+{
+ nsXPConnect* xpc = XPConnect();
+ return xpc->GetContext();
+}
+
+// static
+bool
+nsXPConnect::IsISupportsDescendant(nsIInterfaceInfo* info)
+{
+ bool found = false;
+ if (info)
+ info->HasAncestor(&NS_GET_IID(nsISupports), &found);
+ return found;
+}
+
+void
+xpc::ErrorReport::Init(JSErrorReport* aReport, const char* aToStringResult,
+ bool aIsChrome, uint64_t aWindowID)
+{
+ mCategory = aIsChrome ? NS_LITERAL_CSTRING("chrome javascript")
+ : NS_LITERAL_CSTRING("content javascript");
+ mWindowID = aWindowID;
+
+ ErrorReportToMessageString(aReport, mErrorMsg);
+ if (mErrorMsg.IsEmpty() && aToStringResult) {
+ AppendUTF8toUTF16(aToStringResult, mErrorMsg);
+ }
+
+ if (!aReport->filename) {
+ mFileName.SetIsVoid(true);
+ } else {
+ mFileName.AssignWithConversion(aReport->filename);
+ }
+
+ mSourceLine.Assign(aReport->linebuf(), aReport->linebufLength());
+ const JSErrorFormatString* efs = js::GetErrorMessage(nullptr, aReport->errorNumber);
+
+ if (efs == nullptr) {
+ mErrorMsgName.AssignASCII("");
+ } else {
+ mErrorMsgName.AssignASCII(efs->name);
+ }
+
+ mLineNumber = aReport->lineno;
+ mColumn = aReport->column;
+ mFlags = aReport->flags;
+ mIsMuted = aReport->isMuted;
+}
+
+void
+xpc::ErrorReport::Init(JSContext* aCx, mozilla::dom::Exception* aException,
+ bool aIsChrome, uint64_t aWindowID)
+{
+ mCategory = aIsChrome ? NS_LITERAL_CSTRING("chrome javascript")
+ : NS_LITERAL_CSTRING("content javascript");
+ mWindowID = aWindowID;
+
+ aException->GetErrorMessage(mErrorMsg);
+
+ aException->GetFilename(aCx, mFileName);
+ if (mFileName.IsEmpty()) {
+ mFileName.SetIsVoid(true);
+ }
+ aException->GetLineNumber(aCx, &mLineNumber);
+ aException->GetColumnNumber(&mColumn);
+
+ mFlags = JSREPORT_EXCEPTION;
+}
+
+static LazyLogModule gJSDiagnostics("JSDiagnostics");
+
+void
+xpc::ErrorReport::LogToConsole()
+{
+ LogToConsoleWithStack(nullptr);
+}
+void
+xpc::ErrorReport::LogToConsoleWithStack(JS::HandleObject aStack)
+{
+ // Log to stdout.
+ if (nsContentUtils::DOMWindowDumpEnabled()) {
+ nsAutoCString error;
+ error.AssignLiteral("JavaScript ");
+ if (JSREPORT_IS_STRICT(mFlags))
+ error.AppendLiteral("strict ");
+ if (JSREPORT_IS_WARNING(mFlags))
+ error.AppendLiteral("warning: ");
+ else
+ error.AppendLiteral("error: ");
+ error.Append(NS_LossyConvertUTF16toASCII(mFileName));
+ error.AppendLiteral(", line ");
+ error.AppendInt(mLineNumber, 10);
+ error.AppendLiteral(": ");
+ error.Append(NS_LossyConvertUTF16toASCII(mErrorMsg));
+
+ fprintf(stderr, "%s\n", error.get());
+ fflush(stderr);
+ }
+
+ MOZ_LOG(gJSDiagnostics,
+ JSREPORT_IS_WARNING(mFlags) ? LogLevel::Warning : LogLevel::Error,
+ ("file %s, line %u\n%s", NS_LossyConvertUTF16toASCII(mFileName).get(),
+ mLineNumber, NS_LossyConvertUTF16toASCII(mErrorMsg).get()));
+
+ // Log to the console. We do this last so that we can simply return if
+ // there's no console service without affecting the other reporting
+ // mechanisms.
+ nsCOMPtr<nsIConsoleService> consoleService =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+
+ nsCOMPtr<nsIScriptError> errorObject;
+ if (mWindowID && aStack) {
+ // Only set stack on messages related to a document
+ // As we cache messages in the console service,
+ // we have to ensure not leaking them after the related
+ // context is destroyed and we only track document lifecycle for now.
+ errorObject = new nsScriptErrorWithStack(aStack);
+ } else {
+ errorObject = new nsScriptError();
+ }
+ errorObject->SetErrorMessageName(mErrorMsgName);
+ NS_ENSURE_TRUE_VOID(consoleService);
+
+ nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName, mSourceLine,
+ mLineNumber, mColumn, mFlags,
+ mCategory, mWindowID);
+ NS_ENSURE_SUCCESS_VOID(rv);
+ consoleService->LogMessage(errorObject);
+
+}
+
+/* static */
+void
+xpc::ErrorReport::ErrorReportToMessageString(JSErrorReport* aReport,
+ nsAString& aString)
+{
+ aString.Truncate();
+ if (aReport->message()) {
+ JSFlatString* name = js::GetErrorTypeName(CycleCollectedJSContext::Get()->Context(), aReport->exnType);
+ if (name) {
+ AssignJSFlatString(aString, name);
+ aString.AppendLiteral(": ");
+ }
+ aString.Append(NS_ConvertUTF8toUTF16(aReport->message().c_str()));
+ }
+}
+
+/***************************************************************************/
+
+
+nsresult
+nsXPConnect::GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info)
+{
+ return XPTInterfaceInfoManager::GetSingleton()->GetInfoForIID(aIID, info);
+}
+
+nsresult
+nsXPConnect::GetInfoForName(const char * name, nsIInterfaceInfo** info)
+{
+ nsresult rv = XPTInterfaceInfoManager::GetSingleton()->GetInfoForName(name, info);
+ return NS_FAILED(rv) ? NS_OK : NS_ERROR_NO_INTERFACE;
+}
+
+NS_IMETHODIMP
+nsXPConnect::GarbageCollect(uint32_t reason)
+{
+ GetContext()->GarbageCollect(reason);
+ return NS_OK;
+}
+
+void
+xpc_MarkInCCGeneration(nsISupports* aVariant, uint32_t aGeneration)
+{
+ nsCOMPtr<XPCVariant> variant = do_QueryInterface(aVariant);
+ if (variant) {
+ variant->SetCCGeneration(aGeneration);
+ variant->GetJSVal(); // Unmarks gray JSObject.
+ XPCVariant* weak = variant.get();
+ variant = nullptr;
+ if (weak->IsPurple()) {
+ weak->RemovePurple();
+ }
+ }
+}
+
+void
+xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS)
+{
+ // QIing to nsIXPConnectWrappedJSUnmarkGray may have side effects!
+ nsCOMPtr<nsIXPConnectWrappedJSUnmarkGray> wjsug =
+ do_QueryInterface(aWrappedJS);
+ Unused << wjsug;
+ MOZ_ASSERT(!wjsug, "One should never be able to QI to "
+ "nsIXPConnectWrappedJSUnmarkGray successfully!");
+}
+
+/***************************************************************************/
+/***************************************************************************/
+// nsIXPConnect interface methods...
+
+template<typename T>
+static inline T UnexpectedFailure(T rv)
+{
+ NS_ERROR("This is not supposed to fail!");
+ return rv;
+}
+
+void
+xpc::TraceXPCGlobal(JSTracer* trc, JSObject* obj)
+{
+ if (js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL)
+ mozilla::dom::TraceProtoAndIfaceCache(trc, obj);
+
+ // We might be called from a GC during the creation of a global, before we've
+ // been able to set up the compartment private or the XPCWrappedNativeScope,
+ // so we need to null-check those.
+ xpc::CompartmentPrivate* compartmentPrivate = xpc::CompartmentPrivate::Get(obj);
+ if (compartmentPrivate && compartmentPrivate->scope)
+ compartmentPrivate->scope->TraceInside(trc);
+}
+
+
+namespace xpc {
+
+JSObject*
+CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal,
+ JS::CompartmentOptions& aOptions)
+{
+ MOZ_ASSERT(NS_IsMainThread(), "using a principal off the main thread?");
+ MOZ_ASSERT(principal);
+
+ MOZ_RELEASE_ASSERT(principal != nsContentUtils::GetNullSubjectPrincipal(),
+ "The null subject principal is getting inherited - fix that!");
+
+ RootedObject global(cx,
+ JS_NewGlobalObject(cx, clasp, nsJSPrincipals::get(principal),
+ JS::DontFireOnNewGlobalHook, aOptions));
+ if (!global)
+ return nullptr;
+ JSAutoCompartment ac(cx, global);
+
+ // The constructor automatically attaches the scope to the compartment private
+ // of |global|.
+ (void) new XPCWrappedNativeScope(cx, global);
+
+ if (clasp->flags & JSCLASS_DOM_GLOBAL) {
+#ifdef DEBUG
+ // Verify that the right trace hook is called. Note that this doesn't
+ // work right for wrapped globals, since the tracing situation there is
+ // more complicated. Manual inspection shows that they do the right
+ // thing. Also note that we only check this for JSCLASS_DOM_GLOBAL
+ // classes because xpc::TraceXPCGlobal won't call
+ // TraceProtoAndIfaceCache unless that flag is set.
+ if (!((const js::Class*)clasp)->isWrappedNative())
+ {
+ VerifyTraceProtoAndIfaceCacheCalledTracer trc(cx);
+ TraceChildren(&trc, GCCellPtr(global.get()));
+ MOZ_ASSERT(trc.ok, "Trace hook on global needs to call TraceXPCGlobal for XPConnect compartments.");
+ }
+#endif
+
+ const char* className = clasp->name;
+ AllocateProtoAndIfaceCache(global,
+ (strcmp(className, "Window") == 0 ||
+ strcmp(className, "ChromeWindow") == 0)
+ ? ProtoAndIfaceCache::WindowLike
+ : ProtoAndIfaceCache::NonWindowLike);
+ }
+
+ return global;
+}
+
+void
+InitGlobalObjectOptions(JS::CompartmentOptions& aOptions,
+ nsIPrincipal* aPrincipal)
+{
+ bool shouldDiscardSystemSource = ShouldDiscardSystemSource();
+ bool extraWarningsForSystemJS = ExtraWarningsForSystemJS();
+
+ bool isSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
+
+ if (isSystem) {
+ // Make sure [SecureContext] APIs are visible:
+ aOptions.creationOptions().setSecureContext(true);
+ }
+
+ if (shouldDiscardSystemSource) {
+ bool discardSource = isSystem;
+
+ aOptions.behaviors().setDiscardSource(discardSource);
+ }
+
+ if (extraWarningsForSystemJS) {
+ if (isSystem)
+ aOptions.behaviors().extraWarningsOverride().set(true);
+ }
+}
+
+bool
+InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal, uint32_t aFlags)
+{
+ // Immediately enter the global's compartment so that everything we create
+ // ends up there.
+ JSAutoCompartment ac(aJSContext, aGlobal);
+
+ // Stuff coming through this path always ends up as a DOM global.
+ MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL);
+
+ if (!(aFlags & nsIXPConnect::OMIT_COMPONENTS_OBJECT)) {
+ // XPCCallContext gives us an active request needed to save/restore.
+ if (!CompartmentPrivate::Get(aGlobal)->scope->AttachComponentsObject(aJSContext) ||
+ !XPCNativeWrapper::AttachNewConstructorObject(aJSContext, aGlobal)) {
+ return UnexpectedFailure(false);
+ }
+ }
+
+ if (!(aFlags & nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK))
+ JS_FireOnNewGlobalObject(aJSContext, aGlobal);
+
+ return true;
+}
+
+} // namespace xpc
+
+NS_IMETHODIMP
+nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext,
+ nsISupports* aCOMObj,
+ nsIPrincipal * aPrincipal,
+ uint32_t aFlags,
+ JS::CompartmentOptions& aOptions,
+ nsIXPConnectJSObjectHolder** _retval)
+{
+ MOZ_ASSERT(aJSContext, "bad param");
+ MOZ_ASSERT(aCOMObj, "bad param");
+ MOZ_ASSERT(_retval, "bad param");
+
+ // We pass null for the 'extra' pointer during global object creation, so
+ // we need to have a principal.
+ MOZ_ASSERT(aPrincipal);
+
+ InitGlobalObjectOptions(aOptions, aPrincipal);
+
+ // Call into XPCWrappedNative to make a new global object, scope, and global
+ // prototype.
+ xpcObjectHelper helper(aCOMObj);
+ MOZ_ASSERT(helper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
+ RefPtr<XPCWrappedNative> wrappedGlobal;
+ nsresult rv =
+ XPCWrappedNative::WrapNewGlobal(helper, aPrincipal,
+ aFlags & nsIXPConnect::INIT_JS_STANDARD_CLASSES,
+ aOptions, getter_AddRefs(wrappedGlobal));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Grab a copy of the global and enter its compartment.
+ RootedObject global(aJSContext, wrappedGlobal->GetFlatJSObject());
+ MOZ_ASSERT(JS_IsGlobalObject(global));
+
+ if (!InitGlobalObject(aJSContext, global, aFlags))
+ return UnexpectedFailure(NS_ERROR_FAILURE);
+
+ wrappedGlobal.forget(_retval);
+ return NS_OK;
+}
+
+static nsresult
+NativeInterface2JSObject(HandleObject aScope,
+ nsISupports* aCOMObj,
+ nsWrapperCache* aCache,
+ const nsIID * aIID,
+ bool aAllowWrapping,
+ MutableHandleValue aVal,
+ nsIXPConnectJSObjectHolder** aHolder)
+{
+ AutoJSContext cx;
+ JSAutoCompartment ac(cx, aScope);
+
+ nsresult rv;
+ xpcObjectHelper helper(aCOMObj, aCache);
+ if (!XPCConvert::NativeInterface2JSObject(aVal, aHolder, helper, aIID,
+ aAllowWrapping, &rv))
+ return rv;
+
+ MOZ_ASSERT(aAllowWrapping || !xpc::WrapperFactory::IsXrayWrapper(&aVal.toObject()),
+ "Shouldn't be returning a xray wrapper here");
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::WrapNative(JSContext * aJSContext,
+ JSObject * aScopeArg,
+ nsISupports* aCOMObj,
+ const nsIID & aIID,
+ JSObject** aRetVal)
+{
+ MOZ_ASSERT(aJSContext, "bad param");
+ MOZ_ASSERT(aScopeArg, "bad param");
+ MOZ_ASSERT(aCOMObj, "bad param");
+
+ RootedObject aScope(aJSContext, aScopeArg);
+ RootedValue v(aJSContext);
+ nsresult rv = NativeInterface2JSObject(aScope, aCOMObj, nullptr, &aIID,
+ true, &v, nullptr);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!v.isObjectOrNull())
+ return NS_ERROR_FAILURE;
+
+ *aRetVal = v.toObjectOrNull();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::WrapNativeHolder(JSContext * aJSContext,
+ JSObject * aScopeArg,
+ nsISupports* aCOMObj,
+ const nsIID & aIID,
+ nsIXPConnectJSObjectHolder **aHolder)
+{
+ MOZ_ASSERT(aHolder, "bad param");
+ MOZ_ASSERT(aJSContext, "bad param");
+ MOZ_ASSERT(aScopeArg, "bad param");
+ MOZ_ASSERT(aCOMObj, "bad param");
+
+ RootedObject aScope(aJSContext, aScopeArg);
+ RootedValue v(aJSContext);
+ return NativeInterface2JSObject(aScope, aCOMObj, nullptr, &aIID,
+ true, &v, aHolder);
+}
+
+NS_IMETHODIMP
+nsXPConnect::WrapNativeToJSVal(JSContext* aJSContext,
+ JSObject* aScopeArg,
+ nsISupports* aCOMObj,
+ nsWrapperCache* aCache,
+ const nsIID* aIID,
+ bool aAllowWrapping,
+ MutableHandleValue aVal)
+{
+ MOZ_ASSERT(aJSContext, "bad param");
+ MOZ_ASSERT(aScopeArg, "bad param");
+ MOZ_ASSERT(aCOMObj, "bad param");
+
+ RootedObject aScope(aJSContext, aScopeArg);
+ return NativeInterface2JSObject(aScope, aCOMObj, aCache, aIID,
+ aAllowWrapping, aVal, nullptr);
+}
+
+NS_IMETHODIMP
+nsXPConnect::WrapJS(JSContext * aJSContext,
+ JSObject * aJSObjArg,
+ const nsIID & aIID,
+ void * *result)
+{
+ MOZ_ASSERT(aJSContext, "bad param");
+ MOZ_ASSERT(aJSObjArg, "bad param");
+ MOZ_ASSERT(result, "bad param");
+
+ *result = nullptr;
+
+ RootedObject aJSObj(aJSContext, aJSObjArg);
+ JSAutoCompartment ac(aJSContext, aJSObj);
+
+ nsresult rv = NS_ERROR_UNEXPECTED;
+ if (!XPCConvert::JSObject2NativeInterface(result, aJSObj,
+ &aIID, nullptr, &rv))
+ return rv;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::JSValToVariant(JSContext* cx,
+ HandleValue aJSVal,
+ nsIVariant** aResult)
+{
+ NS_PRECONDITION(aResult, "bad param");
+
+ RefPtr<XPCVariant> variant = XPCVariant::newVariant(cx, aJSVal);
+ variant.forget(aResult);
+ NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::WrapJSAggregatedToNative(nsISupports* aOuter,
+ JSContext* aJSContext,
+ JSObject* aJSObjArg,
+ const nsIID& aIID,
+ void** result)
+{
+ MOZ_ASSERT(aOuter, "bad param");
+ MOZ_ASSERT(aJSContext, "bad param");
+ MOZ_ASSERT(aJSObjArg, "bad param");
+ MOZ_ASSERT(result, "bad param");
+
+ *result = nullptr;
+
+ RootedObject aJSObj(aJSContext, aJSObjArg);
+ nsresult rv;
+ if (!XPCConvert::JSObject2NativeInterface(result, aJSObj,
+ &aIID, aOuter, &rv))
+ return rv;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::GetWrappedNativeOfJSObject(JSContext * aJSContext,
+ JSObject * aJSObjArg,
+ nsIXPConnectWrappedNative** _retval)
+{
+ MOZ_ASSERT(aJSContext, "bad param");
+ MOZ_ASSERT(aJSObjArg, "bad param");
+ MOZ_ASSERT(_retval, "bad param");
+
+ RootedObject aJSObj(aJSContext, aJSObjArg);
+ aJSObj = js::CheckedUnwrap(aJSObj, /* stopAtWindowProxy = */ false);
+ if (!aJSObj || !IS_WN_REFLECTOR(aJSObj)) {
+ *_retval = nullptr;
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<XPCWrappedNative> temp = XPCWrappedNative::Get(aJSObj);
+ temp.forget(_retval);
+ return NS_OK;
+}
+
+already_AddRefed<nsISupports>
+xpc::UnwrapReflectorToISupports(JSObject* reflector)
+{
+ // Unwrap security wrappers, if allowed.
+ reflector = js::CheckedUnwrap(reflector, /* stopAtWindowProxy = */ false);
+ if (!reflector)
+ return nullptr;
+
+ // Try XPCWrappedNatives.
+ if (IS_WN_REFLECTOR(reflector)) {
+ XPCWrappedNative* wn = XPCWrappedNative::Get(reflector);
+ if (!wn)
+ return nullptr;
+ nsCOMPtr<nsISupports> native = wn->Native();
+ return native.forget();
+ }
+
+ // Try DOM objects. This QI without taking a ref first is safe, because
+ // this if non-null our thing will definitely be a DOM object, and we know
+ // their QI to nsISupports doesn't do anything weird.
+ nsCOMPtr<nsISupports> canonical =
+ do_QueryInterface(mozilla::dom::UnwrapDOMObjectToISupports(reflector));
+ return canonical.forget();
+}
+
+NS_IMETHODIMP
+nsXPConnect::GetWrappedNativeOfNativeObject(JSContext * aJSContext,
+ JSObject * aScopeArg,
+ nsISupports* aCOMObj,
+ const nsIID & aIID,
+ nsIXPConnectWrappedNative** _retval)
+{
+ MOZ_ASSERT(aJSContext, "bad param");
+ MOZ_ASSERT(aScopeArg, "bad param");
+ MOZ_ASSERT(aCOMObj, "bad param");
+ MOZ_ASSERT(_retval, "bad param");
+
+ *_retval = nullptr;
+
+ RootedObject aScope(aJSContext, aScopeArg);
+
+ XPCWrappedNativeScope* scope = ObjectScope(aScope);
+ if (!scope)
+ return UnexpectedFailure(NS_ERROR_FAILURE);
+
+ RefPtr<XPCNativeInterface> iface =
+ XPCNativeInterface::GetNewOrUsed(&aIID);
+ if (!iface)
+ return NS_ERROR_FAILURE;
+
+ XPCWrappedNative* wrapper;
+
+ nsresult rv = XPCWrappedNative::GetUsedOnly(aCOMObj, scope, iface, &wrapper);
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+ *_retval = static_cast<nsIXPConnectWrappedNative*>(wrapper);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::GetCurrentJSStack(nsIStackFrame * *aCurrentJSStack)
+{
+ MOZ_ASSERT(aCurrentJSStack, "bad param");
+
+ nsCOMPtr<nsIStackFrame> currentStack = dom::GetCurrentJSStack();
+ currentStack.forget(aCurrentJSStack);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::GetCurrentNativeCallContext(nsAXPCNativeCallContext * *aCurrentNativeCallContext)
+{
+ MOZ_ASSERT(aCurrentNativeCallContext, "bad param");
+
+ *aCurrentNativeCallContext = XPCJSContext::Get()->GetCallContext();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::SetFunctionThisTranslator(const nsIID & aIID,
+ nsIXPCFunctionThisTranslator* aTranslator)
+{
+ XPCJSContext* cx = GetContext();
+ IID2ThisTranslatorMap* map = cx->GetThisTranslatorMap();
+ map->Add(aIID, aTranslator);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::CreateSandbox(JSContext* cx, nsIPrincipal* principal,
+ JSObject** _retval)
+{
+ *_retval = nullptr;
+
+ RootedValue rval(cx);
+ SandboxOptions options;
+ nsresult rv = CreateSandboxObject(cx, &rval, principal, options);
+ MOZ_ASSERT(NS_FAILED(rv) || !rval.isPrimitive(),
+ "Bad return value from xpc_CreateSandboxObject()!");
+
+ if (NS_SUCCEEDED(rv) && !rval.isPrimitive()) {
+ *_retval = rval.toObjectOrNull();
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsXPConnect::EvalInSandboxObject(const nsAString& source, const char* filename,
+ JSContext* cx, JSObject* sandboxArg,
+ int32_t jsVersion,
+ MutableHandleValue rval)
+{
+#ifdef DEBUG
+ {
+ const char *version = JS_VersionToString(JSVersion(jsVersion));
+ MOZ_ASSERT(version && strcmp(version, "unknown") != 0, "Illegal JS version passed");
+ }
+#endif
+ if (!sandboxArg)
+ return NS_ERROR_INVALID_ARG;
+
+ RootedObject sandbox(cx, sandboxArg);
+ nsCString filenameStr;
+ if (filename) {
+ filenameStr.Assign(filename);
+ } else {
+ filenameStr = NS_LITERAL_CSTRING("x-bogus://XPConnect/Sandbox");
+ }
+ return EvalInSandbox(cx, sandbox, source, filenameStr, 1,
+ JSVersion(jsVersion), rval);
+}
+
+NS_IMETHODIMP
+nsXPConnect::GetWrappedNativePrototype(JSContext* aJSContext,
+ JSObject* aScopeArg,
+ nsIClassInfo* aClassInfo,
+ JSObject** aRetVal)
+{
+ RootedObject aScope(aJSContext, aScopeArg);
+ JSAutoCompartment ac(aJSContext, aScope);
+
+ XPCWrappedNativeScope* scope = ObjectScope(aScope);
+ if (!scope)
+ return UnexpectedFailure(NS_ERROR_FAILURE);
+
+ XPCNativeScriptableCreateInfo sciProto;
+ XPCWrappedNative::GatherProtoScriptableCreateInfo(aClassInfo, sciProto);
+
+ AutoMarkingWrappedNativeProtoPtr proto(aJSContext);
+ proto = XPCWrappedNativeProto::GetNewOrUsed(scope, aClassInfo, &sciProto);
+ if (!proto)
+ return UnexpectedFailure(NS_ERROR_FAILURE);
+
+ JSObject* protoObj = proto->GetJSProtoObject();
+ if (!protoObj)
+ return UnexpectedFailure(NS_ERROR_FAILURE);
+
+ *aRetVal = protoObj;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::DebugDump(int16_t depth)
+{
+#ifdef DEBUG
+ depth-- ;
+ XPC_LOG_ALWAYS(("nsXPConnect @ %x with mRefCnt = %d", this, mRefCnt.get()));
+ XPC_LOG_INDENT();
+ XPC_LOG_ALWAYS(("gSelf @ %x", gSelf));
+ XPC_LOG_ALWAYS(("gOnceAliveNowDead is %d", (int)gOnceAliveNowDead));
+ if (mContext) {
+ if (depth)
+ mContext->DebugDump(depth);
+ else
+ XPC_LOG_ALWAYS(("XPCJSContext @ %x", mContext));
+ } else
+ XPC_LOG_ALWAYS(("mContext is null"));
+ XPCWrappedNativeScope::DebugDumpAllScopes(depth);
+ XPC_LOG_OUTDENT();
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::DebugDumpObject(nsISupports* p, int16_t depth)
+{
+#ifdef DEBUG
+ if (!depth)
+ return NS_OK;
+ if (!p) {
+ XPC_LOG_ALWAYS(("*** Cound not dump object with NULL address"));
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIXPConnect> xpc;
+ nsCOMPtr<nsIXPCWrappedJSClass> wjsc;
+ nsCOMPtr<nsIXPConnectWrappedNative> wn;
+ nsCOMPtr<nsIXPConnectWrappedJS> wjs;
+
+ if (NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPConnect),
+ getter_AddRefs(xpc)))) {
+ XPC_LOG_ALWAYS(("Dumping a nsIXPConnect..."));
+ xpc->DebugDump(depth);
+ } else if (NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPCWrappedJSClass),
+ getter_AddRefs(wjsc)))) {
+ XPC_LOG_ALWAYS(("Dumping a nsIXPCWrappedJSClass..."));
+ wjsc->DebugDump(depth);
+ } else if (NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPConnectWrappedNative),
+ getter_AddRefs(wn)))) {
+ XPC_LOG_ALWAYS(("Dumping a nsIXPConnectWrappedNative..."));
+ wn->DebugDump(depth);
+ } else if (NS_SUCCEEDED(p->QueryInterface(NS_GET_IID(nsIXPConnectWrappedJS),
+ getter_AddRefs(wjs)))) {
+ XPC_LOG_ALWAYS(("Dumping a nsIXPConnectWrappedJS..."));
+ wjs->DebugDump(depth);
+ } else {
+ XPC_LOG_ALWAYS(("*** Could not dump the nsISupports @ %x", p));
+ }
+#endif
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::DebugDumpJSStack(bool showArgs,
+ bool showLocals,
+ bool showThisProps)
+{
+ xpc_DumpJSStack(showArgs, showLocals, showThisProps);
+
+ return NS_OK;
+}
+
+char*
+nsXPConnect::DebugPrintJSStack(bool showArgs,
+ bool showLocals,
+ bool showThisProps)
+{
+ JSContext* cx = nsContentUtils::GetCurrentJSContext();
+ if (!cx)
+ printf("there is no JSContext on the nsIThreadJSContextStack!\n");
+ else
+ return xpc_PrintJSStack(cx, showArgs, showLocals, showThisProps);
+
+ return nullptr;
+}
+
+NS_IMETHODIMP
+nsXPConnect::VariantToJS(JSContext* ctx, JSObject* scopeArg, nsIVariant* value,
+ MutableHandleValue _retval)
+{
+ NS_PRECONDITION(ctx, "bad param");
+ NS_PRECONDITION(scopeArg, "bad param");
+ NS_PRECONDITION(value, "bad param");
+
+ RootedObject scope(ctx, scopeArg);
+ MOZ_ASSERT(js::IsObjectInContextCompartment(scope, ctx));
+
+ nsresult rv = NS_OK;
+ if (!XPCVariant::VariantDataToJS(value, &rv, _retval)) {
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPConnect::JSToVariant(JSContext* ctx, HandleValue value, nsIVariant** _retval)
+{
+ NS_PRECONDITION(ctx, "bad param");
+ NS_PRECONDITION(_retval, "bad param");
+
+ RefPtr<XPCVariant> variant = XPCVariant::newVariant(ctx, value);
+ variant.forget(_retval);
+ if (!(*_retval))
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+nsIPrincipal*
+nsXPConnect::GetPrincipal(JSObject* obj, bool allowShortCircuit) const
+{
+ MOZ_ASSERT(IS_WN_REFLECTOR(obj), "What kind of wrapper is this?");
+
+ XPCWrappedNative* xpcWrapper = XPCWrappedNative::Get(obj);
+ if (xpcWrapper) {
+ if (allowShortCircuit) {
+ nsIPrincipal* result = xpcWrapper->GetObjectPrincipal();
+ if (result) {
+ return result;
+ }
+ }
+
+ // If not, check if it points to an nsIScriptObjectPrincipal
+ nsCOMPtr<nsIScriptObjectPrincipal> objPrin =
+ do_QueryInterface(xpcWrapper->Native());
+ if (objPrin) {
+ nsIPrincipal* result = objPrin->GetPrincipal();
+ if (result) {
+ return result;
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+namespace xpc {
+
+bool
+Base64Encode(JSContext* cx, HandleValue val, MutableHandleValue out)
+{
+ MOZ_ASSERT(cx);
+
+ nsAutoCString encodedString;
+ if (!ConvertJSValueToByteString(cx, val, false, encodedString)) {
+ return false;
+ }
+
+ nsAutoCString result;
+ if (NS_FAILED(mozilla::Base64Encode(encodedString, result))) {
+ JS_ReportErrorASCII(cx, "Failed to encode base64 data!");
+ return false;
+ }
+
+ JSString* str = JS_NewStringCopyN(cx, result.get(), result.Length());
+ if (!str)
+ return false;
+
+ out.setString(str);
+ return true;
+}
+
+bool
+Base64Decode(JSContext* cx, HandleValue val, MutableHandleValue out)
+{
+ MOZ_ASSERT(cx);
+
+ nsAutoCString encodedString;
+ if (!ConvertJSValueToByteString(cx, val, false, encodedString)) {
+ return false;
+ }
+
+ nsAutoCString result;
+ if (NS_FAILED(mozilla::Base64Decode(encodedString, result))) {
+ JS_ReportErrorASCII(cx, "Failed to decode base64 string!");
+ return false;
+ }
+
+ JSString* str = JS_NewStringCopyN(cx, result.get(), result.Length());
+ if (!str)
+ return false;
+
+ out.setString(str);
+ return true;
+}
+
+void
+SetLocationForGlobal(JSObject* global, const nsACString& location)
+{
+ MOZ_ASSERT(global);
+ CompartmentPrivate::Get(global)->SetLocation(location);
+}
+
+void
+SetLocationForGlobal(JSObject* global, nsIURI* locationURI)
+{
+ MOZ_ASSERT(global);
+ CompartmentPrivate::Get(global)->SetLocationURI(locationURI);
+}
+
+} // namespace xpc
+
+NS_IMETHODIMP
+nsXPConnect::NotifyDidPaint()
+{
+ JS::NotifyDidPaint(GetContext()->Context());
+ return NS_OK;
+}
+
+static nsresult
+WriteScriptOrFunction(nsIObjectOutputStream* stream, JSContext* cx,
+ JSScript* scriptArg, HandleObject functionObj)
+{
+ // Exactly one of script or functionObj must be given
+ MOZ_ASSERT(!scriptArg != !functionObj);
+
+ RootedScript script(cx, scriptArg);
+ if (!script) {
+ RootedFunction fun(cx, JS_GetObjectFunction(functionObj));
+ script.set(JS_GetFunctionScript(cx, fun));
+ }
+
+ uint8_t flags = 0; // We don't have flags anymore.
+ nsresult rv = stream->Write8(flags);
+ if (NS_FAILED(rv))
+ return rv;
+
+
+ TranscodeBuffer buffer;
+ TranscodeResult code;
+ {
+ if (functionObj)
+ code = EncodeInterpretedFunction(cx, buffer, functionObj);
+ else
+ code = EncodeScript(cx, buffer, script);
+ }
+
+ if (code != TranscodeResult_Ok) {
+ if ((code & TranscodeResult_Failure) != 0)
+ return NS_ERROR_FAILURE;
+ MOZ_ASSERT((code & TranscodeResult_Throw) != 0);
+ JS_ClearPendingException(cx);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ size_t size = buffer.length();
+ if (size > UINT32_MAX)
+ return NS_ERROR_FAILURE;
+ rv = stream->Write32(size);
+ if (NS_SUCCEEDED(rv))
+ rv = stream->WriteBytes(reinterpret_cast<char*>(buffer.begin()), size);
+
+ return rv;
+}
+
+static nsresult
+ReadScriptOrFunction(nsIObjectInputStream* stream, JSContext* cx,
+ JSScript** scriptp, JSObject** functionObjp)
+{
+ // Exactly one of script or functionObj must be given
+ MOZ_ASSERT(!scriptp != !functionObjp);
+
+ uint8_t flags;
+ nsresult rv = stream->Read8(&flags);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // We don't serialize mutedError-ness of scripts, which is fine as long as
+ // we only serialize system and XUL-y things. We can detect this by checking
+ // where the caller wants us to deserialize.
+ MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome() ||
+ CurrentGlobalOrNull(cx) == xpc::CompilationScope());
+
+ uint32_t size;
+ rv = stream->Read32(&size);
+ if (NS_FAILED(rv))
+ return rv;
+
+ char* data;
+ rv = stream->ReadBytes(size, &data);
+ if (NS_FAILED(rv))
+ return rv;
+
+ TranscodeBuffer buffer;
+ buffer.replaceRawBuffer(reinterpret_cast<uint8_t*>(data), size);
+
+ {
+ TranscodeResult code;
+ if (scriptp) {
+ Rooted<JSScript*> script(cx);
+ code = DecodeScript(cx, buffer, &script);
+ if (code == TranscodeResult_Ok)
+ *scriptp = script.get();
+ } else {
+ Rooted<JSFunction*> funobj(cx);
+ code = DecodeInterpretedFunction(cx, buffer, &funobj);
+ if (code == TranscodeResult_Ok)
+ *functionObjp = JS_GetFunctionObject(funobj.get());
+ }
+
+ if (code != TranscodeResult_Ok) {
+ if ((code & TranscodeResult_Failure) != 0)
+ return NS_ERROR_FAILURE;
+ MOZ_ASSERT((code & TranscodeResult_Throw) != 0);
+ JS_ClearPendingException(cx);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsXPConnect::WriteScript(nsIObjectOutputStream* stream, JSContext* cx, JSScript* script)
+{
+ return WriteScriptOrFunction(stream, cx, script, nullptr);
+}
+
+NS_IMETHODIMP
+nsXPConnect::ReadScript(nsIObjectInputStream* stream, JSContext* cx, JSScript** scriptp)
+{
+ return ReadScriptOrFunction(stream, cx, scriptp, nullptr);
+}
+
+NS_IMETHODIMP
+nsXPConnect::WriteFunction(nsIObjectOutputStream* stream, JSContext* cx, JSObject* functionObjArg)
+{
+ RootedObject functionObj(cx, functionObjArg);
+ return WriteScriptOrFunction(stream, cx, nullptr, functionObj);
+}
+
+NS_IMETHODIMP
+nsXPConnect::ReadFunction(nsIObjectInputStream* stream, JSContext* cx, JSObject** functionObjp)
+{
+ return ReadScriptOrFunction(stream, cx, nullptr, functionObjp);
+}
+
+/* These are here to be callable from a debugger */
+extern "C" {
+JS_EXPORT_API(void) DumpJSStack()
+{
+ xpc_DumpJSStack(true, true, false);
+}
+
+JS_EXPORT_API(char*) PrintJSStack()
+{
+ nsresult rv;
+ nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
+ return (NS_SUCCEEDED(rv) && xpc) ?
+ xpc->DebugPrintJSStack(true, true, false) :
+ nullptr;
+}
+
+JS_EXPORT_API(void) DumpCompleteHeap()
+{
+ nsCOMPtr<nsICycleCollectorListener> listener =
+ do_CreateInstance("@mozilla.org/cycle-collector-logger;1");
+ if (!listener) {
+ NS_WARNING("Failed to create CC logger");
+ return;
+ }
+
+ nsCOMPtr<nsICycleCollectorListener> alltracesListener;
+ listener->AllTraces(getter_AddRefs(alltracesListener));
+ if (!alltracesListener) {
+ NS_WARNING("Failed to get all traces logger");
+ return;
+ }
+
+ nsJSContext::CycleCollectNow(alltracesListener);
+}
+
+} // extern "C"
+
+namespace xpc {
+
+bool
+Atob(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (!args.length())
+ return true;
+
+ return xpc::Base64Decode(cx, args[0], args.rval());
+}
+
+bool
+Btoa(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (!args.length())
+ return true;
+
+ return xpc::Base64Encode(cx, args[0], args.rval());
+}
+
+bool
+IsXrayWrapper(JSObject* obj)
+{
+ return WrapperFactory::IsXrayWrapper(obj);
+}
+
+JSAddonId*
+NewAddonId(JSContext* cx, const nsACString& id)
+{
+ JS::RootedString str(cx, JS_NewStringCopyN(cx, id.BeginReading(), id.Length()));
+ if (!str)
+ return nullptr;
+ return JS::NewAddonId(cx, str);
+}
+
+bool
+SetAddonInterposition(const nsACString& addonIdStr, nsIAddonInterposition* interposition)
+{
+ JSAddonId* addonId;
+ // We enter the junk scope just to allocate a string, which actually will go
+ // in the system zone.
+ AutoJSAPI jsapi;
+ if (!jsapi.Init(xpc::PrivilegedJunkScope()))
+ return false;
+ addonId = NewAddonId(jsapi.cx(), addonIdStr);
+ if (!addonId)
+ return false;
+ return XPCWrappedNativeScope::SetAddonInterposition(jsapi.cx(), addonId, interposition);
+}
+
+bool
+AllowCPOWsInAddon(const nsACString& addonIdStr, bool allow)
+{
+ JSAddonId* addonId;
+ // We enter the junk scope just to allocate a string, which actually will go
+ // in the system zone.
+ AutoJSAPI jsapi;
+ if (!jsapi.Init(xpc::PrivilegedJunkScope()))
+ return false;
+ addonId = NewAddonId(jsapi.cx(), addonIdStr);
+ if (!addonId)
+ return false;
+ return XPCWrappedNativeScope::AllowCPOWsInAddon(jsapi.cx(), addonId, allow);
+}
+
+} // namespace xpc
+
+namespace mozilla {
+namespace dom {
+
+bool
+IsChromeOrXBL(JSContext* cx, JSObject* /* unused */)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ JSCompartment* c = js::GetContextCompartment(cx);
+
+ // For remote XUL, we run XBL in the XUL scope. Given that we care about
+ // compat and not security for remote XUL, we just always claim to be XBL.
+ //
+ // Note that, for performance, we don't check AllowXULXBLForPrincipal here,
+ // and instead rely on the fact that AllowContentXBLScope() only returns false in
+ // remote XUL situations.
+ return AccessCheck::isChrome(c) || IsContentXBLScope(c) || !AllowContentXBLScope(c);
+}
+
+namespace workers {
+extern bool IsCurrentThreadRunningChromeWorker();
+} // namespace workers
+
+bool
+ThreadSafeIsChromeOrXBL(JSContext* cx, JSObject* obj)
+{
+ if (NS_IsMainThread()) {
+ return IsChromeOrXBL(cx, obj);
+ }
+ return workers::IsCurrentThreadRunningChromeWorker();
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/js/xpconnect/src/qsObjectHelper.h b/js/xpconnect/src/qsObjectHelper.h
new file mode 100644
index 000000000..a3c753800
--- /dev/null
+++ b/js/xpconnect/src/qsObjectHelper.h
@@ -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
diff --git a/js/xpconnect/src/xpc.msg b/js/xpconnect/src/xpc.msg
new file mode 100644
index 000000000..182cdbba8
--- /dev/null
+++ b/js/xpconnect/src/xpc.msg
@@ -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.")
diff --git a/js/xpconnect/src/xpcObjectHelper.h b/js/xpconnect/src/xpcObjectHelper.h
new file mode 100644
index 000000000..10090a901
--- /dev/null
+++ b/js/xpconnect/src/xpcObjectHelper.h
@@ -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
diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h
new file mode 100644
index 000000000..d7d5586b8
--- /dev/null
+++ b/js/xpconnect/src/xpcprivate.h
@@ -0,0 +1,3426 @@
+/* -*- 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/. */
+
+/*
+ * XPConnect allows JS code to manipulate C++ object and C++ code to manipulate
+ * JS objects. JS manipulation of C++ objects tends to be significantly more
+ * complex. This comment explains how it is orchestrated by XPConnect.
+ *
+ * For each C++ object to be manipulated in JS, there is a corresponding JS
+ * object. This is called the "flattened JS object". By default, there is an
+ * additional C++ object involved of type XPCWrappedNative. The XPCWrappedNative
+ * holds pointers to the C++ object and the flat JS object.
+ *
+ * All XPCWrappedNative objects belong to an XPCWrappedNativeScope. These scopes
+ * are essentially in 1:1 correspondence with JS global objects. The
+ * XPCWrappedNativeScope has a pointer to the JS global object. The parent of a
+ * flattened JS object is, by default, the global JS object corresponding to the
+ * wrapper's XPCWrappedNativeScope (the exception to this rule is when a
+ * PreCreate hook asks for a different parent; see nsIXPCScriptable below).
+ *
+ * Some C++ objects (notably DOM objects) have information associated with them
+ * that lists the interfaces implemented by these objects. A C++ object exposes
+ * this information by implementing nsIClassInfo. If a C++ object implements
+ * nsIClassInfo, then JS code can call its methods without needing to use
+ * QueryInterface first. Typically, all instances of a C++ class share the same
+ * nsIClassInfo instance. (That is, obj->QueryInterface(nsIClassInfo) returns
+ * the same result for every obj of a given class.)
+ *
+ * XPConnect tracks nsIClassInfo information in an XPCWrappedNativeProto object.
+ * A given XPCWrappedNativeScope will have one XPCWrappedNativeProto for each
+ * nsIClassInfo instance being used. The XPCWrappedNativeProto has an associated
+ * JS object, which is used as the prototype of all flattened JS objects created
+ * for C++ objects with the given nsIClassInfo.
+ *
+ * Each XPCWrappedNativeProto has a pointer to its XPCWrappedNativeScope. If an
+ * XPCWrappedNative wraps a C++ object with class info, then it points to its
+ * XPCWrappedNativeProto. Otherwise it points to its XPCWrappedNativeScope. (The
+ * pointers are smooshed together in a tagged union.) Either way it can reach
+ * its scope.
+ *
+ * An XPCWrappedNativeProto keeps track of the set of interfaces implemented by
+ * the C++ object in an XPCNativeSet. (The list of interfaces is obtained by
+ * calling a method on the nsIClassInfo.) An XPCNativeSet is a collection of
+ * XPCNativeInterfaces. Each interface stores the list of members, which can be
+ * methods, constants, getters, or setters.
+ *
+ * An XPCWrappedNative also points to an XPCNativeSet. Initially this starts out
+ * the same as the XPCWrappedNativeProto's set. If there is no proto, it starts
+ * out as a singleton set containing nsISupports. If JS code QI's new interfaces
+ * outside of the existing set, the set will grow. All QueryInterface results
+ * are cached in XPCWrappedNativeTearOff objects, which are linked off of the
+ * XPCWrappedNative.
+ *
+ * Besides having class info, a C++ object may be "scriptable" (i.e., implement
+ * nsIXPCScriptable). This allows it to implement a more DOM-like interface,
+ * besides just exposing XPCOM methods and constants. An nsIXPCScriptable
+ * instance has hooks that correspond to all the normal JSClass hooks. Each
+ * nsIXPCScriptable instance is mirrored by an XPCNativeScriptableInfo in
+ * XPConnect. These can have pointers from XPCWrappedNativeProto and
+ * XPCWrappedNative (since C++ objects can have scriptable info without having
+ * class info).
+ */
+
+/* All the XPConnect private declarations - only include locally. */
+
+#ifndef xpcprivate_h___
+#define xpcprivate_h___
+
+#include "mozilla/Alignment.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/GuardObjects.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReporting.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+
+#include "mozilla/dom/ScriptSettings.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "xpcpublic.h"
+#include "js/TracingAPI.h"
+#include "js/WeakMapPtr.h"
+#include "PLDHashTable.h"
+#include "nscore.h"
+#include "nsXPCOM.h"
+#include "nsAutoPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDebug.h"
+#include "nsISupports.h"
+#include "nsIServiceManager.h"
+#include "nsIClassInfoImpl.h"
+#include "nsIComponentManager.h"
+#include "nsIComponentRegistrar.h"
+#include "nsISupportsPrimitives.h"
+#include "nsMemory.h"
+#include "nsIXPConnect.h"
+#include "nsIInterfaceInfo.h"
+#include "nsIXPCScriptable.h"
+#include "nsIObserver.h"
+#include "nsWeakReference.h"
+#include "nsCOMPtr.h"
+#include "nsXPTCUtils.h"
+#include "xptinfo.h"
+#include "XPCForwards.h"
+#include "XPCLog.h"
+#include "xpccomponents.h"
+#include "xpcexception.h"
+#include "xpcjsid.h"
+#include "prenv.h"
+#include "prclist.h"
+#include "prcvar.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsXPIDLString.h"
+
+#include "MainThreadUtils.h"
+
+#include "nsIConsoleService.h"
+#include "nsIScriptError.h"
+#include "nsIException.h"
+
+#include "nsVariant.h"
+#include "nsIPropertyBag.h"
+#include "nsIProperty.h"
+#include "nsCOMArray.h"
+#include "nsTArray.h"
+#include "nsBaseHashtable.h"
+#include "nsHashKeys.h"
+#include "nsWrapperCache.h"
+#include "nsStringBuffer.h"
+#include "nsDataHashtable.h"
+#include "nsDeque.h"
+
+#include "nsIScriptSecurityManager.h"
+
+#include "nsIPrincipal.h"
+#include "nsJSPrincipals.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "xpcObjectHelper.h"
+
+#include "SandboxPrivate.h"
+#include "BackstagePass.h"
+#include "nsAXPCNativeCallContext.h"
+
+#ifdef XP_WIN
+// Nasty MS defines
+#ifdef GetClassInfo
+#undef GetClassInfo
+#endif
+#ifdef GetClassName
+#undef GetClassName
+#endif
+#endif /* XP_WIN */
+
+/***************************************************************************/
+// default initial sizes for maps (hashtables)
+
+#define XPC_JS_MAP_LENGTH 32
+#define XPC_JS_CLASS_MAP_LENGTH 32
+
+#define XPC_NATIVE_MAP_LENGTH 8
+#define XPC_NATIVE_PROTO_MAP_LENGTH 8
+#define XPC_DYING_NATIVE_PROTO_MAP_LENGTH 8
+#define XPC_NATIVE_INTERFACE_MAP_LENGTH 32
+#define XPC_NATIVE_SET_MAP_LENGTH 32
+#define XPC_THIS_TRANSLATOR_MAP_LENGTH 4
+#define XPC_WRAPPER_MAP_LENGTH 8
+
+/***************************************************************************/
+// data declarations...
+extern const char XPC_CONTEXT_STACK_CONTRACTID[];
+extern const char XPC_EXCEPTION_CONTRACTID[];
+extern const char XPC_CONSOLE_CONTRACTID[];
+extern const char XPC_SCRIPT_ERROR_CONTRACTID[];
+extern const char XPC_ID_CONTRACTID[];
+extern const char XPC_XPCONNECT_CONTRACTID[];
+
+/***************************************************************************/
+// Useful macros...
+
+#define XPC_STRING_GETTER_BODY(dest, src) \
+ NS_ENSURE_ARG_POINTER(dest); \
+ char* result; \
+ if (src) \
+ result = (char*) nsMemory::Clone(src, \
+ sizeof(char)*(strlen(src)+1)); \
+ else \
+ result = nullptr; \
+ *dest = result; \
+ return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY
+
+// If IS_WN_CLASS for the JSClass of an object is true, the object is a
+// wrappednative wrapper, holding the XPCWrappedNative in its private slot.
+static inline bool IS_WN_CLASS(const js::Class* clazz)
+{
+ return clazz->isWrappedNative();
+}
+
+static inline bool IS_WN_REFLECTOR(JSObject* obj)
+{
+ return IS_WN_CLASS(js::GetObjectClass(obj));
+}
+
+/***************************************************************************
+****************************************************************************
+*
+* Core runtime and context classes...
+*
+****************************************************************************
+***************************************************************************/
+
+// We have a general rule internally that getters that return addref'd interface
+// pointer generally do so using an 'out' parm. When interface pointers are
+// returned as function call result values they are not addref'd. Exceptions
+// to this rule are noted explicitly.
+
+class nsXPConnect final : public nsIXPConnect
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCONNECT
+
+ // non-interface implementation
+public:
+ // These get non-addref'd pointers
+ static nsXPConnect* XPConnect()
+ {
+ // Do a release-mode assert that we're not doing anything significant in
+ // XPConnect off the main thread. If you're an extension developer hitting
+ // this, you need to change your code. See bug 716167.
+ if (!MOZ_LIKELY(NS_IsMainThread()))
+ MOZ_CRASH();
+
+ return gSelf;
+ }
+
+ static XPCJSContext* GetContextInstance();
+ XPCJSContext* GetContext() {return mContext;}
+
+ static bool IsISupportsDescendant(nsIInterfaceInfo* info);
+
+ static nsIScriptSecurityManager* SecurityManager()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(gScriptSecurityManager);
+ return gScriptSecurityManager;
+ }
+
+ static nsIPrincipal* SystemPrincipal()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(gSystemPrincipal);
+ return gSystemPrincipal;
+ }
+
+ // This returns an AddRef'd pointer. It does not do this with an 'out' param
+ // only because this form is required by the generic module macro:
+ // NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
+ static nsXPConnect* GetSingleton();
+
+ // Called by module code in dll startup
+ static void InitStatics();
+ // Called by module code on dll shutdown.
+ static void ReleaseXPConnectSingleton();
+
+ bool IsShuttingDown() const {return mShuttingDown;}
+
+ nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
+ nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
+
+ virtual nsIPrincipal* GetPrincipal(JSObject* obj,
+ bool allowShortCircuit) const override;
+
+ void RecordTraversal(void* p, nsISupports* s);
+ virtual char* DebugPrintJSStack(bool showArgs,
+ bool showLocals,
+ bool showThisProps) override;
+
+protected:
+ virtual ~nsXPConnect();
+
+ nsXPConnect();
+
+private:
+ // Singleton instance
+ static nsXPConnect* gSelf;
+ static bool gOnceAliveNowDead;
+
+ XPCJSContext* mContext;
+ bool mShuttingDown;
+
+public:
+ static nsIScriptSecurityManager* gScriptSecurityManager;
+ static nsIPrincipal* gSystemPrincipal;
+};
+
+/***************************************************************************/
+
+class XPCRootSetElem
+{
+public:
+ XPCRootSetElem()
+ {
+#ifdef DEBUG
+ mNext = nullptr;
+ mSelfp = nullptr;
+#endif
+ }
+
+ ~XPCRootSetElem()
+ {
+ MOZ_ASSERT(!mNext, "Must be unlinked");
+ MOZ_ASSERT(!mSelfp, "Must be unlinked");
+ }
+
+ inline XPCRootSetElem* GetNextRoot() { return mNext; }
+ void AddToRootSet(XPCRootSetElem** listHead);
+ void RemoveFromRootSet();
+
+private:
+ XPCRootSetElem* mNext;
+ XPCRootSetElem** mSelfp;
+};
+
+/***************************************************************************/
+
+// In the current xpconnect system there can only be one XPCJSContext.
+// So, xpconnect can only be used on one JSContext within the process.
+
+class WatchdogManager;
+
+enum WatchdogTimestampCategory
+{
+ TimestampContextStateChange = 0,
+ TimestampWatchdogWakeup,
+ TimestampWatchdogHibernateStart,
+ TimestampWatchdogHibernateStop,
+ TimestampCount
+};
+
+class AsyncFreeSnowWhite;
+
+template <class StringType>
+class ShortLivedStringBuffer
+{
+public:
+ StringType* Create()
+ {
+ for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
+ if (!mStrings[i]) {
+ mStrings[i].emplace();
+ return mStrings[i].ptr();
+ }
+ }
+
+ // All our internal string wrappers are used, allocate a new string.
+ return new StringType();
+ }
+
+ void Destroy(StringType* string)
+ {
+ for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
+ if (mStrings[i] && mStrings[i].ptr() == string) {
+ // One of our internal strings is no longer in use, mark
+ // it as such and free its data.
+ mStrings[i].reset();
+ return;
+ }
+ }
+
+ // We're done with a string that's not one of our internal
+ // strings, delete it.
+ delete string;
+ }
+
+ ~ShortLivedStringBuffer()
+ {
+#ifdef DEBUG
+ for (uint32_t i = 0; i < ArrayLength(mStrings); ++i) {
+ MOZ_ASSERT(!mStrings[i], "Short lived string still in use");
+ }
+#endif
+ }
+
+private:
+ mozilla::Maybe<StringType> mStrings[2];
+};
+
+class XPCJSContext final : public mozilla::CycleCollectedJSContext
+{
+public:
+ static XPCJSContext* newXPCJSContext();
+ static XPCJSContext* Get() { return nsXPConnect::XPConnect()->GetContext(); }
+
+ void RemoveWrappedJS(nsXPCWrappedJS* wrapper);
+ void AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const;
+
+ XPCCallContext* GetCallContext() const {return mCallContext;}
+ XPCCallContext* SetCallContext(XPCCallContext* ccx)
+ {XPCCallContext* old = mCallContext; mCallContext = ccx; return old;}
+
+ jsid GetResolveName() const {return mResolveName;}
+ jsid SetResolveName(jsid name)
+ {jsid old = mResolveName; mResolveName = name; return old;}
+
+ XPCWrappedNative* GetResolvingWrapper() const {return mResolvingWrapper;}
+ XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w)
+ {XPCWrappedNative* old = mResolvingWrapper;
+ mResolvingWrapper = w; return old;}
+
+ JSObject2WrappedJSMap* GetMultiCompartmentWrappedJSMap() const
+ {return mWrappedJSMap;}
+
+ IID2WrappedJSClassMap* GetWrappedJSClassMap() const
+ {return mWrappedJSClassMap;}
+
+ IID2NativeInterfaceMap* GetIID2NativeInterfaceMap() const
+ {return mIID2NativeInterfaceMap;}
+
+ ClassInfo2NativeSetMap* GetClassInfo2NativeSetMap() const
+ {return mClassInfo2NativeSetMap;}
+
+ NativeSetMap* GetNativeSetMap() const
+ {return mNativeSetMap;}
+
+ IID2ThisTranslatorMap* GetThisTranslatorMap() const
+ {return mThisTranslatorMap;}
+
+ XPCWrappedNativeProtoMap* GetDyingWrappedNativeProtoMap() const
+ {return mDyingWrappedNativeProtoMap;}
+
+ bool JSContextInitialized(JSContext* cx);
+
+ virtual bool
+ DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp,
+ char (&aName)[72]) const override;
+ virtual bool
+ NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
+ nsCycleCollectionTraversalCallback& aCb) const override;
+
+ virtual void BeforeProcessTask(bool aMightBlock) override;
+ virtual void AfterProcessTask(uint32_t aNewRecursionDepth) override;
+
+ /**
+ * Infrastructure for classes that need to defer part of the finalization
+ * until after the GC has run, for example for objects that we don't want to
+ * destroy during the GC.
+ */
+
+public:
+ bool GetDoingFinalization() const {return mDoingFinalization;}
+
+ // Mapping of often used strings to jsid atoms that live 'forever'.
+ //
+ // To add a new string: add to this list and to XPCJSContext::mStrings
+ // at the top of XPCJSContext.cpp
+ enum {
+ IDX_CONSTRUCTOR = 0 ,
+ IDX_TO_STRING ,
+ IDX_TO_SOURCE ,
+ IDX_LAST_RESULT ,
+ IDX_RETURN_CODE ,
+ IDX_VALUE ,
+ IDX_QUERY_INTERFACE ,
+ IDX_COMPONENTS ,
+ IDX_WRAPPED_JSOBJECT ,
+ IDX_OBJECT ,
+ IDX_FUNCTION ,
+ IDX_PROTOTYPE ,
+ IDX_CREATE_INSTANCE ,
+ IDX_ITEM ,
+ IDX_PROTO ,
+ IDX_ITERATOR ,
+ IDX_EXPOSEDPROPS ,
+ IDX_EVAL ,
+ IDX_CONTROLLERS ,
+ IDX_REALFRAMEELEMENT ,
+ IDX_LENGTH ,
+ IDX_NAME ,
+ IDX_UNDEFINED ,
+ IDX_EMPTYSTRING ,
+ IDX_FILENAME ,
+ IDX_LINENUMBER ,
+ IDX_COLUMNNUMBER ,
+ IDX_STACK ,
+ IDX_MESSAGE ,
+ IDX_LASTINDEX ,
+ IDX_TOTAL_COUNT // just a count of the above
+ };
+
+ JS::HandleId GetStringID(unsigned index) const
+ {
+ MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range");
+ // fromMarkedLocation() is safe because the string is interned.
+ return JS::HandleId::fromMarkedLocation(&mStrIDs[index]);
+ }
+ JS::HandleValue GetStringJSVal(unsigned index) const
+ {
+ MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range");
+ // fromMarkedLocation() is safe because the string is interned.
+ return JS::HandleValue::fromMarkedLocation(&mStrJSVals[index]);
+ }
+ const char* GetStringName(unsigned index) const
+ {
+ MOZ_ASSERT(index < IDX_TOTAL_COUNT, "index out of range");
+ return mStrings[index];
+ }
+
+ virtual bool UsefulToMergeZones() const override;
+ void TraceNativeBlackRoots(JSTracer* trc) override;
+ void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) override;
+ void TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback& cb) override;
+ void UnmarkSkippableJSHolders();
+ void PrepareForForgetSkippable() override;
+ void BeginCycleCollectionCallback() override;
+ void EndCycleCollectionCallback(mozilla::CycleCollectorResults& aResults) override;
+ void DispatchDeferredDeletion(bool aContinuation, bool aPurge = false) override;
+
+ void CustomGCCallback(JSGCStatus status) override;
+ void CustomOutOfMemoryCallback() override;
+ void CustomLargeAllocationFailureCallback() override;
+ static void GCSliceCallback(JSContext* cx,
+ JS::GCProgress progress,
+ const JS::GCDescription& desc);
+ static void DoCycleCollectionCallback(JSContext* cx);
+ static void FinalizeCallback(JSFreeOp* fop,
+ JSFinalizeStatus status,
+ bool isZoneGC,
+ void* data);
+ static void WeakPointerZoneGroupCallback(JSContext* cx, void* data);
+ static void WeakPointerCompartmentCallback(JSContext* cx, JSCompartment* comp, void* data);
+
+ inline void AddVariantRoot(XPCTraceableVariant* variant);
+ inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
+ inline void AddObjectHolderRoot(XPCJSObjectHolder* holder);
+
+ void DebugDump(int16_t depth);
+
+ bool GCIsRunning() const {return mGCIsRunning;}
+
+ ~XPCJSContext();
+
+ ShortLivedStringBuffer<nsString> mScratchStrings;
+ ShortLivedStringBuffer<nsCString> mScratchCStrings;
+
+ void AddGCCallback(xpcGCCallback cb);
+ void RemoveGCCallback(xpcGCCallback cb);
+
+ static void ActivityCallback(void* arg, bool active);
+ static bool InterruptCallback(JSContext* cx);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+ AutoMarkingPtr** GetAutoRootsAdr() {return &mAutoRoots;}
+
+ JSObject* UnprivilegedJunkScope() { return mUnprivilegedJunkScope; }
+ JSObject* PrivilegedJunkScope() { return mPrivilegedJunkScope; }
+ JSObject* CompilationScope() { return mCompilationScope; }
+
+ void InitSingletonScopes();
+ void DeleteSingletonScopes();
+
+ PRTime GetWatchdogTimestamp(WatchdogTimestampCategory aCategory);
+
+ nsresult GetPendingResult() { return mPendingResult; }
+ void SetPendingResult(nsresult rv) { mPendingResult = rv; }
+
+private:
+ XPCJSContext();
+
+ MOZ_IS_CLASS_INIT
+ nsresult Initialize();
+
+ void ReleaseIncrementally(nsTArray<nsISupports*>& array);
+
+ static const char* const mStrings[IDX_TOTAL_COUNT];
+ jsid mStrIDs[IDX_TOTAL_COUNT];
+ JS::Value mStrJSVals[IDX_TOTAL_COUNT];
+
+ XPCCallContext* mCallContext;
+ AutoMarkingPtr* mAutoRoots;
+ jsid mResolveName;
+ XPCWrappedNative* mResolvingWrapper;
+ JSObject2WrappedJSMap* mWrappedJSMap;
+ IID2WrappedJSClassMap* mWrappedJSClassMap;
+ IID2NativeInterfaceMap* mIID2NativeInterfaceMap;
+ ClassInfo2NativeSetMap* mClassInfo2NativeSetMap;
+ NativeSetMap* mNativeSetMap;
+ IID2ThisTranslatorMap* mThisTranslatorMap;
+ XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
+ bool mGCIsRunning;
+ nsTArray<nsISupports*> mNativesToReleaseArray;
+ bool mDoingFinalization;
+ XPCRootSetElem* mVariantRoots;
+ XPCRootSetElem* mWrappedJSRoots;
+ XPCRootSetElem* mObjectHolderRoots;
+ nsTArray<xpcGCCallback> extraGCCallbacks;
+ RefPtr<WatchdogManager> mWatchdogManager;
+ JS::GCSliceCallback mPrevGCSliceCallback;
+ JS::DoCycleCollectionCallback mPrevDoCycleCollectionCallback;
+ JS::PersistentRootedObject mUnprivilegedJunkScope;
+ JS::PersistentRootedObject mPrivilegedJunkScope;
+ JS::PersistentRootedObject mCompilationScope;
+ RefPtr<AsyncFreeSnowWhite> mAsyncSnowWhiteFreer;
+
+ // If we spend too much time running JS code in an event handler, then we
+ // want to show the slow script UI. The timeout T is controlled by prefs. We
+ // invoke the interrupt callback once after T/2 seconds and set
+ // mSlowScriptSecondHalf to true. After another T/2 seconds, we invoke the
+ // interrupt callback again. Since mSlowScriptSecondHalf is now true, it
+ // shows the slow script UI. The reason we invoke the callback twice is to
+ // ensure that putting the computer to sleep while running a script doesn't
+ // cause the UI to be shown. If the laptop goes to sleep during one of the
+ // timeout periods, the script still has the other T/2 seconds to complete
+ // before the slow script UI is shown.
+ bool mSlowScriptSecondHalf;
+
+ // mSlowScriptCheckpoint is set to the time when:
+ // 1. We started processing the current event, or
+ // 2. mSlowScriptSecondHalf was set to true
+ // (whichever comes later). We use it to determine whether the interrupt
+ // callback needs to do anything.
+ mozilla::TimeStamp mSlowScriptCheckpoint;
+ // Accumulates total time we actually waited for telemetry
+ mozilla::TimeDuration mSlowScriptActualWait;
+ bool mTimeoutAccumulated;
+
+ // mPendingResult is used to implement Components.returnCode. Only really
+ // meaningful while calling through XPCWrappedJS.
+ nsresult mPendingResult;
+
+ friend class Watchdog;
+ friend class AutoLockWatchdog;
+ friend class XPCIncrementalReleaseRunnable;
+};
+
+/***************************************************************************/
+
+// No virtuals
+// XPCCallContext is ALWAYS declared as a local variable in some function;
+// i.e. instance lifetime is always controled by some C++ function returning.
+//
+// These things are created frequently in many places. We *intentionally* do
+// not inialialize all members in order to save on construction overhead.
+// Some constructor pass more valid params than others. We init what must be
+// init'd and leave other members undefined. In debug builds the accessors
+// use a CHECK_STATE macro to track whether or not the object is in a valid
+// state to answer the question a caller might be asking. As long as this
+// class is maintained correctly it can do its job without a bunch of added
+// overhead from useless initializations and non-DEBUG error checking.
+//
+// Note that most accessors are inlined.
+
+class MOZ_STACK_CLASS XPCCallContext final : public nsAXPCNativeCallContext
+{
+public:
+ NS_IMETHOD GetCallee(nsISupports** aResult);
+ NS_IMETHOD GetCalleeMethodIndex(uint16_t* aResult);
+ NS_IMETHOD GetJSContext(JSContext** aResult);
+ NS_IMETHOD GetArgc(uint32_t* aResult);
+ NS_IMETHOD GetArgvPtr(JS::Value** aResult);
+ NS_IMETHOD GetCalleeInterface(nsIInterfaceInfo** aResult);
+ NS_IMETHOD GetCalleeClassInfo(nsIClassInfo** aResult);
+ NS_IMETHOD GetPreviousCallContext(nsAXPCNativeCallContext** aResult);
+
+ enum {NO_ARGS = (unsigned) -1};
+
+ explicit XPCCallContext(JSContext* cx,
+ JS::HandleObject obj = nullptr,
+ JS::HandleObject funobj = nullptr,
+ JS::HandleId id = JSID_VOIDHANDLE,
+ unsigned argc = NO_ARGS,
+ JS::Value* argv = nullptr,
+ JS::Value* rval = nullptr);
+
+ virtual ~XPCCallContext();
+
+ inline bool IsValid() const ;
+
+ inline XPCJSContext* GetContext() const ;
+ inline JSContext* GetJSContext() const ;
+ inline bool GetContextPopRequired() const ;
+ inline XPCCallContext* GetPrevCallContext() const ;
+
+ inline JSObject* GetFlattenedJSObject() const ;
+ inline nsISupports* GetIdentityObject() const ;
+ inline XPCWrappedNative* GetWrapper() const ;
+ inline XPCWrappedNativeProto* GetProto() const ;
+
+ inline bool CanGetTearOff() const ;
+ inline XPCWrappedNativeTearOff* GetTearOff() const ;
+
+ inline XPCNativeScriptableInfo* GetScriptableInfo() const ;
+ inline bool CanGetSet() const ;
+ inline XPCNativeSet* GetSet() const ;
+ inline bool CanGetInterface() const ;
+ inline XPCNativeInterface* GetInterface() const ;
+ inline XPCNativeMember* GetMember() const ;
+ inline bool HasInterfaceAndMember() const ;
+ inline jsid GetName() const ;
+ inline bool GetStaticMemberIsLocal() const ;
+ inline unsigned GetArgc() const ;
+ inline JS::Value* GetArgv() const ;
+ inline JS::Value* GetRetVal() const ;
+
+ inline uint16_t GetMethodIndex() const ;
+ inline void SetMethodIndex(uint16_t index) ;
+
+ inline jsid GetResolveName() const;
+ inline jsid SetResolveName(JS::HandleId name);
+
+ inline XPCWrappedNative* GetResolvingWrapper() const;
+ inline XPCWrappedNative* SetResolvingWrapper(XPCWrappedNative* w);
+
+ inline void SetRetVal(const JS::Value& val);
+
+ void SetName(jsid name);
+ void SetArgsAndResultPtr(unsigned argc, JS::Value* argv, JS::Value* rval);
+ void SetCallInfo(XPCNativeInterface* iface, XPCNativeMember* member,
+ bool isSetter);
+
+ nsresult CanCallNow();
+
+ void SystemIsBeingShutDown();
+
+ operator JSContext*() const {return GetJSContext();}
+
+private:
+
+ // no copy ctor or assignment allowed
+ XPCCallContext(const XPCCallContext& r) = delete;
+ XPCCallContext& operator= (const XPCCallContext& r) = delete;
+
+private:
+ // posible values for mState
+ enum State {
+ INIT_FAILED,
+ SYSTEM_SHUTDOWN,
+ HAVE_CONTEXT,
+ HAVE_OBJECT,
+ HAVE_NAME,
+ HAVE_ARGS,
+ READY_TO_CALL,
+ CALL_DONE
+ };
+
+#ifdef DEBUG
+inline void CHECK_STATE(int s) const {MOZ_ASSERT(mState >= s, "bad state");}
+#else
+#define CHECK_STATE(s) ((void)0)
+#endif
+
+private:
+ JSAutoRequest mAr;
+ State mState;
+
+ RefPtr<nsXPConnect> mXPC;
+
+ XPCJSContext* mXPCJSContext;
+ JSContext* mJSContext;
+
+ // ctor does not necessarily init the following. BEWARE!
+
+ XPCCallContext* mPrevCallContext;
+
+ XPCWrappedNative* mWrapper;
+ XPCWrappedNativeTearOff* mTearOff;
+
+ XPCNativeScriptableInfo* mScriptableInfo;
+
+ RefPtr<XPCNativeSet> mSet;
+ RefPtr<XPCNativeInterface> mInterface;
+ XPCNativeMember* mMember;
+
+ JS::RootedId mName;
+ bool mStaticMemberIsLocal;
+
+ unsigned mArgc;
+ JS::Value* mArgv;
+ JS::Value* mRetVal;
+
+ uint16_t mMethodIndex;
+};
+
+/***************************************************************************
+****************************************************************************
+*
+* Core classes for wrapped native objects for use from JavaScript...
+*
+****************************************************************************
+***************************************************************************/
+
+// These are the various JSClasses and callbacks whose use that required
+// visibility from more than one .cpp file.
+
+extern const js::Class XPC_WN_NoHelper_JSClass;
+extern const js::Class XPC_WN_NoMods_Proto_JSClass;
+extern const js::Class XPC_WN_ModsAllowed_Proto_JSClass;
+extern const js::Class XPC_WN_Tearoff_JSClass;
+#define XPC_WN_TEAROFF_RESERVED_SLOTS 1
+#define XPC_WN_TEAROFF_FLAT_OBJECT_SLOT 0
+extern const js::Class XPC_WN_NoHelper_Proto_JSClass;
+
+extern bool
+XPC_WN_CallMethod(JSContext* cx, unsigned argc, JS::Value* vp);
+
+extern bool
+XPC_WN_GetterSetter(JSContext* cx, unsigned argc, JS::Value* vp);
+
+// Maybe this macro should check for class->enumerate ==
+// XPC_WN_Shared_Proto_Enumerate or something rather than checking for
+// 4 classes?
+static inline bool IS_PROTO_CLASS(const js::Class* clazz)
+{
+ return clazz == &XPC_WN_NoMods_Proto_JSClass ||
+ clazz == &XPC_WN_ModsAllowed_Proto_JSClass;
+}
+
+typedef js::HashSet<size_t,
+ js::DefaultHasher<size_t>,
+ js::SystemAllocPolicy> InterpositionWhitelist;
+
+struct InterpositionWhitelistPair {
+ nsIAddonInterposition* interposition;
+ InterpositionWhitelist whitelist;
+};
+
+typedef nsTArray<InterpositionWhitelistPair> InterpositionWhitelistArray;
+
+/***************************************************************************/
+// XPCWrappedNativeScope is one-to-one with a JS global object.
+
+class nsIAddonInterposition;
+class nsXPCComponentsBase;
+class XPCWrappedNativeScope final : public PRCList
+{
+public:
+
+ XPCJSContext*
+ GetContext() const {return XPCJSContext::Get();}
+
+ Native2WrappedNativeMap*
+ GetWrappedNativeMap() const {return mWrappedNativeMap;}
+
+ ClassInfo2WrappedNativeProtoMap*
+ GetWrappedNativeProtoMap() const {return mWrappedNativeProtoMap;}
+
+ nsXPCComponentsBase*
+ GetComponents() const {return mComponents;}
+
+ // Forces the creation of a privileged |Components| object, even in
+ // content scopes. This will crash if used outside of automation.
+ void
+ ForcePrivilegedComponents();
+
+ bool AttachComponentsObject(JSContext* aCx);
+
+ // Returns the JS object reflection of the Components object.
+ bool
+ GetComponentsJSObject(JS::MutableHandleObject obj);
+
+ JSObject*
+ GetGlobalJSObject() const {
+ return mGlobalJSObject;
+ }
+
+ JSObject*
+ GetGlobalJSObjectPreserveColor() const {return mGlobalJSObject.unbarrieredGet();}
+
+ nsIPrincipal*
+ GetPrincipal() const {
+ JSCompartment* c = js::GetObjectCompartment(mGlobalJSObject);
+ return nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
+ }
+
+ JSObject*
+ GetExpandoChain(JS::HandleObject target);
+
+ bool
+ SetExpandoChain(JSContext* cx, JS::HandleObject target, JS::HandleObject chain);
+
+ static void
+ SystemIsBeingShutDown();
+
+ static void
+ TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSContext* cx);
+
+ void TraceSelf(JSTracer* trc) {
+ MOZ_ASSERT(mGlobalJSObject);
+ mGlobalJSObject.trace(trc, "XPCWrappedNativeScope::mGlobalJSObject");
+ }
+
+ void TraceInside(JSTracer* trc) {
+ if (mContentXBLScope)
+ mContentXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
+ for (size_t i = 0; i < mAddonScopes.Length(); i++)
+ mAddonScopes[i].trace(trc, "XPCWrappedNativeScope::mAddonScopes");
+ if (mXrayExpandos.initialized())
+ mXrayExpandos.trace(trc);
+ }
+
+ static void
+ SuspectAllWrappers(XPCJSContext* cx, nsCycleCollectionNoteRootCallback& cb);
+
+ static void
+ SweepAllWrappedNativeTearOffs();
+
+ static void
+ UpdateWeakPointersAfterGC(XPCJSContext* cx);
+
+ static void
+ KillDyingScopes();
+
+ static void
+ DebugDumpAllScopes(int16_t depth);
+
+ void
+ DebugDump(int16_t depth);
+
+ struct ScopeSizeInfo {
+ explicit ScopeSizeInfo(mozilla::MallocSizeOf mallocSizeOf)
+ : mMallocSizeOf(mallocSizeOf),
+ mScopeAndMapSize(0),
+ mProtoAndIfaceCacheSize(0)
+ {}
+
+ mozilla::MallocSizeOf mMallocSizeOf;
+ size_t mScopeAndMapSize;
+ size_t mProtoAndIfaceCacheSize;
+ };
+
+ static void
+ AddSizeOfAllScopesIncludingThis(ScopeSizeInfo* scopeSizeInfo);
+
+ void
+ AddSizeOfIncludingThis(ScopeSizeInfo* scopeSizeInfo);
+
+ bool
+ IsValid() const {return mContext != nullptr;}
+
+ static bool
+ IsDyingScope(XPCWrappedNativeScope* scope);
+
+ typedef js::HashSet<JS::Heap<JSObject*>,
+ js::MovableCellHasher<JS::Heap<JSObject*>>,
+ js::SystemAllocPolicy> DOMExpandoSet;
+
+ bool RegisterDOMExpandoObject(JSObject* expando) {
+ // Expandos are proxy objects, and proxies are always tenured.
+ JS::AssertGCThingMustBeTenured(expando);
+ if (!mDOMExpandoSet) {
+ mDOMExpandoSet = new DOMExpandoSet();
+ if (!mDOMExpandoSet->init(8))
+ return false;
+ }
+ return mDOMExpandoSet->put(JS::Heap<JSObject*>(expando));
+ }
+ void RemoveDOMExpandoObject(JSObject* expando) {
+ if (mDOMExpandoSet) {
+ DOMExpandoSet::Ptr p = mDOMExpandoSet->lookup(JS::Heap<JSObject*>(expando));
+ MOZ_ASSERT(p.found());
+ mDOMExpandoSet->remove(p);
+ }
+ }
+
+ typedef js::HashMap<JSAddonId*,
+ nsCOMPtr<nsIAddonInterposition>,
+ js::PointerHasher<JSAddonId*, 3>,
+ js::SystemAllocPolicy> InterpositionMap;
+
+ typedef js::HashSet<JSAddonId*,
+ js::PointerHasher<JSAddonId*, 3>,
+ js::SystemAllocPolicy> AddonSet;
+
+ // Gets the appropriate scope object for XBL in this scope. The context
+ // must be same-compartment with the global upon entering, and the scope
+ // object is wrapped into the compartment of the global.
+ JSObject* EnsureContentXBLScope(JSContext* cx);
+
+ JSObject* EnsureAddonScope(JSContext* cx, JSAddonId* addonId);
+
+ XPCWrappedNativeScope(JSContext* cx, JS::HandleObject aGlobal);
+
+ nsAutoPtr<JSObject2JSObjectMap> mWaiverWrapperMap;
+
+ bool IsContentXBLScope() { return mIsContentXBLScope; }
+ bool AllowContentXBLScope();
+ bool UseContentXBLScope() { return mUseContentXBLScope; }
+ void ClearContentXBLScope() { mContentXBLScope = nullptr; }
+
+ bool IsAddonScope() { return mIsAddonScope; }
+
+ bool HasInterposition() { return mInterposition; }
+ nsCOMPtr<nsIAddonInterposition> GetInterposition();
+
+ static bool SetAddonInterposition(JSContext* cx,
+ JSAddonId* addonId,
+ nsIAddonInterposition* interp);
+
+ static InterpositionWhitelist* GetInterpositionWhitelist(nsIAddonInterposition* interposition);
+ static bool UpdateInterpositionWhitelist(JSContext* cx,
+ nsIAddonInterposition* interposition);
+
+ void SetAddonCallInterposition() { mHasCallInterpositions = true; }
+ bool HasCallInterposition() { return mHasCallInterpositions; };
+
+ static bool AllowCPOWsInAddon(JSContext* cx, JSAddonId* addonId, bool allow);
+
+protected:
+ virtual ~XPCWrappedNativeScope();
+
+ XPCWrappedNativeScope() = delete;
+
+private:
+ class ClearInterpositionsObserver final : public nsIObserver {
+ ~ClearInterpositionsObserver() {}
+
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ };
+
+ static XPCWrappedNativeScope* gScopes;
+ static XPCWrappedNativeScope* gDyingScopes;
+
+ static bool gShutdownObserverInitialized;
+ static InterpositionMap* gInterpositionMap;
+ static AddonSet* gAllowCPOWAddonSet;
+
+ static InterpositionWhitelistArray* gInterpositionWhitelists;
+
+ XPCJSContext* mContext;
+ Native2WrappedNativeMap* mWrappedNativeMap;
+ ClassInfo2WrappedNativeProtoMap* mWrappedNativeProtoMap;
+ RefPtr<nsXPCComponentsBase> mComponents;
+ XPCWrappedNativeScope* mNext;
+ // The JS global object for this scope. If non-null, this will be the
+ // default parent for the XPCWrappedNatives that have us as the scope,
+ // unless a PreCreate hook overrides it. Note that this _may_ be null (see
+ // constructor).
+ JS::ObjectPtr mGlobalJSObject;
+
+ // XBL Scope. This is is a lazily-created sandbox for non-system scopes.
+ // EnsureContentXBLScope() decides whether it needs to be created or not.
+ // This reference is wrapped into the compartment of mGlobalJSObject.
+ JS::ObjectPtr mContentXBLScope;
+
+ // Lazily created sandboxes for addon code.
+ nsTArray<JS::ObjectPtr> mAddonScopes;
+
+ // This is a service that will be use to interpose on some property accesses on
+ // objects from other scope, for add-on compatibility reasons.
+ nsCOMPtr<nsIAddonInterposition> mInterposition;
+
+ // If this flag is set, we intercept function calls on vanilla JS function objects
+ // from this scope if the caller scope has mInterposition set.
+ bool mHasCallInterpositions;
+
+ nsAutoPtr<DOMExpandoSet> mDOMExpandoSet;
+
+ JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;
+
+ bool mIsContentXBLScope;
+ bool mIsAddonScope;
+
+ // For remote XUL domains, we run all XBL in the content scope for compat
+ // reasons (though we sometimes pref this off for automation). We separately
+ // track the result of this decision (mAllowContentXBLScope), from the decision
+ // of whether to actually _use_ an XBL scope (mUseContentXBLScope), which depends
+ // on the type of global and whether the compartment is system principal
+ // or not.
+ //
+ // This distinction is useful primarily because, if true, we know that we
+ // have no way of distinguishing XBL script from content script for the
+ // given scope. In these (unsupported) situations, we just always claim to
+ // be XBL.
+ bool mAllowContentXBLScope;
+ bool mUseContentXBLScope;
+};
+
+/***************************************************************************/
+// Slots we use for our functions
+#define XPC_FUNCTION_NATIVE_MEMBER_SLOT 0
+#define XPC_FUNCTION_PARENT_OBJECT_SLOT 1
+
+/***************************************************************************/
+// XPCNativeMember represents a single idl declared method, attribute or
+// constant.
+
+// Tight. No virtual methods. Can be bitwise copied (until any resolution done).
+
+class XPCNativeMember final
+{
+public:
+ static bool GetCallInfo(JSObject* funobj,
+ RefPtr<XPCNativeInterface>* pInterface,
+ XPCNativeMember** pMember);
+
+ jsid GetName() const {return mName;}
+
+ uint16_t GetIndex() const {return mIndex;}
+
+ bool GetConstantValue(XPCCallContext& ccx, XPCNativeInterface* iface,
+ JS::Value* pval)
+ {MOZ_ASSERT(IsConstant(),
+ "Only call this if you're sure this is a constant!");
+ return Resolve(ccx, iface, nullptr, pval);}
+
+ bool NewFunctionObject(XPCCallContext& ccx, XPCNativeInterface* iface,
+ JS::HandleObject parent, JS::Value* pval);
+
+ bool IsMethod() const
+ {return 0 != (mFlags & METHOD);}
+
+ bool IsConstant() const
+ {return 0 != (mFlags & CONSTANT);}
+
+ bool IsAttribute() const
+ {return 0 != (mFlags & GETTER);}
+
+ bool IsWritableAttribute() const
+ {return 0 != (mFlags & SETTER_TOO);}
+
+ bool IsReadOnlyAttribute() const
+ {return IsAttribute() && !IsWritableAttribute();}
+
+
+ void SetName(jsid a) {mName = a;}
+
+ void SetMethod(uint16_t index)
+ {mFlags = METHOD; mIndex = index;}
+
+ void SetConstant(uint16_t index)
+ {mFlags = CONSTANT; mIndex = index;}
+
+ void SetReadOnlyAttribute(uint16_t index)
+ {mFlags = GETTER; mIndex = index;}
+
+ void SetWritableAttribute()
+ {MOZ_ASSERT(mFlags == GETTER,"bad"); mFlags = GETTER | SETTER_TOO;}
+
+ static uint16_t GetMaxIndexInInterface()
+ {return (1<<12) - 1;}
+
+ inline XPCNativeInterface* GetInterface() const;
+
+ void SetIndexInInterface(uint16_t index)
+ {mIndexInInterface = index;}
+
+ /* default ctor - leave random contents */
+ XPCNativeMember() {MOZ_COUNT_CTOR(XPCNativeMember);}
+ ~XPCNativeMember() {MOZ_COUNT_DTOR(XPCNativeMember);}
+
+private:
+ bool Resolve(XPCCallContext& ccx, XPCNativeInterface* iface,
+ JS::HandleObject parent, JS::Value* vp);
+
+ enum {
+ METHOD = 0x01,
+ CONSTANT = 0x02,
+ GETTER = 0x04,
+ SETTER_TOO = 0x08
+ // If you add a flag here, you may need to make mFlags wider and either
+ // make mIndexInInterface narrower (and adjust
+ // XPCNativeInterface::NewInstance accordingly) or make this object
+ // bigger.
+ };
+
+private:
+ // our only data...
+ jsid mName;
+ uint16_t mIndex;
+ // mFlags needs to be wide enogh to hold the flags in the above enum.
+ uint16_t mFlags : 4;
+ // mIndexInInterface is the index of this in our XPCNativeInterface's
+ // mMembers. In theory our XPCNativeInterface could have as many as 2^15-1
+ // members (since mMemberCount is 15-bit) but in practice we prevent
+ // creation of XPCNativeInterfaces which have more than 2^12 members.
+ // If the width of this field changes, update GetMaxIndexInInterface.
+ uint16_t mIndexInInterface : 12;
+} JS_HAZ_NON_GC_POINTER; // Only stores a pinned string
+
+/***************************************************************************/
+// XPCNativeInterface represents a single idl declared interface. This is
+// primarily the set of XPCNativeMembers.
+
+// Tight. No virtual methods.
+
+class XPCNativeInterface final
+{
+ public:
+ NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(XPCNativeInterface,
+ DestroyInstance(this))
+
+ static already_AddRefed<XPCNativeInterface> GetNewOrUsed(const nsIID* iid);
+ static already_AddRefed<XPCNativeInterface> GetNewOrUsed(nsIInterfaceInfo* info);
+ static already_AddRefed<XPCNativeInterface> GetNewOrUsed(const char* name);
+ static already_AddRefed<XPCNativeInterface> GetISupports();
+
+ inline nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo.get();}
+ inline jsid GetName() const {return mName;}
+
+ inline const nsIID* GetIID() const;
+ inline const char* GetNameString() const;
+ inline XPCNativeMember* FindMember(jsid name) const;
+
+ inline bool HasAncestor(const nsIID* iid) const;
+ static inline size_t OffsetOfMembers();
+
+ uint16_t GetMemberCount() const {
+ return mMemberCount;
+ }
+ XPCNativeMember* GetMemberAt(uint16_t i) {
+ MOZ_ASSERT(i < mMemberCount, "bad index");
+ return &mMembers[i];
+ }
+
+ void DebugDump(int16_t depth);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+ protected:
+ static already_AddRefed<XPCNativeInterface> NewInstance(nsIInterfaceInfo* aInfo);
+
+ XPCNativeInterface() = delete;
+ XPCNativeInterface(nsIInterfaceInfo* aInfo, jsid aName)
+ : mInfo(aInfo), mName(aName), mMemberCount(0)
+ {}
+ ~XPCNativeInterface();
+
+ void* operator new(size_t, void* p) CPP_THROW_NEW {return p;}
+
+ XPCNativeInterface(const XPCNativeInterface& r) = delete;
+ XPCNativeInterface& operator= (const XPCNativeInterface& r) = delete;
+
+ static void DestroyInstance(XPCNativeInterface* inst);
+
+private:
+ nsCOMPtr<nsIInterfaceInfo> mInfo;
+ jsid mName;
+ uint16_t mMemberCount;
+ XPCNativeMember mMembers[1]; // always last - object sized for array
+};
+
+/***************************************************************************/
+// XPCNativeSetKey is used to key a XPCNativeSet in a NativeSetMap.
+// It represents a new XPCNativeSet we are considering constructing, without
+// requiring that the set actually be built.
+
+class MOZ_STACK_CLASS XPCNativeSetKey final
+{
+public:
+ // This represents an existing set |baseSet|.
+ explicit XPCNativeSetKey(XPCNativeSet* baseSet)
+ : mBaseSet(baseSet), mAddition(nullptr)
+ {
+ MOZ_ASSERT(baseSet);
+ }
+
+ // This represents a new set containing only nsISupports and
+ // |addition|.
+ explicit XPCNativeSetKey(XPCNativeInterface* addition)
+ : mBaseSet(nullptr), mAddition(addition)
+ {
+ MOZ_ASSERT(addition);
+ }
+
+ // This represents the existing set |baseSet| with the interface
+ // |addition| inserted after existing interfaces. |addition| must
+ // not already be present in |baseSet|.
+ explicit XPCNativeSetKey(XPCNativeSet* baseSet,
+ XPCNativeInterface* addition);
+ ~XPCNativeSetKey() {}
+
+ XPCNativeSet* GetBaseSet() const {return mBaseSet;}
+ XPCNativeInterface* GetAddition() const {return mAddition;}
+
+ PLDHashNumber Hash() const;
+
+ // Allow shallow copy
+
+private:
+ RefPtr<XPCNativeSet> mBaseSet;
+ RefPtr<XPCNativeInterface> mAddition;
+};
+
+/***************************************************************************/
+// XPCNativeSet represents an ordered collection of XPCNativeInterface pointers.
+
+class XPCNativeSet final
+{
+ public:
+ NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(XPCNativeSet,
+ DestroyInstance(this))
+
+ static already_AddRefed<XPCNativeSet> GetNewOrUsed(const nsIID* iid);
+ static already_AddRefed<XPCNativeSet> GetNewOrUsed(nsIClassInfo* classInfo);
+ static already_AddRefed<XPCNativeSet> GetNewOrUsed(XPCNativeSetKey* key);
+
+ // This generates a union set.
+ //
+ // If preserveFirstSetOrder is true, the elements from |firstSet| come first,
+ // followed by any non-duplicate items from |secondSet|. If false, the same
+ // algorithm is applied; but if we detect that |secondSet| is a superset of
+ // |firstSet|, we return |secondSet| without worrying about whether the
+ // ordering might differ from |firstSet|.
+ static already_AddRefed<XPCNativeSet> GetNewOrUsed(XPCNativeSet* firstSet,
+ XPCNativeSet* secondSet,
+ bool preserveFirstSetOrder);
+
+ static void ClearCacheEntryForClassInfo(nsIClassInfo* classInfo);
+
+ inline bool FindMember(jsid name, XPCNativeMember** pMember,
+ uint16_t* pInterfaceIndex) const;
+
+ inline bool FindMember(jsid name, XPCNativeMember** pMember,
+ RefPtr<XPCNativeInterface>* pInterface) const;
+
+ inline bool FindMember(JS::HandleId name,
+ XPCNativeMember** pMember,
+ RefPtr<XPCNativeInterface>* pInterface,
+ XPCNativeSet* protoSet,
+ bool* pIsLocal) const;
+
+ inline bool HasInterface(XPCNativeInterface* aInterface) const;
+ inline bool HasInterfaceWithAncestor(XPCNativeInterface* aInterface) const;
+ inline bool HasInterfaceWithAncestor(const nsIID* iid) const;
+
+ inline XPCNativeInterface* FindInterfaceWithIID(const nsIID& iid) const;
+
+ inline XPCNativeInterface* FindNamedInterface(jsid name) const;
+
+ uint16_t GetMemberCount() const {
+ return mMemberCount;
+ }
+ uint16_t GetInterfaceCount() const {
+ return mInterfaceCount;
+ }
+ XPCNativeInterface** GetInterfaceArray() {
+ return mInterfaces;
+ }
+
+ XPCNativeInterface* GetInterfaceAt(uint16_t i)
+ {MOZ_ASSERT(i < mInterfaceCount, "bad index"); return mInterfaces[i];}
+
+ inline bool MatchesSetUpToInterface(const XPCNativeSet* other,
+ XPCNativeInterface* iface) const;
+
+ void DebugDump(int16_t depth);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+ protected:
+ static already_AddRefed<XPCNativeSet> NewInstance(nsTArray<RefPtr<XPCNativeInterface>>&& array);
+ static already_AddRefed<XPCNativeSet> NewInstanceMutate(XPCNativeSetKey* key);
+
+ XPCNativeSet()
+ : mMemberCount(0), mInterfaceCount(0)
+ {}
+ ~XPCNativeSet();
+ void* operator new(size_t, void* p) CPP_THROW_NEW {return p;}
+
+ static void DestroyInstance(XPCNativeSet* inst);
+
+ private:
+ uint16_t mMemberCount;
+ uint16_t mInterfaceCount;
+ // Always last - object sized for array.
+ // These are strong references.
+ XPCNativeInterface* mInterfaces[1];
+};
+
+/***************************************************************************/
+// XPCNativeScriptableFlags is a wrapper class that holds the flags returned
+// from calls to nsIXPCScriptable::GetScriptableFlags(). It has convenience
+// methods to check for particular bitflags.
+
+class XPCNativeScriptableFlags final
+{
+public:
+ explicit XPCNativeScriptableFlags(uint32_t flags = 0) : mFlags(flags) {}
+
+ uint32_t GetFlags() const { return mFlags; }
+ void SetFlags(uint32_t flags) { mFlags = flags; }
+
+ operator uint32_t() const { return GetFlags(); }
+
+ XPCNativeScriptableFlags(const XPCNativeScriptableFlags& r)
+ {
+ mFlags = r.GetFlags();
+ }
+
+ XPCNativeScriptableFlags& operator= (const XPCNativeScriptableFlags& r)
+ {
+ mFlags = r.GetFlags();
+ return *this;
+ }
+
+#ifdef GET_IT
+#undef GET_IT
+#endif
+#define GET_IT(f_) const { return 0 != (mFlags & nsIXPCScriptable:: f_ ); }
+
+ bool WantPreCreate() GET_IT(WANT_PRECREATE)
+ bool WantAddProperty() GET_IT(WANT_ADDPROPERTY)
+ bool WantGetProperty() GET_IT(WANT_GETPROPERTY)
+ bool WantSetProperty() GET_IT(WANT_SETPROPERTY)
+ bool WantEnumerate() GET_IT(WANT_ENUMERATE)
+ bool WantNewEnumerate() GET_IT(WANT_NEWENUMERATE)
+ bool WantResolve() GET_IT(WANT_RESOLVE)
+ bool WantFinalize() GET_IT(WANT_FINALIZE)
+ bool WantCall() GET_IT(WANT_CALL)
+ bool WantConstruct() GET_IT(WANT_CONSTRUCT)
+ bool WantHasInstance() GET_IT(WANT_HASINSTANCE)
+ bool UseJSStubForAddProperty() GET_IT(USE_JSSTUB_FOR_ADDPROPERTY)
+ bool UseJSStubForDelProperty() GET_IT(USE_JSSTUB_FOR_DELPROPERTY)
+ bool UseJSStubForSetProperty() GET_IT(USE_JSSTUB_FOR_SETPROPERTY)
+ bool DontEnumQueryInterface() GET_IT(DONT_ENUM_QUERY_INTERFACE)
+ bool DontAskInstanceForScriptable() GET_IT(DONT_ASK_INSTANCE_FOR_SCRIPTABLE)
+ bool ClassInfoInterfacesOnly() GET_IT(CLASSINFO_INTERFACES_ONLY)
+ bool AllowPropModsDuringResolve() GET_IT(ALLOW_PROP_MODS_DURING_RESOLVE)
+ bool AllowPropModsToPrototype() GET_IT(ALLOW_PROP_MODS_TO_PROTOTYPE)
+ bool IsGlobalObject() GET_IT(IS_GLOBAL_OBJECT)
+ bool DontReflectInterfaceNames() GET_IT(DONT_REFLECT_INTERFACE_NAMES)
+
+#undef GET_IT
+
+private:
+ uint32_t mFlags;
+};
+
+/***************************************************************************/
+// XPCNativeScriptableInfo is a trivial wrapper for nsIXPCScriptable which
+// should be removed eventually.
+
+class XPCNativeScriptableInfo final
+{
+public:
+ static XPCNativeScriptableInfo*
+ Construct(const XPCNativeScriptableCreateInfo* sci);
+
+ nsIXPCScriptable*
+ GetCallback() const { return mCallback; }
+
+ XPCNativeScriptableFlags
+ GetFlags() const { return XPCNativeScriptableFlags(mCallback->GetScriptableFlags()); }
+
+ const JSClass*
+ GetJSClass() { return Jsvalify(mCallback->GetClass()); }
+
+protected:
+ explicit XPCNativeScriptableInfo(nsIXPCScriptable* aCallback)
+ : mCallback(aCallback)
+ {
+ MOZ_COUNT_CTOR(XPCNativeScriptableInfo);
+ }
+public:
+ ~XPCNativeScriptableInfo()
+ {
+ MOZ_COUNT_DTOR(XPCNativeScriptableInfo);
+ }
+private:
+
+ // disable copy ctor and assignment
+ XPCNativeScriptableInfo(const XPCNativeScriptableInfo& r) = delete;
+ XPCNativeScriptableInfo& operator= (const XPCNativeScriptableInfo& r) = delete;
+
+private:
+ nsCOMPtr<nsIXPCScriptable> mCallback;
+};
+
+/***************************************************************************/
+// XPCNativeScriptableCreateInfo is used in creating new wrapper and protos.
+// it abstracts out the scriptable interface pointer and the flags. After
+// creation these are factored differently using XPCNativeScriptableInfo.
+
+class MOZ_STACK_CLASS XPCNativeScriptableCreateInfo final
+{
+public:
+
+ explicit XPCNativeScriptableCreateInfo(const XPCNativeScriptableInfo& si)
+ : mCallback(si.GetCallback()), mFlags(si.GetFlags()) {}
+
+ XPCNativeScriptableCreateInfo(already_AddRefed<nsIXPCScriptable>&& callback,
+ XPCNativeScriptableFlags flags)
+ : mCallback(callback), mFlags(flags) {}
+
+ XPCNativeScriptableCreateInfo()
+ : mFlags(0) {}
+
+
+ nsIXPCScriptable*
+ GetCallback() const {return mCallback;}
+
+ const XPCNativeScriptableFlags&
+ GetFlags() const {return mFlags;}
+
+ void
+ SetCallback(already_AddRefed<nsIXPCScriptable>&& callback)
+ {mCallback = callback;}
+
+ void
+ SetFlags(const XPCNativeScriptableFlags& flags) {mFlags = flags;}
+
+private:
+ // XXX: the flags are the same as the ones gettable from the callback. This
+ // redundancy should be removed eventually.
+ nsCOMPtr<nsIXPCScriptable> mCallback;
+ XPCNativeScriptableFlags mFlags;
+};
+
+/***********************************************/
+// XPCWrappedNativeProto hold the additional shared wrapper data
+// for XPCWrappedNative whose native objects expose nsIClassInfo.
+
+class XPCWrappedNativeProto final
+{
+public:
+ static XPCWrappedNativeProto*
+ GetNewOrUsed(XPCWrappedNativeScope* scope,
+ nsIClassInfo* classInfo,
+ const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
+ bool callPostCreatePrototype = true);
+
+ XPCWrappedNativeScope*
+ GetScope() const {return mScope;}
+
+ XPCJSContext*
+ GetContext() const {return mScope->GetContext();}
+
+ JSObject*
+ GetJSProtoObject() const { return mJSProtoObject; }
+
+ nsIClassInfo*
+ GetClassInfo() const {return mClassInfo;}
+
+ XPCNativeSet*
+ GetSet() const {return mSet;}
+
+ XPCNativeScriptableInfo*
+ GetScriptableInfo() {return mScriptableInfo;}
+
+ bool CallPostCreatePrototype();
+ void JSProtoObjectFinalized(js::FreeOp* fop, JSObject* obj);
+ void JSProtoObjectMoved(JSObject* obj, const JSObject* old);
+
+ void SystemIsBeingShutDown();
+
+ void DebugDump(int16_t depth);
+
+ void TraceSelf(JSTracer* trc) {
+ if (mJSProtoObject)
+ mJSProtoObject.trace(trc, "XPCWrappedNativeProto::mJSProtoObject");
+ }
+
+ void TraceInside(JSTracer* trc) {
+ GetScope()->TraceSelf(trc);
+ }
+
+ void TraceJS(JSTracer* trc) {
+ TraceSelf(trc);
+ TraceInside(trc);
+ }
+
+ void WriteBarrierPre(JSContext* cx)
+ {
+ if (JS::IsIncrementalBarrierNeeded(cx) && mJSProtoObject)
+ mJSProtoObject.writeBarrierPre(cx);
+ }
+
+ // NOP. This is just here to make the AutoMarkingPtr code compile.
+ void Mark() const {}
+ inline void AutoTrace(JSTracer* trc) {}
+
+ ~XPCWrappedNativeProto();
+
+protected:
+ // disable copy ctor and assignment
+ XPCWrappedNativeProto(const XPCWrappedNativeProto& r) = delete;
+ XPCWrappedNativeProto& operator= (const XPCWrappedNativeProto& r) = delete;
+
+ // hide ctor
+ XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
+ nsIClassInfo* ClassInfo,
+ already_AddRefed<XPCNativeSet>&& Set);
+
+ bool Init(const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
+ bool callPostCreatePrototype);
+
+private:
+#ifdef DEBUG
+ static int32_t gDEBUG_LiveProtoCount;
+#endif
+
+private:
+ XPCWrappedNativeScope* mScope;
+ JS::ObjectPtr mJSProtoObject;
+ nsCOMPtr<nsIClassInfo> mClassInfo;
+ RefPtr<XPCNativeSet> mSet;
+ XPCNativeScriptableInfo* mScriptableInfo;
+};
+
+/***********************************************/
+// XPCWrappedNativeTearOff represents the info needed to make calls to one
+// interface on the underlying native object of a XPCWrappedNative.
+
+class XPCWrappedNativeTearOff final
+{
+public:
+ bool IsAvailable() const {return mInterface == nullptr;}
+ bool IsReserved() const {return mInterface == (XPCNativeInterface*)1;}
+ bool IsValid() const {return !IsAvailable() && !IsReserved();}
+ void SetReserved() {mInterface = (XPCNativeInterface*)1;}
+
+ XPCNativeInterface* GetInterface() const {return mInterface;}
+ nsISupports* GetNative() const {return mNative;}
+ JSObject* GetJSObject();
+ JSObject* GetJSObjectPreserveColor() const;
+ void SetInterface(XPCNativeInterface* Interface) {mInterface = Interface;}
+ void SetNative(nsISupports* Native) {mNative = Native;}
+ already_AddRefed<nsISupports> TakeNative() { return mNative.forget(); }
+ void SetJSObject(JSObject* JSObj);
+
+ void JSObjectFinalized() {SetJSObject(nullptr);}
+ void JSObjectMoved(JSObject* obj, const JSObject* old);
+
+ XPCWrappedNativeTearOff()
+ : mInterface(nullptr), mJSObject(nullptr)
+ {
+ MOZ_COUNT_CTOR(XPCWrappedNativeTearOff);
+ }
+ ~XPCWrappedNativeTearOff();
+
+ // NOP. This is just here to make the AutoMarkingPtr code compile.
+ inline void TraceJS(JSTracer* trc) {}
+ inline void AutoTrace(JSTracer* trc) {}
+
+ void Mark() {mJSObject.setFlags(1);}
+ void Unmark() {mJSObject.unsetFlags(1);}
+ bool IsMarked() const {return mJSObject.hasFlag(1);}
+
+ XPCWrappedNativeTearOff* AddTearOff()
+ {
+ MOZ_ASSERT(!mNextTearOff);
+ mNextTearOff = mozilla::MakeUnique<XPCWrappedNativeTearOff>();
+ return mNextTearOff.get();
+ }
+
+ XPCWrappedNativeTearOff* GetNextTearOff() {return mNextTearOff.get();}
+
+private:
+ XPCWrappedNativeTearOff(const XPCWrappedNativeTearOff& r) = delete;
+ XPCWrappedNativeTearOff& operator= (const XPCWrappedNativeTearOff& r) = delete;
+
+private:
+ XPCNativeInterface* mInterface;
+ // mNative is an nsRefPtr not an nsCOMPtr because it may not be the canonical
+ // nsISupports pointer.
+ RefPtr<nsISupports> mNative;
+ JS::TenuredHeap<JSObject*> mJSObject;
+ mozilla::UniquePtr<XPCWrappedNativeTearOff> mNextTearOff;
+};
+
+
+/***************************************************************************/
+// XPCWrappedNative the wrapper around one instance of a native xpcom object
+// to be used from JavaScript.
+
+class XPCWrappedNative final : public nsIXPConnectWrappedNative
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
+ NS_DECL_NSIXPCONNECTWRAPPEDNATIVE
+
+ NS_DECL_CYCLE_COLLECTION_CLASS(XPCWrappedNative)
+
+ nsIPrincipal* GetObjectPrincipal() const;
+
+ bool
+ IsValid() const { return mFlatJSObject.hasFlag(FLAT_JS_OBJECT_VALID); }
+
+#define XPC_SCOPE_WORD(s) (intptr_t(s))
+#define XPC_SCOPE_MASK (intptr_t(0x3))
+#define XPC_SCOPE_TAG (intptr_t(0x1))
+#define XPC_WRAPPER_EXPIRED (intptr_t(0x2))
+
+ static inline bool
+ IsTaggedScope(XPCWrappedNativeScope* s)
+ {return XPC_SCOPE_WORD(s) & XPC_SCOPE_TAG;}
+
+ static inline XPCWrappedNativeScope*
+ TagScope(XPCWrappedNativeScope* s)
+ {MOZ_ASSERT(!IsTaggedScope(s), "bad pointer!");
+ return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) | XPC_SCOPE_TAG);}
+
+ static inline XPCWrappedNativeScope*
+ UnTagScope(XPCWrappedNativeScope* s)
+ {return (XPCWrappedNativeScope*)(XPC_SCOPE_WORD(s) & ~XPC_SCOPE_TAG);}
+
+ inline bool
+ IsWrapperExpired() const
+ {return XPC_SCOPE_WORD(mMaybeScope) & XPC_WRAPPER_EXPIRED;}
+
+ bool
+ HasProto() const {return !IsTaggedScope(mMaybeScope);}
+
+ XPCWrappedNativeProto*
+ GetProto() const
+ {return HasProto() ?
+ (XPCWrappedNativeProto*)
+ (XPC_SCOPE_WORD(mMaybeProto) & ~XPC_SCOPE_MASK) : nullptr;}
+
+ void SetProto(XPCWrappedNativeProto* p);
+
+ XPCWrappedNativeScope*
+ GetScope() const
+ {return GetProto() ? GetProto()->GetScope() :
+ (XPCWrappedNativeScope*)
+ (XPC_SCOPE_WORD(mMaybeScope) & ~XPC_SCOPE_MASK);}
+
+ nsISupports*
+ GetIdentityObject() const {return mIdentity;}
+
+ /**
+ * This getter clears the gray bit before handing out the JSObject which
+ * means that the object is guaranteed to be kept alive past the next CC.
+ */
+ JSObject* GetFlatJSObject() const { return mFlatJSObject; }
+
+ /**
+ * This getter does not change the color of the JSObject meaning that the
+ * object returned is not guaranteed to be kept alive past the next CC.
+ *
+ * This should only be called if you are certain that the return value won't
+ * be passed into a JS API function and that it won't be stored without
+ * being rooted (or otherwise signaling the stored value to the CC).
+ */
+ JSObject*
+ GetFlatJSObjectPreserveColor() const {
+ return mFlatJSObject.unbarrieredGetPtr();
+ }
+
+ XPCNativeSet*
+ GetSet() const {return mSet;}
+
+ void
+ SetSet(already_AddRefed<XPCNativeSet> set) {mSet = set;}
+
+ static XPCWrappedNative* Get(JSObject* obj) {
+ MOZ_ASSERT(IS_WN_REFLECTOR(obj));
+ return (XPCWrappedNative*)js::GetObjectPrivate(obj);
+ }
+
+private:
+ inline void
+ ExpireWrapper()
+ {mMaybeScope = (XPCWrappedNativeScope*)
+ (XPC_SCOPE_WORD(mMaybeScope) | XPC_WRAPPER_EXPIRED);}
+
+public:
+
+ XPCNativeScriptableInfo*
+ GetScriptableInfo() const {return mScriptableInfo;}
+
+ nsIXPCScriptable* // call this wrong and you deserve to crash
+ GetScriptableCallback() const {return mScriptableInfo->GetCallback();}
+
+ nsIClassInfo*
+ GetClassInfo() const {return IsValid() && HasProto() ?
+ GetProto()->GetClassInfo() : nullptr;}
+
+ bool
+ HasMutatedSet() const {return IsValid() &&
+ (!HasProto() ||
+ GetSet() != GetProto()->GetSet());}
+
+ XPCJSContext*
+ GetContext() const {XPCWrappedNativeScope* scope = GetScope();
+ return scope ? scope->GetContext() : nullptr;}
+
+ static nsresult
+ WrapNewGlobal(xpcObjectHelper& nativeHelper,
+ nsIPrincipal* principal, bool initStandardClasses,
+ JS::CompartmentOptions& aOptions,
+ XPCWrappedNative** wrappedGlobal);
+
+ static nsresult
+ GetNewOrUsed(xpcObjectHelper& helper,
+ XPCWrappedNativeScope* Scope,
+ XPCNativeInterface* Interface,
+ XPCWrappedNative** wrapper);
+
+ static nsresult
+ GetUsedOnly(nsISupports* Object,
+ XPCWrappedNativeScope* Scope,
+ XPCNativeInterface* Interface,
+ XPCWrappedNative** wrapper);
+
+ void FlatJSObjectFinalized();
+ void FlatJSObjectMoved(JSObject* obj, const JSObject* old);
+
+ void SystemIsBeingShutDown();
+
+ enum CallMode {CALL_METHOD, CALL_GETTER, CALL_SETTER};
+
+ static bool CallMethod(XPCCallContext& ccx,
+ CallMode mode = CALL_METHOD);
+
+ static bool GetAttribute(XPCCallContext& ccx)
+ {return CallMethod(ccx, CALL_GETTER);}
+
+ static bool SetAttribute(XPCCallContext& ccx)
+ {return CallMethod(ccx, CALL_SETTER);}
+
+ inline bool HasInterfaceNoQI(const nsIID& iid);
+
+ XPCWrappedNativeTearOff* FindTearOff(XPCNativeInterface* aInterface,
+ bool needJSObject = false,
+ nsresult* pError = nullptr);
+ XPCWrappedNativeTearOff* FindTearOff(const nsIID& iid);
+
+ void Mark() const {}
+
+ inline void TraceInside(JSTracer* trc) {
+ if (HasProto())
+ GetProto()->TraceSelf(trc);
+ else
+ GetScope()->TraceSelf(trc);
+
+ JSObject* obj = mFlatJSObject.unbarrieredGetPtr();
+ if (obj && JS_IsGlobalObject(obj)) {
+ xpc::TraceXPCGlobal(trc, obj);
+ }
+ }
+
+ void TraceJS(JSTracer* trc) {
+ TraceInside(trc);
+ }
+
+ void TraceSelf(JSTracer* trc) {
+ // If this got called, we're being kept alive by someone who really
+ // needs us alive and whole. Do not let our mFlatJSObject go away.
+ // This is the only time we should be tracing our mFlatJSObject,
+ // normally somebody else is doing that.
+ JS::TraceEdge(trc, &mFlatJSObject, "XPCWrappedNative::mFlatJSObject");
+ }
+
+ static void Trace(JSTracer* trc, JSObject* obj);
+
+ void AutoTrace(JSTracer* trc) {
+ TraceSelf(trc);
+ }
+
+ inline void SweepTearOffs();
+
+ // Returns a string that shuld be free'd using JS_smprintf_free (or null).
+ char* ToString(XPCWrappedNativeTearOff* to = nullptr) const;
+
+ static void GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo,
+ XPCNativeScriptableCreateInfo& sciProto);
+
+ bool HasExternalReference() const {return mRefCnt > 1;}
+
+ void Suspect(nsCycleCollectionNoteRootCallback& cb);
+ void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
+
+ // Make ctor and dtor protected (rather than private) to placate nsCOMPtr.
+protected:
+ XPCWrappedNative() = delete;
+
+ // This ctor is used if this object will have a proto.
+ XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
+ XPCWrappedNativeProto* aProto);
+
+ // This ctor is used if this object will NOT have a proto.
+ XPCWrappedNative(already_AddRefed<nsISupports>&& aIdentity,
+ XPCWrappedNativeScope* aScope,
+ already_AddRefed<XPCNativeSet>&& aSet);
+
+ virtual ~XPCWrappedNative();
+ void Destroy();
+
+ void UpdateScriptableInfo(XPCNativeScriptableInfo* si);
+
+private:
+ enum {
+ // Flags bits for mFlatJSObject:
+ FLAT_JS_OBJECT_VALID = JS_BIT(0)
+ };
+
+ bool Init(const XPCNativeScriptableCreateInfo* sci);
+ bool FinishInit();
+
+ bool ExtendSet(XPCNativeInterface* aInterface);
+
+ nsresult InitTearOff(XPCWrappedNativeTearOff* aTearOff,
+ XPCNativeInterface* aInterface,
+ bool needJSObject);
+
+ bool InitTearOffJSObject(XPCWrappedNativeTearOff* to);
+
+public:
+ static const XPCNativeScriptableCreateInfo& GatherScriptableCreateInfo(nsISupports* obj,
+ nsIClassInfo* classInfo,
+ XPCNativeScriptableCreateInfo& sciProto,
+ XPCNativeScriptableCreateInfo& sciWrapper);
+
+private:
+ union
+ {
+ XPCWrappedNativeScope* mMaybeScope;
+ XPCWrappedNativeProto* mMaybeProto;
+ };
+ RefPtr<XPCNativeSet> mSet;
+ JS::TenuredHeap<JSObject*> mFlatJSObject;
+ XPCNativeScriptableInfo* mScriptableInfo;
+ XPCWrappedNativeTearOff mFirstTearOff;
+};
+
+/***************************************************************************
+****************************************************************************
+*
+* Core classes for wrapped JSObject for use from native code...
+*
+****************************************************************************
+***************************************************************************/
+
+// this interfaces exists so we can refcount nsXPCWrappedJSClass
+// {2453EBA0-A9B8-11d2-BA64-00805F8A5DD7}
+#define NS_IXPCONNECT_WRAPPED_JS_CLASS_IID \
+{ 0x2453eba0, 0xa9b8, 0x11d2, \
+ { 0xba, 0x64, 0x0, 0x80, 0x5f, 0x8a, 0x5d, 0xd7 } }
+
+class nsIXPCWrappedJSClass : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXPCONNECT_WRAPPED_JS_CLASS_IID)
+ NS_IMETHOD DebugDump(int16_t depth) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIXPCWrappedJSClass,
+ NS_IXPCONNECT_WRAPPED_JS_CLASS_IID)
+
+/*************************/
+// nsXPCWrappedJSClass represents the sharable factored out common code and
+// data for nsXPCWrappedJS instances for the same interface type.
+
+class nsXPCWrappedJSClass final : public nsIXPCWrappedJSClass
+{
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_IMETHOD DebugDump(int16_t depth) override;
+public:
+
+ static already_AddRefed<nsXPCWrappedJSClass>
+ GetNewOrUsed(JSContext* cx,
+ REFNSIID aIID,
+ bool allowNonScriptable = false);
+
+ REFNSIID GetIID() const {return mIID;}
+ XPCJSContext* GetContext() const {return mContext;}
+ nsIInterfaceInfo* GetInterfaceInfo() const {return mInfo;}
+ const char* GetInterfaceName();
+
+ static bool IsWrappedJS(nsISupports* aPtr);
+
+ NS_IMETHOD DelegatedQueryInterface(nsXPCWrappedJS* self, REFNSIID aIID,
+ void** aInstancePtr);
+
+ JSObject* GetRootJSObject(JSContext* cx, JSObject* aJSObj);
+
+ NS_IMETHOD CallMethod(nsXPCWrappedJS* wrapper, uint16_t methodIndex,
+ const XPTMethodDescriptor* info,
+ nsXPTCMiniVariant* params);
+
+ JSObject* CallQueryInterfaceOnJSObject(JSContext* cx,
+ JSObject* jsobj, REFNSIID aIID);
+
+ static nsresult BuildPropertyEnumerator(XPCCallContext& ccx,
+ JSObject* aJSObj,
+ nsISimpleEnumerator** aEnumerate);
+
+ static nsresult GetNamedPropertyAsVariant(XPCCallContext& ccx,
+ JSObject* aJSObj,
+ const nsAString& aName,
+ nsIVariant** aResult);
+
+private:
+ // aSyntheticException, if not null, is the exception we should be using.
+ // If null, look for an exception on the JSContext hanging off the
+ // XPCCallContext.
+ static nsresult CheckForException(XPCCallContext & ccx,
+ mozilla::dom::AutoEntryScript& aes,
+ const char * aPropertyName,
+ const char * anInterfaceName,
+ nsIException* aSyntheticException = nullptr);
+ virtual ~nsXPCWrappedJSClass();
+
+ nsXPCWrappedJSClass() = delete;
+ nsXPCWrappedJSClass(JSContext* cx, REFNSIID aIID,
+ nsIInterfaceInfo* aInfo);
+
+ bool IsReflectable(uint16_t i) const
+ {return (bool)(mDescriptors[i/32] & (1 << (i%32)));}
+ void SetReflectable(uint16_t i, bool b)
+ {if (b) mDescriptors[i/32] |= (1 << (i%32));
+ else mDescriptors[i/32] &= ~(1 << (i%32));}
+
+ bool GetArraySizeFromParam(JSContext* cx,
+ const XPTMethodDescriptor* method,
+ const nsXPTParamInfo& param,
+ uint16_t methodIndex,
+ uint8_t paramIndex,
+ nsXPTCMiniVariant* params,
+ uint32_t* result) const;
+
+ bool GetInterfaceTypeFromParam(JSContext* cx,
+ const XPTMethodDescriptor* method,
+ const nsXPTParamInfo& param,
+ uint16_t methodIndex,
+ const nsXPTType& type,
+ nsXPTCMiniVariant* params,
+ nsID* result) const;
+
+ static void CleanupPointerArray(const nsXPTType& datum_type,
+ uint32_t array_count,
+ void** arrayp);
+
+ static void CleanupPointerTypeObject(const nsXPTType& type,
+ void** pp);
+
+ void CleanupOutparams(JSContext* cx, uint16_t methodIndex, const nsXPTMethodInfo* info,
+ nsXPTCMiniVariant* nativeParams, bool inOutOnly, uint8_t n) const;
+
+private:
+ XPCJSContext* mContext;
+ nsCOMPtr<nsIInterfaceInfo> mInfo;
+ char* mName;
+ nsIID mIID;
+ uint32_t* mDescriptors;
+};
+
+/*************************/
+// nsXPCWrappedJS is a wrapper for a single JSObject for use from native code.
+// nsXPCWrappedJS objects are chained together to represent the various
+// interface on the single underlying (possibly aggregate) JSObject.
+
+class nsXPCWrappedJS final : protected nsAutoXPTCStub,
+ public nsIXPConnectWrappedJSUnmarkGray,
+ public nsSupportsWeakReference,
+ public nsIPropertyBag,
+ public XPCRootSetElem
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
+ NS_DECL_NSIXPCONNECTWRAPPEDJS
+ NS_DECL_NSIXPCONNECTWRAPPEDJSUNMARKGRAY
+ NS_DECL_NSISUPPORTSWEAKREFERENCE
+ NS_DECL_NSIPROPERTYBAG
+
+ NS_DECL_CYCLE_COLLECTION_SKIPPABLE_CLASS_AMBIGUOUS(nsXPCWrappedJS, nsIXPConnectWrappedJS)
+
+ NS_IMETHOD CallMethod(uint16_t methodIndex,
+ const XPTMethodDescriptor* info,
+ nsXPTCMiniVariant* params) override;
+
+ /*
+ * This is rarely called directly. Instead one usually calls
+ * XPCConvert::JSObject2NativeInterface which will handles cases where the
+ * JS object is already a wrapped native or a DOM object.
+ */
+
+ static nsresult
+ GetNewOrUsed(JS::HandleObject aJSObj,
+ REFNSIID aIID,
+ nsXPCWrappedJS** wrapper);
+
+ nsISomeInterface* GetXPTCStub() { return mXPTCStub; }
+
+ /**
+ * This getter does not change the color of the JSObject meaning that the
+ * object returned is not guaranteed to be kept alive past the next CC.
+ *
+ * This should only be called if you are certain that the return value won't
+ * be passed into a JS API function and that it won't be stored without
+ * being rooted (or otherwise signaling the stored value to the CC).
+ */
+ JSObject* GetJSObjectPreserveColor() const { return mJSObj.unbarrieredGet(); }
+
+ // Returns true if the wrapper chain contains references to multiple
+ // compartments. If the wrapper chain contains references to multiple
+ // compartments, then it must be registered on the XPCJSContext. Otherwise,
+ // it should be registered in the CompartmentPrivate for the compartment of
+ // the root's JS object. This will only return correct results when called
+ // on the root wrapper and will assert if not called on a root wrapper.
+ bool IsMultiCompartment() const;
+
+ nsXPCWrappedJSClass* GetClass() const {return mClass;}
+ REFNSIID GetIID() const {return GetClass()->GetIID();}
+ nsXPCWrappedJS* GetRootWrapper() const {return mRoot;}
+ nsXPCWrappedJS* GetNextWrapper() const {return mNext;}
+
+ nsXPCWrappedJS* Find(REFNSIID aIID);
+ nsXPCWrappedJS* FindInherited(REFNSIID aIID);
+ nsXPCWrappedJS* FindOrFindInherited(REFNSIID aIID) {
+ nsXPCWrappedJS* wrapper = Find(aIID);
+ if (wrapper)
+ return wrapper;
+ return FindInherited(aIID);
+ }
+
+ bool IsRootWrapper() const { return mRoot == this; }
+ bool IsValid() const { return bool(mJSObj); }
+ void SystemIsBeingShutDown();
+
+ // These two methods are used by JSObject2WrappedJSMap::FindDyingJSObjects
+ // to find non-rooting wrappers for dying JS objects. See the top of
+ // XPCWrappedJS.cpp for more details.
+ bool IsSubjectToFinalization() const {return IsValid() && mRefCnt == 1;}
+ void UpdateObjectPointerAfterGC() {JS_UpdateWeakPointerAfterGC(&mJSObj);}
+
+ bool IsAggregatedToNative() const {return mRoot->mOuter != nullptr;}
+ nsISupports* GetAggregatedNativeObject() const {return mRoot->mOuter;}
+ void SetAggregatedNativeObject(nsISupports* aNative) {
+ MOZ_ASSERT(aNative);
+ if (mRoot->mOuter) {
+ MOZ_ASSERT(mRoot->mOuter == aNative,
+ "Only one aggregated native can be set");
+ return;
+ }
+ mRoot->mOuter = aNative;
+ }
+
+ void TraceJS(JSTracer* trc);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ virtual ~nsXPCWrappedJS();
+protected:
+ nsXPCWrappedJS() = delete;
+ nsXPCWrappedJS(JSContext* cx,
+ JSObject* aJSObj,
+ nsXPCWrappedJSClass* aClass,
+ nsXPCWrappedJS* root,
+ nsresult* rv);
+
+ bool CanSkip();
+ void Destroy();
+ void Unlink();
+
+private:
+ JSCompartment* Compartment() const {
+ return js::GetObjectCompartment(mJSObj.unbarrieredGet());
+ }
+
+ JS::Heap<JSObject*> mJSObj;
+ RefPtr<nsXPCWrappedJSClass> mClass;
+ nsXPCWrappedJS* mRoot; // If mRoot != this, it is an owning pointer.
+ nsXPCWrappedJS* mNext;
+ nsCOMPtr<nsISupports> mOuter; // only set in root
+};
+
+/***************************************************************************/
+
+class XPCJSObjectHolder final : public nsIXPConnectJSObjectHolder,
+ public XPCRootSetElem
+{
+public:
+ // all the interface method declarations...
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCONNECTJSOBJECTHOLDER
+
+ // non-interface implementation
+
+public:
+ void TraceJS(JSTracer* trc);
+
+ explicit XPCJSObjectHolder(JSObject* obj);
+
+private:
+ virtual ~XPCJSObjectHolder();
+
+ XPCJSObjectHolder() = delete;
+
+ JS::Heap<JSObject*> mJSObj;
+};
+
+/***************************************************************************
+****************************************************************************
+*
+* All manner of utility classes follow...
+*
+****************************************************************************
+***************************************************************************/
+
+class xpcProperty : public nsIProperty
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPROPERTY
+
+ xpcProperty(const char16_t* aName, uint32_t aNameLen, nsIVariant* aValue);
+
+private:
+ virtual ~xpcProperty() {}
+
+ nsString mName;
+ nsCOMPtr<nsIVariant> mValue;
+};
+
+/***************************************************************************/
+// class here just for static methods
+class XPCConvert
+{
+public:
+ static bool IsMethodReflectable(const XPTMethodDescriptor& info);
+
+ /**
+ * Convert a native object into a JS::Value.
+ *
+ * @param d [out] the resulting JS::Value
+ * @param s the native object we're working with
+ * @param type the type of object that s is
+ * @param iid the interface of s that we want
+ * @param scope the default scope to put on the new JSObject's parent
+ * chain
+ * @param pErr [out] relevant error code, if any.
+ */
+
+ static bool NativeData2JS(JS::MutableHandleValue d,
+ const void* s, const nsXPTType& type,
+ const nsID* iid, nsresult* pErr);
+
+ static bool JSData2Native(void* d, JS::HandleValue s,
+ const nsXPTType& type,
+ const nsID* iid,
+ nsresult* pErr);
+
+ /**
+ * Convert a native nsISupports into a JSObject.
+ *
+ * @param dest [out] the resulting JSObject
+ * @param src the native object we're working with
+ * @param iid the interface of src that we want (may be null)
+ * @param cache the wrapper cache for src (may be null, in which case src
+ * will be QI'ed to get the cache)
+ * @param allowNativeWrapper if true, this method may wrap the resulting
+ * JSObject in an XPCNativeWrapper and return that, as needed.
+ * @param pErr [out] relevant error code, if any.
+ * @param src_is_identity optional performance hint. Set to true only
+ * if src is the identity pointer.
+ */
+ static bool NativeInterface2JSObject(JS::MutableHandleValue d,
+ nsIXPConnectJSObjectHolder** dest,
+ xpcObjectHelper& aHelper,
+ const nsID* iid,
+ bool allowNativeWrapper,
+ nsresult* pErr);
+
+ static bool GetNativeInterfaceFromJSObject(void** dest, JSObject* src,
+ const nsID* iid,
+ nsresult* pErr);
+ static bool JSObject2NativeInterface(void** dest, JS::HandleObject src,
+ const nsID* iid,
+ nsISupports* aOuter,
+ nsresult* pErr);
+
+ // Note - This return the XPCWrappedNative, rather than the native itself,
+ // for the WN case. You probably want UnwrapReflectorToISupports.
+ static bool GetISupportsFromJSObject(JSObject* obj, nsISupports** iface);
+
+ /**
+ * Convert a native array into a JS::Value.
+ *
+ * @param d [out] the resulting JS::Value
+ * @param s the native array we're working with
+ * @param type the type of objects in the array
+ * @param iid the interface of each object in the array that we want
+ * @param count the number of items in the array
+ * @param scope the default scope to put on the new JSObjects' parent chain
+ * @param pErr [out] relevant error code, if any.
+ */
+ static bool NativeArray2JS(JS::MutableHandleValue d, const void** s,
+ const nsXPTType& type, const nsID* iid,
+ uint32_t count, nsresult* pErr);
+
+ static bool JSArray2Native(void** d, JS::HandleValue s,
+ uint32_t count, const nsXPTType& type,
+ const nsID* iid, nsresult* pErr);
+
+ static bool JSTypedArray2Native(void** d,
+ JSObject* jsarray,
+ uint32_t count,
+ const nsXPTType& type,
+ nsresult* pErr);
+
+ static bool NativeStringWithSize2JS(JS::MutableHandleValue d, const void* s,
+ const nsXPTType& type,
+ uint32_t count,
+ nsresult* pErr);
+
+ static bool JSStringWithSize2Native(void* d, JS::HandleValue s,
+ uint32_t count, const nsXPTType& type,
+ nsresult* pErr);
+
+ static nsresult JSValToXPCException(JS::MutableHandleValue s,
+ const char* ifaceName,
+ const char* methodName,
+ nsIException** exception);
+
+ static nsresult ConstructException(nsresult rv, const char* message,
+ const char* ifaceName,
+ const char* methodName,
+ nsISupports* data,
+ nsIException** exception,
+ JSContext* cx,
+ JS::Value* jsExceptionPtr);
+
+private:
+ XPCConvert() = delete;
+
+};
+
+/***************************************************************************/
+// code for throwing exceptions into JS
+
+class nsXPCException;
+
+class XPCThrower
+{
+public:
+ static void Throw(nsresult rv, JSContext* cx);
+ static void Throw(nsresult rv, XPCCallContext& ccx);
+ static void ThrowBadResult(nsresult rv, nsresult result, XPCCallContext& ccx);
+ static void ThrowBadParam(nsresult rv, unsigned paramNum, XPCCallContext& ccx);
+ static bool SetVerbosity(bool state)
+ {bool old = sVerbose; sVerbose = state; return old;}
+
+ static bool CheckForPendingException(nsresult result, JSContext* cx);
+
+private:
+ static void Verbosify(XPCCallContext& ccx,
+ char** psz, bool own);
+
+private:
+ static bool sVerbose;
+};
+
+/***************************************************************************/
+
+class nsXPCException
+{
+public:
+ static bool NameAndFormatForNSResult(nsresult rv,
+ const char** name,
+ const char** format);
+
+ static const void* IterateNSResults(nsresult* rv,
+ const char** name,
+ const char** format,
+ const void** iterp);
+
+ static uint32_t GetNSResultCount();
+};
+
+/***************************************************************************/
+/*
+* nsJSID implements nsIJSID. It is also used by nsJSIID and nsJSCID as a
+* member (as a hidden implementaion detail) to which they delegate many calls.
+*/
+
+// Initialization is done on demand, and calling the destructor below is always
+// safe.
+extern void xpc_DestroyJSxIDClassObjects();
+
+class nsJSID final : public nsIJSID
+{
+public:
+ NS_DEFINE_STATIC_CID_ACCESSOR(NS_JS_ID_CID)
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIJSID
+
+ bool InitWithName(const nsID& id, const char* nameString);
+ bool SetName(const char* name);
+ void SetNameToNoString()
+ {MOZ_ASSERT(!mName, "name already set"); mName = const_cast<char*>(gNoString);}
+ bool NameIsSet() const {return nullptr != mName;}
+ const nsID& ID() const {return mID;}
+ bool IsValid() const {return !mID.Equals(GetInvalidIID());}
+
+ static already_AddRefed<nsJSID> NewID(const char* str);
+ static already_AddRefed<nsJSID> NewID(const nsID& id);
+
+ nsJSID();
+
+ void Reset();
+ const nsID& GetInvalidIID() const;
+
+protected:
+ virtual ~nsJSID();
+ static const char gNoString[];
+ nsID mID;
+ char* mNumber;
+ char* mName;
+};
+
+
+// nsJSIID
+
+class nsJSIID : public nsIJSIID,
+ public nsIXPCScriptable
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // we manually delagate these to nsJSID
+ NS_DECL_NSIJSID
+
+ // we implement the rest...
+ NS_DECL_NSIJSIID
+ NS_DECL_NSIXPCSCRIPTABLE
+
+ static already_AddRefed<nsJSIID> NewID(nsIInterfaceInfo* aInfo);
+
+ explicit nsJSIID(nsIInterfaceInfo* aInfo);
+ nsJSIID() = delete;
+
+private:
+ virtual ~nsJSIID();
+
+ nsCOMPtr<nsIInterfaceInfo> mInfo;
+};
+
+// nsJSCID
+
+class nsJSCID : public nsIJSCID, public nsIXPCScriptable
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ // we manually delagate these to nsJSID
+ NS_DECL_NSIJSID
+
+ // we implement the rest...
+ NS_DECL_NSIJSCID
+ NS_DECL_NSIXPCSCRIPTABLE
+
+ static already_AddRefed<nsJSCID> NewID(const char* str);
+
+ nsJSCID();
+
+private:
+ virtual ~nsJSCID();
+
+ void ResolveName();
+
+private:
+ RefPtr<nsJSID> mDetails;
+};
+
+
+/***************************************************************************/
+// 'Components' object implementations. nsXPCComponentsBase has the
+// less-privileged stuff that we're willing to expose to XBL.
+
+class nsXPCComponentsBase : public nsIXPCComponentsBase
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCCOMPONENTSBASE
+
+public:
+ void SystemIsBeingShutDown() { ClearMembers(); }
+
+ XPCWrappedNativeScope* GetScope() { return mScope; }
+
+protected:
+ virtual ~nsXPCComponentsBase();
+
+ explicit nsXPCComponentsBase(XPCWrappedNativeScope* aScope);
+ virtual void ClearMembers();
+
+ XPCWrappedNativeScope* mScope;
+
+ // Unprivileged members from nsIXPCComponentsBase.
+ RefPtr<nsXPCComponents_Interfaces> mInterfaces;
+ RefPtr<nsXPCComponents_InterfacesByID> mInterfacesByID;
+ RefPtr<nsXPCComponents_Results> mResults;
+
+ friend class XPCWrappedNativeScope;
+};
+
+class nsXPCComponents : public nsXPCComponentsBase,
+ public nsIXPCComponents
+{
+public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_FORWARD_NSIXPCCOMPONENTSBASE(nsXPCComponentsBase::)
+ NS_DECL_NSIXPCCOMPONENTS
+
+protected:
+ explicit nsXPCComponents(XPCWrappedNativeScope* aScope);
+ virtual ~nsXPCComponents();
+ virtual void ClearMembers() override;
+
+ // Privileged members added by nsIXPCComponents.
+ RefPtr<nsXPCComponents_Classes> mClasses;
+ RefPtr<nsXPCComponents_ClassesByID> mClassesByID;
+ RefPtr<nsXPCComponents_ID> mID;
+ RefPtr<nsXPCComponents_Exception> mException;
+ RefPtr<nsXPCComponents_Constructor> mConstructor;
+ RefPtr<nsXPCComponents_Utils> mUtils;
+
+ friend class XPCWrappedNativeScope;
+};
+
+
+/***************************************************************************/
+
+extern JSObject*
+xpc_NewIDObject(JSContext* cx, JS::HandleObject jsobj, const nsID& aID);
+
+extern const nsID*
+xpc_JSObjectToID(JSContext* cx, JSObject* obj);
+
+extern bool
+xpc_JSObjectIsID(JSContext* cx, JSObject* obj);
+
+/***************************************************************************/
+// in XPCDebug.cpp
+
+extern bool
+xpc_DumpJSStack(bool showArgs, bool showLocals, bool showThisProps);
+
+// Return a newly-allocated string containing a representation of the
+// current JS stack. It is the *caller's* responsibility to free this
+// string with JS_smprintf_free().
+extern char*
+xpc_PrintJSStack(JSContext* cx, bool showArgs, bool showLocals,
+ bool showThisProps);
+
+/***************************************************************************/
+
+// Definition of nsScriptError, defined here because we lack a place to put
+// XPCOM objects associated with the JavaScript engine.
+class nsScriptErrorBase : public nsIScriptError {
+public:
+ nsScriptErrorBase();
+
+ // TODO - do something reasonable on getting null from these babies.
+
+ NS_DECL_NSICONSOLEMESSAGE
+ NS_DECL_NSISCRIPTERROR
+
+protected:
+ virtual ~nsScriptErrorBase();
+
+ void
+ InitializeOnMainThread();
+
+ nsString mMessage;
+ nsString mMessageName;
+ nsString mSourceName;
+ uint32_t mLineNumber;
+ nsString mSourceLine;
+ uint32_t mColumnNumber;
+ uint32_t mFlags;
+ nsCString mCategory;
+ // mOuterWindowID is set on the main thread from InitializeOnMainThread().
+ uint64_t mOuterWindowID;
+ uint64_t mInnerWindowID;
+ int64_t mTimeStamp;
+ // mInitializedOnMainThread and mIsFromPrivateWindow are set on the main
+ // thread from InitializeOnMainThread().
+ mozilla::Atomic<bool> mInitializedOnMainThread;
+ bool mIsFromPrivateWindow;
+};
+
+class nsScriptError final : public nsScriptErrorBase {
+public:
+ nsScriptError() {}
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+private:
+ virtual ~nsScriptError() {}
+};
+
+class nsScriptErrorWithStack : public nsScriptErrorBase {
+public:
+ explicit nsScriptErrorWithStack(JS::HandleObject);
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptErrorWithStack)
+
+ NS_IMETHOD Init(const nsAString& message,
+ const nsAString& sourceName,
+ const nsAString& sourceLine,
+ uint32_t lineNumber,
+ uint32_t columnNumber,
+ uint32_t flags,
+ const char* category) override;
+
+ NS_IMETHOD GetStack(JS::MutableHandleValue) override;
+ NS_IMETHOD ToString(nsACString& aResult) override;
+
+private:
+ virtual ~nsScriptErrorWithStack();
+ // Complete stackframe where the error happened.
+ // Must be SavedFrame object.
+ JS::Heap<JSObject*> mStack;
+};
+
+/******************************************************************************
+ * Handles pre/post script processing.
+ */
+class MOZ_RAII AutoScriptEvaluate
+{
+public:
+ /**
+ * Saves the JSContext as well as initializing our state
+ * @param cx The JSContext, this can be null, we don't do anything then
+ */
+ explicit AutoScriptEvaluate(JSContext * cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+ : mJSContext(cx), mEvaluated(false) {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ }
+
+ /**
+ * Does the pre script evaluation.
+ * This function should only be called once, and will assert if called
+ * more than once
+ */
+
+ bool StartEvaluating(JS::HandleObject scope);
+
+ /**
+ * Does the post script evaluation.
+ */
+ ~AutoScriptEvaluate();
+private:
+ JSContext* mJSContext;
+ mozilla::Maybe<JS::AutoSaveExceptionState> mState;
+ bool mEvaluated;
+ mozilla::Maybe<JSAutoCompartment> mAutoCompartment;
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+
+ // No copying or assignment allowed
+ AutoScriptEvaluate(const AutoScriptEvaluate&) = delete;
+ AutoScriptEvaluate & operator =(const AutoScriptEvaluate&) = delete;
+};
+
+/***************************************************************************/
+class MOZ_RAII AutoResolveName
+{
+public:
+ AutoResolveName(XPCCallContext& ccx, JS::HandleId name
+ MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
+ mOld(ccx, XPCJSContext::Get()->SetResolveName(name))
+#ifdef DEBUG
+ ,mCheck(ccx, name)
+#endif
+ {
+ MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+ }
+ ~AutoResolveName()
+ {
+#ifdef DEBUG
+ jsid old =
+#endif
+ XPCJSContext::Get()->SetResolveName(mOld);
+ MOZ_ASSERT(old == mCheck, "Bad Nesting!");
+ }
+
+private:
+ JS::RootedId mOld;
+#ifdef DEBUG
+ JS::RootedId mCheck;
+#endif
+ MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+/***************************************************************************/
+// AutoMarkingPtr is the base class for the various AutoMarking pointer types
+// below. This system allows us to temporarily protect instances of our garbage
+// collected types after they are constructed but before they are safely
+// attached to other rooted objects.
+// This base class has pure virtual support for marking.
+
+class AutoMarkingPtr
+{
+ public:
+ explicit AutoMarkingPtr(JSContext* cx) {
+ mRoot = XPCJSContext::Get()->GetAutoRootsAdr();
+ mNext = *mRoot;
+ *mRoot = this;
+ }
+
+ virtual ~AutoMarkingPtr() {
+ if (mRoot) {
+ MOZ_ASSERT(*mRoot == this);
+ *mRoot = mNext;
+ }
+ }
+
+ void TraceJSAll(JSTracer* trc) {
+ for (AutoMarkingPtr* cur = this; cur; cur = cur->mNext)
+ cur->TraceJS(trc);
+ }
+
+ void MarkAfterJSFinalizeAll() {
+ for (AutoMarkingPtr* cur = this; cur; cur = cur->mNext)
+ cur->MarkAfterJSFinalize();
+ }
+
+ protected:
+ virtual void TraceJS(JSTracer* trc) = 0;
+ virtual void MarkAfterJSFinalize() = 0;
+
+ private:
+ AutoMarkingPtr** mRoot;
+ AutoMarkingPtr* mNext;
+};
+
+template<class T>
+class TypedAutoMarkingPtr : public AutoMarkingPtr
+{
+ public:
+ explicit TypedAutoMarkingPtr(JSContext* cx) : AutoMarkingPtr(cx), mPtr(nullptr) {}
+ TypedAutoMarkingPtr(JSContext* cx, T* ptr) : AutoMarkingPtr(cx), mPtr(ptr) {}
+
+ T* get() const { return mPtr; }
+ operator T*() const { return mPtr; }
+ T* operator->() const { return mPtr; }
+
+ TypedAutoMarkingPtr<T>& operator =(T* ptr) { mPtr = ptr; return *this; }
+
+ protected:
+ virtual void TraceJS(JSTracer* trc)
+ {
+ if (mPtr) {
+ mPtr->TraceJS(trc);
+ mPtr->AutoTrace(trc);
+ }
+ }
+
+ virtual void MarkAfterJSFinalize()
+ {
+ if (mPtr)
+ mPtr->Mark();
+ }
+
+ private:
+ T* mPtr;
+};
+
+typedef TypedAutoMarkingPtr<XPCWrappedNative> AutoMarkingWrappedNativePtr;
+typedef TypedAutoMarkingPtr<XPCWrappedNativeTearOff> AutoMarkingWrappedNativeTearOffPtr;
+typedef TypedAutoMarkingPtr<XPCWrappedNativeProto> AutoMarkingWrappedNativeProtoPtr;
+
+/***************************************************************************/
+namespace xpc {
+// Allocates a string that grants all access ("AllAccess")
+char*
+CloneAllAccess();
+
+// Returns access if wideName is in list
+char*
+CheckAccessList(const char16_t* wideName, const char* const list[]);
+} /* namespace xpc */
+
+/***************************************************************************/
+// in xpcvariant.cpp...
+
+// {1809FD50-91E8-11d5-90F9-0010A4E73D9A}
+#define XPCVARIANT_IID \
+ {0x1809fd50, 0x91e8, 0x11d5, \
+ { 0x90, 0xf9, 0x0, 0x10, 0xa4, 0xe7, 0x3d, 0x9a } }
+
+// {DC524540-487E-4501-9AC7-AAA784B17C1C}
+#define XPCVARIANT_CID \
+ {0xdc524540, 0x487e, 0x4501, \
+ { 0x9a, 0xc7, 0xaa, 0xa7, 0x84, 0xb1, 0x7c, 0x1c } }
+
+class XPCVariant : public nsIVariant
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIVARIANT
+ NS_DECL_CYCLE_COLLECTION_CLASS(XPCVariant)
+
+ // If this class ever implements nsIWritableVariant, take special care with
+ // the case when mJSVal is JSVAL_STRING, since we don't own the data in
+ // that case.
+
+ // We #define and iid so that out module local code can use QI to detect
+ // if a given nsIVariant is in fact an XPCVariant.
+ NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID)
+
+ static already_AddRefed<XPCVariant> newVariant(JSContext* cx, const JS::Value& aJSVal);
+
+ /**
+ * This getter clears the gray bit before handing out the Value if the Value
+ * represents a JSObject. That means that the object is guaranteed to be
+ * kept alive past the next CC.
+ */
+ JS::Value GetJSVal() const {
+ return mJSVal;
+ }
+
+ /**
+ * This getter does not change the color of the Value (if it represents a
+ * JSObject) meaning that the value returned is not guaranteed to be kept
+ * alive past the next CC.
+ *
+ * This should only be called if you are certain that the return value won't
+ * be passed into a JS API function and that it won't be stored without
+ * being rooted (or otherwise signaling the stored value to the CC).
+ */
+ JS::Value GetJSValPreserveColor() const { return mJSVal.unbarrieredGet(); }
+
+ XPCVariant(JSContext* cx, const JS::Value& aJSVal);
+
+ /**
+ * Convert a variant into a JS::Value.
+ *
+ * @param ccx the context for the whole procedure
+ * @param variant the variant to convert
+ * @param scope the default scope to put on the new JSObject's parent chain
+ * @param pErr [out] relevant error code, if any.
+ * @param pJSVal [out] the resulting jsval.
+ */
+ static bool VariantDataToJS(nsIVariant* variant,
+ nsresult* pErr, JS::MutableHandleValue pJSVal);
+
+ bool IsPurple()
+ {
+ return mRefCnt.IsPurple();
+ }
+
+ void RemovePurple()
+ {
+ mRefCnt.RemovePurple();
+ }
+
+ void SetCCGeneration(uint32_t aGen)
+ {
+ mCCGeneration = aGen;
+ }
+
+ uint32_t CCGeneration() { return mCCGeneration; }
+protected:
+ virtual ~XPCVariant() { }
+
+ bool InitializeData(JSContext* cx);
+
+protected:
+ nsDiscriminatedUnion mData;
+ JS::Heap<JS::Value> mJSVal;
+ bool mReturnRawObject : 1;
+ uint32_t mCCGeneration : 31;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID)
+
+class XPCTraceableVariant: public XPCVariant,
+ public XPCRootSetElem
+{
+public:
+ XPCTraceableVariant(JSContext* cx, const JS::Value& aJSVal)
+ : XPCVariant(cx, aJSVal)
+ {
+ nsXPConnect::GetContextInstance()->AddVariantRoot(this);
+ }
+
+ virtual ~XPCTraceableVariant();
+
+ void TraceJS(JSTracer* trc);
+};
+
+/***************************************************************************/
+// Utilities
+
+inline void*
+xpc_GetJSPrivate(JSObject* obj)
+{
+ return js::GetObjectPrivate(obj);
+}
+
+inline JSContext*
+xpc_GetSafeJSContext()
+{
+ return XPCJSContext::Get()->Context();
+}
+
+namespace xpc {
+
+JSAddonId*
+NewAddonId(JSContext* cx, const nsACString& id);
+
+// JSNatives to expose atob and btoa in various non-DOM XPConnect scopes.
+bool
+Atob(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
+Btoa(JSContext* cx, unsigned argc, JS::Value* vp);
+
+// Helper function that creates a JSFunction that wraps a native function that
+// forwards the call to the original 'callable'.
+class FunctionForwarderOptions;
+bool
+NewFunctionForwarder(JSContext* cx, JS::HandleId id, JS::HandleObject callable,
+ FunctionForwarderOptions& options, JS::MutableHandleValue vp);
+
+// Old fashioned xpc error reporter. Try to use JS_ReportError instead.
+nsresult
+ThrowAndFail(nsresult errNum, JSContext* cx, bool* retval);
+
+struct GlobalProperties {
+ GlobalProperties() {
+ mozilla::PodZero(this);
+
+ }
+ bool Parse(JSContext* cx, JS::HandleObject obj);
+ bool DefineInXPCComponents(JSContext* cx, JS::HandleObject obj);
+ bool DefineInSandbox(JSContext* cx, JS::HandleObject obj);
+ bool CSS : 1;
+ bool indexedDB : 1;
+ bool XMLHttpRequest : 1;
+ bool TextDecoder : 1;
+ bool TextEncoder : 1;
+ bool URL : 1;
+ bool URLSearchParams : 1;
+ bool atob : 1;
+ bool btoa : 1;
+ bool Blob : 1;
+ bool Directory : 1;
+ bool File : 1;
+ bool crypto : 1;
+ bool rtcIdentityProvider : 1;
+ bool fetch : 1;
+ bool caches : 1;
+ bool fileReader: 1;
+private:
+ bool Define(JSContext* cx, JS::HandleObject obj);
+};
+
+// Infallible.
+already_AddRefed<nsIXPCComponents_utils_Sandbox>
+NewSandboxConstructor();
+
+// Returns true if class of 'obj' is SandboxClass.
+bool
+IsSandbox(JSObject* obj);
+
+class MOZ_STACK_CLASS OptionsBase {
+public:
+ explicit OptionsBase(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : mCx(cx)
+ , mObject(cx, options)
+ { }
+
+ virtual bool Parse() = 0;
+
+protected:
+ bool ParseValue(const char* name, JS::MutableHandleValue prop, bool* found = nullptr);
+ bool ParseBoolean(const char* name, bool* prop);
+ bool ParseObject(const char* name, JS::MutableHandleObject prop);
+ bool ParseJSString(const char* name, JS::MutableHandleString prop);
+ bool ParseString(const char* name, nsCString& prop);
+ bool ParseString(const char* name, nsString& prop);
+ bool ParseId(const char* name, JS::MutableHandleId id);
+ bool ParseUInt32(const char* name, uint32_t* prop);
+
+ JSContext* mCx;
+ JS::RootedObject mObject;
+};
+
+class MOZ_STACK_CLASS SandboxOptions : public OptionsBase {
+public:
+ explicit SandboxOptions(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : OptionsBase(cx, options)
+ , wantXrays(true)
+ , allowWaivers(true)
+ , wantComponents(true)
+ , wantExportHelpers(false)
+ , isWebExtensionContentScript(false)
+ , waiveInterposition(false)
+ , proto(cx)
+ , addonId(cx)
+ , writeToGlobalPrototype(false)
+ , sameZoneAs(cx)
+ , freshZone(false)
+ , invisibleToDebugger(false)
+ , discardSource(false)
+ , metadata(cx)
+ , userContextId(0)
+ , originAttributes(cx)
+ { }
+
+ virtual bool Parse();
+
+ bool wantXrays;
+ bool allowWaivers;
+ bool wantComponents;
+ bool wantExportHelpers;
+ bool isWebExtensionContentScript;
+ bool waiveInterposition;
+ JS::RootedObject proto;
+ nsCString sandboxName;
+ JS::RootedString addonId;
+ bool writeToGlobalPrototype;
+ JS::RootedObject sameZoneAs;
+ bool freshZone;
+ bool invisibleToDebugger;
+ bool discardSource;
+ GlobalProperties globalProperties;
+ JS::RootedValue metadata;
+ uint32_t userContextId;
+ JS::RootedObject originAttributes;
+
+protected:
+ bool ParseGlobalProperties();
+};
+
+class MOZ_STACK_CLASS CreateObjectInOptions : public OptionsBase {
+public:
+ explicit CreateObjectInOptions(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : OptionsBase(cx, options)
+ , defineAs(cx, JSID_VOID)
+ { }
+
+ virtual bool Parse() { return ParseId("defineAs", &defineAs); }
+
+ JS::RootedId defineAs;
+};
+
+class MOZ_STACK_CLASS ExportFunctionOptions : public OptionsBase {
+public:
+ explicit ExportFunctionOptions(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : OptionsBase(cx, options)
+ , defineAs(cx, JSID_VOID)
+ , allowCrossOriginArguments(false)
+ { }
+
+ virtual bool Parse() {
+ return ParseId("defineAs", &defineAs) &&
+ ParseBoolean("allowCrossOriginArguments", &allowCrossOriginArguments);
+ }
+
+ JS::RootedId defineAs;
+ bool allowCrossOriginArguments;
+};
+
+class MOZ_STACK_CLASS FunctionForwarderOptions : public OptionsBase {
+public:
+ explicit FunctionForwarderOptions(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : OptionsBase(cx, options)
+ , allowCrossOriginArguments(false)
+ { }
+
+ JSObject* ToJSObject(JSContext* cx) {
+ JS::RootedObject obj(cx, JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
+ if (!obj)
+ return nullptr;
+
+ JS::RootedValue val(cx);
+ unsigned attrs = JSPROP_READONLY | JSPROP_PERMANENT;
+ val = JS::BooleanValue(allowCrossOriginArguments);
+ if (!JS_DefineProperty(cx, obj, "allowCrossOriginArguments", val, attrs))
+ return nullptr;
+
+ return obj;
+ }
+
+ virtual bool Parse() {
+ return ParseBoolean("allowCrossOriginArguments", &allowCrossOriginArguments);
+ }
+
+ bool allowCrossOriginArguments;
+};
+
+class MOZ_STACK_CLASS StackScopedCloneOptions : public OptionsBase {
+public:
+ explicit StackScopedCloneOptions(JSContext* cx = xpc_GetSafeJSContext(),
+ JSObject* options = nullptr)
+ : OptionsBase(cx, options)
+ , wrapReflectors(false)
+ , cloneFunctions(false)
+ , deepFreeze(false)
+ { }
+
+ virtual bool Parse() {
+ return ParseBoolean("wrapReflectors", &wrapReflectors) &&
+ ParseBoolean("cloneFunctions", &cloneFunctions) &&
+ ParseBoolean("deepFreeze", &deepFreeze);
+ }
+
+ // When a reflector is encountered, wrap it rather than aborting the clone.
+ bool wrapReflectors;
+
+ // When a function is encountered, clone it (exportFunction-style) rather than
+ // aborting the clone.
+ bool cloneFunctions;
+
+ // If true, the resulting object is deep-frozen after being cloned.
+ bool deepFreeze;
+};
+
+JSObject*
+CreateGlobalObject(JSContext* cx, const JSClass* clasp, nsIPrincipal* principal,
+ JS::CompartmentOptions& aOptions);
+
+// Modify the provided compartment options, consistent with |aPrincipal| and
+// with globally-cached values of various preferences.
+//
+// Call this function *before* |aOptions| is used to create the corresponding
+// global object, as not all of the options it sets can be modified on an
+// existing global object. (The type system should make this obvious, because
+// you can't get a *mutable* JS::CompartmentOptions& from an existing global
+// object.)
+void
+InitGlobalObjectOptions(JS::CompartmentOptions& aOptions,
+ nsIPrincipal* aPrincipal);
+
+// Finish initializing an already-created, not-yet-exposed-to-script global
+// object. This will attach a Components object (if necessary) and call
+// |JS_FireOnNewGlobalObject| (if necessary).
+//
+// If you must modify compartment options, see InitGlobalObjectOptions above.
+bool
+InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal,
+ uint32_t aFlags);
+
+// Helper for creating a sandbox object to use for evaluating
+// untrusted code completely separated from all other code in the
+// system using EvalInSandbox(). Takes the JSContext on which to
+// do setup etc on, puts the sandbox object in *vp (which must be
+// rooted by the caller), and uses the principal that's either
+// directly passed in prinOrSop or indirectly as an
+// nsIScriptObjectPrincipal holding the principal. If no principal is
+// reachable through prinOrSop, a new null principal will be created
+// and used.
+nsresult
+CreateSandboxObject(JSContext* cx, JS::MutableHandleValue vp, nsISupports* prinOrSop,
+ xpc::SandboxOptions& options);
+// Helper for evaluating scripts in a sandbox object created with
+// CreateSandboxObject(). The caller is responsible of ensuring
+// that *rval doesn't get collected during the call or usage after the
+// call. This helper will use filename and lineNo for error reporting,
+// and if no filename is provided it will use the codebase from the
+// principal and line number 1 as a fallback.
+nsresult
+EvalInSandbox(JSContext* cx, JS::HandleObject sandbox, const nsAString& source,
+ const nsACString& filename, int32_t lineNo,
+ JSVersion jsVersion, JS::MutableHandleValue rval);
+
+nsresult
+GetSandboxAddonId(JSContext* cx, JS::HandleObject sandboxArg,
+ JS::MutableHandleValue rval);
+
+// Helper for retrieving metadata stored in a reserved slot. The metadata
+// is set during the sandbox creation using the "metadata" option.
+nsresult
+GetSandboxMetadata(JSContext* cx, JS::HandleObject sandboxArg,
+ JS::MutableHandleValue rval);
+
+nsresult
+SetSandboxMetadata(JSContext* cx, JS::HandleObject sandboxArg,
+ JS::HandleValue metadata);
+
+bool
+CreateObjectIn(JSContext* cx, JS::HandleValue vobj, CreateObjectInOptions& options,
+ JS::MutableHandleValue rval);
+
+bool
+EvalInWindow(JSContext* cx, const nsAString& source, JS::HandleObject scope,
+ JS::MutableHandleValue rval);
+
+bool
+ExportFunction(JSContext* cx, JS::HandleValue vscope, JS::HandleValue vfunction,
+ JS::HandleValue voptions, JS::MutableHandleValue rval);
+
+bool
+CloneInto(JSContext* cx, JS::HandleValue vobj, JS::HandleValue vscope,
+ JS::HandleValue voptions, JS::MutableHandleValue rval);
+
+bool
+StackScopedClone(JSContext* cx, StackScopedCloneOptions& options, JS::MutableHandleValue val);
+
+} /* namespace xpc */
+
+
+/***************************************************************************/
+// Inlined utilities.
+
+inline bool
+xpc_ForcePropertyResolve(JSContext* cx, JS::HandleObject obj, jsid id);
+
+inline jsid
+GetJSIDByIndex(JSContext* cx, unsigned index);
+
+namespace xpc {
+
+enum WrapperDenialType {
+ WrapperDenialForXray = 0,
+ WrapperDenialForCOW,
+ WrapperDenialTypeCount
+};
+bool ReportWrapperDenial(JSContext* cx, JS::HandleId id, WrapperDenialType type, const char* reason);
+
+class CompartmentPrivate
+{
+public:
+ enum LocationHint {
+ LocationHintRegular,
+ LocationHintAddon
+ };
+
+ explicit CompartmentPrivate(JSCompartment* c);
+
+ ~CompartmentPrivate();
+
+ static CompartmentPrivate* Get(JSCompartment* compartment)
+ {
+ MOZ_ASSERT(compartment);
+ void* priv = JS_GetCompartmentPrivate(compartment);
+ return static_cast<CompartmentPrivate*>(priv);
+ }
+
+ static CompartmentPrivate* Get(JSObject* object)
+ {
+ JSCompartment* compartment = js::GetObjectCompartment(object);
+ return Get(compartment);
+ }
+
+ // Controls whether this compartment gets Xrays to same-origin. This behavior
+ // is deprecated, but is still the default for sandboxes for compatibity
+ // reasons.
+ bool wantXrays;
+
+ // Controls whether this compartment is allowed to waive Xrays to content
+ // that it subsumes. This should generally be true, except in cases where we
+ // want to prevent code from depending on Xray Waivers (which might make it
+ // more portable to other browser architectures).
+ bool allowWaivers;
+
+ // This flag is intended for a very specific use, internal to Gecko. It may
+ // go away or change behavior at any time. It should not be added to any
+ // documentation and it should not be used without consulting the XPConnect
+ // module owner.
+ bool writeToGlobalPrototype;
+
+ // When writeToGlobalPrototype is true, we use this flag to temporarily
+ // disable the writeToGlobalPrototype behavior (when resolving standard
+ // classes, for example).
+ bool skipWriteToGlobalPrototype;
+
+ // This scope corresponds to a WebExtension content script, and receives
+ // various bits of special compatibility behavior.
+ bool isWebExtensionContentScript;
+
+ // Even if an add-on needs interposition, it does not necessary need it
+ // for every scope. If this flag is set we waive interposition for this
+ // scope.
+ bool waiveInterposition;
+
+ // If CPOWs are disabled for browser code via the
+ // dom.ipc.cpows.forbid-unsafe-from-browser preferences, then only
+ // add-ons can use CPOWs. This flag allows a non-addon scope
+ // to opt into CPOWs. It's necessary for the implementation of
+ // RemoteAddonsParent.jsm.
+ bool allowCPOWs;
+
+ // This is only ever set during mochitest runs when enablePrivilege is called.
+ // It's intended as a temporary stopgap measure until we can finish ripping out
+ // enablePrivilege. Once set, this value is never unset (i.e., it doesn't follow
+ // the old scoping rules of enablePrivilege).
+ //
+ // Using it in production is inherently unsafe.
+ bool universalXPConnectEnabled;
+
+ // This is only ever set during mochitest runs when enablePrivilege is called.
+ // It allows the SpecialPowers scope to waive the normal chrome security
+ // wrappers and expose properties directly to content. This lets us avoid a
+ // bunch of overhead and complexity in our SpecialPowers automation glue.
+ //
+ // Using it in production is inherently unsafe.
+ bool forcePermissiveCOWs;
+
+ // Whether we've emitted a warning about a property that was filtered out
+ // by a security wrapper. See XrayWrapper.cpp.
+ bool wrapperDenialWarnings[WrapperDenialTypeCount];
+
+ // The scriptability of this compartment.
+ Scriptability scriptability;
+
+ // Our XPCWrappedNativeScope. This is non-null if and only if this is an
+ // XPConnect compartment.
+ XPCWrappedNativeScope* scope;
+
+ const nsACString& GetLocation() {
+ if (location.IsEmpty() && locationURI) {
+
+ nsCOMPtr<nsIXPConnectWrappedJS> jsLocationURI =
+ do_QueryInterface(locationURI);
+ if (jsLocationURI) {
+ // We cannot call into JS-implemented nsIURI objects, because
+ // we are iterating over the JS heap at this point.
+ location =
+ NS_LITERAL_CSTRING("<JS-implemented nsIURI location>");
+ } else if (NS_FAILED(locationURI->GetSpec(location))) {
+ location = NS_LITERAL_CSTRING("<unknown location>");
+ }
+ }
+ return location;
+ }
+ bool GetLocationURI(nsIURI** aURI) {
+ return GetLocationURI(LocationHintRegular, aURI);
+ }
+ bool GetLocationURI(LocationHint aLocationHint, nsIURI** aURI) {
+ if (locationURI) {
+ nsCOMPtr<nsIURI> rval = locationURI;
+ rval.forget(aURI);
+ return true;
+ }
+ return TryParseLocationURI(aLocationHint, aURI);
+ }
+ void SetLocation(const nsACString& aLocation) {
+ if (aLocation.IsEmpty())
+ return;
+ if (!location.IsEmpty() || locationURI)
+ return;
+ location = aLocation;
+ }
+ void SetLocationURI(nsIURI* aLocationURI) {
+ if (!aLocationURI)
+ return;
+ if (locationURI)
+ return;
+ locationURI = aLocationURI;
+ }
+
+ JSObject2WrappedJSMap* GetWrappedJSMap() const { return mWrappedJSMap; }
+ void UpdateWeakPointersAfterGC(XPCJSContext* context);
+
+ size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
+
+private:
+ nsCString location;
+ nsCOMPtr<nsIURI> locationURI;
+ JSObject2WrappedJSMap* mWrappedJSMap;
+
+ bool TryParseLocationURI(LocationHint aType, nsIURI** aURI);
+};
+
+bool IsUniversalXPConnectEnabled(JSCompartment* compartment);
+bool IsUniversalXPConnectEnabled(JSContext* cx);
+bool EnableUniversalXPConnect(JSContext* cx);
+
+inline void
+CrashIfNotInAutomation()
+{
+ MOZ_RELEASE_ASSERT(IsInAutomation());
+}
+
+inline XPCWrappedNativeScope*
+ObjectScope(JSObject* obj)
+{
+ return CompartmentPrivate::Get(obj)->scope;
+}
+
+JSObject* NewOutObject(JSContext* cx);
+bool IsOutObject(JSContext* cx, JSObject* obj);
+
+nsresult HasInstance(JSContext* cx, JS::HandleObject objArg, const nsID* iid, bool* bp);
+
+nsIPrincipal* GetObjectPrincipal(JSObject* obj);
+
+} // namespace xpc
+
+namespace mozilla {
+namespace dom {
+extern bool
+DefineStaticJSVals(JSContext* cx);
+} // namespace dom
+} // namespace mozilla
+
+bool
+xpc_LocalizeContext(JSContext* cx);
+void
+xpc_DelocalizeContext(JSContext* cx);
+
+/***************************************************************************/
+// Inlines use the above - include last.
+
+#include "XPCInlines.h"
+
+/***************************************************************************/
+// Maps have inlines that use the above - include last.
+
+#include "XPCMaps.h"
+
+/***************************************************************************/
+
+#endif /* xpcprivate_h___ */
diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h
new file mode 100644
index 000000000..fc8670d46
--- /dev/null
+++ b/js/xpconnect/src/xpcpublic.h
@@ -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