LCOV - code coverage report
Current view: top level - dom/script - ScriptSettings.h (source / functions) Hit Total Coverage
Test: output.info Lines: 26 33 78.8 %
Date: 2017-07-14 16:53:18 Functions: 12 15 80.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13