dom/base/ScriptSettings.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial