ScriptSettings.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /* Utilities for managing the script settings object stack defined in webapps */
  6. #ifndef mozilla_dom_ScriptSettings_h
  7. #define mozilla_dom_ScriptSettings_h
  8. #include "MainThreadUtils.h"
  9. #include "nsIGlobalObject.h"
  10. #include "nsIPrincipal.h"
  11. #include "mozilla/Maybe.h"
  12. #include "jsapi.h"
  13. #include "js/Debug.h"
  14. class nsPIDOMWindowInner;
  15. class nsGlobalWindow;
  16. class nsIScriptContext;
  17. class nsIDocument;
  18. class nsIDocShell;
  19. namespace mozilla {
  20. namespace dom {
  21. /*
  22. * System-wide setup/teardown routines. Init and Destroy should be invoked
  23. * once each, at startup and shutdown (respectively).
  24. */
  25. void InitScriptSettings();
  26. void DestroyScriptSettings();
  27. bool ScriptSettingsInitialized();
  28. /*
  29. * Static helpers in ScriptSettings which track the number of listeners
  30. * of Javascript RunToCompletion events. These should be used by the code in
  31. * nsDocShell::SetRecordProfileTimelineMarkers to indicate to script
  32. * settings that script run-to-completion needs to be monitored.
  33. * SHOULD BE CALLED ONLY BY MAIN THREAD.
  34. */
  35. void UseEntryScriptProfiling();
  36. void UnuseEntryScriptProfiling();
  37. // To implement a web-compatible browser, it is often necessary to obtain the
  38. // global object that is "associated" with the currently-running code. This
  39. // process is made more complicated by the fact that, historically, different
  40. // algorithms have operated with different definitions of the "associated"
  41. // global.
  42. //
  43. // HTML5 formalizes this into two concepts: the "incumbent global" and the
  44. // "entry global". The incumbent global corresponds to the global of the
  45. // current script being executed, whereas the entry global corresponds to the
  46. // global of the script where the current JS execution began.
  47. //
  48. // There is also a potentially-distinct third global that is determined by the
  49. // current compartment. This roughly corresponds with the notion of Realms in
  50. // ECMAScript.
  51. //
  52. // Suppose some event triggers an event listener in window |A|, which invokes a
  53. // scripted function in window |B|, which invokes the |window.location.href|
  54. // setter in window |C|. The entry global would be |A|, the incumbent global
  55. // would be |B|, and the current compartment would be that of |C|.
  56. //
  57. // In general, it's best to use to use the most-closely-associated global
  58. // unless the spec says to do otherwise. In 95% of the cases, the global of
  59. // the current compartment (GetCurrentGlobal()) is the right thing. For
  60. // example, WebIDL constructors (new C.XMLHttpRequest()) are initialized with
  61. // the global of the current compartment (i.e. |C|).
  62. //
  63. // The incumbent global is very similar, but differs in a few edge cases. For
  64. // example, if window |B| does |C.location.href = "..."|, the incumbent global
  65. // used for the navigation algorithm is B, because no script from |C| was ever run.
  66. //
  67. // The entry global is used for various things like computing base URIs, mostly
  68. // for historical reasons.
  69. //
  70. // Note that all of these functions return bonafide global objects. This means
  71. // that, for Windows, they always return the inner.
  72. // Returns the global associated with the top-most Candidate Entry Point on
  73. // the Script Settings Stack. See the HTML spec. This may be null.
  74. nsIGlobalObject* GetEntryGlobal();
  75. // If the entry global is a window, returns its extant document. Otherwise,
  76. // returns null.
  77. nsIDocument* GetEntryDocument();
  78. // Returns the global associated with the top-most entry of the the Script
  79. // Settings Stack. See the HTML spec. This may be null.
  80. nsIGlobalObject* GetIncumbentGlobal();
  81. // Returns the global associated with the current compartment. This may be null.
  82. nsIGlobalObject* GetCurrentGlobal();
  83. // JS-implemented WebIDL presents an interesting situation with respect to the
  84. // subject principal. A regular C++-implemented API can simply examine the
  85. // compartment of the most-recently-executed script, and use that to infer the
  86. // responsible party. However, JS-implemented APIs are run with system
  87. // principal, and thus clobber the subject principal of the script that
  88. // invoked the API. So we have to do some extra work to keep track of this
  89. // information.
  90. //
  91. // We therefore implement the following behavior:
  92. // * Each Script Settings Object has an optional WebIDL Caller Principal field.
  93. // This defaults to null.
  94. // * When we push an Entry Point in preparation to run a JS-implemented WebIDL
  95. // callback, we grab the subject principal at the time of invocation, and
  96. // store that as the WebIDL Caller Principal.
  97. // * When non-null, callers can query this principal from script via an API on
  98. // Components.utils.
  99. nsIPrincipal* GetWebIDLCallerPrincipal();
  100. // This may be used by callers that know that their incumbent global is non-
  101. // null (i.e. they know there have been no System Caller pushes since the
  102. // inner-most script execution).
  103. inline JSObject& IncumbentJSGlobal()
  104. {
  105. return *GetIncumbentGlobal()->GetGlobalJSObject();
  106. }
  107. // Returns whether JSAPI is active right now. If it is not, working with a
  108. // JSContext you grab from somewhere random is not OK and you should be doing
  109. // AutoJSAPI or AutoEntryScript to get yourself a properly set up JSContext.
  110. bool IsJSAPIActive();
  111. namespace danger {
  112. // Get the JSContext for this thread. This is in the "danger" namespace because
  113. // we generally want people using AutoJSAPI instead, unless they really know
  114. // what they're doing.
  115. JSContext* GetJSContext();
  116. } // namespace danger
  117. JS::RootingContext* RootingCx();
  118. class ScriptSettingsStack;
  119. class ScriptSettingsStackEntry {
  120. friend class ScriptSettingsStack;
  121. public:
  122. ~ScriptSettingsStackEntry();
  123. bool NoJSAPI() const { return mType == eNoJSAPI; }
  124. bool IsEntryCandidate() const {
  125. return mType == eEntryScript || mType == eNoJSAPI;
  126. }
  127. bool IsIncumbentCandidate() { return mType != eJSAPI; }
  128. bool IsIncumbentScript() { return mType == eIncumbentScript; }
  129. protected:
  130. enum Type {
  131. eEntryScript,
  132. eIncumbentScript,
  133. eJSAPI,
  134. eNoJSAPI
  135. };
  136. ScriptSettingsStackEntry(nsIGlobalObject *aGlobal,
  137. Type aEntryType);
  138. nsCOMPtr<nsIGlobalObject> mGlobalObject;
  139. Type mType;
  140. private:
  141. ScriptSettingsStackEntry *mOlder;
  142. };
  143. /*
  144. * For any interaction with JSAPI, an AutoJSAPI (or one of its subclasses)
  145. * must be on the stack.
  146. *
  147. * This base class should be instantiated as-is when the caller wants to use
  148. * JSAPI but doesn't expect to run script. The caller must then call one of its
  149. * Init functions before being able to access the JSContext through cx().
  150. * Its current duties are as-follows (see individual Init comments for details):
  151. *
  152. * * Grabbing an appropriate JSContext, and, on the main thread, pushing it onto
  153. * the JSContext stack.
  154. * * Entering an initial (possibly null) compartment, to ensure that the
  155. * previously entered compartment for that JSContext is not used by mistake.
  156. * * Reporting any exceptions left on the JSRuntime, unless the caller steals
  157. * or silences them.
  158. * * On main thread, entering a JSAutoRequest.
  159. *
  160. * Additionally, the following duties are planned, but not yet implemented:
  161. *
  162. * * De-poisoning the JSRuntime to allow manipulation of JSAPI. This requires
  163. * implementing the poisoning first. For now, this de-poisoning
  164. * effectively corresponds to having a non-null cx on the stack.
  165. *
  166. * In situations where the consumer expects to run script, AutoEntryScript
  167. * should be used, which does additional manipulation of the script settings
  168. * stack. In bug 991758, we'll add hard invariants to SpiderMonkey, such that
  169. * any attempt to run script without an AutoEntryScript on the stack will
  170. * fail. This prevents system code from accidentally triggering script
  171. * execution at inopportune moments via surreptitious getters and proxies.
  172. */
  173. class MOZ_STACK_CLASS AutoJSAPI : protected ScriptSettingsStackEntry {
  174. public:
  175. // Trivial constructor. One of the Init functions must be called before
  176. // accessing the JSContext through cx().
  177. AutoJSAPI();
  178. ~AutoJSAPI();
  179. // This uses the SafeJSContext (or worker equivalent), and enters a null
  180. // compartment, so that the consumer is forced to select a compartment to
  181. // enter before manipulating objects.
  182. //
  183. // This variant will ensure that any errors reported by this AutoJSAPI as it
  184. // comes off the stack will not fire error events or be associated with any
  185. // particular web-visible global.
  186. void Init();
  187. // This uses the SafeJSContext (or worker equivalent), and enters the
  188. // compartment of aGlobalObject.
  189. // If aGlobalObject or its associated JS global are null then it returns
  190. // false and use of cx() will cause an assertion.
  191. //
  192. // If aGlobalObject represents a web-visible global, errors reported by this
  193. // AutoJSAPI as it comes off the stack will fire the relevant error events and
  194. // show up in the corresponding web console.
  195. MOZ_MUST_USE bool Init(nsIGlobalObject* aGlobalObject);
  196. // This is a helper that grabs the native global associated with aObject and
  197. // invokes the above Init() with that.
  198. MOZ_MUST_USE bool Init(JSObject* aObject);
  199. // Unsurprisingly, this uses aCx and enters the compartment of aGlobalObject.
  200. // If aGlobalObject or its associated JS global are null then it returns
  201. // false and use of cx() will cause an assertion.
  202. // If aCx is null it will cause an assertion.
  203. //
  204. // If aGlobalObject represents a web-visible global, errors reported by this
  205. // AutoJSAPI as it comes off the stack will fire the relevant error events and
  206. // show up in the corresponding web console.
  207. MOZ_MUST_USE bool Init(nsIGlobalObject* aGlobalObject, JSContext* aCx);
  208. // Convenience functions to take an nsPIDOMWindow* or nsGlobalWindow*,
  209. // when it is more easily available than an nsIGlobalObject.
  210. MOZ_MUST_USE bool Init(nsPIDOMWindowInner* aWindow);
  211. MOZ_MUST_USE bool Init(nsPIDOMWindowInner* aWindow, JSContext* aCx);
  212. MOZ_MUST_USE bool Init(nsGlobalWindow* aWindow);
  213. MOZ_MUST_USE bool Init(nsGlobalWindow* aWindow, JSContext* aCx);
  214. JSContext* cx() const {
  215. MOZ_ASSERT(mCx, "Must call Init before using an AutoJSAPI");
  216. MOZ_ASSERT(IsStackTop());
  217. return mCx;
  218. }
  219. #ifdef DEBUG
  220. bool IsStackTop() const;
  221. #endif
  222. // If HasException, report it. Otherwise, a no-op.
  223. void ReportException();
  224. bool HasException() const {
  225. MOZ_ASSERT(IsStackTop());
  226. return JS_IsExceptionPending(cx());
  227. };
  228. // Transfers ownership of the current exception from the JS engine to the
  229. // caller. Callers must ensure that HasException() is true, and that cx()
  230. // is in a non-null compartment.
  231. //
  232. // Note that this fails if and only if we OOM while wrapping the exception
  233. // into the current compartment.
  234. MOZ_MUST_USE bool StealException(JS::MutableHandle<JS::Value> aVal);
  235. // Peek the current exception from the JS engine, without stealing it.
  236. // Callers must ensure that HasException() is true, and that cx() is in a
  237. // non-null compartment.
  238. //
  239. // Note that this fails if and only if we OOM while wrapping the exception
  240. // into the current compartment.
  241. MOZ_MUST_USE bool PeekException(JS::MutableHandle<JS::Value> aVal);
  242. void ClearException() {
  243. MOZ_ASSERT(IsStackTop());
  244. JS_ClearPendingException(cx());
  245. }
  246. protected:
  247. // Protected constructor for subclasses. This constructor initialises the
  248. // AutoJSAPI, so Init must NOT be called on subclasses that use this.
  249. AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, Type aType);
  250. private:
  251. mozilla::Maybe<JSAutoRequest> mAutoRequest;
  252. mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
  253. JSContext *mCx;
  254. // Whether we're mainthread or not; set when we're initialized.
  255. bool mIsMainThread;
  256. Maybe<JS::WarningReporter> mOldWarningReporter;
  257. void InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
  258. JSContext* aCx, bool aIsMainThread);
  259. AutoJSAPI(const AutoJSAPI&) = delete;
  260. AutoJSAPI& operator= (const AutoJSAPI&) = delete;
  261. };
  262. /*
  263. * A class that represents a new script entry point.
  264. *
  265. * |aReason| should be a statically-allocated C string naming the reason we're
  266. * invoking JavaScript code: "setTimeout", "event", and so on. The devtools use
  267. * these strings to label JS execution in timeline and profiling displays.
  268. */
  269. class MOZ_STACK_CLASS AutoEntryScript : public AutoJSAPI {
  270. public:
  271. AutoEntryScript(nsIGlobalObject* aGlobalObject,
  272. const char *aReason,
  273. bool aIsMainThread = NS_IsMainThread());
  274. AutoEntryScript(JSObject* aObject, // Any object from the relevant global
  275. const char *aReason,
  276. bool aIsMainThread = NS_IsMainThread());
  277. ~AutoEntryScript();
  278. void SetWebIDLCallerPrincipal(nsIPrincipal *aPrincipal) {
  279. mWebIDLCallerPrincipal = aPrincipal;
  280. }
  281. private:
  282. // A subclass of AutoEntryMonitor that notifies the docshell.
  283. class DocshellEntryMonitor final : public JS::dbg::AutoEntryMonitor
  284. {
  285. public:
  286. DocshellEntryMonitor(JSContext* aCx, const char* aReason);
  287. // Please note that |aAsyncCause| here is owned by the caller, and its
  288. // lifetime must outlive the lifetime of the DocshellEntryMonitor object.
  289. // In practice, |aAsyncCause| is identical to |aReason| passed into
  290. // the AutoEntryScript constructor, so the lifetime requirements are
  291. // trivially satisfied by |aReason| being a statically allocated string.
  292. void Entry(JSContext* aCx, JSFunction* aFunction,
  293. JS::Handle<JS::Value> aAsyncStack,
  294. const char* aAsyncCause) override
  295. {
  296. Entry(aCx, aFunction, nullptr, aAsyncStack, aAsyncCause);
  297. }
  298. void Entry(JSContext* aCx, JSScript* aScript,
  299. JS::Handle<JS::Value> aAsyncStack,
  300. const char* aAsyncCause) override
  301. {
  302. Entry(aCx, nullptr, aScript, aAsyncStack, aAsyncCause);
  303. }
  304. void Exit(JSContext* aCx) override;
  305. private:
  306. void Entry(JSContext* aCx, JSFunction* aFunction, JSScript* aScript,
  307. JS::Handle<JS::Value> aAsyncStack,
  308. const char* aAsyncCause);
  309. const char* mReason;
  310. };
  311. // It's safe to make this a weak pointer, since it's the subject principal
  312. // when we go on the stack, so can't go away until after we're gone. In
  313. // particular, this is only used from the CallSetup constructor, and only in
  314. // the aIsJSImplementedWebIDL case. And in that case, the subject principal
  315. // is the principal of the callee function that is part of the CallArgs just a
  316. // bit up the stack, and which will outlive us. So we know the principal
  317. // can't go away until then either.
  318. nsIPrincipal* MOZ_NON_OWNING_REF mWebIDLCallerPrincipal;
  319. friend nsIPrincipal* GetWebIDLCallerPrincipal();
  320. Maybe<DocshellEntryMonitor> mDocShellEntryMonitor;
  321. };
  322. /*
  323. * A class that can be used to force a particular incumbent script on the stack.
  324. */
  325. class AutoIncumbentScript : protected ScriptSettingsStackEntry {
  326. public:
  327. explicit AutoIncumbentScript(nsIGlobalObject* aGlobalObject);
  328. ~AutoIncumbentScript();
  329. private:
  330. JS::AutoHideScriptedCaller mCallerOverride;
  331. };
  332. /*
  333. * A class to put the JS engine in an unusable state. The subject principal
  334. * will become System, the information on the script settings stack is
  335. * rendered inaccessible, and JSAPI may not be manipulated until the class is
  336. * either popped or an AutoJSAPI instance is subsequently pushed.
  337. *
  338. * This class may not be instantiated if an exception is pending.
  339. */
  340. class AutoNoJSAPI : protected ScriptSettingsStackEntry {
  341. public:
  342. explicit AutoNoJSAPI();
  343. ~AutoNoJSAPI();
  344. };
  345. } // namespace dom
  346. /**
  347. * Use AutoJSContext when you need a JS context on the stack but don't have one
  348. * passed as a parameter. AutoJSContext will take care of finding the most
  349. * appropriate JS context and release it when leaving the stack.
  350. */
  351. class MOZ_RAII AutoJSContext {
  352. public:
  353. explicit AutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
  354. operator JSContext*() const;
  355. protected:
  356. JSContext* mCx;
  357. dom::AutoJSAPI mJSAPI;
  358. MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
  359. };
  360. /**
  361. * AutoSafeJSContext is similar to AutoJSContext but will only return the safe
  362. * JS context. That means it will never call nsContentUtils::GetCurrentJSContext().
  363. *
  364. * Note - This is deprecated. Please use AutoJSAPI instead.
  365. */
  366. class MOZ_RAII AutoSafeJSContext : public dom::AutoJSAPI {
  367. public:
  368. explicit AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
  369. operator JSContext*() const
  370. {
  371. return cx();
  372. }
  373. private:
  374. MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
  375. };
  376. /**
  377. * Use AutoSlowOperation when native side calls many JS callbacks in a row
  378. * and slow script dialog should be activated if too much time is spent going
  379. * through those callbacks.
  380. * AutoSlowOperation puts a JSAutoRequest on the stack so that we don't continue
  381. * to reset the watchdog and CheckForInterrupt can be then used to check whether
  382. * JS execution should be interrupted.
  383. */
  384. class MOZ_RAII AutoSlowOperation : public dom::AutoJSAPI
  385. {
  386. public:
  387. explicit AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
  388. void CheckForInterrupt();
  389. private:
  390. MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
  391. };
  392. } // namespace mozilla
  393. #endif // mozilla_dom_ScriptSettings_h