Bug 964239 - Docshell and session history changes to add baseURI information to srcdoc loads. r=bz
authorJames Kitchener <jkitch.bug@gmail.com>
Thu, 06 Feb 2014 09:46:29 -0500
changeset 167606 bdf66d134585e55e8220bfd465395bab3b2b943a
parent 167605 710e2f5dca1c99f8b370ffa556cb2d8738485b6a
child 167607 c0e3b8b4e441a02d43b9719a3d5e8a71da1424f0
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersbz
bugs964239
milestone30.0a1
Bug 964239 - Docshell and session history changes to add baseURI information to srcdoc loads. r=bz
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsDocShellLoadInfo.cpp
docshell/base/nsDocShellLoadInfo.h
docshell/base/nsIDocShell.idl
docshell/base/nsIDocShellLoadInfo.idl
docshell/shistory/public/nsISHEntry.idl
docshell/shistory/src/nsSHEntry.cpp
docshell/shistory/src/nsSHEntry.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1332,16 +1332,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
     bool inheritOwner = false;
     bool ownerIsExplicit = false;
     bool sendReferrer = true;
     bool isSrcdoc = false;
     nsCOMPtr<nsISHEntry> shEntry;
     nsXPIDLString target;
     nsAutoString srcdoc;
     nsCOMPtr<nsIDocShell> sourceDocShell;
+    nsCOMPtr<nsIURI> baseURI;
 
     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);
@@ -1362,16 +1363,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
         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));
+        aLoadInfo->GetBaseURI(getter_AddRefs(baseURI));
     }
 
 #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",
@@ -1608,16 +1610,17 @@ nsDocShell::LoadURI(nsIURI * aURI,
                         NullString(),    // No forced download
                         postStream,
                         headersStream,
                         loadType,
                         nullptr,         // No SHEntry
                         aFirstParty,
                         srcdoc,
                         sourceDocShell,
+                        baseURI,
                         nullptr,         // No nsIDocShell
                         nullptr);        // No nsIRequest
 }
 
 NS_IMETHODIMP
 nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI * aURI,
                        const nsACString &aContentType,
                        const nsACString &aContentCharset,
@@ -4804,17 +4807,18 @@ 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(), this, nullptr, nullptr);
+                        nullptr, true, NullString(), this, nullptr, nullptr,
+                        nullptr);
 }
 
 
 NS_IMETHODIMP
 nsDocShell::Reload(uint32_t aReloadFlags)
 {
     if (!IsNavigationAllowed()) {
       return NS_OK; // JS may not handle returning of an error code
@@ -4850,39 +4854,42 @@ nsDocShell::Reload(uint32_t aReloadFlags
     else {
         nsCOMPtr<nsIDocument> doc(do_GetInterface(GetAsSupports(this)));
 
         // Do not inherit owner from document
         uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
         nsAutoString srcdoc;
         nsIPrincipal* principal = nullptr;
         nsAutoString contentTypeHint;
+        nsCOMPtr<nsIURI> baseURI;
         if (doc) {
             principal = doc->NodePrincipal();
             doc->GetContentType(contentTypeHint);
 
             if (doc->IsSrcdocDocument()) {
                 doc->GetSrcdocData(srcdoc);
                 flags |= INTERNAL_LOAD_FLAGS_IS_SRCDOC;
+                baseURI = doc->GetBaseURI();
             }
         }
         rv = InternalLoad(mCurrentURI,
                           mReferrerURI,
                           principal,
                           flags,
                           nullptr,         // No window target
                           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
+                          baseURI,
                           nullptr,         // No nsIDocShell
                           nullptr);        // No nsIRequest
     }
 
     return rv;
 }
 
 NS_IMETHODIMP
@@ -8678,42 +8685,45 @@ 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, nsIDocShell* aSourceDocShell) :
+                      const nsAString &aSrcdoc, nsIDocShell* aSourceDocShell,
+                      nsIURI * aBaseURI) :
         mSrcdoc(aSrcdoc),
         mDocShell(aDocShell),
         mURI(aURI),
         mReferrer(aReferrer),
         mOwner(aOwner),
         mPostData(aPostData),
         mHeadersData(aHeadersData),
         mSHEntry(aSHEntry),
         mFlags(aFlags),
         mLoadType(aLoadType),
         mFirstParty(aFirstParty),
-        mSourceDocShell(aSourceDocShell)
+        mSourceDocShell(aSourceDocShell),
+        mBaseURI(aBaseURI)
     {
         // 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, mSourceDocShell, nullptr, nullptr);
+                                       mSrcdoc, mSourceDocShell, mBaseURI,
+                                       nullptr, nullptr);
     }
 
 private:
 
     // Use IDL strings so .get() returns null by default
     nsXPIDLString mWindowTarget;
     nsXPIDLCString mTypeHint;
     nsString mSrcdoc;
@@ -8724,16 +8734,17 @@ private:
     nsCOMPtr<nsISupports> mOwner;
     nsCOMPtr<nsIInputStream> mPostData;
     nsCOMPtr<nsIInputStream> mHeadersData;
     nsCOMPtr<nsISHEntry> mSHEntry;
     uint32_t mFlags;
     uint32_t mLoadType;
     bool mFirstParty;
     nsCOMPtr<nsIDocShell> mSourceDocShell;
+    nsCOMPtr<nsIURI> mBaseURI;
 };
 
 /**
  * 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
@@ -8758,16 +8769,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
                          const nsAString& aFileName,
                          nsIInputStream * aPostData,
                          nsIInputStream * aHeadersData,
                          uint32_t aLoadType,
                          nsISHEntry * aSHEntry,
                          bool aFirstParty,
                          const nsAString &aSrcdoc,
                          nsIDocShell* aSourceDocShell,
+                         nsIURI* aBaseURI,
                          nsIDocShell** aDocShell,
                          nsIRequest** aRequest)
 {
     nsresult rv = NS_OK;
     mOriginalUriString.Truncate();
 
 #ifdef PR_LOGGING
     if (gDocShellLeakLog && PR_LOG_TEST(gDocShellLeakLog, PR_LOG_DEBUG)) {
@@ -9013,16 +9025,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
                                               NullString(),    // No forced download
                                               aPostData,
                                               aHeadersData,
                                               aLoadType,
                                               aSHEntry,
                                               aFirstParty,
                                               aSrcdoc,
                                               aSourceDocShell,
+                                              aBaseURI,
                                               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...
@@ -9084,17 +9097,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
                 mLoadType = LOAD_NORMAL_REPLACE;
             }
             
             // Do this asynchronously
             nsCOMPtr<nsIRunnable> ev =
                 new InternalLoadEvent(this, aURI, aReferrer, aOwner, aFlags,
                                       aTypeHint, aPostData, aHeadersData,
                                       aLoadType, aSHEntry, aFirstParty, aSrcdoc,
-                                      aSourceDocShell);
+                                      aSourceDocShell, aBaseURI);
             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
@@ -9533,17 +9546,17 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     nsCOMPtr<nsIRequest> req;
     rv = DoURILoad(aURI, aReferrer,
                    !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
                    owner, aTypeHint, aFileName, aPostData, aHeadersData,
                    aFirstParty, aDocShell, getter_AddRefs(req),
                    (aFlags & INTERNAL_LOAD_FLAGS_FIRST_LOAD) != 0,
                    (aFlags & INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER) != 0,
                    (aFlags & INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES) != 0,
-                   srcdoc);
+                   srcdoc, aBaseURI);
     if (req && aRequest)
         NS_ADDREF(*aRequest = req);
 
     if (NS_FAILED(rv)) {
         nsCOMPtr<nsIChannel> chan(do_QueryInterface(req));
         DisplayLoadError(rv, aURI, nullptr, chan);
     }
 
@@ -9612,17 +9625,18 @@ nsDocShell::DoURILoad(nsIURI * aURI,
                       nsIInputStream * aPostData,
                       nsIInputStream * aHeadersData,
                       bool aFirstParty,
                       nsIDocShell ** aDocShell,
                       nsIRequest ** aRequest,
                       bool aIsNewWindowTarget,
                       bool aBypassClassifier,
                       bool aForceAllowCookies,
-                      const nsAString &aSrcdoc)
+                      const nsAString &aSrcdoc,
+                      nsIURI * aBaseURI)
 {
 #ifdef MOZ_VISUAL_EVENT_TRACER
     nsAutoCString urlSpec;
     aURI->GetAsciiSpec(urlSpec);
     MOZ_EVENT_TRACER_NAME_OBJECT(this, urlSpec.BeginReading());
     MOZ_EVENT_TRACER_EXEC(this, "docshell::pageload");
 #endif
 
@@ -9697,25 +9711,29 @@ nsDocShell::DoURILoad(nsIURI * aURI,
         NS_ENSURE_SUCCESS(rv,rv);
         bool isViewSource;
         aURI->SchemeIs("view-source",&isViewSource);
 
         if (isViewSource) {
             nsViewSourceHandler *vsh = nsViewSourceHandler::GetInstance();
             NS_ENSURE_TRUE(vsh,NS_ERROR_FAILURE);
 
-            rv = vsh->NewSrcdocChannel(aURI, aSrcdoc,getter_AddRefs(channel));
+            rv = vsh->NewSrcdocChannel(aURI, aSrcdoc, aBaseURI,
+                                       getter_AddRefs(channel));
             NS_ENSURE_SUCCESS(rv, rv);
         }
         else {
             rv = NS_NewInputStreamChannel(getter_AddRefs(channel),aURI,
                                           aSrcdoc,
                                           NS_LITERAL_CSTRING("text/html"),
                                           true);
             NS_ENSURE_SUCCESS(rv, rv);
+            nsCOMPtr<nsIInputStreamChannel> isc = do_QueryInterface(channel);
+            MOZ_ASSERT(isc);
+            isc->SetBaseURI(aBaseURI);
         }
     }
 
     nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
         do_QueryInterface(channel);
     if (appCacheChannel) {
         // Any document load should not inherit application cache.
         appCacheChannel->SetInheritApplicationCache(false);
@@ -10964,16 +10982,19 @@ nsDocShell::AddToSessionHistory(nsIURI *
     nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(aChannel);
     if (inStrmChan) {
         bool isSrcdocChannel;
         inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
         if (isSrcdocChannel) {
             nsAutoString srcdoc;
             inStrmChan->GetSrcdocData(srcdoc);
             entry->SetSrcdocData(srcdoc);
+            nsCOMPtr<nsIURI> baseURI;
+            inStrmChan->GetBaseURI(getter_AddRefs(baseURI));
+            entry->SetBaseURI(baseURI);
         }
     }
     /* If cache got a 'no-store', ask SH not to store
      * HistoryLayoutState. By default, SH will set this
      * flag to true and save HistoryLayoutState.
      */    
     if (discardLayoutState) {
         entry->SetSaveLayoutStateFlag(false);
@@ -11117,19 +11138,21 @@ nsDocShell::LoadHistoryEntry(nsISHEntry 
         return NS_BINDING_ABORTED;
     }
 
     // Do not inherit owner from document (security-critical!);
     uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
 
     nsAutoString srcdoc;
     bool isSrcdoc;
+    nsCOMPtr<nsIURI> baseURI;
     aEntry->GetIsSrcdocEntry(&isSrcdoc);
     if (isSrcdoc) {
         aEntry->GetSrcdocData(srcdoc);
+        aEntry->GetBaseURI(getter_AddRefs(baseURI));
         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
@@ -11144,16 +11167,17 @@ nsDocShell::LoadHistoryEntry(nsISHEntry 
                       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
+                      baseURI,
                       nullptr,            // No nsIDocShell
                       nullptr);           // No nsIRequest
     return rv;
 }
 
 NS_IMETHODIMP nsDocShell::GetShouldSaveLayoutState(bool* aShould)
 {
     *aShould = false;
@@ -12559,16 +12583,17 @@ nsDocShell::OnLinkClickSync(nsIContent *
                              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
+                             nullptr,                   // baseURI not needed
                              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
@@ -282,17 +282,18 @@ protected:
                                nsIInputStream * aPostData,
                                nsIInputStream * aHeadersData,
                                bool firstParty,
                                nsIDocShell ** aDocShell,
                                nsIRequest ** aRequest,
                                bool aIsNewWindowTarget,
                                bool aBypassClassifier,
                                bool aForceAllowCookies,
-                               const nsAString &aSrcdoc);
+                               const nsAString &aSrcdoc,
+                               nsIURI * baseURI);
     NS_IMETHOD AddHeadersToChannel(nsIInputStream * aHeadersData, 
                                   nsIChannel * aChannel);
     virtual nsresult DoChannelLoad(nsIChannel * aChannel,
                                    nsIURILoader * aURILoader,
                                    bool aBypassClassifier);
 
     nsresult ScrollToAnchor(nsACString & curHash, nsACString & newHash,
                             uint32_t aLoadType);
--- a/docshell/base/nsDocShellLoadInfo.cpp
+++ b/docshell/base/nsDocShellLoadInfo.cpp
@@ -220,15 +220,30 @@ NS_IMETHODIMP nsDocShellLoadInfo::GetSou
 }
 
 NS_IMETHODIMP nsDocShellLoadInfo::SetSourceDocShell(nsIDocShell* aSourceDocShell)
 {
    mSourceDocShell = aSourceDocShell;
    return NS_OK;
 }
 
+NS_IMETHODIMP nsDocShellLoadInfo::GetBaseURI(nsIURI** aBaseURI)
+{
+   NS_ENSURE_ARG_POINTER(aBaseURI);
+
+   *aBaseURI = mBaseURI;
+   NS_IF_ADDREF(*aBaseURI);
+   return NS_OK;
+}
+
+NS_IMETHODIMP nsDocShellLoadInfo::SetBaseURI(nsIURI* aBaseURI)
+{
+   mBaseURI = aBaseURI;
+   return NS_OK;
+}
+
 //*****************************************************************************
 // nsDocShellLoadInfo: Helpers
 //*****************************************************************************   
 
 //*****************************************************************************
 // nsDocShellLoadInfo: Accessors
 //*****************************************************************************   
--- a/docshell/base/nsDocShellLoadInfo.h
+++ b/docshell/base/nsDocShellLoadInfo.h
@@ -40,11 +40,12 @@ protected:
   nsDocShellInfoLoadType           mLoadType;
   nsCOMPtr<nsISHEntry>             mSHEntry;
   nsString                         mTarget;
   nsCOMPtr<nsIInputStream>         mPostDataStream;
   nsCOMPtr<nsIInputStream>         mHeadersStream;
   bool                             mIsSrcdocLoad;
   nsString                         mSrcdocData;
   nsCOMPtr<nsIDocShell>            mSourceDocShell;
+  nsCOMPtr<nsIURI>                 mBaseURI;
 };
 
 #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(af035c67-1690-431b-9c4d-d38e3cc3137a)]
+[scriptable, builtinclass, uuid(d0eaef67-4234-47de-b05a-9c7b324eb4f4)]
 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.
    *
@@ -134,31 +134,35 @@ interface nsIDocShell : nsIDocShellTreeI
    * @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.
+   * @param aBaseURI        - The base URI to be used for the load.  Set in
+   *                          srcdoc loads as it cannot otherwise be inferred
+   *                          in certain situations such as view-source.
    */
   [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,
+                              in nsIURI aBaseURI,
                               out nsIDocShell aDocShell,
                               out nsIRequest aRequest);
 
   /**
    * Do either a history.pushState() or history.replaceState() operation,
    * depending on the value of aReplace.
    */
   [implicit_jscontext]
--- a/docshell/base/nsIDocShellLoadInfo.idl
+++ b/docshell/base/nsIDocShellLoadInfo.idl
@@ -13,17 +13,17 @@
  
 interface nsIURI;
 interface nsIInputStream;
 interface nsISHEntry;
 interface nsIDocShell;
 
 typedef long nsDocShellInfoLoadType;
 
-[scriptable, uuid(c6b15de3-2f4f-4e80-bb20-95f43b5598c7)]
+[scriptable, uuid(c8d3b1e1-565a-427e-9d68-b109910ce9b7)]
 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.
      */
@@ -93,9 +93,15 @@ interface nsIDocShellLoadInfo : nsISuppo
     /** 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;
+
+    /**
+     * Used for srcdoc loads to give view-source knowledge of the load's base
+     * URI as this information isn't embedded in the load's URI.
+     */
+    attribute nsIURI baseURI;
 };
--- a/docshell/shistory/public/nsISHEntry.idl
+++ b/docshell/shistory/public/nsISHEntry.idl
@@ -25,17 +25,17 @@ interface nsIBFCacheEntry;
 struct nsIntRect;
 class nsDocShellEditorData;
 class nsSHEntryShared;
 %}
 [ref] native nsIntRect(nsIntRect);
 [ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
 [ptr] native nsSHEntryShared(nsSHEntryShared);
 
-[scriptable, uuid(c2a5827e-0fc0-11e3-bb95-59e799890b3c)]
+[scriptable, uuid(9eed7e92-1121-46f2-95e5-2f5c0dca46f0)]
 interface nsISHEntry : nsISupports
 {
     /**
      * A readonly property that returns the URI
      * of the current entry. The object returned is
      * of type nsIURI
      */
     readonly attribute nsIURI URI;
@@ -272,16 +272,23 @@ interface nsISHEntry : nsISupports
 
     /**
      * Contents of the srcdoc attribute in a srcdoc iframe to be loaded instead
      * of the URI.  Similar to a Data URI, this information is needed to
      * recreate the document at a later stage.
      * Setting this sets isSrcdocEntry to true
      */
     attribute AString srcdocData;
+
+    /**
+     * When isSrcdocEntry is true, this contains the baseURI of the srcdoc
+     * document for use in situations where it cannot otherwise be determined,
+     * for example with view-source.
+     */
+    attribute nsIURI baseURI;
 };
 
 [scriptable, uuid(bb66ac35-253b-471f-a317-3ece940f04c5)]
 interface nsISHEntryInternal : nsISupports
 {
     [notxpcom] void RemoveFromBFCacheAsync();
     [notxpcom] void RemoveFromBFCacheSync();
 
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -48,16 +48,17 @@ nsSHEntry::nsSHEntry(const nsSHEntry &ot
   , mID(other.mID)
   , mScrollPositionX(0)  // XXX why not copy?
   , mScrollPositionY(0)  // XXX why not copy?
   , mParent(other.mParent)
   , mURIWasModified(other.mURIWasModified)
   , mStateData(other.mStateData)
   , mIsSrcdocEntry(other.mIsSrcdocEntry)
   , mSrcdocData(other.mSrcdocData)
+  , mBaseURI(other.mBaseURI)
 {
 }
 
 static bool
 ClearParentPtr(nsISHEntry* aEntry, void* /* aData */)
 {
   if (aEntry) {
     aEntry->SetParent(nullptr);
@@ -511,17 +512,30 @@ nsSHEntry::GetSrcdocData(nsAString &aSrc
 NS_IMETHODIMP
 nsSHEntry::SetSrcdocData(const nsAString &aSrcdocData)
 {
   mSrcdocData = aSrcdocData;
   mIsSrcdocEntry = true;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsSHEntry::GetBaseURI(nsIURI **aBaseURI)
+{
+  *aBaseURI = mBaseURI;
+  NS_IF_ADDREF(*aBaseURI);
+  return NS_OK;
+}
 
+NS_IMETHODIMP
+nsSHEntry::SetBaseURI(nsIURI *aBaseURI)
+{
+  mBaseURI = aBaseURI;
+  return NS_OK;
+}
 
 //*****************************************************************************
 //    nsSHEntry: nsISHContainer
 //*****************************************************************************
 
 NS_IMETHODIMP 
 nsSHEntry::GetChildCount(int32_t * aCount)
 {
--- a/docshell/shistory/src/nsSHEntry.h
+++ b/docshell/shistory/src/nsSHEntry.h
@@ -57,11 +57,12 @@ private:
   int32_t                  mScrollPositionX;
   int32_t                  mScrollPositionY;
   nsISHEntry*              mParent;
   nsCOMArray<nsISHEntry>   mChildren;
   bool                     mURIWasModified;
   nsCOMPtr<nsIStructuredCloneContainer> mStateData;
   bool                     mIsSrcdocEntry;
   nsString                 mSrcdocData;
+  nsCOMPtr<nsIURI>         mBaseURI;
 };
 
 #endif /* nsSHEntry_h */