1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/base/ScriptSettings.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,220 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +// vim: ft=cpp tw=78 sw=2 et ts=2 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 +/* Utilities for managing the script settings object stack defined in webapps */ 1.11 + 1.12 +#ifndef mozilla_dom_ScriptSettings_h 1.13 +#define mozilla_dom_ScriptSettings_h 1.14 + 1.15 +#include "nsCxPusher.h" 1.16 +#include "MainThreadUtils.h" 1.17 +#include "nsIGlobalObject.h" 1.18 +#include "nsIPrincipal.h" 1.19 + 1.20 +#include "mozilla/Maybe.h" 1.21 + 1.22 +class nsIGlobalObject; 1.23 + 1.24 +namespace mozilla { 1.25 +namespace dom { 1.26 + 1.27 +/* 1.28 + * System-wide setup/teardown routines. Init and Destroy should be invoked 1.29 + * once each, at startup and shutdown (respectively). 1.30 + */ 1.31 +void InitScriptSettings(); 1.32 +void DestroyScriptSettings(); 1.33 + 1.34 +// This mostly gets the entry global, but doesn't entirely match the spec in 1.35 +// certain edge cases. It's good enough for some purposes, but not others. If 1.36 +// you want to call this function, ping bholley and describe your use-case. 1.37 +nsIGlobalObject* BrokenGetEntryGlobal(); 1.38 + 1.39 +// Note: We don't yet expose GetEntryGlobal, because in order for it to be 1.40 +// correct, we first need to replace a bunch of explicit cx pushing in the 1.41 +// browser with AutoEntryScript. But GetIncumbentGlobal is simpler, because it 1.42 +// can mostly be inferred from the JS stack. 1.43 +nsIGlobalObject* GetIncumbentGlobal(); 1.44 + 1.45 +// JS-implemented WebIDL presents an interesting situation with respect to the 1.46 +// subject principal. A regular C++-implemented API can simply examine the 1.47 +// compartment of the most-recently-executed script, and use that to infer the 1.48 +// responsible party. However, JS-implemented APIs are run with system 1.49 +// principal, and thus clobber the subject principal of the script that 1.50 +// invoked the API. So we have to do some extra work to keep track of this 1.51 +// information. 1.52 +// 1.53 +// We therefore implement the following behavior: 1.54 +// * Each Script Settings Object has an optional WebIDL Caller Principal field. 1.55 +// This defaults to null. 1.56 +// * When we push an Entry Point in preparation to run a JS-implemented WebIDL 1.57 +// callback, we grab the subject principal at the time of invocation, and 1.58 +// store that as the WebIDL Caller Principal. 1.59 +// * When non-null, callers can query this principal from script via an API on 1.60 +// Components.utils. 1.61 +nsIPrincipal* GetWebIDLCallerPrincipal(); 1.62 + 1.63 +// This may be used by callers that know that their incumbent global is non- 1.64 +// null (i.e. they know there have been no System Caller pushes since the 1.65 +// inner-most script execution). 1.66 +inline JSObject& IncumbentJSGlobal() 1.67 +{ 1.68 + return *GetIncumbentGlobal()->GetGlobalJSObject(); 1.69 +} 1.70 + 1.71 +class ScriptSettingsStack; 1.72 +struct ScriptSettingsStackEntry { 1.73 + nsCOMPtr<nsIGlobalObject> mGlobalObject; 1.74 + bool mIsCandidateEntryPoint; 1.75 + 1.76 + ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, bool aCandidate) 1.77 + : mGlobalObject(aGlobal) 1.78 + , mIsCandidateEntryPoint(aCandidate) 1.79 + { 1.80 + MOZ_ASSERT(mGlobalObject); 1.81 + MOZ_ASSERT(mGlobalObject->GetGlobalJSObject(), 1.82 + "Must have an actual JS global for the duration on the stack"); 1.83 + MOZ_ASSERT(JS_IsGlobalObject(mGlobalObject->GetGlobalJSObject()), 1.84 + "No outer windows allowed"); 1.85 + } 1.86 + 1.87 + ~ScriptSettingsStackEntry() { 1.88 + // We must have an actual JS global for the entire time this is on the stack. 1.89 + MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject()); 1.90 + } 1.91 + 1.92 + bool NoJSAPI() { return this == &NoJSAPISingleton; } 1.93 + static ScriptSettingsStackEntry NoJSAPISingleton; 1.94 + 1.95 +private: 1.96 + ScriptSettingsStackEntry() : mGlobalObject(nullptr) 1.97 + , mIsCandidateEntryPoint(true) 1.98 + {} 1.99 +}; 1.100 + 1.101 +/* 1.102 + * For any interaction with JSAPI, an AutoJSAPI (or one of its subclasses) 1.103 + * must be on the stack. 1.104 + * 1.105 + * This base class should be instantiated as-is when the caller wants to use 1.106 + * JSAPI but doesn't expect to run script. Its current duties are as-follows: 1.107 + * 1.108 + * * Grabbing an appropriate JSContext, and, on the main thread, pushing it onto 1.109 + * the JSContext stack. 1.110 + * * Entering a null compartment, so that the consumer is forced to select a 1.111 + * compartment to enter before manipulating objects. 1.112 + * 1.113 + * Additionally, the following duties are planned, but not yet implemented: 1.114 + * 1.115 + * * De-poisoning the JSRuntime to allow manipulation of JSAPI. We can't 1.116 + * actually implement this poisoning until all the JSContext pushing in the 1.117 + * system goes through AutoJSAPI (see bug 951991). For now, this de-poisoning 1.118 + * effectively corresponds to having a non-null cx on the stack. 1.119 + * * Reporting any exceptions left on the JSRuntime, unless the caller steals 1.120 + * or silences them. 1.121 + * * Entering a JSAutoRequest. At present, this is handled by the cx pushing 1.122 + * on the main thread, and by other code on workers. Depending on the order 1.123 + * in which various cleanup lands, this may never be necessary, because 1.124 + * JSAutoRequests may go away. 1.125 + * 1.126 + * In situations where the consumer expects to run script, AutoEntryScript 1.127 + * should be used, which does additional manipulation of the script settings 1.128 + * stack. In bug 991758, we'll add hard invariants to SpiderMonkey, such that 1.129 + * any attempt to run script without an AutoEntryScript on the stack will 1.130 + * fail. This prevents system code from accidentally triggering script 1.131 + * execution at inopportune moments via surreptitious getters and proxies. 1.132 + */ 1.133 +class AutoJSAPI { 1.134 +public: 1.135 + // Public constructor for use when the base class is constructed as-is. It 1.136 + // uses the SafeJSContext (or worker equivalent), and enters a null 1.137 + // compartment. 1.138 + AutoJSAPI(); 1.139 + JSContext* cx() const { return mCx; } 1.140 + 1.141 + bool CxPusherIsStackTop() { return mCxPusher.ref().IsStackTop(); } 1.142 + 1.143 +protected: 1.144 + // Protected constructor, allowing subclasses to specify a particular cx to 1.145 + // be used. 1.146 + AutoJSAPI(JSContext *aCx, bool aIsMainThread, bool aSkipNullAC = false); 1.147 + 1.148 +private: 1.149 + mozilla::Maybe<AutoCxPusher> mCxPusher; 1.150 + mozilla::Maybe<JSAutoNullCompartment> mNullAc; 1.151 + JSContext *mCx; 1.152 +}; 1.153 + 1.154 +// Note - the ideal way to implement this is with an accessor on AutoJSAPI 1.155 +// that lets us select the error reporting target. But at present, 1.156 +// implementing it that way would require us to destroy and reconstruct 1.157 +// mCxPusher, which is pretty wasteful. So we do this for now, since it should 1.158 +// be pretty easy to switch things over later. 1.159 +// 1.160 +// This should only be used on the main thread. 1.161 +class AutoJSAPIWithErrorsReportedToWindow : public AutoJSAPI { 1.162 + public: 1.163 + AutoJSAPIWithErrorsReportedToWindow(nsIScriptContext* aScx); 1.164 + // Equivalent to AutoJSAPI if aGlobal is not a Window. 1.165 + AutoJSAPIWithErrorsReportedToWindow(nsIGlobalObject* aGlobalObject); 1.166 +}; 1.167 + 1.168 +/* 1.169 + * A class that represents a new script entry point. 1.170 + */ 1.171 +class AutoEntryScript : public AutoJSAPI, 1.172 + protected ScriptSettingsStackEntry { 1.173 +public: 1.174 + AutoEntryScript(nsIGlobalObject* aGlobalObject, 1.175 + bool aIsMainThread = NS_IsMainThread(), 1.176 + // Note: aCx is mandatory off-main-thread. 1.177 + JSContext* aCx = nullptr); 1.178 + ~AutoEntryScript(); 1.179 + 1.180 + void SetWebIDLCallerPrincipal(nsIPrincipal *aPrincipal) { 1.181 + mWebIDLCallerPrincipal = aPrincipal; 1.182 + } 1.183 + 1.184 +private: 1.185 + JSAutoCompartment mAc; 1.186 + dom::ScriptSettingsStack& mStack; 1.187 + nsCOMPtr<nsIPrincipal> mWebIDLCallerPrincipal; 1.188 + friend nsIPrincipal* GetWebIDLCallerPrincipal(); 1.189 +}; 1.190 + 1.191 +/* 1.192 + * A class that can be used to force a particular incumbent script on the stack. 1.193 + */ 1.194 +class AutoIncumbentScript : protected ScriptSettingsStackEntry { 1.195 +public: 1.196 + AutoIncumbentScript(nsIGlobalObject* aGlobalObject); 1.197 + ~AutoIncumbentScript(); 1.198 +private: 1.199 + dom::ScriptSettingsStack& mStack; 1.200 + JS::AutoHideScriptedCaller mCallerOverride; 1.201 +}; 1.202 + 1.203 +/* 1.204 + * A class to put the JS engine in an unusable state. The subject principal 1.205 + * will become System, the information on the script settings stack is 1.206 + * rendered inaccessible, and JSAPI may not be manipulated until the class is 1.207 + * either popped or an AutoJSAPI instance is subsequently pushed. 1.208 + * 1.209 + * This class may not be instantiated if an exception is pending. 1.210 + */ 1.211 +class AutoNoJSAPI { 1.212 +public: 1.213 + AutoNoJSAPI(bool aIsMainThread = NS_IsMainThread()); 1.214 + ~AutoNoJSAPI(); 1.215 +private: 1.216 + dom::ScriptSettingsStack& mStack; 1.217 + mozilla::Maybe<AutoCxPusher> mCxPusher; 1.218 +}; 1.219 + 1.220 +} // namespace dom 1.221 +} // namespace mozilla 1.222 + 1.223 +#endif // mozilla_dom_ScriptSettings_h