gecko-dev/js/xpconnect/src/nsXPConnect.cpp

1289 строки
38 KiB
C++

/* -*- 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 "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/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"
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;
uint32_t nsXPConnect::gReportAllJSExceptions = 0;
// 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()
: mRuntime(nullptr),
mShuttingDown(false)
{
mRuntime = XPCJSRuntime::newXPCJSRuntime(this);
char* reportableEnv = PR_GetEnv("MOZ_REPORT_ALL_JS_EXCEPTIONS");
if (reportableEnv && *reportableEnv)
gReportAllJSExceptions = 1;
}
nsXPConnect::~nsXPConnect()
{
mRuntime->DeleteSingletonScopes();
mRuntime->DestroyJSContextStack();
// 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. :-(
mRuntime->GarbageCollect(JS::gcreason::XPCONNECT_SHUTDOWN);
mShuttingDown = true;
XPCWrappedNativeScope::SystemIsBeingShutDown();
mRuntime->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 XPCJSRuntime, because doing so destroys the
// maps that our finalize callback depends on.
mRuntime->GarbageCollect(JS::gcreason::XPCONNECT_SHUTDOWN);
NS_RELEASE(gSystemPrincipal);
gScriptSecurityManager = nullptr;
// shutdown the logging system
XPC_LOG_FINISH();
delete mRuntime;
gSelf = nullptr;
gOnceAliveNowDead = true;
}
// static
void
nsXPConnect::InitStatics()
{
gSelf = new nsXPConnect();
gOnceAliveNowDead = false;
if (!gSelf->mRuntime) {
NS_RUNTIMEABORT("Couldn't create XPCJSRuntime.");
}
// 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);
// Initialize the SafeJSContext.
gSelf->mRuntime->GetJSContextStack()->InitSafeJSContext();
// Initialize our singleton scopes.
gSelf->mRuntime->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
XPCJSRuntime*
nsXPConnect::GetRuntimeInstance()
{
nsXPConnect* xpc = XPConnect();
return xpc->GetRuntime();
}
// 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* aFallbackMessage,
bool aIsChrome, uint64_t aWindowID)
{
mCategory = aIsChrome ? NS_LITERAL_CSTRING("chrome javascript")
: NS_LITERAL_CSTRING("content javascript");
mWindowID = aWindowID;
const char16_t* m = static_cast<const char16_t*>(aReport->ucmessage);
if (m) {
JSFlatString* name = js::GetErrorTypeName(CycleCollectedJSRuntime::Get()->Runtime(), aReport->exnType);
if (name) {
AssignJSFlatString(mErrorMsg, name);
mErrorMsg.AppendLiteral(": ");
}
mErrorMsg.Append(m);
}
if (mErrorMsg.IsEmpty() && aFallbackMessage) {
mErrorMsg.AssignWithConversion(aFallbackMessage);
}
if (!aReport->filename) {
mFileName.SetIsVoid(true);
} else {
mFileName.AssignWithConversion(aReport->filename);
}
mSourceLine = static_cast<const char16_t*>(aReport->uclinebuf);
mLineNumber = aReport->lineno;
mColumn = aReport->column;
mFlags = aReport->flags;
mIsMuted = aReport->isMuted;
}
static PRLogModuleInfo* gJSDiagnostics;
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);
}
// Log to the PR Log Module.
if (!gJSDiagnostics)
gJSDiagnostics = PR_NewLogModule("JSDiagnostics");
if (gJSDiagnostics) {
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();
}
NS_ENSURE_TRUE_VOID(consoleService && errorObject);
nsresult rv = errorObject->InitWithWindowID(mErrorMsg, mFileName, mSourceLine,
mLineNumber, mColumn, mFlags,
mCategory, mWindowID);
NS_ENSURE_SUCCESS_VOID(rv);
consoleService->LogMessage(errorObject);
}
/***************************************************************************/
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)
{
GetRuntime()->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)
{
nsCOMPtr<nsIXPConnectWrappedJS> wjs = do_QueryInterface(aWrappedJS);
if (wjs) {
// Unmarks gray JSObject.
static_cast<nsXPCWrappedJS*>(wjs.get())->GetJSObject();
}
}
/***************************************************************************/
/***************************************************************************/
// 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);
#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.
if (!((const js::Class*)clasp)->ext.isWrappedNative)
{
VerifyTraceProtoAndIfaceCacheCalledTracer trc(JS_GetRuntime(cx));
JS_TraceChildren(&trc, global, JS::TraceKind::Object);
MOZ_ASSERT(trc.ok, "Trace hook on global needs to call TraceXPCGlobal for XPConnect compartments.");
}
#endif
if (clasp->flags & JSCLASS_DOM_GLOBAL) {
const char* className = clasp->name;
AllocateProtoAndIfaceCache(global,
(strcmp(className, "Window") == 0 ||
strcmp(className, "ChromeWindow") == 0)
? ProtoAndIfaceCache::WindowLike
: ProtoAndIfaceCache::NonWindowLike);
}
return global;
}
bool
InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal, uint32_t aFlags)
{
// Immediately enter the global's compartment, so that everything else we
// create ends up there.
JSAutoCompartment ac(aJSContext, aGlobal);
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 (ShouldDiscardSystemSource()) {
nsIPrincipal* prin = GetObjectPrincipal(aGlobal);
bool isSystem = nsContentUtils::IsSystemPrincipal(prin);
if (!isSystem) {
short status = prin->GetAppStatus();
isSystem = status == nsIPrincipal::APP_STATUS_PRIVILEGED ||
status == nsIPrincipal::APP_STATUS_CERTIFIED;
}
JS::CompartmentOptionsRef(aGlobal).setDiscardSource(isSystem);
}
if (ExtraWarningsForSystemJS()) {
nsIPrincipal* prin = GetObjectPrincipal(aGlobal);
bool isSystem = nsContentUtils::IsSystemPrincipal(prin);
if (isSystem)
JS::CompartmentOptionsRef(aGlobal).extraWarningsOverride().set(true);
}
// Stuff coming through this path always ends up as a DOM global.
MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL);
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);
// Call into XPCWrappedNative to make a new global object, scope, and global
// prototype.
xpcObjectHelper helper(aCOMObj);
MOZ_ASSERT(helper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
nsRefPtr<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,
nullptr, 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");
nsRefPtr<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, /* stopAtOuter = */ false);
if (!aJSObj || !IS_WN_REFLECTOR(aJSObj)) {
*_retval = nullptr;
return NS_ERROR_FAILURE;
}
nsRefPtr<XPCWrappedNative> temp = XPCWrappedNative::Get(aJSObj);
temp.forget(_retval);
return NS_OK;
}
nsISupports*
xpc::UnwrapReflectorToISupports(JSObject* reflector)
{
// Unwrap security wrappers, if allowed.
reflector = js::CheckedUnwrap(reflector, /* stopAtOuter = */ false);
if (!reflector)
return nullptr;
// Try XPCWrappedNatives.
if (IS_WN_REFLECTOR(reflector)) {
XPCWrappedNative* wn = XPCWrappedNative::Get(reflector);
if (!wn)
return nullptr;
return wn->Native();
}
// Try DOM objects.
nsCOMPtr<nsISupports> canonical =
do_QueryInterface(mozilla::dom::UnwrapDOMObjectToISupports(reflector));
return canonical;
}
NS_IMETHODIMP_(nsISupports*)
nsXPConnect::GetNativeOfWrapper(JSContext* aJSContext,
JSObject* aJSObj)
{
return UnwrapReflectorToISupports(aJSObj);
}
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);
AutoMarkingNativeInterfacePtr iface(aJSContext);
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 = XPCJSRuntime::Get()->GetCallContext();
return NS_OK;
}
NS_IMETHODIMP
nsXPConnect::SetFunctionThisTranslator(const nsIID & aIID,
nsIXPCFunctionThisTranslator* aTranslator)
{
XPCJSRuntime* rt = GetRuntime();
IID2ThisTranslatorMap* map = rt->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 (mRuntime) {
if (depth)
mRuntime->DebugDump(depth);
else
XPC_LOG_ALWAYS(("XPCJSRuntime @ %x", mRuntime));
} else
XPC_LOG_ALWAYS(("mRuntime 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 = 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");
nsRefPtr<XPCVariant> variant = XPCVariant::newVariant(ctx, value);
variant.forget(_retval);
if (!(*_retval))
return NS_ERROR_FAILURE;
return NS_OK;
}
NS_IMETHODIMP
nsXPConnect::SetReportAllJSExceptions(bool newval)
{
// Ignore if the environment variable was set.
if (gReportAllJSExceptions != 1)
gReportAllJSExceptions = newval ? 2 : 0;
return NS_OK;
}
/* virtual */
JSContext*
nsXPConnect::GetCurrentJSContext()
{
return GetRuntime()->GetJSContextStack()->Peek();
}
/* virtual */
JSContext*
nsXPConnect::GetSafeJSContext()
{
return GetRuntime()->GetJSContextStack()->GetSafeJSContext();
}
namespace xpc {
bool
PushNullJSContext()
{
return XPCJSRuntime::Get()->GetJSContextStack()->Push(nullptr);
}
void
PopNullJSContext()
{
MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == nullptr);
XPCJSRuntime::Get()->GetJSContextStack()->Pop();
}
} // namespace xpc
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_ReportError(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_ReportError(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(GetRuntime()->Runtime());
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;
uint32_t size;
void* data;
{
if (functionObj)
data = JS_EncodeInterpretedFunction(cx, functionObj, &size);
else
data = JS_EncodeScript(cx, script, &size);
}
if (!data)
return NS_ERROR_OUT_OF_MEMORY;
MOZ_ASSERT(size);
rv = stream->Write32(size);
if (NS_SUCCEEDED(rv))
rv = stream->WriteBytes(static_cast<char*>(data), size);
js_free(data);
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;
{
if (scriptp) {
JSScript* script = JS_DecodeScript(cx, data, size);
if (!script)
rv = NS_ERROR_OUT_OF_MEMORY;
else
*scriptp = script;
} else {
JSObject* funobj = JS_DecodeInterpretedFunction(cx, data, size);
if (!funobj)
rv = NS_ERROR_OUT_OF_MEMORY;
else
*functionObjp = funobj;
}
}
free(data);
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;
jsapi.Init(xpc::PrivilegedJunkScope());
addonId = NewAddonId(jsapi.cx(), addonIdStr);
if (!addonId)
return false;
return XPCWrappedNativeScope::SetAddonInterposition(jsapi.cx(), addonId, interposition);
}
} // 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 dom
} // namespace mozilla