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
//*****************************************************************************