1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/xpconnect/src/nsCxPusher.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,264 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 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 +#include "nsCxPusher.h" 1.11 + 1.12 +#include "nsIScriptContext.h" 1.13 +#include "mozilla/dom/EventTarget.h" 1.14 +#include "nsDOMJSUtils.h" 1.15 +#include "xpcprivate.h" 1.16 +#include "WorkerPrivate.h" 1.17 + 1.18 +using mozilla::dom::EventTarget; 1.19 +using mozilla::DebugOnly; 1.20 + 1.21 +bool 1.22 +nsCxPusher::Push(EventTarget *aCurrentTarget) 1.23 +{ 1.24 + MOZ_ASSERT(mPusher.empty()); 1.25 + NS_ENSURE_TRUE(aCurrentTarget, false); 1.26 + nsresult rv; 1.27 + nsIScriptContext* scx = 1.28 + aCurrentTarget->GetContextForEventHandlers(&rv); 1.29 +#ifdef DEBUG_smaug 1.30 + NS_ENSURE_SUCCESS(rv, false); 1.31 +#else 1.32 + if(NS_FAILED(rv)) { 1.33 + return false; 1.34 + } 1.35 +#endif 1.36 + 1.37 + if (!scx) { 1.38 + // The target may have a special JS context for event handlers. 1.39 + JSContext* cx = aCurrentTarget->GetJSContextForEventHandlers(); 1.40 + if (cx) { 1.41 + mPusher.construct(cx); 1.42 + } 1.43 + 1.44 + // Nothing to do here, I guess. Have to return true so that event firing 1.45 + // will still work correctly even if there is no associated JSContext 1.46 + return true; 1.47 + } 1.48 + 1.49 + mPusher.construct(scx->GetNativeContext()); 1.50 + return true; 1.51 +} 1.52 + 1.53 +bool 1.54 +nsCxPusher::RePush(EventTarget *aCurrentTarget) 1.55 +{ 1.56 + if (mPusher.empty()) { 1.57 + return Push(aCurrentTarget); 1.58 + } 1.59 + 1.60 + if (aCurrentTarget) { 1.61 + nsresult rv; 1.62 + nsIScriptContext* scx = 1.63 + aCurrentTarget->GetContextForEventHandlers(&rv); 1.64 + if (NS_FAILED(rv)) { 1.65 + mPusher.destroy(); 1.66 + return false; 1.67 + } 1.68 + 1.69 + // If we have the same script context and native context is still 1.70 + // alive, no need to Pop/Push. 1.71 + if (scx && scx == mPusher.ref().GetScriptContext() && 1.72 + scx->GetNativeContext()) { 1.73 + return true; 1.74 + } 1.75 + } 1.76 + 1.77 + mPusher.destroy(); 1.78 + return Push(aCurrentTarget); 1.79 +} 1.80 + 1.81 +void 1.82 +nsCxPusher::Push(JSContext *cx) 1.83 +{ 1.84 + mPusher.construct(cx); 1.85 +} 1.86 + 1.87 +void 1.88 +nsCxPusher::PushNull() 1.89 +{ 1.90 + // Note: The Maybe<> template magic seems to need the static_cast below to 1.91 + // work right on some older compilers. 1.92 + mPusher.construct(static_cast<JSContext*>(nullptr), /* aAllowNull = */ true); 1.93 +} 1.94 + 1.95 +void 1.96 +nsCxPusher::Pop() 1.97 +{ 1.98 + if (!mPusher.empty()) 1.99 + mPusher.destroy(); 1.100 +} 1.101 + 1.102 +namespace mozilla { 1.103 + 1.104 +AutoCxPusher::AutoCxPusher(JSContext* cx, bool allowNull) 1.105 +{ 1.106 + MOZ_ASSERT_IF(!allowNull, cx); 1.107 + 1.108 + // Hold a strong ref to the nsIScriptContext, if any. This ensures that we 1.109 + // only destroy the mContext of an nsJSContext when it is not on the cx stack 1.110 + // (and therefore not in use). See nsJSContext::DestroyJSContext(). 1.111 + if (cx) 1.112 + mScx = GetScriptContextFromJSContext(cx); 1.113 + 1.114 + XPCJSContextStack *stack = XPCJSRuntime::Get()->GetJSContextStack(); 1.115 + if (!stack->Push(cx)) { 1.116 + MOZ_CRASH(); 1.117 + } 1.118 + mStackDepthAfterPush = stack->Count(); 1.119 + 1.120 +#ifdef DEBUG 1.121 + mPushedContext = cx; 1.122 + mCompartmentDepthOnEntry = cx ? js::GetEnterCompartmentDepth(cx) : 0; 1.123 +#endif 1.124 + 1.125 + // Enter a request and a compartment for the duration that the cx is on the 1.126 + // stack if non-null. 1.127 + if (cx) { 1.128 + mAutoRequest.construct(cx); 1.129 + 1.130 + // DOM JSContexts don't store their default compartment object on the cx. 1.131 + JSObject *compartmentObject = mScx ? mScx->GetWindowProxy() 1.132 + : js::DefaultObjectForContextOrNull(cx); 1.133 + if (compartmentObject) 1.134 + mAutoCompartment.construct(cx, compartmentObject); 1.135 + } 1.136 +} 1.137 + 1.138 +AutoCxPusher::~AutoCxPusher() 1.139 +{ 1.140 + // GC when we pop a script entry point. This is a useful heuristic that helps 1.141 + // us out on certain (flawed) benchmarks like sunspider, because it lets us 1.142 + // avoid GCing during the timing loop. 1.143 + // 1.144 + // NB: We need to take care to only do this if we're in a compartment, 1.145 + // otherwise JS_MaybeGC will segfault. 1.146 + if (mScx && !mAutoCompartment.empty()) 1.147 + JS_MaybeGC(nsXPConnect::XPConnect()->GetCurrentJSContext()); 1.148 + 1.149 + // Leave the compartment and request before popping. 1.150 + mAutoCompartment.destroyIfConstructed(); 1.151 + mAutoRequest.destroyIfConstructed(); 1.152 + 1.153 + // When we push a context, we may save the frame chain and pretend like we 1.154 + // haven't entered any compartment. This gets restored on Pop(), but we can 1.155 + // run into trouble if a Push/Pop are interleaved with a 1.156 + // JSAutoEnterCompartment. Make sure the compartment depth right before we 1.157 + // pop is the same as it was right after we pushed. 1.158 + MOZ_ASSERT_IF(mPushedContext, mCompartmentDepthOnEntry == 1.159 + js::GetEnterCompartmentDepth(mPushedContext)); 1.160 + DebugOnly<JSContext*> stackTop; 1.161 + MOZ_ASSERT(mPushedContext == nsXPConnect::XPConnect()->GetCurrentJSContext()); 1.162 + XPCJSRuntime::Get()->GetJSContextStack()->Pop(); 1.163 + mScx = nullptr; 1.164 +} 1.165 + 1.166 +bool 1.167 +AutoCxPusher::IsStackTop() 1.168 +{ 1.169 + uint32_t currentDepth = XPCJSRuntime::Get()->GetJSContextStack()->Count(); 1.170 + MOZ_ASSERT(currentDepth >= mStackDepthAfterPush); 1.171 + return currentDepth == mStackDepthAfterPush; 1.172 +} 1.173 + 1.174 +AutoJSContext::AutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL) 1.175 + : mCx(nullptr) 1.176 +{ 1.177 + Init(false MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT); 1.178 +} 1.179 + 1.180 +AutoJSContext::AutoJSContext(bool aSafe MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) 1.181 + : mCx(nullptr) 1.182 +{ 1.183 + Init(aSafe MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT); 1.184 +} 1.185 + 1.186 +void 1.187 +AutoJSContext::Init(bool aSafe MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL) 1.188 +{ 1.189 + JS::AutoAssertNoGC nogc; 1.190 + MOZ_ASSERT(!mCx, "mCx should not be initialized!"); 1.191 + 1.192 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.193 + 1.194 + nsXPConnect *xpc = nsXPConnect::XPConnect(); 1.195 + if (!aSafe) { 1.196 + mCx = xpc->GetCurrentJSContext(); 1.197 + } 1.198 + 1.199 + if (!mCx) { 1.200 + mCx = xpc->GetSafeJSContext(); 1.201 + mPusher.construct(mCx); 1.202 + } 1.203 +} 1.204 + 1.205 +AutoJSContext::operator JSContext*() const 1.206 +{ 1.207 + return mCx; 1.208 +} 1.209 + 1.210 +ThreadsafeAutoJSContext::ThreadsafeAutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL) 1.211 +{ 1.212 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.213 + 1.214 + if (NS_IsMainThread()) { 1.215 + mCx = nullptr; 1.216 + mAutoJSContext.construct(); 1.217 + } else { 1.218 + mCx = mozilla::dom::workers::GetCurrentThreadJSContext(); 1.219 + mRequest.construct(mCx); 1.220 + } 1.221 +} 1.222 + 1.223 +ThreadsafeAutoJSContext::operator JSContext*() const 1.224 +{ 1.225 + if (mCx) { 1.226 + return mCx; 1.227 + } else { 1.228 + return mAutoJSContext.ref(); 1.229 + } 1.230 +} 1.231 + 1.232 +AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL) 1.233 + : AutoJSContext(true MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) 1.234 + , mAc(mCx, XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal()) 1.235 +{ 1.236 +} 1.237 + 1.238 +ThreadsafeAutoSafeJSContext::ThreadsafeAutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL) 1.239 +{ 1.240 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.241 + 1.242 + if (NS_IsMainThread()) { 1.243 + mCx = nullptr; 1.244 + mAutoSafeJSContext.construct(); 1.245 + } else { 1.246 + mCx = mozilla::dom::workers::GetCurrentThreadJSContext(); 1.247 + mRequest.construct(mCx); 1.248 + } 1.249 +} 1.250 + 1.251 +ThreadsafeAutoSafeJSContext::operator JSContext*() const 1.252 +{ 1.253 + if (mCx) { 1.254 + return mCx; 1.255 + } else { 1.256 + return mAutoSafeJSContext.ref(); 1.257 + } 1.258 +} 1.259 + 1.260 +AutoPushJSContext::AutoPushJSContext(JSContext *aCx) : mCx(aCx) 1.261 +{ 1.262 + if (mCx && mCx != nsXPConnect::XPConnect()->GetCurrentJSContext()) { 1.263 + mPusher.construct(mCx); 1.264 + } 1.265 +} 1.266 + 1.267 +} // namespace mozilla