diff --git a/browser/base/content/tab-content.js b/browser/base/content/tab-content.js index 8ce954805578..34eb58627207 100644 --- a/browser/base/content/tab-content.js +++ b/browser/base/content/tab-content.js @@ -607,6 +607,97 @@ addMessageListener("Browser:AppTab", function(message) { } }); +let PrerenderContentHandler = { + init() { + this._pending = []; + this._idMonotonic = 0; + this._initialized = true; + addMessageListener("Prerender:Canceled", this); + addMessageListener("Prerender:Swapped", this); + }, + + get initialized() { + return !!this._initialized; + }, + + receiveMessage(aMessage) { + switch (aMessage.name) { + case "Prerender:Canceled": { + for (let i = 0; i < this._pending.length; ++i) { + if (this._pending[i].id === aMessage.data.id) { + if (this._pending[i].failure) { + this._pending[i].failure.run(); + } + // Remove the item from the array + this._pending.splice(i, 1); + break; + } + } + break; + } + case "Prerender:Swapped": { + for (let i = 0; i < this._pending.length; ++i) { + if (this._pending[i].id === aMessage.data.id) { + if (this._pending[i].success) { + this._pending[i].success.run(); + } + // Remove the item from the array + this._pending.splice(i, 1); + break; + } + } + break; + } + } + }, + + startPrerenderingDocument(aHref, aReferrer) { + // XXX: Make this constant a pref + if (this._pending.length >= 2) { + return; + } + + let id = ++this._idMonotonic; + sendAsyncMessage("Prerender:Request", { + href: aHref.spec, + referrer: aReferrer ? aReferrer.spec : null, + id: id, + }); + + this._pending.push({ + href: aHref, + referrer: aReferrer, + id: id, + success: null, + failure: null, + }); + }, + + shouldSwitchToPrerenderedDocument(aHref, aReferrer, aSuccess, aFailure) { + // Check if we think there is a prerendering document pending for the given + // href and referrer. If we think there is one, we will send a message to + // the parent process asking it to do a swap, and hook up the success and + // failure listeners. + for (let i = 0; i < this._pending.length; ++i) { + let p = this._pending[i]; + if (p.href.equals(aHref) && p.referrer.equals(aReferrer)) { + p.success = aSuccess; + p.failure = aFailure; + sendAsyncMessage("Prerender:Swap", {id: p.id}); + return true; + } + } + + return false; + } +}; + +if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) { + // We only want to initialize the PrerenderContentHandler in the content + // process. Outside of the content process, this should be unused. + PrerenderContentHandler.init(); +} + var WebBrowserChrome = { onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) { return BrowserUtils.onBeforeLinkTraversal(originalTarget, linkURI, linkNode, isAppTab); @@ -630,6 +721,20 @@ var WebBrowserChrome = { reloadInFreshProcess: function(aDocShell, aURI, aReferrer) { E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, true); return true; + }, + + startPrerenderingDocument: function(aHref, aReferrer) { + if (PrerenderContentHandler.initialized) { + PrerenderContentHandler.startPrerenderingDocument(aHref, aReferrer); + } + }, + + shouldSwitchToPrerenderedDocument: function(aHref, aReferrer, aSuccess, aFailure) { + if (PrerenderContentHandler.initialized) { + return PrerenderContentHandler.shouldSwitchToPrerenderedDocument( + aHref, aReferrer, aSuccess, aFailure); + } + return false; } }; diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index e54bd77e79b0..39d46002a27f 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1500,6 +1500,7 @@ + + { + browser.frameloader.messageManager. + sendAsyncMessage("Prerender:Canceled", { id: data.id }); + }; + + let tab = this.getTabForBrowser(browser); + if (!tab) { + // No tab? + sendCancelPrerendering(); + break; + } + + if (tab.hidden) { + // Skip prerender on hidden tab. + sendCancelPrerendering(); + break; + } + + if (browser.canGoForward) { + // Skip prerender on history navigation as we don't support it + // yet. Remove this check once bug 1323650 is implemented. + sendCancelPrerendering(); + break; + } + + if (!data.href) { + // If we don't have data.href, loadOneTab will load about:blank + // which is meaningless for prerendering. + sendCancelPrerendering(); + break; + } + + let groupedSHistory = browser.frameLoader.ensureGroupedSHistory(); + + let newTab = this.loadOneTab(data.href, { + referrerURI: (data.referrer ? makeURI(data.referrer) : null), + referrerPolicy: Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT, + postData: null, + allowThirdPartyFixup: true, + relatedToCurrent: true, + isPrerendered: true, + }); + let partialSHistory = newTab.linkedBrowser.frameLoader.partialSessionHistory; + groupedSHistory.addPrerenderingPartialSHistory(partialSHistory, data.id); + break; + } + + case "Prerender:Cancel": { + let groupedSHistory = browser.frameLoader.groupedSessionHistory; + if (groupedSHistory) { + groupedSHistory.cancelPrerendering(data.id); + } + break; + } + + case "Prerender:Swap": { + let frameloader = browser.frameLoader; + let groupedSHistory = browser.frameLoader.groupedSessionHistory; + if (groupedSHistory) { + groupedSHistory.activatePrerendering(data.id).then( + () => frameloader.messageManager.sendAsyncMessage("Prerender:Swapped", data), + () => frameloader.messageManager.sendAsyncMessage("Prerender:Canceled", data), + ); + } + break; + } + } return undefined; ]]> @@ -4922,6 +5005,11 @@ this._findAsYouType = Services.prefs.getBoolPref("accessibility.typeaheadfind"); Services.prefs.addObserver("accessibility.typeaheadfind", this, false); messageManager.addMessageListener("Findbar:Keypress", this); + + // Add listeners for prerender messages + messageManager.addMessageListener("Prerender:Request", this); + messageManager.addMessageListener("Prerender:Cancel", this); + messageManager.addMessageListener("Prerender:Swap", this); ]]> diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index 84a8ad2ce77e..4602646b1b1e 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -1575,6 +1575,7 @@ nsDocShell::LoadURI(nsIURI* aURI, srcdoc, sourceDocShell, baseURI, + false, nullptr, // No nsIDocShell nullptr); // No nsIRequest } @@ -5367,8 +5368,8 @@ nsDocShell::LoadErrorPage(nsIURI* aURI, const char16_t* aURL, nsContentUtils::GetSystemPrincipal(), nullptr, INTERNAL_LOAD_FLAGS_NONE, EmptyString(), nullptr, NullString(), nullptr, nullptr, LOAD_ERROR_PAGE, - nullptr, true, NullString(), this, nullptr, nullptr, - nullptr); + nullptr, true, NullString(), this, nullptr, false, + nullptr, nullptr); } NS_IMETHODIMP @@ -5460,6 +5461,7 @@ nsDocShell::Reload(uint32_t aReloadFlags) srcdoc, // srcdoc argument for iframe this, // For reloads we are the source baseURI, + false, nullptr, // No nsIDocShell nullptr); // No nsIRequest } @@ -7956,7 +7958,8 @@ nsDocShell::EnsureContentViewer() nsresult nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal, nsIURI* aBaseURI, - bool aTryToSaveOldPresentation) + bool aTryToSaveOldPresentation, + bool aCheckPermitUnload) { nsCOMPtr blankDoc; nsCOMPtr viewer; @@ -7989,31 +7992,32 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal, bool hadTiming = mTiming; bool toBeReset = MaybeInitTiming(); if (mContentViewer) { - // We've got a content viewer already. Make sure the user - // permits us to discard the current document and replace it - // with about:blank. And also ensure we fire the unload events - // in the current document. + if (aCheckPermitUnload) { + // We've got a content viewer already. Make sure the user + // permits us to discard the current document and replace it + // with about:blank. And also ensure we fire the unload events + // in the current document. - // Unload gets fired first for - // document loaded from the session history. - mTiming->NotifyBeforeUnload(); + // Unload gets fired first for + // document loaded from the session history. + mTiming->NotifyBeforeUnload(); - bool okToUnload; - rv = mContentViewer->PermitUnload(&okToUnload); + bool okToUnload; + rv = mContentViewer->PermitUnload(&okToUnload); - if (NS_SUCCEEDED(rv) && !okToUnload) { - // The user chose not to unload the page, interrupt the load. - MaybeResetInitTiming(toBeReset); - return NS_ERROR_FAILURE; + if (NS_SUCCEEDED(rv) && !okToUnload) { + // The user chose not to unload the page, interrupt the load. + MaybeResetInitTiming(toBeReset); + return NS_ERROR_FAILURE; + } + if (mTiming) { + mTiming->NotifyUnloadAccepted(mCurrentURI); + } } mSavingOldViewer = aTryToSaveOldPresentation && CanSavePresentation(LOAD_NORMAL, nullptr, nullptr); - if (mTiming) { - mTiming->NotifyUnloadAccepted(mCurrentURI); - } - // Make sure to blow away our mLoadingURI just in case. No loads // from inside this pagehide. mLoadingURI = nullptr; @@ -8097,6 +8101,12 @@ nsDocShell::CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal) return CreateAboutBlankContentViewer(aPrincipal, nullptr); } +NS_IMETHODIMP +nsDocShell::ForceCreateAboutBlankContentViewer(nsIPrincipal* aPrincipal) +{ + return CreateAboutBlankContentViewer(aPrincipal, nullptr, true, false); +} + bool nsDocShell::CanSavePresentation(uint32_t aLoadType, nsIRequest* aNewRequest, @@ -9578,7 +9588,7 @@ public: nsIInputStream* aHeadersData, uint32_t aLoadType, nsISHEntry* aSHEntry, bool aFirstParty, const nsAString& aSrcdoc, nsIDocShell* aSourceDocShell, - nsIURI* aBaseURI) + nsIURI* aBaseURI, bool aCheckForPrerender) : mSrcdoc(aSrcdoc) , mDocShell(aDocShell) , mURI(aURI) @@ -9596,6 +9606,7 @@ public: , mFirstParty(aFirstParty) , mSourceDocShell(aSourceDocShell) , mBaseURI(aBaseURI) + , mCheckForPrerender(aCheckForPrerender) { // Make sure to keep null things null as needed if (aTypeHint) { @@ -9615,7 +9626,7 @@ public: NullString(), mPostData, mHeadersData, mLoadType, mSHEntry, mFirstParty, mSrcdoc, mSourceDocShell, mBaseURI, - nullptr, nullptr); + mCheckForPrerender, nullptr, nullptr); } private: @@ -9640,6 +9651,7 @@ private: bool mFirstParty; nsCOMPtr mSourceDocShell; nsCOMPtr mBaseURI; + bool mCheckForPrerender; }; /** @@ -9712,6 +9724,7 @@ nsDocShell::InternalLoad(nsIURI* aURI, const nsAString& aSrcdoc, nsIDocShell* aSourceDocShell, nsIURI* aBaseURI, + bool aCheckForPrerender, nsIDocShell** aDocShell, nsIRequest** aRequest) { @@ -10071,6 +10084,7 @@ nsDocShell::InternalLoad(nsIURI* aURI, aSrcdoc, aSourceDocShell, aBaseURI, + aCheckForPrerender, aDocShell, aRequest); if (rv == NS_ERROR_NO_CONTENT) { @@ -10141,7 +10155,7 @@ nsDocShell::InternalLoad(nsIURI* aURI, aTriggeringPrincipal, principalToInherit, aFlags, aTypeHint, aPostData, aHeadersData, aLoadType, aSHEntry, aFirstParty, aSrcdoc, - aSourceDocShell, aBaseURI); + aSourceDocShell, aBaseURI, false); return NS_DispatchToCurrentThread(ev); } @@ -10528,6 +10542,25 @@ nsDocShell::InternalLoad(nsIURI* aURI, mTiming->NotifyUnloadAccepted(mCurrentURI); } + if (browserChrome3 && aCheckForPrerender) { + nsCOMPtr ev = + new InternalLoadEvent(this, aURI, aOriginalURI, aLoadReplace, + aReferrer, aReferrerPolicy, + aTriggeringPrincipal, principalToInherit, + aFlags, aTypeHint, aPostData, aHeadersData, + aLoadType, aSHEntry, aFirstParty, aSrcdoc, + aSourceDocShell, aBaseURI, false); + // We don't need any success handler since in that case + // OnPartialSessionHistoryDeactive would be called, and it would ensure + // docshell loads about:blank. + bool shouldSwitch = false; + rv = browserChrome3->ShouldSwitchToPrerenderedDocument( + aURI, mCurrentURI, nullptr, ev, &shouldSwitch); + if (NS_SUCCEEDED(rv) && shouldSwitch) { + return NS_OK; + } + } + // Whenever a top-level browsing context is navigated, the user agent MUST // lock the orientation of the document to the document's default // orientation. We don't explicitly check for a top-level browsing context @@ -12531,6 +12564,7 @@ nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType) srcdoc, nullptr, // Source docshell, see comment above baseURI, + false, nullptr, // No nsIDocShell nullptr); // No nsIRequest return rv; @@ -14036,6 +14070,7 @@ nsDocShell::OnLinkClickSync(nsIContent* aContent, NullString(), // No srcdoc this, // We are the source nullptr, // baseURI not needed + true, // Check for prerendered doc aDocShell, // DocShell out-param aRequest); // Request out-param if (NS_SUCCEEDED(rv)) { @@ -14702,6 +14737,14 @@ nsDocShell::GetCommandManager() return mCommandManager; } +NS_IMETHODIMP +nsDocShell::GetIsProcessLocked(bool* aIsLocked) +{ + MOZ_ASSERT(aIsLocked); + *aIsLocked = GetProcessLockReason() != PROCESS_LOCK_NONE; + return NS_OK; +} + NS_IMETHODIMP nsDocShell::GetProcessLockReason(uint32_t* aReason) { diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h index ba48dc7415e7..cd1882c2eeba 100644 --- a/docshell/base/nsDocShell.h +++ b/docshell/base/nsDocShell.h @@ -331,7 +331,8 @@ protected: // passed in, the about:blank principal will end up being used. nsresult CreateAboutBlankContentViewer(nsIPrincipal* aPrincipal, nsIURI* aBaseURI, - bool aTryToSaveOldPresentation = true); + bool aTryToSaveOldPresentation = true, + bool aCheckPermitUnload = true); nsresult CreateContentViewer(const nsACString& aContentType, nsIRequest* aRequest, nsIStreamListener** aContentHandler); diff --git a/docshell/base/nsIDocShell.idl b/docshell/base/nsIDocShell.idl index 1db1762bdecc..319ff0ffa9cc 100644 --- a/docshell/base/nsIDocShell.idl +++ b/docshell/base/nsIDocShell.idl @@ -187,10 +187,11 @@ interface nsIDocShell : nsIDocShellTreeItem in nsIInputStream aHeadersStream, in unsigned long aLoadFlags, in nsISHEntry aSHEntry, - in boolean firstParty, + in boolean aFirstParty, in AString aSrcdoc, in nsIDocShell aSourceDocShell, in nsIURI aBaseURI, + in boolean aCheckForPrerender, out nsIDocShell aDocShell, out nsIRequest aRequest); @@ -673,6 +674,13 @@ interface nsIDocShell : nsIDocShellTreeItem */ void createAboutBlankContentViewer(in nsIPrincipal aPrincipal); + /** + * Like createAboutBlankContentViewer, but don't check for permit unload. + * Only used by special session history operation. + * @param aPrincipal the principal to use for the new document. + */ + [noscript] void forceCreateAboutBlankContentViewer(in nsIPrincipal aPrincipal); + /** * Upon getting, returns the canonical encoding label of the document * currently loaded into this docshell. @@ -1107,7 +1115,12 @@ interface nsIDocShell : nsIDocShellTreeItem * A DocShell is locked to the current process if it would be * content-observable for a process switch to occur before performing a * navigation load. It is important to ensure that a DocShell is not process - * locked before perfoming process changing loads. + * locked before performing process changing loads. + */ + [infallible] readonly attribute boolean isProcessLocked; + /** + * Return PROCESS_LOCK_NONE if docShell is not locked to current process, + * otherwise return the reason why process is locked. */ [infallible] readonly attribute unsigned long processLockReason; /** diff --git a/docshell/shistory/nsSHistory.cpp b/docshell/shistory/nsSHistory.cpp index 1c43a95966cb..d67616c78efa 100644 --- a/docshell/shistory/nsSHistory.cpp +++ b/docshell/shistory/nsSHistory.cpp @@ -510,7 +510,10 @@ nsSHistory::OnPartialSessionHistoryDeactive() return NS_OK; } - if (NS_FAILED(mRootDocShell->CreateAboutBlankContentViewer(nullptr))) { + // At this point we've swapped out to an invisble tab, and can not prompt here. + // The check should have been done in nsDocShell::InternalLoad, so we'd + // just force docshell to load about:blank. + if (NS_FAILED(mRootDocShell->ForceCreateAboutBlankContentViewer(nullptr))) { return NS_ERROR_FAILURE; } diff --git a/dom/base/GroupedSHistory.cpp b/dom/base/GroupedSHistory.cpp index 7c83893575be..3a1270a6fb39 100644 --- a/dom/base/GroupedSHistory.cpp +++ b/dom/base/GroupedSHistory.cpp @@ -225,7 +225,16 @@ GroupedSHistory::PurgePartialHistories(uint32_t aLastPartialIndexToKeep) /* static */ bool GroupedSHistory::GroupedHistoryEnabled() { - return Preferences::GetBool("browser.groupedhistory.enabled", false); + static bool sGroupedSHistoryEnabled = false; + static bool sGroupedSHistoryPrefCached = false; + if (!sGroupedSHistoryPrefCached) { + sGroupedSHistoryPrefCached = true; + Preferences::AddBoolVarCache(&sGroupedSHistoryEnabled, + "browser.groupedhistory.enabled", + false); + } + + return sGroupedSHistoryEnabled; } void diff --git a/dom/base/Link.cpp b/dom/base/Link.cpp index b36836e6a0af..b29e0bc7ccaf 100644 --- a/dom/base/Link.cpp +++ b/dom/base/Link.cpp @@ -85,7 +85,7 @@ Link::CancelDNSPrefetch(nsWrapperCache::FlagsType aDeferredFlag, } void -Link::TryDNSPrefetchPreconnectOrPrefetch() +Link::TryDNSPrefetchPreconnectOrPrefetchOrPrerender() { MOZ_ASSERT(mElement->IsInComposedDoc()); if (!ElementHasHref()) { @@ -128,6 +128,14 @@ Link::TryDNSPrefetchPreconnectOrPrefetch() } } + if (linkTypes & nsStyleLinkElement::ePRERENDER) { + nsCOMPtr uri(GetURI()); + if (uri && mElement->OwnerDoc()) { + mElement->OwnerDoc()->PrerenderHref(uri); + return; + } + } + if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) { if (nsHTMLDNSPrefetch::IsAllowed(mElement->OwnerDoc())) { nsHTMLDNSPrefetch::PrefetchLow(this); diff --git a/dom/base/Link.h b/dom/base/Link.h index 3a358505a3c5..96625dc5923d 100644 --- a/dom/base/Link.h +++ b/dom/base/Link.h @@ -123,7 +123,7 @@ public: nsWrapperCache::FlagsType aRequestedFlag); // This is called by HTMLLinkElement. - void TryDNSPrefetchPreconnectOrPrefetch(); + void TryDNSPrefetchPreconnectOrPrefetchOrPrerender(); void CancelPrefetch(); protected: diff --git a/dom/base/nsContentSink.cpp b/dom/base/nsContentSink.cpp index 44703f8464d8..60971ba10600 100644 --- a/dom/base/nsContentSink.cpp +++ b/dom/base/nsContentSink.cpp @@ -716,6 +716,14 @@ nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref, PrefetchHref(aHref, mDocument, hasPrefetch); } + if (linkTypes & nsStyleLinkElement::ePRERENDER) { + nsCOMPtr href; + nsresult rv = NS_NewURI(getter_AddRefs(href), aHref); + if (NS_SUCCEEDED(rv)) { + mDocument->PrerenderHref(href); + } + } + if (!aHref.IsEmpty() && (linkTypes & nsStyleLinkElement::eDNS_PREFETCH)) { PrefetchDNS(aHref); } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index ceddff0edf41..f454030aa7b4 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -2918,6 +2918,73 @@ nsIDocument::IsScriptTracking(const nsACString& aURL) const return mTrackingScripts.Contains(aURL); } +bool +nsIDocument::PrerenderHref(nsIURI* aHref) +{ + MOZ_ASSERT(aHref); + + static bool sPrerenderEnabled = false; + static bool sPrerenderPrefCached = false; + if (!sPrerenderPrefCached) { + sPrerenderPrefCached = true; + Preferences::AddBoolVarCache(&sPrerenderEnabled, + "dom.linkPrerender.enabled", + false); + } + + // Check if prerender is enabled + if (!sPrerenderEnabled) { + return false; + } + + nsCOMPtr referrer = GetDocumentURI(); + bool urisMatch = false; + aHref->EqualsExceptRef(referrer, &urisMatch); + if (urisMatch) { + // Prerender current document isn't quite meaningful, and we may not be able + // to load it out of process. + return false; + } + + nsCOMPtr docShell = GetDocShell(); + nsCOMPtr webNav = do_QueryInterface(docShell); + NS_ENSURE_TRUE(webNav, false); + + bool canGoForward = false; + nsresult rv = webNav->GetCanGoForward(&canGoForward); + if (NS_FAILED(rv) || canGoForward) { + // Skip prerender on history navigation as we don't support it yet. + // Remove this check once bug 1323650 is implemented. + return false; + } + + // Check if the document is in prerender state. We don't prerender in a + // prerendered document. + if (docShell->GetIsPrerendered()) { + return false; + } + + // Adopting an out-of-process prerendered document is conceptually similar to + // switching dochshell's process, since it's the same browsing context from + // other browsing contexts' perspective. If we're locked in current process, + // we can not prerender out-of-process. + if (docShell->GetIsProcessLocked()) { + return false; + } + + TabChild* tabChild = TabChild::GetFrom(docShell); + NS_ENSURE_TRUE(tabChild, false); + + nsCOMPtr wbc3; + tabChild->GetWebBrowserChrome(getter_AddRefs(wbc3)); + NS_ENSURE_TRUE(wbc3, false); + + rv = wbc3->StartPrerenderingDocument(aHref, referrer); + NS_ENSURE_SUCCESS(rv, false); + + return true; +} + NS_IMETHODIMP nsDocument::GetApplicationCache(nsIApplicationCache **aApplicationCache) { diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index ae4928cba18d..54675185dde9 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -2881,6 +2881,8 @@ public: void NoteScriptTrackingStatus(const nsACString& aURL, bool isTracking); bool IsScriptTracking(const nsACString& aURL) const; + bool PrerenderHref(nsIURI* aHref); + protected: bool GetUseCounter(mozilla::UseCounter aUseCounter) { diff --git a/dom/base/nsStyleLinkElement.cpp b/dom/base/nsStyleLinkElement.cpp index 8ab2dab0bb4d..6ddec96c7d01 100644 --- a/dom/base/nsStyleLinkElement.cpp +++ b/dom/base/nsStyleLinkElement.cpp @@ -162,7 +162,9 @@ static uint32_t ToLinkMask(const nsAString& aLink, nsIPrincipal* aPrincipal) return nsStyleLinkElement::eHTMLIMPORT; else if (aLink.EqualsLiteral("preconnect")) return nsStyleLinkElement::ePRECONNECT; - else + else if (aLink.EqualsLiteral("prerender")) + return nsStyleLinkElement::ePRERENDER; + else return 0; } diff --git a/dom/base/nsStyleLinkElement.h b/dom/base/nsStyleLinkElement.h index a41ae5e1d7af..519f1c6fb293 100644 --- a/dom/base/nsStyleLinkElement.h +++ b/dom/base/nsStyleLinkElement.h @@ -63,7 +63,8 @@ public: eNEXT = 0x00000008, eALTERNATE = 0x00000010, eHTMLIMPORT = 0x00000020, - ePRECONNECT = 0x00000040 + ePRECONNECT = 0x00000040, + ePRERENDER = 0x00000080 }; // The return value is a bitwise or of 0 or more RelValues. diff --git a/dom/html/HTMLLinkElement.cpp b/dom/html/HTMLLinkElement.cpp index 0a2cdaaf40bb..f781b036e779 100644 --- a/dom/html/HTMLLinkElement.cpp +++ b/dom/html/HTMLLinkElement.cpp @@ -169,7 +169,7 @@ HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, } if (IsInComposedDoc()) { - TryDNSPrefetchPreconnectOrPrefetch(); + TryDNSPrefetchPreconnectOrPrefetchOrPrerender(); } void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal; @@ -389,7 +389,7 @@ HTMLLinkElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, if ((aName == nsGkAtoms::rel || aName == nsGkAtoms::href) && IsInComposedDoc()) { - TryDNSPrefetchPreconnectOrPrefetch(); + TryDNSPrefetchPreconnectOrPrefetchOrPrerender(); } UpdateStyleSheetInternal(nullptr, nullptr, diff --git a/dom/xul/test/file_bug1069772.xul b/dom/xul/test/file_bug1069772.xul index b909852469f0..2cd9050fa9bf 100644 --- a/dom/xul/test/file_bug1069772.xul +++ b/dom/xul/test/file_bug1069772.xul @@ -108,8 +108,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1069772 function makePrerenderedBrowserActive(browser) { let promise = waitForVisibilityChange(browser); - browser.setAttribute('prerendered', false); - browser.makePrerenderedBrowserActive(); + browser.removeAttribute('prerendered'); + browser.frameLoader.makePrerenderedLoaderActive(); return promise.then(() => browser); } diff --git a/dom/xul/test/file_bug1271240.xul b/dom/xul/test/file_bug1271240.xul index 2edd5a1b403f..26c9a6202dda 100644 --- a/dom/xul/test/file_bug1271240.xul +++ b/dom/xul/test/file_bug1271240.xul @@ -71,7 +71,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1271240 } function makePrerenderedBrowserActive(browser) { - browser.makePrerenderedBrowserActive(); + browser.frameLoader.makePrerenderedLoaderActive(); return browser; } diff --git a/embedding/browser/nsIWebBrowserChrome3.idl b/embedding/browser/nsIWebBrowserChrome3.idl index c74397741e91..d4796187b3f2 100644 --- a/embedding/browser/nsIWebBrowserChrome3.idl +++ b/embedding/browser/nsIWebBrowserChrome3.idl @@ -8,6 +8,7 @@ interface nsIDocShell; interface nsIInputStream; +interface nsIRunnable; /** * nsIWebBrowserChrome3 is an extension to nsIWebBrowserChrome2. @@ -60,4 +61,36 @@ interface nsIWebBrowserChrome3 : nsIWebBrowserChrome2 bool reloadInFreshProcess(in nsIDocShell aDocShell, in nsIURI aURI, in nsIURI aReferrer); + + /** + * Tell the browser to start prerendering the given document. This prerendering + * _must_ be for the toplevel document. + * + * @param aHref The URI to begin prerendering + * @param aReferrer The URI of the document requesting the prerender. + */ + void startPrerenderingDocument(in nsIURI aHref, in nsIURI aReferrer); + + /** + * Check if there's a prerendered document which matches given URI / + * referrer, and try to switch to the prerendered document immediately if + * there is. + * + * @param aHref + * The URI which is being loaded. + * @param aReferrer + * The referrer for the current load. + * @param aSuccess + * (Optional) a runnable which will be run if the swap is successful. + * @param aFailure + * (Optional) a runnable which will be run if the swap is not + * successful. Note it's not invoked if the function returns false. + * + * @return True if there is a matched prerendered document to swap with, + * false otherwise. + */ + bool shouldSwitchToPrerenderedDocument(in nsIURI aHref, + in nsIURI aReferrer, + in nsIRunnable aSuccess, + in nsIRunnable aFailure); }; diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml index ed0a269cef21..326da4daaa25 100644 --- a/toolkit/content/widgets/browser.xml +++ b/toolkit/content/widgets/browser.xml @@ -307,17 +307,6 @@ - - - - - - diff --git a/xpfe/appshell/nsContentTreeOwner.cpp b/xpfe/appshell/nsContentTreeOwner.cpp index 78db2058cb6b..832d3e2a542e 100644 --- a/xpfe/appshell/nsContentTreeOwner.cpp +++ b/xpfe/appshell/nsContentTreeOwner.cpp @@ -423,6 +423,24 @@ NS_IMETHODIMP nsContentTreeOwner::ReloadInFreshProcess(nsIDocShell* aDocShell, return NS_OK; } +NS_IMETHODIMP nsContentTreeOwner::StartPrerenderingDocument(nsIURI* aHref, + nsIURI* aReferrer) +{ + NS_WARNING("Cannot prerender a document in the parent process"); + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP nsContentTreeOwner::ShouldSwitchToPrerenderedDocument(nsIURI* aHref, + nsIURI* aReferrer, + nsIRunnable* aSuccess, + nsIRunnable* aFailure, + bool* aRetval) +{ + NS_WARNING("Cannot switch to prerendered document in the parent process"); + *aRetval = false; + return NS_OK; +} + //***************************************************************************** // nsContentTreeOwner::nsIWebBrowserChrome2 //*****************************************************************************