dom/base/ScriptSettings.h

changeset 0
6474c204b198
     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

mercurial