js/xpconnect/src/XPCJSContextStack.cpp

changeset 0
6474c204b198
     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 +}

mercurial