dom/base/ScriptSettings.h

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 /* Utilities for managing the script settings object stack defined in webapps */
michael@0 8
michael@0 9 #ifndef mozilla_dom_ScriptSettings_h
michael@0 10 #define mozilla_dom_ScriptSettings_h
michael@0 11
michael@0 12 #include "nsCxPusher.h"
michael@0 13 #include "MainThreadUtils.h"
michael@0 14 #include "nsIGlobalObject.h"
michael@0 15 #include "nsIPrincipal.h"
michael@0 16
michael@0 17 #include "mozilla/Maybe.h"
michael@0 18
michael@0 19 class nsIGlobalObject;
michael@0 20
michael@0 21 namespace mozilla {
michael@0 22 namespace dom {
michael@0 23
michael@0 24 /*
michael@0 25 * System-wide setup/teardown routines. Init and Destroy should be invoked
michael@0 26 * once each, at startup and shutdown (respectively).
michael@0 27 */
michael@0 28 void InitScriptSettings();
michael@0 29 void DestroyScriptSettings();
michael@0 30
michael@0 31 // This mostly gets the entry global, but doesn't entirely match the spec in
michael@0 32 // certain edge cases. It's good enough for some purposes, but not others. If
michael@0 33 // you want to call this function, ping bholley and describe your use-case.
michael@0 34 nsIGlobalObject* BrokenGetEntryGlobal();
michael@0 35
michael@0 36 // Note: We don't yet expose GetEntryGlobal, because in order for it to be
michael@0 37 // correct, we first need to replace a bunch of explicit cx pushing in the
michael@0 38 // browser with AutoEntryScript. But GetIncumbentGlobal is simpler, because it
michael@0 39 // can mostly be inferred from the JS stack.
michael@0 40 nsIGlobalObject* GetIncumbentGlobal();
michael@0 41
michael@0 42 // JS-implemented WebIDL presents an interesting situation with respect to the
michael@0 43 // subject principal. A regular C++-implemented API can simply examine the
michael@0 44 // compartment of the most-recently-executed script, and use that to infer the
michael@0 45 // responsible party. However, JS-implemented APIs are run with system
michael@0 46 // principal, and thus clobber the subject principal of the script that
michael@0 47 // invoked the API. So we have to do some extra work to keep track of this
michael@0 48 // information.
michael@0 49 //
michael@0 50 // We therefore implement the following behavior:
michael@0 51 // * Each Script Settings Object has an optional WebIDL Caller Principal field.
michael@0 52 // This defaults to null.
michael@0 53 // * When we push an Entry Point in preparation to run a JS-implemented WebIDL
michael@0 54 // callback, we grab the subject principal at the time of invocation, and
michael@0 55 // store that as the WebIDL Caller Principal.
michael@0 56 // * When non-null, callers can query this principal from script via an API on
michael@0 57 // Components.utils.
michael@0 58 nsIPrincipal* GetWebIDLCallerPrincipal();
michael@0 59
michael@0 60 // This may be used by callers that know that their incumbent global is non-
michael@0 61 // null (i.e. they know there have been no System Caller pushes since the
michael@0 62 // inner-most script execution).
michael@0 63 inline JSObject& IncumbentJSGlobal()
michael@0 64 {
michael@0 65 return *GetIncumbentGlobal()->GetGlobalJSObject();
michael@0 66 }
michael@0 67
michael@0 68 class ScriptSettingsStack;
michael@0 69 struct ScriptSettingsStackEntry {
michael@0 70 nsCOMPtr<nsIGlobalObject> mGlobalObject;
michael@0 71 bool mIsCandidateEntryPoint;
michael@0 72
michael@0 73 ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, bool aCandidate)
michael@0 74 : mGlobalObject(aGlobal)
michael@0 75 , mIsCandidateEntryPoint(aCandidate)
michael@0 76 {
michael@0 77 MOZ_ASSERT(mGlobalObject);
michael@0 78 MOZ_ASSERT(mGlobalObject->GetGlobalJSObject(),
michael@0 79 "Must have an actual JS global for the duration on the stack");
michael@0 80 MOZ_ASSERT(JS_IsGlobalObject(mGlobalObject->GetGlobalJSObject()),
michael@0 81 "No outer windows allowed");
michael@0 82 }
michael@0 83
michael@0 84 ~ScriptSettingsStackEntry() {
michael@0 85 // We must have an actual JS global for the entire time this is on the stack.
michael@0 86 MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject());
michael@0 87 }
michael@0 88
michael@0 89 bool NoJSAPI() { return this == &NoJSAPISingleton; }
michael@0 90 static ScriptSettingsStackEntry NoJSAPISingleton;
michael@0 91
michael@0 92 private:
michael@0 93 ScriptSettingsStackEntry() : mGlobalObject(nullptr)
michael@0 94 , mIsCandidateEntryPoint(true)
michael@0 95 {}
michael@0 96 };
michael@0 97
michael@0 98 /*
michael@0 99 * For any interaction with JSAPI, an AutoJSAPI (or one of its subclasses)
michael@0 100 * must be on the stack.
michael@0 101 *
michael@0 102 * This base class should be instantiated as-is when the caller wants to use
michael@0 103 * JSAPI but doesn't expect to run script. Its current duties are as-follows:
michael@0 104 *
michael@0 105 * * Grabbing an appropriate JSContext, and, on the main thread, pushing it onto
michael@0 106 * the JSContext stack.
michael@0 107 * * Entering a null compartment, so that the consumer is forced to select a
michael@0 108 * compartment to enter before manipulating objects.
michael@0 109 *
michael@0 110 * Additionally, the following duties are planned, but not yet implemented:
michael@0 111 *
michael@0 112 * * De-poisoning the JSRuntime to allow manipulation of JSAPI. We can't
michael@0 113 * actually implement this poisoning until all the JSContext pushing in the
michael@0 114 * system goes through AutoJSAPI (see bug 951991). For now, this de-poisoning
michael@0 115 * effectively corresponds to having a non-null cx on the stack.
michael@0 116 * * Reporting any exceptions left on the JSRuntime, unless the caller steals
michael@0 117 * or silences them.
michael@0 118 * * Entering a JSAutoRequest. At present, this is handled by the cx pushing
michael@0 119 * on the main thread, and by other code on workers. Depending on the order
michael@0 120 * in which various cleanup lands, this may never be necessary, because
michael@0 121 * JSAutoRequests may go away.
michael@0 122 *
michael@0 123 * In situations where the consumer expects to run script, AutoEntryScript
michael@0 124 * should be used, which does additional manipulation of the script settings
michael@0 125 * stack. In bug 991758, we'll add hard invariants to SpiderMonkey, such that
michael@0 126 * any attempt to run script without an AutoEntryScript on the stack will
michael@0 127 * fail. This prevents system code from accidentally triggering script
michael@0 128 * execution at inopportune moments via surreptitious getters and proxies.
michael@0 129 */
michael@0 130 class AutoJSAPI {
michael@0 131 public:
michael@0 132 // Public constructor for use when the base class is constructed as-is. It
michael@0 133 // uses the SafeJSContext (or worker equivalent), and enters a null
michael@0 134 // compartment.
michael@0 135 AutoJSAPI();
michael@0 136 JSContext* cx() const { return mCx; }
michael@0 137
michael@0 138 bool CxPusherIsStackTop() { return mCxPusher.ref().IsStackTop(); }
michael@0 139
michael@0 140 protected:
michael@0 141 // Protected constructor, allowing subclasses to specify a particular cx to
michael@0 142 // be used.
michael@0 143 AutoJSAPI(JSContext *aCx, bool aIsMainThread, bool aSkipNullAC = false);
michael@0 144
michael@0 145 private:
michael@0 146 mozilla::Maybe<AutoCxPusher> mCxPusher;
michael@0 147 mozilla::Maybe<JSAutoNullCompartment> mNullAc;
michael@0 148 JSContext *mCx;
michael@0 149 };
michael@0 150
michael@0 151 // Note - the ideal way to implement this is with an accessor on AutoJSAPI
michael@0 152 // that lets us select the error reporting target. But at present,
michael@0 153 // implementing it that way would require us to destroy and reconstruct
michael@0 154 // mCxPusher, which is pretty wasteful. So we do this for now, since it should
michael@0 155 // be pretty easy to switch things over later.
michael@0 156 //
michael@0 157 // This should only be used on the main thread.
michael@0 158 class AutoJSAPIWithErrorsReportedToWindow : public AutoJSAPI {
michael@0 159 public:
michael@0 160 AutoJSAPIWithErrorsReportedToWindow(nsIScriptContext* aScx);
michael@0 161 // Equivalent to AutoJSAPI if aGlobal is not a Window.
michael@0 162 AutoJSAPIWithErrorsReportedToWindow(nsIGlobalObject* aGlobalObject);
michael@0 163 };
michael@0 164
michael@0 165 /*
michael@0 166 * A class that represents a new script entry point.
michael@0 167 */
michael@0 168 class AutoEntryScript : public AutoJSAPI,
michael@0 169 protected ScriptSettingsStackEntry {
michael@0 170 public:
michael@0 171 AutoEntryScript(nsIGlobalObject* aGlobalObject,
michael@0 172 bool aIsMainThread = NS_IsMainThread(),
michael@0 173 // Note: aCx is mandatory off-main-thread.
michael@0 174 JSContext* aCx = nullptr);
michael@0 175 ~AutoEntryScript();
michael@0 176
michael@0 177 void SetWebIDLCallerPrincipal(nsIPrincipal *aPrincipal) {
michael@0 178 mWebIDLCallerPrincipal = aPrincipal;
michael@0 179 }
michael@0 180
michael@0 181 private:
michael@0 182 JSAutoCompartment mAc;
michael@0 183 dom::ScriptSettingsStack& mStack;
michael@0 184 nsCOMPtr<nsIPrincipal> mWebIDLCallerPrincipal;
michael@0 185 friend nsIPrincipal* GetWebIDLCallerPrincipal();
michael@0 186 };
michael@0 187
michael@0 188 /*
michael@0 189 * A class that can be used to force a particular incumbent script on the stack.
michael@0 190 */
michael@0 191 class AutoIncumbentScript : protected ScriptSettingsStackEntry {
michael@0 192 public:
michael@0 193 AutoIncumbentScript(nsIGlobalObject* aGlobalObject);
michael@0 194 ~AutoIncumbentScript();
michael@0 195 private:
michael@0 196 dom::ScriptSettingsStack& mStack;
michael@0 197 JS::AutoHideScriptedCaller mCallerOverride;
michael@0 198 };
michael@0 199
michael@0 200 /*
michael@0 201 * A class to put the JS engine in an unusable state. The subject principal
michael@0 202 * will become System, the information on the script settings stack is
michael@0 203 * rendered inaccessible, and JSAPI may not be manipulated until the class is
michael@0 204 * either popped or an AutoJSAPI instance is subsequently pushed.
michael@0 205 *
michael@0 206 * This class may not be instantiated if an exception is pending.
michael@0 207 */
michael@0 208 class AutoNoJSAPI {
michael@0 209 public:
michael@0 210 AutoNoJSAPI(bool aIsMainThread = NS_IsMainThread());
michael@0 211 ~AutoNoJSAPI();
michael@0 212 private:
michael@0 213 dom::ScriptSettingsStack& mStack;
michael@0 214 mozilla::Maybe<AutoCxPusher> mCxPusher;
michael@0 215 };
michael@0 216
michael@0 217 } // namespace dom
michael@0 218 } // namespace mozilla
michael@0 219
michael@0 220 #endif // mozilla_dom_ScriptSettings_h

mercurial