1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/src/XPCJSContextStack.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,202 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* Implement global service to track stack of JSContext. */ 1.11 + 1.12 +#include "xpcprivate.h" 1.13 +#include "XPCWrapper.h" 1.14 +#include "nsDOMJSUtils.h" 1.15 +#include "nsNullPrincipal.h" 1.16 +#include "mozilla/dom/BindingUtils.h" 1.17 + 1.18 +using namespace mozilla; 1.19 +using namespace JS; 1.20 +using namespace xpc; 1.21 +using mozilla::dom::DestroyProtoAndIfaceCache; 1.22 + 1.23 +/***************************************************************************/ 1.24 + 1.25 +XPCJSContextStack::~XPCJSContextStack() 1.26 +{ 1.27 + if (mSafeJSContext) { 1.28 + mSafeJSContextGlobal = nullptr; 1.29 + JS_DestroyContextNoGC(mSafeJSContext); 1.30 + mSafeJSContext = nullptr; 1.31 + } 1.32 +} 1.33 + 1.34 +JSContext* 1.35 +XPCJSContextStack::Pop() 1.36 +{ 1.37 + MOZ_ASSERT(!mStack.IsEmpty()); 1.38 + 1.39 + uint32_t idx = mStack.Length() - 1; // The thing we're popping 1.40 + 1.41 + JSContext *cx = mStack[idx].cx; 1.42 + 1.43 + mStack.RemoveElementAt(idx); 1.44 + if (idx == 0) { 1.45 + js::Debug_SetActiveJSContext(mRuntime->Runtime(), nullptr); 1.46 + return cx; 1.47 + } 1.48 + 1.49 + --idx; // Advance to new top of the stack 1.50 + 1.51 + XPCJSContextInfo &e = mStack[idx]; 1.52 + if (e.cx && e.savedFrameChain) { 1.53 + // Pop() can be called outside any request for e.cx. 1.54 + JSAutoRequest ar(e.cx); 1.55 + JS_RestoreFrameChain(e.cx); 1.56 + e.savedFrameChain = false; 1.57 + } 1.58 + js::Debug_SetActiveJSContext(mRuntime->Runtime(), e.cx); 1.59 + return cx; 1.60 +} 1.61 + 1.62 +bool 1.63 +XPCJSContextStack::Push(JSContext *cx) 1.64 +{ 1.65 + js::Debug_SetActiveJSContext(mRuntime->Runtime(), cx); 1.66 + if (mStack.Length() == 0) { 1.67 + mStack.AppendElement(cx); 1.68 + return true; 1.69 + } 1.70 + 1.71 + XPCJSContextInfo &e = mStack[mStack.Length() - 1]; 1.72 + if (e.cx) { 1.73 + // The cx we're pushing is also stack-top. In general we still need to 1.74 + // call JS_SaveFrameChain here. But if that would put us in a 1.75 + // compartment that's same-origin with the current one, we can skip it. 1.76 + nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager(); 1.77 + if ((e.cx == cx) && ssm) { 1.78 + // DOM JSContexts don't store their default compartment object on 1.79 + // the cx, so in those cases we need to fetch it via the scx 1.80 + // instead. And in some cases (i.e. the SafeJSContext), we have no 1.81 + // default compartment object at all. 1.82 + RootedObject defaultScope(cx, GetDefaultScopeFromJSContext(cx)); 1.83 + if (defaultScope) { 1.84 + nsIPrincipal *currentPrincipal = 1.85 + GetCompartmentPrincipal(js::GetContextCompartment(cx)); 1.86 + nsIPrincipal *defaultPrincipal = GetObjectPrincipal(defaultScope); 1.87 + if (currentPrincipal->Equals(defaultPrincipal)) { 1.88 + mStack.AppendElement(cx); 1.89 + return true; 1.90 + } 1.91 + } 1.92 + } 1.93 + 1.94 + { 1.95 + // Push() can be called outside any request for e.cx. 1.96 + JSAutoRequest ar(e.cx); 1.97 + if (!JS_SaveFrameChain(e.cx)) 1.98 + return false; 1.99 + e.savedFrameChain = true; 1.100 + } 1.101 + } 1.102 + 1.103 + mStack.AppendElement(cx); 1.104 + return true; 1.105 +} 1.106 + 1.107 +bool 1.108 +XPCJSContextStack::HasJSContext(JSContext *cx) 1.109 +{ 1.110 + for (uint32_t i = 0; i < mStack.Length(); i++) 1.111 + if (cx == mStack[i].cx) 1.112 + return true; 1.113 + return false; 1.114 +} 1.115 + 1.116 +static bool 1.117 +SafeGlobalResolve(JSContext *cx, HandleObject obj, HandleId id) 1.118 +{ 1.119 + bool resolved; 1.120 + return JS_ResolveStandardClass(cx, obj, id, &resolved); 1.121 +} 1.122 + 1.123 +static void 1.124 +SafeFinalize(JSFreeOp *fop, JSObject* obj) 1.125 +{ 1.126 + SandboxPrivate* sop = 1.127 + static_cast<SandboxPrivate*>(xpc_GetJSPrivate(obj)); 1.128 + sop->ForgetGlobalObject(); 1.129 + NS_IF_RELEASE(sop); 1.130 + DestroyProtoAndIfaceCache(obj); 1.131 +} 1.132 + 1.133 +const JSClass xpc::SafeJSContextGlobalClass = { 1.134 + "global_for_XPCJSContextStack_SafeJSContext", 1.135 + XPCONNECT_GLOBAL_FLAGS, 1.136 + JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, 1.137 + JS_EnumerateStub, SafeGlobalResolve, JS_ConvertStub, SafeFinalize, 1.138 + nullptr, nullptr, nullptr, JS_GlobalObjectTraceHook 1.139 +}; 1.140 + 1.141 +JSContext* 1.142 +XPCJSContextStack::GetSafeJSContext() 1.143 +{ 1.144 + MOZ_ASSERT(mSafeJSContext); 1.145 + return mSafeJSContext; 1.146 +} 1.147 + 1.148 +JSObject* 1.149 +XPCJSContextStack::GetSafeJSContextGlobal() 1.150 +{ 1.151 + MOZ_ASSERT(mSafeJSContextGlobal); 1.152 + return mSafeJSContextGlobal; 1.153 +} 1.154 + 1.155 +JSContext* 1.156 +XPCJSContextStack::InitSafeJSContext() 1.157 +{ 1.158 + MOZ_ASSERT(!mSafeJSContext); 1.159 + 1.160 + // Start by getting the principal holder and principal for this 1.161 + // context. If we can't manage that, don't bother with the rest. 1.162 + nsRefPtr<nsNullPrincipal> principal = new nsNullPrincipal(); 1.163 + nsresult rv = principal->Init(); 1.164 + if (NS_FAILED(rv)) 1.165 + MOZ_CRASH(); 1.166 + 1.167 + nsXPConnect* xpc = nsXPConnect::XPConnect(); 1.168 + JSRuntime *rt = xpc->GetRuntime()->Runtime(); 1.169 + if (!rt) 1.170 + MOZ_CRASH(); 1.171 + 1.172 + mSafeJSContext = JS_NewContext(rt, 8192); 1.173 + if (!mSafeJSContext) 1.174 + MOZ_CRASH(); 1.175 + JSAutoRequest req(mSafeJSContext); 1.176 + ContextOptionsRef(mSafeJSContext).setNoDefaultCompartmentObject(true); 1.177 + 1.178 + JS_SetErrorReporter(mSafeJSContext, xpc::SystemErrorReporter); 1.179 + 1.180 + JS::CompartmentOptions options; 1.181 + options.setZone(JS::SystemZone) 1.182 + .setTrace(TraceXPCGlobal); 1.183 + mSafeJSContextGlobal = CreateGlobalObject(mSafeJSContext, 1.184 + &SafeJSContextGlobalClass, 1.185 + principal, options); 1.186 + if (!mSafeJSContextGlobal) 1.187 + MOZ_CRASH(); 1.188 + 1.189 + // Note: make sure to set the private before calling 1.190 + // InitClasses 1.191 + nsRefPtr<SandboxPrivate> sp = new SandboxPrivate(principal, mSafeJSContextGlobal); 1.192 + JS_SetPrivate(mSafeJSContextGlobal, sp.forget().take()); 1.193 + 1.194 + // After this point either glob is null and the 1.195 + // nsIScriptObjectPrincipal ownership is either handled by the 1.196 + // nsCOMPtr or dealt with, or we'll release in the finalize 1.197 + // hook. 1.198 + if (NS_FAILED(xpc->InitClasses(mSafeJSContext, mSafeJSContextGlobal))) 1.199 + MOZ_CRASH(); 1.200 + 1.201 + JS::RootedObject glob(mSafeJSContext, mSafeJSContextGlobal); 1.202 + JS_FireOnNewGlobalObject(mSafeJSContext, glob); 1.203 + 1.204 + return mSafeJSContext; 1.205 +}