Bug 1315105 - Part 2: Implement <link rel=prerender> behind a pref, r=smaug

MozReview-Commit-ID: ARET98o1FTU

--HG--
extra : rebase_source : fd549baa1a4a180db1ca6701191f081033811d0f
This commit is contained in:
Michael Layzell 2016-12-19 15:05:31 +08:00
parent 3244cf77dd
commit 0fa642800b
20 changed files with 442 additions and 52 deletions

View File

@ -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;
}
};

View File

@ -1500,6 +1500,7 @@
<parameter name="aPostData"/>
<parameter name="aLoadInBackground"/>
<parameter name="aAllowThirdPartyFixup"/>
<parameter name="aIsPrerendered"/>
<body>
<![CDATA[
var aReferrerPolicy;
@ -1535,6 +1536,7 @@
aRelatedBrowser = params.relatedBrowser;
aOriginPrincipal = params.originPrincipal;
aOpener = params.opener;
aIsPrerendered = params.isPrerendered;
}
var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
@ -1557,7 +1559,8 @@
userContextId: aUserContextId,
originPrincipal: aOriginPrincipal,
relatedBrowser: aRelatedBrowser,
opener: aOpener });
opener: aOpener,
isPrerendered: aIsPrerendered });
if (!bgLoad)
this.selectedTab = tab;
@ -1935,7 +1938,7 @@
<![CDATA[
// Supported parameters:
// userContextId, remote, remoteType, isPreloadBrowser,
// uriIsAboutBlank, permanentKey
// uriIsAboutBlank, permanentKey, isPrerendered
const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
@ -1947,6 +1950,10 @@
b.setAttribute("contextmenu", this.getAttribute("contentcontextmenu"));
b.setAttribute("tooltip", this.getAttribute("contenttooltip"));
if (aParams.isPrerendered) {
b.setAttribute("prerendered", "true");
}
if (aParams.userContextId) {
b.setAttribute("usercontextid", aParams.userContextId);
}
@ -2035,7 +2042,7 @@
"use strict";
// Supported parameters:
// forceNotRemote, preferredRemoteType, userContextId
// forceNotRemote, preferredRemoteType, userContextId, isPrerendered
let uriIsAboutBlank = !aURI || aURI == "about:blank";
@ -2068,7 +2075,8 @@
uriIsAboutBlank: uriIsAboutBlank,
userContextId: aParams.userContextId,
relatedBrowser: aParams.relatedBrowser,
opener: aParams.opener});
opener: aParams.opener,
isPrerendered: aParams.isPrerendered});
}
let notificationbox = this.getNotificationBox(browser);
@ -2128,6 +2136,7 @@
<parameter name="aPostData"/>
<parameter name="aOwner"/>
<parameter name="aAllowThirdPartyFixup"/>
<parameter name="aIsPrerendered"/>
<body>
<![CDATA[
"use strict";
@ -2170,6 +2179,7 @@
aOriginPrincipal = params.originPrincipal;
aDisallowInheritPrincipal = params.disallowInheritPrincipal;
aOpener = params.opener;
aIsPrerendered = params.isPrerendered;
}
// if we're adding tabs, we're past interrupt mode, ditch the owner
@ -2187,6 +2197,10 @@
t.setAttribute("label", aURI);
}
if (aIsPrerendered) {
t.setAttribute("hidden", "true");
}
if (aUserContextId) {
t.setAttribute("usercontextid", aUserContextId);
ContextualIdentityService.setTabStyle(t);
@ -2239,6 +2253,7 @@
userContextId: aUserContextId,
relatedBrowser: aRelatedBrowser,
opener: aOpener,
isPrerendered: aIsPrerendered,
};
let { usingPreloadedContent } = this._linkBrowserToTab(t, aURI, browserParams);
let b = t.linkedBrowser;
@ -4790,6 +4805,74 @@
break;
}
case "Prerender:Request": {
let sendCancelPrerendering = () => {
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;
]]></body>
@ -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);
]]>
</constructor>

View File

@ -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<nsIDocument> blankDoc;
nsCOMPtr<nsIContentViewer> 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<nsIDocShell> mSourceDocShell;
nsCOMPtr<nsIURI> 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<nsIRunnable> 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)
{

View File

@ -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);

View File

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

View File

@ -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;
}

View File

@ -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

View File

@ -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<nsIURI> uri(GetURI());
if (uri && mElement->OwnerDoc()) {
mElement->OwnerDoc()->PrerenderHref(uri);
return;
}
}
if (linkTypes & nsStyleLinkElement::eDNS_PREFETCH) {
if (nsHTMLDNSPrefetch::IsAllowed(mElement->OwnerDoc())) {
nsHTMLDNSPrefetch::PrefetchLow(this);

View File

@ -123,7 +123,7 @@ public:
nsWrapperCache::FlagsType aRequestedFlag);
// This is called by HTMLLinkElement.
void TryDNSPrefetchPreconnectOrPrefetch();
void TryDNSPrefetchPreconnectOrPrefetchOrPrerender();
void CancelPrefetch();
protected:

View File

@ -716,6 +716,14 @@ nsContentSink::ProcessLink(const nsSubstring& aAnchor, const nsSubstring& aHref,
PrefetchHref(aHref, mDocument, hasPrefetch);
}
if (linkTypes & nsStyleLinkElement::ePRERENDER) {
nsCOMPtr<nsIURI> 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);
}

View File

@ -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<nsIURI> 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<nsIDocShell> docShell = GetDocShell();
nsCOMPtr<nsIWebNavigation> 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<nsIWebBrowserChrome3> 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)
{

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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.

View File

@ -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,

View File

@ -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);
}

View File

@ -71,7 +71,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1271240
}
function makePrerenderedBrowserActive(browser) {
browser.makePrerenderedBrowserActive();
browser.frameLoader.makePrerenderedLoaderActive();
return browser;
}

View File

@ -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);
};

View File

@ -307,17 +307,6 @@
</body>
</method>
<method name="makePrerenderedBrowserActive">
<body>
<![CDATA[
let frameLoader = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
if (frameLoader) {
frameLoader.makePrerenderedLoaderActive();
}
]]>
</body>
</method>
<property name="imageDocument"
readonly="true">
<getter>

View File

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