Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 // vim: ft=cpp tw=78 sw=2 et ts=2
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 "mozilla/dom/ScriptSettings.h"
8 #include "mozilla/ThreadLocal.h"
9 #include "mozilla/Assertions.h"
11 #include "jsapi.h"
12 #include "xpcpublic.h"
13 #include "nsIGlobalObject.h"
14 #include "nsIScriptGlobalObject.h"
15 #include "nsIScriptContext.h"
16 #include "nsContentUtils.h"
17 #include "nsTArray.h"
18 #include "nsJSUtils.h"
20 namespace mozilla {
21 namespace dom {
23 class ScriptSettingsStack;
24 static mozilla::ThreadLocal<ScriptSettingsStack*> sScriptSettingsTLS;
26 ScriptSettingsStackEntry ScriptSettingsStackEntry::NoJSAPISingleton;
28 class ScriptSettingsStack {
29 public:
30 static ScriptSettingsStack& Ref() {
31 return *sScriptSettingsTLS.get();
32 }
33 ScriptSettingsStack() {};
35 void Push(ScriptSettingsStackEntry* aSettings) {
36 // The bottom-most entry must always be a candidate entry point.
37 MOZ_ASSERT_IF(mStack.Length() == 0 || mStack.LastElement()->NoJSAPI(),
38 aSettings->mIsCandidateEntryPoint);
39 mStack.AppendElement(aSettings);
40 }
42 void PushNoJSAPI() {
43 mStack.AppendElement(&ScriptSettingsStackEntry::NoJSAPISingleton);
44 }
46 void Pop() {
47 MOZ_ASSERT(mStack.Length() > 0);
48 mStack.RemoveElementAt(mStack.Length() - 1);
49 }
51 ScriptSettingsStackEntry* Incumbent() {
52 if (!mStack.Length()) {
53 return nullptr;
54 }
55 return mStack.LastElement();
56 }
58 nsIGlobalObject* IncumbentGlobal() {
59 ScriptSettingsStackEntry *entry = Incumbent();
60 return entry ? entry->mGlobalObject : nullptr;
61 }
63 ScriptSettingsStackEntry* EntryPoint() {
64 if (!mStack.Length())
65 return nullptr;
66 for (int i = mStack.Length() - 1; i >= 0; --i) {
67 if (mStack[i]->mIsCandidateEntryPoint) {
68 return mStack[i];
69 }
70 }
71 MOZ_ASSUME_UNREACHABLE("Non-empty stack should always have an entry point");
72 }
74 nsIGlobalObject* EntryGlobal() {
75 ScriptSettingsStackEntry *entry = EntryPoint();
76 return entry ? entry->mGlobalObject : nullptr;
77 }
79 private:
80 // These pointers are caller-owned.
81 nsTArray<ScriptSettingsStackEntry*> mStack;
82 };
84 void
85 InitScriptSettings()
86 {
87 if (!sScriptSettingsTLS.initialized()) {
88 bool success = sScriptSettingsTLS.init();
89 if (!success) {
90 MOZ_CRASH();
91 }
92 }
94 ScriptSettingsStack* ptr = new ScriptSettingsStack();
95 sScriptSettingsTLS.set(ptr);
96 }
98 void DestroyScriptSettings()
99 {
100 ScriptSettingsStack* ptr = sScriptSettingsTLS.get();
101 MOZ_ASSERT(ptr);
102 sScriptSettingsTLS.set(nullptr);
103 delete ptr;
104 }
106 // This mostly gets the entry global, but doesn't entirely match the spec in
107 // certain edge cases. It's good enough for some purposes, but not others. If
108 // you want to call this function, ping bholley and describe your use-case.
109 nsIGlobalObject*
110 BrokenGetEntryGlobal()
111 {
112 // We need the current JSContext in order to check the JS for
113 // scripted frames that may have appeared since anyone last
114 // manipulated the stack. If it's null, that means that there
115 // must be no entry global on the stack.
116 JSContext *cx = nsContentUtils::GetCurrentJSContextForThread();
117 if (!cx) {
118 MOZ_ASSERT(ScriptSettingsStack::Ref().EntryGlobal() == nullptr);
119 return nullptr;
120 }
122 return nsJSUtils::GetDynamicScriptGlobal(cx);
123 }
125 // Note: When we're ready to expose it, GetEntryGlobal will look similar to
126 // GetIncumbentGlobal below.
128 nsIGlobalObject*
129 GetIncumbentGlobal()
130 {
131 // We need the current JSContext in order to check the JS for
132 // scripted frames that may have appeared since anyone last
133 // manipulated the stack. If it's null, that means that there
134 // must be no entry global on the stack, and therefore no incumbent
135 // global either.
136 JSContext *cx = nsContentUtils::GetCurrentJSContextForThread();
137 if (!cx) {
138 MOZ_ASSERT(ScriptSettingsStack::Ref().EntryGlobal() == nullptr);
139 return nullptr;
140 }
142 // See what the JS engine has to say. If we've got a scripted caller
143 // override in place, the JS engine will lie to us and pretend that
144 // there's nothing on the JS stack, which will cause us to check the
145 // incumbent script stack below.
146 if (JSObject *global = JS::GetScriptedCallerGlobal(cx)) {
147 return xpc::GetNativeForGlobal(global);
148 }
150 // Ok, nothing from the JS engine. Let's use whatever's on the
151 // explicit stack.
152 return ScriptSettingsStack::Ref().IncumbentGlobal();
153 }
155 nsIPrincipal*
156 GetWebIDLCallerPrincipal()
157 {
158 MOZ_ASSERT(NS_IsMainThread());
159 ScriptSettingsStackEntry *entry = ScriptSettingsStack::Ref().EntryPoint();
161 // If we have an entry point that is not the NoJSAPI singleton, we know it
162 // must be an AutoEntryScript.
163 if (!entry || entry->NoJSAPI()) {
164 return nullptr;
165 }
166 AutoEntryScript* aes = static_cast<AutoEntryScript*>(entry);
168 // We can't yet rely on the Script Settings Stack to properly determine the
169 // entry script, because there are still lots of places in the tree where we
170 // don't yet use an AutoEntryScript (bug 951991 tracks this work). In the
171 // mean time though, we can make some observations to hack around the
172 // problem:
173 //
174 // (1) All calls into JS-implemented WebIDL go through CallSetup, which goes
175 // through AutoEntryScript.
176 // (2) The top candidate entry point in the Script Settings Stack is the
177 // entry point if and only if no other JSContexts have been pushed on
178 // top of the push made by that entry's AutoEntryScript.
179 //
180 // Because of (1), all of the cases where we might return a non-null
181 // WebIDL Caller are guaranteed to have put an entry on the Script Settings
182 // Stack, so we can restrict our search to that. Moreover, (2) gives us a
183 // criterion to determine whether an entry in the Script Setting Stack means
184 // that we should return a non-null WebIDL Caller.
185 //
186 // Once we fix bug 951991, this can all be simplified.
187 if (!aes->CxPusherIsStackTop()) {
188 return nullptr;
189 }
191 return aes->mWebIDLCallerPrincipal;
192 }
194 static JSContext*
195 FindJSContext(nsIGlobalObject* aGlobalObject)
196 {
197 MOZ_ASSERT(NS_IsMainThread());
198 JSContext *cx = nullptr;
199 nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobalObject);
200 if (sgo && sgo->GetScriptContext()) {
201 cx = sgo->GetScriptContext()->GetNativeContext();
202 }
203 if (!cx) {
204 cx = nsContentUtils::GetSafeJSContext();
205 }
206 return cx;
207 }
209 AutoJSAPI::AutoJSAPI()
210 : mCx(nsContentUtils::GetDefaultJSContextForThread())
211 {
212 if (NS_IsMainThread()) {
213 mCxPusher.construct(mCx);
214 }
216 // Leave the cx in a null compartment.
217 mNullAc.construct(mCx);
218 }
220 AutoJSAPI::AutoJSAPI(JSContext *aCx, bool aIsMainThread, bool aSkipNullAc)
221 : mCx(aCx)
222 {
223 MOZ_ASSERT_IF(aIsMainThread, NS_IsMainThread());
224 if (aIsMainThread) {
225 mCxPusher.construct(mCx);
226 }
228 // In general we want to leave the cx in a null compartment, but we let
229 // subclasses skip this if they plan to immediately enter a compartment.
230 if (!aSkipNullAc) {
231 mNullAc.construct(mCx);
232 }
233 }
235 AutoJSAPIWithErrorsReportedToWindow::AutoJSAPIWithErrorsReportedToWindow(nsIScriptContext* aScx)
236 : AutoJSAPI(aScx->GetNativeContext(), /* aIsMainThread = */ true)
237 {
238 }
240 AutoJSAPIWithErrorsReportedToWindow::AutoJSAPIWithErrorsReportedToWindow(nsIGlobalObject* aGlobalObject)
241 : AutoJSAPI(FindJSContext(aGlobalObject), /* aIsMainThread = */ true)
242 {
243 }
245 AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
246 bool aIsMainThread,
247 JSContext* aCx)
248 : AutoJSAPI(aCx ? aCx : FindJSContext(aGlobalObject), aIsMainThread, /* aSkipNullAc = */ true)
249 , ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true)
250 , mAc(cx(), aGlobalObject->GetGlobalJSObject())
251 , mStack(ScriptSettingsStack::Ref())
252 {
253 MOZ_ASSERT(aGlobalObject);
254 MOZ_ASSERT_IF(!aCx, aIsMainThread); // cx is mandatory off-main-thread.
255 MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == FindJSContext(aGlobalObject));
256 mStack.Push(this);
257 }
259 AutoEntryScript::~AutoEntryScript()
260 {
261 MOZ_ASSERT(mStack.Incumbent() == this);
262 mStack.Pop();
263 }
265 AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
266 : ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ false)
267 , mStack(ScriptSettingsStack::Ref())
268 , mCallerOverride(nsContentUtils::GetCurrentJSContextForThread())
269 {
270 mStack.Push(this);
271 }
273 AutoIncumbentScript::~AutoIncumbentScript()
274 {
275 MOZ_ASSERT(mStack.Incumbent() == this);
276 mStack.Pop();
277 }
279 AutoNoJSAPI::AutoNoJSAPI(bool aIsMainThread)
280 : mStack(ScriptSettingsStack::Ref())
281 {
282 MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContextForThread(),
283 !JS_IsExceptionPending(nsContentUtils::GetCurrentJSContextForThread()));
284 if (aIsMainThread) {
285 mCxPusher.construct(static_cast<JSContext*>(nullptr),
286 /* aAllowNull = */ true);
287 }
288 mStack.PushNoJSAPI();
289 }
291 AutoNoJSAPI::~AutoNoJSAPI()
292 {
293 mStack.Pop();
294 }
296 } // namespace dom
297 } // namespace mozilla