Bug 951991 - Implement GetEntryGlobal. r=smaug
authorBobby Holley <bobbyholley@gmail.com>
Tue, 19 Aug 2014 12:02:05 -0700
changeset 222053 5ed2046aa6c68721b4becee018b8e16a193f3365
parent 222052 6b5285b794baafa5819921930cbd22c3a3e0a8b2
child 222054 5d6a67963f48ad5c212fe7d7c88b1db9acb6700c
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs951991
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 951991 - Implement GetEntryGlobal. r=smaug
content/base/src/WebSocket.cpp
dom/base/ScriptSettings.cpp
dom/base/ScriptSettings.h
dom/base/nsGlobalWindow.cpp
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -681,17 +681,17 @@ WebSocket::Init(JSContext* aCx,
 
   // Don't allow https:// to open ws://
   if (!mSecure &&
       !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
                             false)) {
     // Confirmed we are opening plain ws:// and want to prevent this from a
     // secure context (e.g. https). Check the principal's uri to determine if
     // we were loaded from https.
-    nsCOMPtr<nsIGlobalObject> globalObject(BrokenGetEntryGlobal());
+    nsCOMPtr<nsIGlobalObject> globalObject(GetEntryGlobal());
     if (globalObject) {
       nsCOMPtr<nsIPrincipal> principal(globalObject->PrincipalOrNull());
       if (principal) {
         nsCOMPtr<nsIURI> uri;
         principal->GetURI(getter_AddRefs(uri));
         if (uri) {
           bool originIsHttps = false;
           rv = uri->SchemeIs("https", &originIsHttps);
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -117,38 +117,22 @@ ScriptSettingsStackEntry::ScriptSettings
 ScriptSettingsStackEntry::~ScriptSettingsStackEntry()
 {
   // We must have an actual JS global for the entire time this is on the stack.
   MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject());
 
   ScriptSettingsStack::Pop(this);
 }
 
-// This mostly gets the entry global, but doesn't entirely match the spec in
-// certain edge cases. It's good enough for some purposes, but not others. If
-// you want to call this function, ping bholley and describe your use-case.
 nsIGlobalObject*
-BrokenGetEntryGlobal()
+GetEntryGlobal()
 {
-  // We need the current JSContext in order to check the JS for
-  // scripted frames that may have appeared since anyone last
-  // manipulated the stack. If it's null, that means that there
-  // must be no entry global on the stack.
-  JSContext *cx = nsContentUtils::GetCurrentJSContextForThread();
-  if (!cx) {
-    MOZ_ASSERT(ScriptSettingsStack::EntryGlobal() == nullptr);
-    return nullptr;
-  }
-
-  return nsJSUtils::GetDynamicScriptGlobal(cx);
+  return ScriptSettingsStack::EntryGlobal();
 }
 
-// Note: When we're ready to expose it, GetEntryGlobal will look similar to
-// GetIncumbentGlobal below.
-
 nsIGlobalObject*
 GetIncumbentGlobal()
 {
   // We need the current JSContext in order to check the JS for
   // scripted frames that may have appeared since anyone last
   // manipulated the stack. If it's null, that means that there
   // must be no entry global on the stack, and therefore no incumbent
   // global either.
@@ -166,16 +150,32 @@ GetIncumbentGlobal()
     return xpc::GetNativeForGlobal(global);
   }
 
   // Ok, nothing from the JS engine. Let's use whatever's on the
   // explicit stack.
   return ScriptSettingsStack::IncumbentGlobal();
 }
 
+nsIGlobalObject*
+GetCurrentGlobal()
+{
+  JSContext *cx = nsContentUtils::GetCurrentJSContextForThread();
+  if (!cx) {
+    return nullptr;
+  }
+
+  JSObject *global = JS::CurrentGlobalOrNull(cx);
+  if (!global) {
+    return nullptr;
+  }
+
+  return xpc::GetNativeForGlobal(global);
+}
+
 nsIPrincipal*
 GetWebIDLCallerPrincipal()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ScriptSettingsStackEntry *entry = ScriptSettingsStack::EntryPoint();
 
   // If we have an entry point that is not NoJSAPI, we know it must be an
   // AutoEntryScript.
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -58,27 +58,63 @@ private:
 
 /*
  * System-wide setup/teardown routines. Init and Destroy should be invoked
  * once each, at startup and shutdown (respectively).
  */
 void InitScriptSettings();
 void DestroyScriptSettings();
 
-// This mostly gets the entry global, but doesn't entirely match the spec in
-// certain edge cases. It's good enough for some purposes, but not others. If
-// you want to call this function, ping bholley and describe your use-case.
-nsIGlobalObject* BrokenGetEntryGlobal();
+// To implement a web-compatible browser, it is often necessary to obtain the
+// global object that is "associated" with the currently-running code. This
+// process is made more complicated by the fact that, historically, different
+// algorithms have operated with different definitions of the "associated"
+// global.
+//
+// HTML5 formalizes this into two concepts: the "incumbent global" and the
+// "entry global". The incumbent global corresponds to the global of the
+// current script being executed, whereas the entry global corresponds to the
+// global of the script where the current JS execution began.
+//
+// There is also a potentially-distinct third global that is determined by the
+// current compartment. This roughly corresponds with the notion of Realms in
+// ECMAScript.
+//
+// Suppose some event triggers an event listener in window |A|, which invokes a
+// scripted function in window |B|, which invokes the |window.location.href|
+// setter in window |C|. The entry global would be |A|, the incumbent global
+// would be |B|, and the current compartment would be that of |C|.
+//
+// In general, it's best to use to use the most-closely-associated global
+// unless the spec says to do otherwise. In 95% of the cases, the global of
+// the current compartment (GetCurrentGlobal()) is the right thing. For
+// example, WebIDL constructors (new C.XMLHttpRequest()) are initialized with
+// the global of the current compartment (i.e. |C|).
+//
+// The incumbent global is very similar, but differs in a few edge cases. For
+// example, if window |B| does |C.location.href = "..."|, the incumbent global
+// used for the navigation algorithm is B, because no script from |C| was ever run.
+//
+// The entry global is used for various things like computing base URIs, mostly
+// for historical reasons.
+//
+// Note that all of these functions return bonafide global objects. This means
+// that, for Windows, they always return the inner.
 
-// Note: We don't yet expose GetEntryGlobal, because in order for it to be
-// correct, we first need to replace a bunch of explicit cx pushing in the
-// browser with AutoEntryScript. But GetIncumbentGlobal is simpler, because it
-// can mostly be inferred from the JS stack.
+// Returns the global associated with the top-most Candidate Entry Point on
+// the Script Settings Stack. See the HTML spec. This may be null.
+nsIGlobalObject* GetEntryGlobal();
+
+// Returns the global associated with the top-most entry of the the Script
+// Settings Stack. See the HTML spec. This may be null.
 nsIGlobalObject* GetIncumbentGlobal();
 
+// Returns the global associated with the current compartment. This may be null.
+nsIGlobalObject* GetCurrentGlobal();
+
 // JS-implemented WebIDL presents an interesting situation with respect to the
 // subject principal. A regular C++-implemented API can simply examine the
 // compartment of the most-recently-executed script, and use that to infer the
 // responsible party. However, JS-implemented APIs are run with system
 // principal, and thus clobber the subject principal of the script that
 // invoked the API. So we have to do some extra work to keep track of this
 // information.
 //
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -8249,17 +8249,17 @@ nsGlobalWindow::PostMessageMoz(JSContext
       return;
     }
   }
 
   // Convert the provided origin string into a URI for comparison purposes.
   nsCOMPtr<nsIPrincipal> providedPrincipal;
 
   if (aTargetOrigin.EqualsASCII("/")) {
-    providedPrincipal = BrokenGetEntryGlobal()->PrincipalOrNull();
+    providedPrincipal = GetEntryGlobal()->PrincipalOrNull();
     if (NS_WARN_IF(!providedPrincipal))
       return;
   }
 
   // "*" indicates no specific origin is required.
   else if (!aTargetOrigin.EqualsASCII("*")) {
     nsCOMPtr<nsIURI> originURI;
     if (NS_FAILED(NS_NewURI(getter_AddRefs(originURI), aTargetOrigin))) {
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -60,16 +60,17 @@
 #include "mozilla/dom/DOMStorage.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 #ifdef USEWEAKREFS
 #include "nsIWeakReference.h"
 #endif
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 /****************************************************************
  ******************** nsWatcherWindowEntry **********************
  ****************************************************************/
 
 class nsWindowWatcher;
 
 struct nsWatcherWindowEntry {
@@ -881,17 +882,17 @@ nsWindowWatcher::OpenWindowInternal(nsID
     newDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
     NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
 
     if (subjectPrincipal) {
       loadInfo->SetOwner(subjectPrincipal);
     }
 
     nsCOMPtr<nsPIDOMWindow> referrerWindow =
-      do_QueryInterface(dom::BrokenGetEntryGlobal());
+      do_QueryInterface(GetEntryGlobal());
     if (!referrerWindow) {
       referrerWindow = do_QueryInterface(aParent);
     }
     if (referrerWindow) {
       /* use the URL from the *extant* document, if any. The usual accessor
          GetDocument will synchronously create an about:blank document if
          it has no better answer, and we only care about a real document.
          Also using GetDocument to force document creation seems to