Bug 785310 - HTML5 sandboxed iframe should not be able to perform top navigation when scripts are allowed. r=bholley, r=smaug
authorBob Owen <bobowencode@gmail.com>
Mon, 13 Jan 2014 07:58:16 +0000
changeset 163849 337e1ea7a3906df9a9aeb75a3a17ee4ffaf53a1e
parent 163848 f9374ef0fbedf9b17e10575b23aa9ba57ec46f35
child 163850 4002565ecee504a2f89be0bd4e142fb6480b5720
push id38565
push userryanvm@gmail.com
push dateThu, 16 Jan 2014 20:38:12 +0000
treeherdermozilla-inbound@46ad3c81fd55 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley, smaug
bugs785310
milestone29.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 785310 - HTML5 sandboxed iframe should not be able to perform top navigation when scripts are allowed. r=bholley, r=smaug
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsDocShellLoadInfo.cpp
docshell/base/nsDocShellLoadInfo.h
docshell/base/nsIDocShell.idl
docshell/base/nsIDocShellLoadInfo.idl
dom/base/nsLocation.cpp
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
embedding/components/windowwatcher/src/nsWindowWatcher.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1318,16 +1318,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
     nsCOMPtr<nsISupports> owner;
     bool inheritOwner = false;
     bool ownerIsExplicit = false;
     bool sendReferrer = true;
     bool isSrcdoc = false;
     nsCOMPtr<nsISHEntry> shEntry;
     nsXPIDLString target;
     nsAutoString srcdoc;
+    nsCOMPtr<nsIDocShell> sourceDocShell;
 
     uint32_t loadType = MAKE_LOAD_TYPE(LOAD_NORMAL, aLoadFlags);    
 
     NS_ENSURE_ARG(aURI);
 
     if (!StartupTimeline::HasRecord(StartupTimeline::FIRST_LOAD_URI) &&
         mItemType == typeContent && !NS_IsAboutBlank(aURI)) {
         StartupTimeline::RecordOnce(StartupTimeline::FIRST_LOAD_URI);
@@ -1347,16 +1348,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
         aLoadInfo->GetOwnerIsExplicit(&ownerIsExplicit);
         aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
         aLoadInfo->GetTarget(getter_Copies(target));
         aLoadInfo->GetPostDataStream(getter_AddRefs(postStream));
         aLoadInfo->GetHeadersStream(getter_AddRefs(headersStream));
         aLoadInfo->GetSendReferrer(&sendReferrer);
         aLoadInfo->GetIsSrcdocLoad(&isSrcdoc);
         aLoadInfo->GetSrcdocData(srcdoc);
+        aLoadInfo->GetSourceDocShell(getter_AddRefs(sourceDocShell));
     }
 
 #if defined(PR_LOGGING) && defined(DEBUG)
     if (PR_LOG_TEST(gDocShellLog, PR_LOG_DEBUG)) {
         nsAutoCString uristr;
         aURI->GetAsciiSpec(uristr);
         PR_LOG(gDocShellLog, PR_LOG_DEBUG,
                ("nsDocShell[%p]: loading %s with flags 0x%08x",
@@ -1592,16 +1594,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
                         nullptr,         // No type hint
                         NullString(),    // No forced download
                         postStream,
                         headersStream,
                         loadType,
                         nullptr,         // No SHEntry
                         aFirstParty,
                         srcdoc,
+                        sourceDocShell,
                         nullptr,         // No nsIDocShell
                         nullptr);        // No nsIRequest
 }
 
 NS_IMETHODIMP
 nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI,
                        const nsACString &aContentType,
                        const nsACString &aContentCharset,
@@ -3298,21 +3301,17 @@ nsDocShell::FindItemWithName(const char1
 
         if (foundItem && !CanAccessItem(foundItem, aOriginalRequestor)) {
             foundItem = nullptr;
         }
 
         // DoFindItemWithName only returns active items and we don't check if
         // the item is active for the special cases.
         if (foundItem) {
-            if (IsSandboxedFrom(foundItem, aOriginalRequestor)) {
-                return NS_ERROR_DOM_INVALID_ACCESS_ERR;
-            } else {
-                foundItem.swap(*_retval);
-            }
+            foundItem.swap(*_retval);
         }
         return NS_OK;
     }
 }
 
 nsresult
 nsDocShell::DoFindItemWithName(const char16_t* aName,
                                nsISupports* aRequestor,
@@ -3375,77 +3374,79 @@ nsDocShell::DoFindItemWithName(const cha
     if (mTreeOwner && mTreeOwner != reqAsTreeOwner) {
         return mTreeOwner->
             FindItemWithName(aName, this, aOriginalRequestor, _retval);
     }
 
     return NS_OK;
 }
 
-/* static */
 bool
-nsDocShell::IsSandboxedFrom(nsIDocShellTreeItem* aTargetItem,
-                            nsIDocShellTreeItem* aAccessingItem)
-{
-    // aAccessingItem cannot be sandboxed from itself.
-    if (SameCOMIdentity(aTargetItem, aAccessingItem)) {
+nsDocShell::IsSandboxedFrom(nsIDocShell* aTargetDocShell)
+{
+    // If no target then not sandboxed.
+    if (!aTargetDocShell) {
+        return false;
+    }
+
+    // We cannot be sandboxed from ourselves.
+    if (aTargetDocShell == this) {
         return false;
     }
 
     uint32_t sandboxFlags = 0;
 
-    nsCOMPtr<nsIDocument> doc = do_GetInterface(aAccessingItem);
+    nsCOMPtr<nsIDocument> doc = mContentViewer->GetDocument();
     if (doc) {
         sandboxFlags = doc->GetSandboxFlags();
     }
 
-    // If no flags, aAccessingItem is not sandboxed at all.
+    // If no flags, we are not sandboxed at all.
     if (!sandboxFlags) {
         return false;
     }
 
-    // If aTargetItem has an ancestor, it is not top level.
+    // If aTargetDocShell has an ancestor, it is not top level.
     nsCOMPtr<nsIDocShellTreeItem> ancestorOfTarget;
-    aTargetItem->GetSameTypeParent(getter_AddRefs(ancestorOfTarget));
+    aTargetDocShell->GetSameTypeParent(getter_AddRefs(ancestorOfTarget));
     if (ancestorOfTarget) {
         do {
-            // aAccessingItem is not sandboxed if it is an ancestor of target.
-            if (SameCOMIdentity(aAccessingItem, ancestorOfTarget)) {
+            // We are not sandboxed if we are an ancestor of target.
+            if (ancestorOfTarget == this) {
                 return false;
             }
             nsCOMPtr<nsIDocShellTreeItem> tempTreeItem;
             ancestorOfTarget->GetSameTypeParent(getter_AddRefs(tempTreeItem));
             tempTreeItem.swap(ancestorOfTarget);
         } while (ancestorOfTarget);
 
-        // Otherwise, aAccessingItem is sandboxed from aTargetItem.
+        // Otherwise, we are sandboxed from aTargetDocShell.
         return true;
     }
 
-    // aTargetItem is top level, is aAccessingItem the "one permitted sandboxed
-    // navigator", i.e. did aAccessingItem open aTargetItem?
-    nsCOMPtr<nsIDocShell> targetDocShell = do_QueryInterface(aTargetItem);
+    // aTargetDocShell is top level, are we the "one permitted sandboxed
+    // navigator", i.e. did we open aTargetDocShell?
     nsCOMPtr<nsIDocShell> permittedNavigator;
-    targetDocShell->
+    aTargetDocShell->
         GetOnePermittedSandboxedNavigator(getter_AddRefs(permittedNavigator));
-    if (SameCOMIdentity(aAccessingItem, permittedNavigator)) {
+    if (permittedNavigator == this) {
         return false;
     }
 
-    // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, aAccessingItem is
-    // not sandboxed from its top.
+    // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed
+    // from our top.
     if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) {
         nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
-        aAccessingItem->GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
-        if (SameCOMIdentity(aTargetItem, rootTreeItem)) {
+        GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
+        if (SameCOMIdentity(aTargetDocShell, rootTreeItem)) {
             return false;
         }
     }
 
-    // Otherwise, aAccessingItem is sandboxed from aTargetItem.
+    // Otherwise, we are sandboxed from aTargetDocShell.
     return true;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetTreeOwner(nsIDocShellTreeOwner ** aTreeOwner)
 {
     NS_ENSURE_ARG_POINTER(aTreeOwner);
 
@@ -4788,17 +4789,17 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, 
 
     nsCOMPtr<nsIURI> errorPageURI;
     rv = NS_NewURI(getter_AddRefs(errorPageURI), errorPageUrl);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return InternalLoad(errorPageURI, nullptr, nullptr,
                         INTERNAL_LOAD_FLAGS_INHERIT_OWNER, nullptr, nullptr,
                         NullString(), nullptr, nullptr, LOAD_ERROR_PAGE,
-                        nullptr, true, NullString(), nullptr,nullptr);
+                        nullptr, true, NullString(), this, nullptr, nullptr);
 }
 
 
 NS_IMETHODIMP
 nsDocShell::Reload(uint32_t aReloadFlags)
 {
     if (!IsNavigationAllowed()) {
       return NS_OK; // JS may not handle returning of an error code
@@ -4856,16 +4857,17 @@ nsDocShell::Reload(uint32_t aReloadFlags
                           NS_LossyConvertUTF16toASCII(contentTypeHint).get(),
                           NullString(),    // No forced download
                           nullptr,         // No post data
                           nullptr,         // No headers data
                           loadType,        // Load type
                           nullptr,         // No SHEntry
                           true,
                           srcdoc,          // srcdoc argument for iframe
+                          this,            // For reloads we are the source
                           nullptr,         // No nsIDocShell
                           nullptr);        // No nsIRequest
     }
 
     return rv;
 }
 
 NS_IMETHODIMP
@@ -8631,41 +8633,42 @@ void CopyFavicon(nsIURI *aOldURI, nsIURI
 class InternalLoadEvent : public nsRunnable
 {
 public:
     InternalLoadEvent(nsDocShell* aDocShell, nsIURI * aURI, nsIURI * aReferrer,
                       nsISupports * aOwner, uint32_t aFlags,
                       const char* aTypeHint, nsIInputStream * aPostData,
                       nsIInputStream * aHeadersData, uint32_t aLoadType,
                       nsISHEntry * aSHEntry, bool aFirstParty,
-                      const nsAString &aSrcdoc) :
+                      const nsAString &aSrcdoc, nsIDocShell* aSourceDocShell) :
         mSrcdoc(aSrcdoc),
         mDocShell(aDocShell),
         mURI(aURI),
         mReferrer(aReferrer),
         mOwner(aOwner),
         mPostData(aPostData),
         mHeadersData(aHeadersData),
         mSHEntry(aSHEntry),
         mFlags(aFlags),
         mLoadType(aLoadType),
-        mFirstParty(aFirstParty)
+        mFirstParty(aFirstParty),
+        mSourceDocShell(aSourceDocShell)
     {
         // Make sure to keep null things null as needed
         if (aTypeHint) {
             mTypeHint = aTypeHint;
         }
     }
 
     NS_IMETHOD Run() {
         return mDocShell->InternalLoad(mURI, mReferrer, mOwner, mFlags,
                                        nullptr, mTypeHint.get(),
                                        NullString(), mPostData, mHeadersData,
                                        mLoadType, mSHEntry, mFirstParty,
-                                       mSrcdoc, nullptr, nullptr);
+                                       mSrcdoc, mSourceDocShell, nullptr, nullptr);
     }
 
 private:
 
     // Use IDL strings so .get() returns null by default
     nsXPIDLString mWindowTarget;
     nsXPIDLCString mTypeHint;
     nsString mSrcdoc;
@@ -8675,16 +8678,17 @@ private:
     nsCOMPtr<nsIURI> mReferrer;
     nsCOMPtr<nsISupports> mOwner;
     nsCOMPtr<nsIInputStream> mPostData;
     nsCOMPtr<nsIInputStream> mHeadersData;
     nsCOMPtr<nsISHEntry> mSHEntry;
     uint32_t mFlags;
     uint32_t mLoadType;
     bool mFirstParty;
+    nsCOMPtr<nsIDocShell> mSourceDocShell;
 };
 
 /**
  * Returns true if we started an asynchronous load (i.e., from the network), but
  * the document we're loading there hasn't yet become this docshell's active
  * document.
  *
  * When JustStartedNetworkLoad is true, you should be careful about modifying
@@ -8708,16 +8712,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
                          const char* aTypeHint,
                          const nsAString& aFileName,
                          nsIInputStream * aPostData,
                          nsIInputStream * aHeadersData,
                          uint32_t aLoadType,
                          nsISHEntry * aSHEntry,
                          bool aFirstParty,
                          const nsAString &aSrcdoc,
+                         nsIDocShell* aSourceDocShell,
                          nsIDocShell** aDocShell,
                          nsIRequest** aRequest)
 {
     nsresult rv = NS_OK;
     mOriginalUriString.Truncate();
 
 #ifdef PR_LOGGING
     if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
@@ -8771,20 +8776,19 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     int16_t shouldLoad = nsIContentPolicy::ACCEPT;
     uint32_t contentType;
     bool isNewDocShell = false;
     bool isTargetTopLevelDocShell = false;
     nsCOMPtr<nsIDocShell> targetDocShell;
     if (aWindowTarget && *aWindowTarget) {
         // Locate the target DocShell.
         nsCOMPtr<nsIDocShellTreeItem> targetItem;
-        if (FindItemWithName(aWindowTarget, nullptr, this,
-               getter_AddRefs(targetItem)) == NS_ERROR_DOM_INVALID_ACCESS_ERR) {
-            return NS_ERROR_DOM_INVALID_ACCESS_ERR;
-        }
+        rv = FindItemWithName(aWindowTarget, nullptr, this,
+                              getter_AddRefs(targetItem));
+        NS_ENSURE_SUCCESS(rv, rv);
 
         targetDocShell = do_QueryInterface(targetItem);
         // If the targetDocShell doesn't exist, then this is a new docShell
         // and we should consider this a TYPE_DOCUMENT load
         isNewDocShell = !targetDocShell;
 
         // If the targetDocShell and the rootDocShell are the same, then the
         // targetDocShell is the top level document and hence we should
@@ -8963,16 +8967,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
                                               aTypeHint,
                                               NullString(),    // No forced download
                                               aPostData,
                                               aHeadersData,
                                               aLoadType,
                                               aSHEntry,
                                               aFirstParty,
                                               aSrcdoc,
+                                              aSourceDocShell,
                                               aDocShell,
                                               aRequest);
             if (rv == NS_ERROR_NO_CONTENT) {
                 // XXXbz except we never reach this code!
                 if (isNewWindow) {
                     //
                     // At this point, a new window has been created, but the
                     // URI did not have any data associated with it...
@@ -9017,51 +9022,58 @@ nsDocShell::InternalLoad(nsIURI * aURI,
 
     NS_ENSURE_STATE(!HasUnloadedParent());
 
     rv = CheckLoadingPermissions();
     if (NS_FAILED(rv)) {
         return rv;
     }
 
-    // If this docshell is owned by a frameloader, make sure to cancel
-    // possible frameloader initialization before loading a new page.
-    nsCOMPtr<nsIDocShellTreeItem> parent;
-    GetParent(getter_AddRefs(parent));
-    if (parent) {
-      nsCOMPtr<nsIDocument> doc = do_GetInterface(parent);
-      if (doc) {
-        doc->TryCancelFrameLoaderInitialization(this);
-      }
-    }
-
     if (mFiredUnloadEvent) {
         if (IsOKToLoadURI(aURI)) {
             NS_PRECONDITION(!aWindowTarget || !*aWindowTarget,
                             "Shouldn't have a window target here!");
 
             // If this is a replace load, make whatever load triggered
             // the unload event also a replace load, so we don't
             // create extra history entries.
             if (LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
                 mLoadType = LOAD_NORMAL_REPLACE;
             }
             
             // Do this asynchronously
             nsCOMPtr<nsIRunnable> ev =
                 new InternalLoadEvent(this, aURI, aReferrer, aOwner, aFlags,
                                       aTypeHint, aPostData, aHeadersData,
-                                      aLoadType, aSHEntry, aFirstParty, aSrcdoc);
+                                      aLoadType, aSHEntry, aFirstParty, aSrcdoc,
+                                      aSourceDocShell);
             return NS_DispatchToCurrentThread(ev);
         }
 
         // Just ignore this load attempt
         return NS_OK;
     }
 
+    // If a source docshell has been passed, check to see if we are sandboxed
+    // from it as the result of an iframe or CSP sandbox.
+    if (aSourceDocShell && aSourceDocShell->IsSandboxedFrom(this)) {
+        return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+    }
+
+    // If this docshell is owned by a frameloader, make sure to cancel
+    // possible frameloader initialization before loading a new page.
+    nsCOMPtr<nsIDocShellTreeItem> parent;
+    GetParent(getter_AddRefs(parent));
+    if (parent) {
+        nsCOMPtr<nsIDocument> doc = do_GetInterface(parent);
+        if (doc) {
+            doc->TryCancelFrameLoaderInitialization(this);
+        }
+    }
+
     // Before going any further vet loads initiated by external programs.
     if (aLoadType == LOAD_NORMAL_EXTERNAL) {
         // Disallow external chrome: loads targetted at content windows
         bool isChrome = false;
         if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) && isChrome) {
             NS_WARNING("blocked external chrome: url -- use '-chrome' option");
             return NS_ERROR_FAILURE;
         }
@@ -11062,29 +11074,34 @@ nsDocShell::LoadHistoryEntry(nsISHEntry 
     if (isSrcdoc) {
         aEntry->GetSrcdocData(srcdoc);
         flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
     }
     else {
         srcdoc = NullString();
     }
 
+    // Passing nullptr as aSourceDocShell gives the same behaviour as before
+    // aSourceDocShell was introduced. According to spec we should be passing
+    // the source browsing context that was used when the history entry was
+    // first created. bug 947716 has been created to address this issue.
     rv = InternalLoad(uri,
                       referrerURI,
                       owner,
                       flags,
                       nullptr,            // No window target
                       contentType.get(),  // Type hint
                       NullString(),       // No forced file download
                       postData,           // Post data stream
                       nullptr,            // No headers stream
                       aLoadType,          // Load type
                       aEntry,             // SHEntry
                       true,
                       srcdoc,
+                      nullptr,            // Source docshell, see comment above
                       nullptr,            // No nsIDocShell
                       nullptr);           // No nsIRequest
     return rv;
 }
 
 NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(bool* aShould)
 {
     *aShould = false;
@@ -12489,16 +12506,17 @@ nsDocShell::OnLinkClickSync(nsIContent *
                              NS_LossyConvertUTF16toASCII(typeHint).get(),
                              aFileName,                 // Download as file
                              aPostDataStream,           // Post data stream
                              aHeadersDataStream,        // Headers stream
                              LOAD_LINK,                 // Load type
                              nullptr,                   // No SHEntry
                              true,                      // first party site
                              NullString(),              // No srcdoc
+                             this,                      // We are the source
                              aDocShell,                 // DocShell out-param
                              aRequest);                 // Request out-param
   if (NS_SUCCEEDED(rv)) {
     DispatchPings(aContent, aURI, referer);
   }
   return rv;
 }
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -884,20 +884,16 @@ private:
 
     // Separate function to do the actual name (i.e. not _top, _self etc.)
     // searching for FindItemWithName.
     nsresult DoFindItemWithName(const char16_t* aName,
                                 nsISupports* aRequestor,
                                 nsIDocShellTreeItem* aOriginalRequestor,
                                 nsIDocShellTreeItem** _retval);
 
-    // Check whether accessing item is sandboxed from the target item.
-    static bool IsSandboxedFrom(nsIDocShellTreeItem* aTargetItem,
-                                nsIDocShellTreeItem* aAccessingItem);
-
 #ifdef DEBUG
     // We're counting the number of |nsDocShells| to help find leaks
     static unsigned long gNumberOfDocShells;
 #endif /* DEBUG */
 
 public:
     class InterfaceRequestorProxy : public nsIInterfaceRequestor {
     public:
--- a/docshell/base/nsDocShellLoadInfo.cpp
+++ b/docshell/base/nsDocShellLoadInfo.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Local Includes
 #include "nsDocShellLoadInfo.h"
 #include "nsISHEntry.h"
 #include "nsIInputStream.h"
 #include "nsIURI.h"
+#include "nsIDocShell.h"
 
 //*****************************************************************************
 //***    nsDocShellLoadInfo: Object Management
 //*****************************************************************************
 
 nsDocShellLoadInfo::nsDocShellLoadInfo()
   : mInheritOwner(false),
     mOwnerIsExplicit(false),
@@ -205,16 +206,29 @@ NS_IMETHODIMP nsDocShellLoadInfo::GetSrc
 
 NS_IMETHODIMP nsDocShellLoadInfo::SetSrcdocData(const nsAString &aSrcdocData)
 {
    mSrcdocData = aSrcdocData;
    mIsSrcdocLoad = true;
    return NS_OK;
 }
 
+NS_IMETHODIMP nsDocShellLoadInfo::GetSourceDocShell(nsIDocShell** aSourceDocShell)
+{
+   MOZ_ASSERT(aSourceDocShell);
+   nsCOMPtr<nsIDocShell> result = mSourceDocShell;
+   result.forget(aSourceDocShell);
+   return NS_OK;
+}
+
+NS_IMETHODIMP nsDocShellLoadInfo::SetSourceDocShell(nsIDocShell* aSourceDocShell)
+{
+   mSourceDocShell = aSourceDocShell;
+   return NS_OK;
+}
 
 //*****************************************************************************
 // nsDocShellLoadInfo: Helpers
 //*****************************************************************************   
 
 //*****************************************************************************
 // nsDocShellLoadInfo: Accessors
 //*****************************************************************************   
--- a/docshell/base/nsDocShellLoadInfo.h
+++ b/docshell/base/nsDocShellLoadInfo.h
@@ -13,16 +13,17 @@
 #include "nsString.h"
 
 // Interfaces Needed
 #include "nsIDocShellLoadInfo.h"
 
 class nsIInputStream;
 class nsISHEntry;
 class nsIURI;
+class nsIDocShell;
 
 class nsDocShellLoadInfo : public nsIDocShellLoadInfo
 {
 public:
   nsDocShellLoadInfo();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOCSHELLLOADINFO
@@ -38,11 +39,12 @@ protected:
   bool                             mSendReferrer;
   nsDocShellInfoLoadType           mLoadType;
   nsCOMPtr<nsISHEntry>             mSHEntry;
   nsString                         mTarget;
   nsCOMPtr<nsIInputStream>         mPostDataStream;
   nsCOMPtr<nsIInputStream>         mHeadersStream;
   bool                             mIsSrcdocLoad;
   nsString                         mSrcdocData;
+  nsCOMPtr<nsIDocShell>            mSourceDocShell;
 };
 
 #endif /* nsDocShellLoadInfo_h__ */
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -39,17 +39,17 @@ interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(e3ea830d-2614-4aeb-9ec3-b8f744b03b80)]
+[scriptable, builtinclass, uuid(24732b66-e37e-444e-96a2-8b8d1fa292fc)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -133,30 +133,32 @@ interface nsIDocShell : nsIDocShellTreeI
    * @param aPostDataStream - Post data stream (if POSTing)
    * @param aHeadersStream  - Stream containing "extra" request headers...
    * @param aLoadFlags      - Flags to modify load behaviour. Flags are defined
    *                          in nsIWebNavigation.
    * @param aSHEntry        - Active Session History entry (if loading from SH)
    * @param aSrcdoc           When INTERNAL_LOAD_FLAGS_IS_SRCDOC is set, the
    *                          contents of this parameter will be loaded instead
    *                          of aURI.
+   * @param aSourceDocShell - The source browsing context for the navigation.
    */
   [noscript]void internalLoad(in nsIURI aURI,
                               in nsIURI aReferrer,
                               in nsISupports aOwner,
                               in uint32_t aFlags,
                               in wstring aWindowTarget,
                               in string aTypeHint,
                               in AString aFileName,
                               in nsIInputStream aPostDataStream,
                               in nsIInputStream aHeadersStream,
                               in unsigned long aLoadFlags,
                               in nsISHEntry aSHEntry,
                               in boolean firstParty,
                               in AString aSrcdoc,
+                              in nsIDocShell aSourceDocShell,
                               out nsIDocShell aDocShell,
                               out nsIRequest aRequest);
 
   /**
    * Do either a history.pushState() or history.replaceState() operation,
    * depending on the value of aReplace.
    */
   [implicit_jscontext]
@@ -781,16 +783,22 @@ interface nsIDocShell : nsIDocShellTreeI
   /**
    * When a new browsing context is opened by a sandboxed document, it needs to
    * keep track of the browsing context that opened it, so that it can be
    * navigated by it.  This is the "one permitted sandboxed navigator".
    */
   attribute nsIDocShell onePermittedSandboxedNavigator;
 
   /**
+   * Returns true if we are sandboxed from aTargetDocShell.
+   * aTargetDocShell - the browsing context we are attempting to navigate.
+   */
+  [noscript,notxpcom,nostdcall] bool isSandboxedFrom(in nsIDocShell aTargetDocShell);
+
+  /**
    * This member variable determines whether a document has Mixed Active Content that
    * was initially blocked from loading, but the user has choosen to override the
    * block and allow the content to load. mMixedContentChannel is set to the document's
    * channel when the user allows mixed content. The nsMixedContentBlocker content policy
    * checks if the document's root channel matches the mMixedContentChannel.  If it matches,
    * then Mixed Content is loaded.  If it does match, mixed content is blocked.
    *
    * A match implies that there is definitely mixed active content on a page that was
--- a/docshell/base/nsIDocShellLoadInfo.idl
+++ b/docshell/base/nsIDocShellLoadInfo.idl
@@ -9,20 +9,21 @@
 /**
  * The nsIDocShellLoadInfo interface defines an interface for specifying
  * setup information used in a nsIDocShell::loadURI call.
  */
  
 interface nsIURI;
 interface nsIInputStream;
 interface nsISHEntry;
+interface nsIDocShell;
 
 typedef long nsDocShellInfoLoadType;
 
-[scriptable, uuid(5df91211-37db-47e9-8f83-2d5b0cb2eb9e)]
+[scriptable, uuid(c6b15de3-2f4f-4e80-bb20-95f43b5598c7)]
 interface nsIDocShellLoadInfo : nsISupports
 {
     /** This is the referrer for the load. */
     attribute nsIURI referrer;
 
     /** The owner of the load, that is, the entity responsible for 
      *  causing the load to occur. This should be a nsIPrincipal typically.
      */
@@ -90,9 +91,11 @@ interface nsIDocShellLoadInfo : nsISuppo
     readonly attribute boolean isSrcdocLoad;
 
     /** When set, the load will be interpreted as a srcdoc load, where contents
      * of this string will be loaded instead of the URI.  Setting srcdocData
      * sets isSrcdocLoad to true
      */
     attribute AString srcdocData;
 
+    /** When set, this is the Source Browsing Context for the navigation. */
+    attribute nsIDocShell sourceDocShell;
 };
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -232,16 +232,23 @@ nsLocation::SetURI(nsIURI* aURI, bool aR
       return NS_ERROR_FAILURE;
 
     if (aReplace) {
       loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContentAndReplace);
     } else {
       loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContent);
     }
 
+    // Get the incumbent script's browsing context to set as source.
+    nsCOMPtr<nsPIDOMWindow> sourceWindow =
+      do_QueryInterface(mozilla::dom::GetIncumbentGlobal());
+    if (sourceWindow) {
+      loadInfo->SetSourceDocShell(sourceWindow->GetDocShell());
+    }
+
     return docShell->LoadURI(aURI, loadInfo,
                              nsIWebNavigation::LOAD_FLAGS_NONE, true);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.cpp
@@ -469,22 +469,33 @@ nsWindowWatcher::OpenWindowInternal(nsID
   featuresSpecified = false;
   if (aFeatures) {
     features.Assign(aFeatures);
     featuresSpecified = true;
     features.StripWhitespace();
   }
 
   // try to find an extant window with the given name
-  nsCOMPtr<nsIDOMWindow> foundWindow;
-  if (SafeGetWindowByName(name, aParent, getter_AddRefs(foundWindow)) ==
-      NS_ERROR_DOM_INVALID_ACCESS_ERR) {
-    return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+  nsCOMPtr<nsIDOMWindow> foundWindow = SafeGetWindowByName(name, aParent);
+  GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
+
+  // Do sandbox checks here, instead of waiting until nsIDocShell::LoadURI.
+  // The state of the window can change before this call and if we are blocked
+  // because of sandboxing, we wouldn't want that to happen.
+  nsCOMPtr<nsPIDOMWindow> parentWindow = do_QueryInterface(aParent);
+  nsCOMPtr<nsIDocShell> parentDocShell;
+  if (parentWindow) {
+    parentDocShell = parentWindow->GetDocShell();
+    if (parentDocShell) {
+      nsCOMPtr<nsIDocShell> foundDocShell = do_QueryInterface(newDocShellItem);
+      if (parentDocShell->IsSandboxedFrom(foundDocShell)) {
+        return NS_ERROR_DOM_INVALID_ACCESS_ERR;
+      }
+    }
   }
-  GetWindowTreeItem(foundWindow, getter_AddRefs(newDocShellItem));
 
   // no extant window? make a new one.
 
   // If no parent, consider it chrome.
   bool hasChromeParent = true;
   if (aParent) {
     // Check if the parent document has chrome privileges.
     nsCOMPtr<nsIDOMDocument> domdoc;
@@ -666,19 +677,19 @@ nsWindowWatcher::OpenWindowInternal(nsID
          downstream consumer can treat the indicator to mean simply
          that the new window is subject to popup control. */
       nsCOMPtr<nsIWindowCreator2> windowCreator2(do_QueryInterface(mWindowCreator));
       if (windowCreator2) {
         uint32_t contextFlags = 0;
         bool popupConditions = false;
 
         // is the parent under popup conditions?
-        nsCOMPtr<nsPIDOMWindow> piWindow(do_QueryInterface(aParent));
-        if (piWindow)
-          popupConditions = piWindow->IsLoadingOrRunningTimeout();
+        if (parentWindow) {
+          popupConditions = parentWindow->IsLoadingOrRunningTimeout();
+        }
 
         // chrome is always allowed, so clear the flag if the opener is chrome
         if (popupConditions) {
           popupConditions = !isCallerChrome;
         }
 
         if (popupConditions)
           contextFlags |= nsIWindowCreator2::PARENT_IS_LOADING_OR_RUNNING_TIMEOUT;
@@ -717,19 +728,19 @@ nsWindowWatcher::OpenWindowInternal(nsID
 
   nsCOMPtr<nsIDocShell> newDocShell(do_QueryInterface(newDocShellItem));
   NS_ENSURE_TRUE(newDocShell, NS_ERROR_UNEXPECTED);
 
   // Set up sandboxing attributes if the window is new.
   // The flags can only be non-zero for new windows.
   if (activeDocsSandboxFlags != 0) {
     newDocShell->SetSandboxFlags(activeDocsSandboxFlags);
-    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aParent);
-    if (window) {
-      newDocShell->SetOnePermittedSandboxedNavigator(window->GetDocShell());
+    if (parentWindow) {
+      newDocShell->
+        SetOnePermittedSandboxedNavigator(parentWindow->GetDocShell());
     }
   }
   
   rv = ReadyOpenedDocShellItem(newDocShellItem, aParent, windowIsNew, _retval);
   if (NS_FAILED(rv))
     return rv;
 
   /* disable persistence of size/position in popups (determined by
@@ -903,21 +914,16 @@ nsWindowWatcher::OpenWindowInternal(nsID
                          loadInfo,
                          windowIsNew
                            ? static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_FIRST_LOAD)
                            : static_cast<uint32_t>(nsIWebNavigation::LOAD_FLAGS_NONE),
                          true);
   }
 
   // Copy the current session storage for the current domain.
-  nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aParent);
-  nsIDocShell* parentDocShell = nullptr;
-  if (piWindow)
-    parentDocShell = piWindow->GetDocShell();
-
   if (subjectPrincipal && parentDocShell) {
     nsCOMPtr<nsIDOMStorageManager> parentStorageManager = do_QueryInterface(parentDocShell);
     nsCOMPtr<nsIDOMStorageManager> newStorageManager = do_QueryInterface(newDocShell);
 
     if (parentStorageManager && newStorageManager) {
       nsCOMPtr<nsIDOMStorage> storage;
       parentStorageManager->GetStorage(subjectPrincipal, isPrivateBrowsingWindow, getter_AddRefs(storage));
       if (storage)
@@ -1273,38 +1279,37 @@ nsWindowWatcher::GetChromeForWindow(nsID
 NS_IMETHODIMP
 nsWindowWatcher::GetWindowByName(const char16_t *aTargetName, 
                                  nsIDOMWindow *aCurrentWindow,
                                  nsIDOMWindow **aResult)
 {
   if (!aResult) {
     return NS_ERROR_INVALID_ARG;
   }
-  nsresult rv;
 
   *aResult = nullptr;
 
   nsCOMPtr<nsIDocShellTreeItem> treeItem;
 
   nsCOMPtr<nsIDocShellTreeItem> startItem;
   GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
   if (startItem) {
     // Note: original requestor is null here, per idl comments
-    rv = startItem->FindItemWithName(aTargetName, nullptr, nullptr,
+    startItem->FindItemWithName(aTargetName, nullptr, nullptr,
                                 getter_AddRefs(treeItem));
   }
   else {
     // Note: original requestor is null here, per idl comments
-    rv = FindItemWithName(aTargetName, nullptr, nullptr, getter_AddRefs(treeItem));
+    FindItemWithName(aTargetName, nullptr, nullptr, getter_AddRefs(treeItem));
   }
 
   nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(treeItem);
   domWindow.swap(*aResult);
 
-  return rv;
+  return NS_OK;
 }
 
 bool
 nsWindowWatcher::AddEnumerator(nsWatcherWindowEnumerator* inEnumerator)
 {
   // (requires a lock; assumes it's called by someone holding the lock)
   return mEnumeratorList.AppendElement(inEnumerator) != nullptr;
 }
@@ -1700,44 +1705,39 @@ nsWindowWatcher::GetCallerTreeItem(nsIDo
 
   if (!callerItem) {
     callerItem = aParentItem;
   }
 
   return callerItem.forget();
 }
 
-nsresult
+already_AddRefed<nsIDOMWindow>
 nsWindowWatcher::SafeGetWindowByName(const nsAString& aName,
-                                     nsIDOMWindow* aCurrentWindow,
-                                     nsIDOMWindow** aResult)
+                                     nsIDOMWindow* aCurrentWindow)
 {
-  *aResult = nullptr;
-  nsresult rv;
-  
   nsCOMPtr<nsIDocShellTreeItem> startItem;
   GetWindowTreeItem(aCurrentWindow, getter_AddRefs(startItem));
 
   nsCOMPtr<nsIDocShellTreeItem> callerItem = GetCallerTreeItem(startItem);
 
   const nsAFlatString& flatName = PromiseFlatString(aName);
 
   nsCOMPtr<nsIDocShellTreeItem> foundItem;
   if (startItem) {
-    rv = startItem->FindItemWithName(flatName.get(), nullptr, callerItem,
+    startItem->FindItemWithName(flatName.get(), nullptr, callerItem,
                                 getter_AddRefs(foundItem));
   }
   else {
-    rv = FindItemWithName(flatName.get(), nullptr, callerItem,
+    FindItemWithName(flatName.get(), nullptr, callerItem,
                      getter_AddRefs(foundItem));
   }
 
   nsCOMPtr<nsIDOMWindow> foundWin = do_GetInterface(foundItem);
-  foundWin.swap(*aResult);
-  return rv;
+  return foundWin.forget();
 }
 
 /* Fetch the nsIDOMWindow corresponding to the given nsIDocShellTreeItem.
    This forces the creation of a script context, if one has not already
    been created. Note it also sets the window's opener to the parent,
    if applicable -- because it's just convenient, that's all. null aParent
    is acceptable. */
 nsresult
--- a/embedding/components/windowwatcher/src/nsWindowWatcher.h
+++ b/embedding/components/windowwatcher/src/nsWindowWatcher.h
@@ -59,19 +59,18 @@ protected:
 
   // Get the caller tree item.  Look on the JS stack, then fall back
   // to the parent if there's nothing there.
   already_AddRefed<nsIDocShellTreeItem>
     GetCallerTreeItem(nsIDocShellTreeItem* aParentItem);
   
   // Unlike GetWindowByName this will look for a caller on the JS
   // stack, and then fall back on aCurrentWindow if it can't find one.
-  nsresult SafeGetWindowByName(const nsAString& aName,
-                               nsIDOMWindow* aCurrentWindow,
-                               nsIDOMWindow** aResult);
+  already_AddRefed<nsIDOMWindow>
+    SafeGetWindowByName(const nsAString& aName, nsIDOMWindow* aCurrentWindow);
 
   // Just like OpenWindowJS, but knows whether it got called via OpenWindowJS
   // (which means called from script) or called via OpenWindow.
   nsresult OpenWindowInternal(nsIDOMWindow *aParent,
                               const char *aUrl,
                               const char *aName,
                               const char *aFeatures,
                               bool aCalledFromJS,