Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsCxPusher.h"
9 #include "nsIScriptContext.h"
10 #include "mozilla/dom/EventTarget.h"
11 #include "nsDOMJSUtils.h"
12 #include "xpcprivate.h"
13 #include "WorkerPrivate.h"
15 using mozilla::dom::EventTarget;
16 using mozilla::DebugOnly;
18 bool
19 nsCxPusher::Push(EventTarget *aCurrentTarget)
20 {
21 MOZ_ASSERT(mPusher.empty());
22 NS_ENSURE_TRUE(aCurrentTarget, false);
23 nsresult rv;
24 nsIScriptContext* scx =
25 aCurrentTarget->GetContextForEventHandlers(&rv);
26 #ifdef DEBUG_smaug
27 NS_ENSURE_SUCCESS(rv, false);
28 #else
29 if(NS_FAILED(rv)) {
30 return false;
31 }
32 #endif
34 if (!scx) {
35 // The target may have a special JS context for event handlers.
36 JSContext* cx = aCurrentTarget->GetJSContextForEventHandlers();
37 if (cx) {
38 mPusher.construct(cx);
39 }
41 // Nothing to do here, I guess. Have to return true so that event firing
42 // will still work correctly even if there is no associated JSContext
43 return true;
44 }
46 mPusher.construct(scx->GetNativeContext());
47 return true;
48 }
50 bool
51 nsCxPusher::RePush(EventTarget *aCurrentTarget)
52 {
53 if (mPusher.empty()) {
54 return Push(aCurrentTarget);
55 }
57 if (aCurrentTarget) {
58 nsresult rv;
59 nsIScriptContext* scx =
60 aCurrentTarget->GetContextForEventHandlers(&rv);
61 if (NS_FAILED(rv)) {
62 mPusher.destroy();
63 return false;
64 }
66 // If we have the same script context and native context is still
67 // alive, no need to Pop/Push.
68 if (scx && scx == mPusher.ref().GetScriptContext() &&
69 scx->GetNativeContext()) {
70 return true;
71 }
72 }
74 mPusher.destroy();
75 return Push(aCurrentTarget);
76 }
78 void
79 nsCxPusher::Push(JSContext *cx)
80 {
81 mPusher.construct(cx);
82 }
84 void
85 nsCxPusher::PushNull()
86 {
87 // Note: The Maybe<> template magic seems to need the static_cast below to
88 // work right on some older compilers.
89 mPusher.construct(static_cast<JSContext*>(nullptr), /* aAllowNull = */ true);
90 }
92 void
93 nsCxPusher::Pop()
94 {
95 if (!mPusher.empty())
96 mPusher.destroy();
97 }
99 namespace mozilla {
101 AutoCxPusher::AutoCxPusher(JSContext* cx, bool allowNull)
102 {
103 MOZ_ASSERT_IF(!allowNull, cx);
105 // Hold a strong ref to the nsIScriptContext, if any. This ensures that we
106 // only destroy the mContext of an nsJSContext when it is not on the cx stack
107 // (and therefore not in use). See nsJSContext::DestroyJSContext().
108 if (cx)
109 mScx = GetScriptContextFromJSContext(cx);
111 XPCJSContextStack *stack = XPCJSRuntime::Get()->GetJSContextStack();
112 if (!stack->Push(cx)) {
113 MOZ_CRASH();
114 }
115 mStackDepthAfterPush = stack->Count();
117 #ifdef DEBUG
118 mPushedContext = cx;
119 mCompartmentDepthOnEntry = cx ? js::GetEnterCompartmentDepth(cx) : 0;
120 #endif
122 // Enter a request and a compartment for the duration that the cx is on the
123 // stack if non-null.
124 if (cx) {
125 mAutoRequest.construct(cx);
127 // DOM JSContexts don't store their default compartment object on the cx.
128 JSObject *compartmentObject = mScx ? mScx->GetWindowProxy()
129 : js::DefaultObjectForContextOrNull(cx);
130 if (compartmentObject)
131 mAutoCompartment.construct(cx, compartmentObject);
132 }
133 }
135 AutoCxPusher::~AutoCxPusher()
136 {
137 // GC when we pop a script entry point. This is a useful heuristic that helps
138 // us out on certain (flawed) benchmarks like sunspider, because it lets us
139 // avoid GCing during the timing loop.
140 //
141 // NB: We need to take care to only do this if we're in a compartment,
142 // otherwise JS_MaybeGC will segfault.
143 if (mScx && !mAutoCompartment.empty())
144 JS_MaybeGC(nsXPConnect::XPConnect()->GetCurrentJSContext());
146 // Leave the compartment and request before popping.
147 mAutoCompartment.destroyIfConstructed();
148 mAutoRequest.destroyIfConstructed();
150 // When we push a context, we may save the frame chain and pretend like we
151 // haven't entered any compartment. This gets restored on Pop(), but we can
152 // run into trouble if a Push/Pop are interleaved with a
153 // JSAutoEnterCompartment. Make sure the compartment depth right before we
154 // pop is the same as it was right after we pushed.
155 MOZ_ASSERT_IF(mPushedContext, mCompartmentDepthOnEntry ==
156 js::GetEnterCompartmentDepth(mPushedContext));
157 DebugOnly<JSContext*> stackTop;
158 MOZ_ASSERT(mPushedContext == nsXPConnect::XPConnect()->GetCurrentJSContext());
159 XPCJSRuntime::Get()->GetJSContextStack()->Pop();
160 mScx = nullptr;
161 }
163 bool
164 AutoCxPusher::IsStackTop()
165 {
166 uint32_t currentDepth = XPCJSRuntime::Get()->GetJSContextStack()->Count();
167 MOZ_ASSERT(currentDepth >= mStackDepthAfterPush);
168 return currentDepth == mStackDepthAfterPush;
169 }
171 AutoJSContext::AutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
172 : mCx(nullptr)
173 {
174 Init(false MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT);
175 }
177 AutoJSContext::AutoJSContext(bool aSafe MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
178 : mCx(nullptr)
179 {
180 Init(aSafe MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT);
181 }
183 void
184 AutoJSContext::Init(bool aSafe MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
185 {
186 JS::AutoAssertNoGC nogc;
187 MOZ_ASSERT(!mCx, "mCx should not be initialized!");
189 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
191 nsXPConnect *xpc = nsXPConnect::XPConnect();
192 if (!aSafe) {
193 mCx = xpc->GetCurrentJSContext();
194 }
196 if (!mCx) {
197 mCx = xpc->GetSafeJSContext();
198 mPusher.construct(mCx);
199 }
200 }
202 AutoJSContext::operator JSContext*() const
203 {
204 return mCx;
205 }
207 ThreadsafeAutoJSContext::ThreadsafeAutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
208 {
209 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
211 if (NS_IsMainThread()) {
212 mCx = nullptr;
213 mAutoJSContext.construct();
214 } else {
215 mCx = mozilla::dom::workers::GetCurrentThreadJSContext();
216 mRequest.construct(mCx);
217 }
218 }
220 ThreadsafeAutoJSContext::operator JSContext*() const
221 {
222 if (mCx) {
223 return mCx;
224 } else {
225 return mAutoJSContext.ref();
226 }
227 }
229 AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
230 : AutoJSContext(true MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
231 , mAc(mCx, XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContextGlobal())
232 {
233 }
235 ThreadsafeAutoSafeJSContext::ThreadsafeAutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
236 {
237 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
239 if (NS_IsMainThread()) {
240 mCx = nullptr;
241 mAutoSafeJSContext.construct();
242 } else {
243 mCx = mozilla::dom::workers::GetCurrentThreadJSContext();
244 mRequest.construct(mCx);
245 }
246 }
248 ThreadsafeAutoSafeJSContext::operator JSContext*() const
249 {
250 if (mCx) {
251 return mCx;
252 } else {
253 return mAutoSafeJSContext.ref();
254 }
255 }
257 AutoPushJSContext::AutoPushJSContext(JSContext *aCx) : mCx(aCx)
258 {
259 if (mCx && mCx != nsXPConnect::XPConnect()->GetCurrentJSContext()) {
260 mPusher.construct(mCx);
261 }
262 }
264 } // namespace mozilla