--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -617,16 +617,22 @@ var WebBrowserChrome = {
shouldLoadURI: function(aDocShell, aURI, aReferrer) {
if (!E10SUtils.shouldLoadURI(aDocShell, aURI, aReferrer)) {
E10SUtils.redirectLoad(aDocShell, aURI, aReferrer);
return false;
}
return true;
},
+
+ // Try to reload the currently active or currently loading page in a new process.
+ reloadInFreshProcess: function(aDocShell, aURI, aReferrer) {
+ E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, true);
+ return true;
+ }
};
if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
let tabchild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsITabChild);
tabchild.webBrowserChrome = WebBrowserChrome;
}
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1654,16 +1654,17 @@
}
]]></body>
</method>
<method name="updateBrowserRemoteness">
<parameter name="aBrowser"/>
<parameter name="aShouldBeRemote"/>
<parameter name="aOpener"/>
+ <parameter name="aFreshProcess"/>
<body>
<![CDATA[
let isRemote = aBrowser.getAttribute("remote") == "true";
// If we are passed an opener, we must be making the browser non-remote, and
// if the browser is _currently_ non-remote, we need the openers to match,
// because it is already too late to change it.
if (aOpener) {
@@ -1671,17 +1672,17 @@
throw new Exception("Cannot set an opener on a browser which should be remote!");
}
if (!isRemote && aBrowser.contentWindow.opener != aOpener) {
throw new Exception("Cannot change opener on an already non-remote browser!");
}
}
// Abort if we're not going to change anything
- if (isRemote == aShouldBeRemote) {
+ if (isRemote == aShouldBeRemote && !aFreshProcess) {
return false;
}
let tab = this.getTabForBrowser(aBrowser);
let evt = document.createEvent("Events");
evt.initEvent("BeforeTabRemotenessChange", true, false);
tab.dispatchEvent(evt);
@@ -1718,18 +1719,28 @@
// NB: This works with the hack in the browser constructor that
// turns this normal property into a field.
aBrowser.relatedBrowser = relatedBrowser;
// Set the opener window on the browser, such that when the frame
// loader is created the opener is set correctly.
aBrowser.presetOpenerWindow(aOpener);
+ // Set the freshProcess attribute so that the frameloader knows to
+ // create a new process
+ if (aFreshProcess) {
+ aBrowser.setAttribute("freshProcess", "true");
+ }
+
parent.appendChild(aBrowser);
+ // Remove the freshProcess attribute if we set it, as we don't
+ // want it to apply for the next time the frameloader is created
+ aBrowser.removeAttribute("freshProcess");
+
aBrowser.userTypedValue = oldUserTypedValue;
if (hadStartedLoad) {
aBrowser.urlbarChangeTracker.startedLoad();
}
aBrowser.droppedLinkHandler = droppedLinkHandler;
// Switching a browser's remoteness will create a new frameLoader.
@@ -1782,16 +1793,32 @@
evt.initEvent("TabRemotenessChange", true, false);
tab.dispatchEvent(evt);
return true;
]]>
</body>
</method>
+ <method name="switchBrowserIntoFreshProcess">
+ <parameter name="aBrowser"/>
+ <body>
+ <![CDATA[
+ if (!gMultiProcessBrowser) {
+ return this.updateBrowserRemoteness(aBrowser, false);
+ }
+
+ return this.updateBrowserRemoteness(aBrowser,
+ /* aShouldBeRemote */ true,
+ /* aOpener */ null,
+ /* aFreshProcess */ true);
+ ]]>
+ </body>
+ </method>
+
<method name="updateBrowserRemotenessByURL">
<parameter name="aBrowser"/>
<parameter name="aURL"/>
<body>
<![CDATA[
if (!gMultiProcessBrowser)
return this.updateBrowserRemoteness(aBrowser, false);
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -2609,17 +2609,23 @@ var SessionStoreInternal = {
let window = tab.ownerGlobal;
// The tab or its window might be gone.
if (!window || !window.__SSi || window.closed) {
return;
}
let tabState = TabState.clone(tab);
- let options = {restoreImmediately: true};
+ let options = {
+ restoreImmediately: true,
+ // We want to make sure that this information is passed to restoreTab
+ // whether or not a historyIndex is passed in. Thus, we extract it from
+ // the loadArguments.
+ reloadInFreshProcess: !!recentLoadArguments.reloadInFreshProcess,
+ };
if (historyIndex >= 0) {
tabState.index = historyIndex + 1;
tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
} else {
options.loadArguments = recentLoadArguments;
}
@@ -3269,16 +3275,17 @@ var SessionStoreInternal = {
"must reset tab before calling restoreTab()");
let restoreImmediately = options.restoreImmediately;
let loadArguments = options.loadArguments;
let browser = tab.linkedBrowser;
let window = tab.ownerGlobal;
let tabbrowser = window.gBrowser;
let forceOnDemand = options.forceOnDemand;
+ let reloadInFreshProcess = options.reloadInFreshProcess;
let willRestoreImmediately = restoreImmediately ||
tabbrowser.selectedBrowser == browser ||
loadArguments;
if (!willRestoreImmediately && !forceOnDemand) {
TabRestoreQueue.add(tab);
}
@@ -3391,37 +3398,36 @@ var SessionStoreInternal = {
// Restore tab attributes.
if ("attributes" in tabData) {
TabAttributes.set(tab, tabData.attributes);
}
// This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but
// it ensures each window will have its selected tab loaded.
if (willRestoreImmediately) {
- this.restoreTabContent(tab, loadArguments);
+ this.restoreTabContent(tab, loadArguments, reloadInFreshProcess);
} else if (!forceOnDemand) {
this.restoreNextTab();
}
// Decrease the busy state counter after we're done.
this._setWindowStateReady(window);
},
/**
* Kicks off restoring the given tab.
*
* @param aTab
* the tab to restore
* @param aLoadArguments
* optional load arguments used for loadURI()
- * @param aRemotenessSwitch
- * true if we're restoring a tab's content because we flipped
- * its remoteness (out-of-process) state.
+ * @param aReloadInFreshProcess
+ * true if we want to reload into a fresh process
*/
- restoreTabContent: function (aTab, aLoadArguments = null) {
+ restoreTabContent: function (aTab, aLoadArguments = null, aReloadInFreshProcess = false) {
if (aTab.hasAttribute("customizemode")) {
return;
}
let browser = aTab.linkedBrowser;
let window = aTab.ownerGlobal;
let tabbrowser = window.gBrowser;
let tabData = TabState.clone(aTab);
@@ -3436,17 +3442,23 @@ var SessionStoreInternal = {
}
// We have to mark this tab as restoring first, otherwise
// the "pending" attribute will be applied to the linked
// browser, which removes it from the display list. We cannot
// flip the remoteness of any browser that is not being displayed.
this.markTabAsRestoring(aTab);
- let isRemotenessUpdate = tabbrowser.updateBrowserRemotenessByURL(browser, uri);
+ let isRemotenessUpdate = false;
+ if (aReloadInFreshProcess) {
+ isRemotenessUpdate = tabbrowser.switchBrowserIntoFreshProcess(browser);
+ } else {
+ isRemotenessUpdate = tabbrowser.updateBrowserRemotenessByURL(browser, uri);
+ }
+
if (isRemotenessUpdate) {
// We updated the remoteness, so we need to send the history down again.
//
// Start a new epoch to discard all frame script messages relating to a
// previous epoch. All async messages that are still on their way to chrome
// will be ignored and don't override any tab data set when restoring.
let epoch = this.startNextEpoch(browser);
--- a/browser/modules/E10SUtils.jsm
+++ b/browser/modules/E10SUtils.jsm
@@ -91,27 +91,28 @@ this.E10SUtils = {
// Inner frames should always load in the current process
if (aDocShell.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeParent)
return true;
// If the URI can be loaded in the current process then continue
return this.canLoadURIInProcess(aURI.spec, Services.appinfo.processType);
},
- redirectLoad: function(aDocShell, aURI, aReferrer) {
+ redirectLoad: function(aDocShell, aURI, aReferrer, aFreshProcess) {
// Retarget the load to the correct process
let messageManager = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
let sessionHistory = aDocShell.getInterface(Ci.nsIWebNavigation).sessionHistory;
messageManager.sendAsyncMessage("Browser:LoadURI", {
loadOptions: {
uri: aURI.spec,
flags: Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
referrer: aReferrer ? aReferrer.spec : null,
+ reloadInFreshProcess: !!aFreshProcess,
},
historyIndex: sessionHistory.requestedIndex,
});
return false;
},
wrapHandlingUserInput: function(aWindow, aIsHandling, aCallback) {
var handlingUserInput;
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -44,11 +44,10 @@ DocGroup::DocGroup(TabGroup* aTabGroup,
DocGroup::~DocGroup()
{
MOZ_ASSERT(mDocuments.IsEmpty());
mTabGroup->mDocGroups.RemoveEntry(mKey);
}
NS_IMPL_ISUPPORTS(DocGroup, nsISupports)
-
}
}
--- a/dom/base/TabGroup.cpp
+++ b/dom/base/TabGroup.cpp
@@ -111,16 +111,30 @@ TabGroup::FindItemWithName(const nsAStri
break;
}
}
}
return NS_OK;
}
+nsTArray<nsPIDOMWindowOuter*>
+TabGroup::GetTopLevelWindows()
+{
+ nsTArray<nsPIDOMWindowOuter*> array;
+
+ for (nsPIDOMWindowOuter* outerWindow : mWindows) {
+ if (!outerWindow->GetScriptableParentOrNull()) {
+ array.AppendElement(outerWindow);
+ }
+ }
+
+ return array;
+}
+
NS_IMPL_ISUPPORTS(TabGroup, nsISupports)
TabGroup::HashEntry::HashEntry(const nsACString* aKey)
: nsCStringHashKey(aKey), mDocGroup(nullptr)
{}
}
}
--- a/dom/base/TabGroup.h
+++ b/dom/base/TabGroup.h
@@ -92,16 +92,18 @@ public:
// It is illegal to pass in the special case-insensitive names "_blank",
// "_self", "_parent" or "_top", as those should be handled elsewhere.
nsresult
FindItemWithName(const nsAString& aName,
nsIDocShellTreeItem* aRequestor,
nsIDocShellTreeItem* aOriginalRequestor,
nsIDocShellTreeItem** aFoundItem);
+ nsTArray<nsPIDOMWindowOuter*> GetTopLevelWindows();
+
private:
~TabGroup();
DocGroupMap mDocGroups;
nsTArray<nsPIDOMWindowOuter*> mWindows;
};
} // namespace dom
} // namespace mozilla
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -109,16 +109,17 @@
#include "nsIChromeRegistry.h"
#include "nsIConsoleService.h"
#include "nsIContent.h"
#include "nsIContentInlines.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIContentSink.h"
#include "nsIContentViewer.h"
#include "nsIDocShell.h"
+#include "nsIDocShellTreeOwner.h"
#include "nsIDocument.h"
#include "nsIDocumentEncoder.h"
#include "nsIDOMChromeWindow.h"
#include "nsIDOMDocument.h"
#include "nsIDOMDocumentType.h"
#include "nsIDOMEvent.h"
#include "nsIDOMElement.h"
#include "nsIDOMHTMLElement.h"
@@ -204,16 +205,18 @@
#include "HTMLSplitOnSpacesTokenizer.h"
#include "nsContentTypeParser.h"
#include "nsICookiePermission.h"
#include "mozIThirdPartyUtil.h"
#include "nsICookieService.h"
#include "mozilla/EnumSet.h"
#include "mozilla/BloomFilter.h"
#include "TabChild.h"
+#include "mozilla/dom/DocGroup.h"
+#include "mozilla/dom/TabGroup.h"
#include "nsIBidiKeyboard.h"
#if defined(XP_WIN)
// Undefine LoadImage to prevent naming conflict with Windows.
#undef LoadImage
#endif
@@ -9628,8 +9631,145 @@ nsContentUtils::GetCustomPrototype(nsIDo
RefPtr<CustomElementRegistry> registry(window->CustomElements());
if (!registry) {
return;
}
return registry->GetCustomPrototype(aAtom, aPrototype);
}
+
+/* static */ bool
+nsContentUtils::AttemptLargeAllocationLoad(nsIHttpChannel* aChannel)
+{
+ MOZ_ASSERT(aChannel);
+
+ nsCOMPtr<nsILoadGroup> loadGroup;
+ nsresult rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+ if (NS_WARN_IF(NS_FAILED(rv) || !loadGroup)) {
+ return false;
+ }
+
+ nsCOMPtr<nsIInterfaceRequestor> callbacks;
+ rv = loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+ if (NS_WARN_IF(NS_FAILED(rv) || !callbacks)) {
+ return false;
+ }
+
+ nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
+ if (NS_WARN_IF(!loadContext)) {
+ return false;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> window;
+ rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
+ if (NS_WARN_IF(NS_FAILED(rv) || !window)) {
+ return false;
+ }
+
+ nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(window);
+ if (NS_WARN_IF(!outer)) {
+ return false;
+ }
+
+ nsIDocument* doc = outer->GetExtantDoc();
+
+ // If we aren't in the content process, we cannot perform a cross-process
+ // load, so abort right away
+ if (NS_WARN_IF(!XRE_IsContentProcess())) {
+ if (doc) {
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("DOM"),
+ doc,
+ nsContentUtils::eDOM_PROPERTIES,
+ "LargeAllocationNonE10S");
+ }
+ return false;
+ }
+
+ // Check if we are a toplevel window
+ if (NS_WARN_IF(outer->GetScriptableParentOrNull())) {
+ if (doc) {
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("DOM"),
+ doc,
+ nsContentUtils::eDOM_PROPERTIES,
+ "LargeAllocationInIFrame");
+ }
+ return false;
+ }
+
+ // If we have any other toplevel windows in our tab group, then we cannot
+ // perform the navigation.
+ TabGroup* tabGroup = nsGlobalWindow::Cast(outer)->TabGroup();
+ nsTArray<nsPIDOMWindowOuter*> toplevelWindows = tabGroup->GetTopLevelWindows();
+ if (NS_WARN_IF(toplevelWindows.Length() > 1)) {
+ if (doc) {
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("DOM"),
+ doc,
+ nsContentUtils::eDOM_PROPERTIES,
+ "LargeAllocationRelatedBrowsingContexts");
+ }
+ return false;
+ }
+ MOZ_ASSERT(toplevelWindows.Length() == 1);
+ MOZ_ASSERT(toplevelWindows[0] == outer);
+
+ // Get the request method, and check if it is a GET request. If it is not GET,
+ // then we cannot perform a large allocation load.
+ nsAutoCString requestMethod;
+ rv = aChannel->GetRequestMethod(requestMethod);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ if (NS_WARN_IF(!requestMethod.LowerCaseEqualsLiteral("get"))) {
+ if (doc) {
+ nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("DOM"),
+ doc,
+ nsContentUtils::eDOM_PROPERTIES,
+ "LargeAllocationNonGetRequest");
+ }
+ return false;
+ }
+
+ TabChild* tabChild = TabChild::GetFrom(outer);
+ NS_ENSURE_TRUE(tabChild, false);
+
+ if (tabChild->TakeIsFreshProcess()) {
+ NS_WARNING("Already in a fresh process, ignoring Large-Allocation header!");
+ if (doc) {
+ nsContentUtils::ReportToConsole(nsIScriptError::infoFlag,
+ NS_LITERAL_CSTRING("DOM"),
+ doc,
+ nsContentUtils::eDOM_PROPERTIES,
+ "LargeAllocationSuccess");
+ }
+ return false;
+ }
+
+ // At this point the fress process load should succeed! We just need to get
+ // ourselves a nsIWebBrowserChrome3 to ask to perform the reload. We should
+ // have one, as we have already confirmed that we are running in a content
+ // process.
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ outer->GetDocShell()->GetTreeOwner(getter_AddRefs(treeOwner));
+ NS_ENSURE_TRUE(treeOwner, false);
+
+ nsCOMPtr<nsIWebBrowserChrome3> wbc3 = do_GetInterface(treeOwner);
+ NS_ENSURE_TRUE(wbc3, false);
+
+ nsCOMPtr<nsIURI> uri;
+ rv = aChannel->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, false);
+ NS_ENSURE_TRUE(uri, false);
+
+ nsCOMPtr<nsIURI> referrer;
+ rv = aChannel->GetReferrer(getter_AddRefs(referrer));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ // Actually perform the cross process load
+ bool reloadSucceeded = false;
+ rv = wbc3->ReloadInFreshProcess(outer->GetDocShell(), uri, referrer, &reloadSucceeded);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ return reloadSucceeded;
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2700,16 +2700,18 @@ public:
mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
mozilla::dom::CustomElementDefinition* aDefinition = nullptr);
static void GetCustomPrototype(nsIDocument* aDoc,
int32_t aNamespaceID,
nsIAtom* aAtom,
JS::MutableHandle<JSObject*> prototype);
+ static bool AttemptLargeAllocationLoad(nsIHttpChannel* aChannel);
+
private:
static bool InitializeEventTable();
static nsresult EnsureStringBundle(PropertiesFile aFile);
static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
nsIPrincipal* aPrincipal);
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -169,20 +169,27 @@ nsFrameLoader::nsFrameLoader(Element* aO
, mHideCalled(false)
, mNetworkCreated(aNetworkCreated)
, mRemoteBrowserShown(false)
, mRemoteFrame(false)
, mClipSubdocument(true)
, mClampScrollPosition(true)
, mObservingOwnerContent(false)
, mVisible(true)
+ , mFreshProcess(false)
{
mRemoteFrame = ShouldUseRemoteProcess();
MOZ_ASSERT(!mRemoteFrame || !aOpener,
"Cannot pass aOpener for a remote frame!");
+
+ // Check if we are supposed to load into a fresh process
+ mFreshProcess = mOwnerContent->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::freshProcess,
+ nsGkAtoms::_true,
+ eCaseMatters);
}
nsFrameLoader::~nsFrameLoader()
{
if (mMessageManager) {
mMessageManager->Disconnect();
}
MOZ_RELEASE_ASSERT(mDestroyCalled);
@@ -2722,17 +2729,18 @@ nsFrameLoader::TryRemoteBrowser()
PROFILER_LABEL("nsFrameLoader", "CreateRemoteBrowser",
js::ProfileEntry::Category::OTHER);
MutableTabContext context;
nsresult rv = GetNewTabContext(&context);
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<Element> ownerElement = mOwnerContent;
- mRemoteBrowser = ContentParent::CreateBrowserOrApp(context, ownerElement, openerContentParent);
+ mRemoteBrowser = ContentParent::CreateBrowserOrApp(context, ownerElement,
+ openerContentParent, mFreshProcess);
if (!mRemoteBrowser) {
return false;
}
MaybeUpdatePrimaryTabParent(eTabParentChanged);
mChildID = mRemoteBrowser->Manager()->ChildID();
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -392,11 +392,12 @@ private:
bool mClipSubdocument : 1;
bool mClampScrollPosition : 1;
bool mObservingOwnerContent : 1;
// Backs nsIFrameLoader::{Get,Set}Visible. Visibility state here relates to
// whether this frameloader's <iframe mozbrowser> is setVisible(true)'ed, and
// doesn't necessarily correlate with docshell/document visibility.
bool mVisible : 1;
+ bool mFreshProcess : 1;
};
#endif
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -414,16 +414,17 @@ GK_ATOM(formnovalidate, "formnovalidate"
GK_ATOM(formtarget, "formtarget")
GK_ATOM(frame, "frame")
GK_ATOM(frameborder, "frameborder")
GK_ATOM(frameset, "frameset")
GK_ATOM(from, "from")
GK_ATOM(fullscreenchange, "fullscreenchange")
GK_ATOM(fullscreenerror, "fullscreenerror")
GK_ATOM(functionAvailable, "function-available")
+GK_ATOM(freshProcess, "freshProcess")
GK_ATOM(generateId, "generate-id")
GK_ATOM(getter, "getter")
GK_ATOM(glyphchar, "glyphchar")
GK_ATOM(glyphid, "glyphid")
GK_ATOM(grid, "grid")
GK_ATOM(grippy, "grippy")
GK_ATOM(group, "group")
GK_ATOM(groupingSeparator, "grouping-separator")
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -514,16 +514,17 @@ ContentParentsMemoryReporter::CollectRep
numQueuedMessages, desc, aData);
}
return NS_OK;
}
nsDataHashtable<nsStringHashKey, ContentParent*>* ContentParent::sAppContentParents;
nsTArray<ContentParent*>* ContentParent::sNonAppContentParents;
+nsTArray<ContentParent*>* ContentParent::sLargeAllocationContentParents;
nsTArray<ContentParent*>* ContentParent::sPrivateContent;
StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents;
#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
UniquePtr<SandboxBrokerPolicyFactory> ContentParent::sSandboxBrokerPolicyFactory;
#endif
// This is true when subprocess launching is enabled. This is the
// case between StartUp() and ShutDown() or JoinAllSubprocesses().
@@ -743,58 +744,77 @@ ContentParent::JoinAllSubprocesses()
}
sCanLaunchSubprocesses = false;
}
/*static*/ already_AddRefed<ContentParent>
ContentParent::GetNewOrUsedBrowserProcess(bool aForBrowserElement,
ProcessPriority aPriority,
- ContentParent* aOpener)
-{
- if (!sNonAppContentParents)
- sNonAppContentParents = new nsTArray<ContentParent*>();
-
- int32_t maxContentProcesses = Preferences::GetInt("dom.ipc.processCount", 1);
- if (maxContentProcesses < 1)
- maxContentProcesses = 1;
-
- if (sNonAppContentParents->Length() >= uint32_t(maxContentProcesses)) {
- uint32_t startIdx = rand() % sNonAppContentParents->Length();
+ ContentParent* aOpener,
+ bool aFreshProcess)
+{
+ nsTArray<ContentParent*>* contentParents;
+ int32_t maxContentParents;
+
+ // Decide which pool of content parents we are going to be pulling from based
+ // on the aFreshProcess flag.
+ if (aFreshProcess) {
+ if (!sLargeAllocationContentParents) {
+ sLargeAllocationContentParents = new nsTArray<ContentParent*>();
+ }
+ contentParents = sLargeAllocationContentParents;
+
+ maxContentParents = Preferences::GetInt("dom.ipc.dedicatedProcessCount", 2);
+ } else {
+ if (!sNonAppContentParents) {
+ sNonAppContentParents = new nsTArray<ContentParent*>();
+ }
+ contentParents = sNonAppContentParents;
+
+ maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1);
+ }
+
+ if (maxContentParents < 1) {
+ maxContentParents = 1;
+ }
+
+ if (contentParents->Length() >= uint32_t(maxContentParents)) {
+ uint32_t startIdx = rand() % contentParents->Length();
uint32_t currIdx = startIdx;
do {
- RefPtr<ContentParent> p = (*sNonAppContentParents)[currIdx];
- NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContntParents?");
- if (p->mOpener == aOpener) {
- return p.forget();
- }
- currIdx = (currIdx + 1) % sNonAppContentParents->Length();
+ RefPtr<ContentParent> p = (*contentParents)[currIdx];
+ NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContntParents?");
+ if (p->mOpener == aOpener) {
+ return p.forget();
+ }
+ currIdx = (currIdx + 1) % contentParents->Length();
} while (currIdx != startIdx);
}
// Try to take and transform the preallocated process into browser.
RefPtr<ContentParent> p = PreallocatedProcessManager::Take();
if (p) {
p->TransformPreallocatedIntoBrowser(aOpener);
} else {
// Failed in using the preallocated process: fork from the chrome process.
p = new ContentParent(/* app = */ nullptr,
aOpener,
aForBrowserElement,
- /* isForPreallocated = */ false);
+ /* isForPreallocated = */ false);
if (!p->LaunchSubprocess(aPriority)) {
return nullptr;
}
p->Init();
}
p->ForwardKnownInfo();
- sNonAppContentParents->AppendElement(p);
+ contentParents->AppendElement(p);
return p.forget();
}
/*static*/ ProcessPriority
ContentParent::GetInitialProcessPriority(Element* aFrameElement)
{
// Frames with mozapptype == critical which are expecting a system message
// get FOREGROUND_HIGH priority.
@@ -1043,17 +1063,18 @@ ContentParent::RecvInitVideoDecoderManag
{
GPUProcessManager::Get()->CreateContentVideoDecoderManager(OtherPid(), aEndpoint);
return true;
}
/*static*/ TabParent*
ContentParent::CreateBrowserOrApp(const TabContext& aContext,
Element* aFrameElement,
- ContentParent* aOpenerContentParent)
+ ContentParent* aOpenerContentParent,
+ bool aFreshProcess)
{
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
if (!sCanLaunchSubprocesses) {
return nullptr;
}
if (TabParent* parent = TabParent::GetNextTabParent()) {
@@ -1078,17 +1099,19 @@ ContentParent::CreateBrowserOrApp(const
constructorSender = CreateContentBridgeParent(aContext, initialPriority,
openerTabId, &tabId);
} else {
if (aOpenerContentParent) {
constructorSender = aOpenerContentParent;
} else {
constructorSender =
GetNewOrUsedBrowserProcess(aContext.IsMozBrowserElement(),
- initialPriority);
+ initialPriority,
+ nullptr,
+ aFreshProcess);
if (!constructorSender) {
return nullptr;
}
}
tabId = AllocateTabId(openerTabId,
aContext.AsIPCTabContext(),
constructorSender->ChildID());
}
@@ -1126,16 +1149,20 @@ ContentParent::CreateBrowserOrApp(const
// DeallocPBrowserParent() releases this ref.
tp.forget().take(), tabId,
aContext.AsIPCTabContext(),
chromeFlags,
constructorSender->ChildID(),
constructorSender->IsForApp(),
constructorSender->IsForBrowser());
+ if (aFreshProcess) {
+ Unused << browser->SendSetFreshProcess();
+ }
+
if (browser) {
RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser);
constructedTabParent->SetOwnerElement(aFrameElement);
return constructedTabParent;
}
}
return nullptr;
}
@@ -1238,16 +1265,20 @@ ContentParent::CreateBrowserOrApp(const
RefPtr<TabParent>(tp).forget().take(),
tabId,
aContext.AsIPCTabContext(),
chromeFlags,
parent->ChildID(),
parent->IsForApp(),
parent->IsForBrowser());
+ if (aFreshProcess) {
+ Unused << browser->SendSetFreshProcess();
+ }
+
if (browser) {
RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser);
constructedTabParent->SetOwnerElement(aFrameElement);
}
if (isInContentProcess) {
// Just return directly without the following check in content process.
return TabParent::GetFrom(browser);
@@ -1599,21 +1630,31 @@ ContentParent::MarkAsDead()
if (!mAppManifestURL.IsEmpty()) {
if (sAppContentParents) {
sAppContentParents->Remove(mAppManifestURL);
if (!sAppContentParents->Count()) {
delete sAppContentParents;
sAppContentParents = nullptr;
}
}
- } else if (sNonAppContentParents) {
- sNonAppContentParents->RemoveElement(this);
- if (!sNonAppContentParents->Length()) {
- delete sNonAppContentParents;
- sNonAppContentParents = nullptr;
+ } else {
+ if (sNonAppContentParents) {
+ sNonAppContentParents->RemoveElement(this);
+ if (!sNonAppContentParents->Length()) {
+ delete sNonAppContentParents;
+ sNonAppContentParents = nullptr;
+ }
+ }
+
+ if (sLargeAllocationContentParents) {
+ sLargeAllocationContentParents->RemoveElement(this);
+ if (!sLargeAllocationContentParents->Length()) {
+ delete sLargeAllocationContentParents;
+ sLargeAllocationContentParents = nullptr;
+ }
}
}
if (sPrivateContent) {
sPrivateContent->RemoveElement(this);
if (!sPrivateContent->Length()) {
delete sPrivateContent;
sPrivateContent = nullptr;
@@ -2128,18 +2169,20 @@ ContentParent::~ContentParent()
mForceKillTimer->Cancel();
}
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// We should be removed from all these lists in ActorDestroy.
MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this));
if (mAppManifestURL.IsEmpty()) {
- MOZ_ASSERT(!sNonAppContentParents ||
- !sNonAppContentParents->Contains(this));
+ MOZ_ASSERT((!sNonAppContentParents ||
+ !sNonAppContentParents->Contains(this)) &&
+ (!sLargeAllocationContentParents ||
+ !sLargeAllocationContentParents->Contains(this)));
} else {
// In general, we expect sAppContentParents->Get(mAppManifestURL) to be
// nullptr. But it could be that we created another ContentParent for
// this app after we did this->ActorDestroy(), so the right check is
// that sAppContentParents->Get(mAppManifestURL) != this.
MOZ_ASSERT(!sAppContentParents ||
sAppContentParents->Get(mAppManifestURL) != this);
}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -130,32 +130,34 @@ public:
* 1. browser iframe
* 2. remote xul <browser>
* 3. normal iframe
*/
static already_AddRefed<ContentParent>
GetNewOrUsedBrowserProcess(bool aForBrowserElement = false,
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
- ContentParent* aOpener = nullptr);
+ ContentParent* aOpener = nullptr,
+ bool aFreshProcess = false);
/**
* Create a subprocess suitable for use as a preallocated app process.
*/
static already_AddRefed<ContentParent> PreallocateAppProcess();
/**
* Get or create a content process for the given TabContext. aFrameElement
* should be the frame/iframe element with which this process will
* associated.
*/
static TabParent*
CreateBrowserOrApp(const TabContext& aContext,
Element* aFrameElement,
- ContentParent* aOpenerContentParent);
+ ContentParent* aOpenerContentParent,
+ bool aFreshProcess = false);
static void GetAll(nsTArray<ContentParent*>& aArray);
static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);
enum CPIteratorPolicy {
eLive,
eAll
@@ -568,16 +570,17 @@ protected:
bool ShouldContinueFromReplyTimeout() override;
void OnVarChanged(const GfxVarUpdate& aVar) override;
void OnCompositorUnexpectedShutdown() override;
private:
static nsDataHashtable<nsStringHashKey, ContentParent*> *sAppContentParents;
static nsTArray<ContentParent*>* sNonAppContentParents;
+ static nsTArray<ContentParent*>* sLargeAllocationContentParents;
static nsTArray<ContentParent*>* sPrivateContent;
static StaticAutoPtr<LinkedList<ContentParent> > sContentParents;
static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
Monitor* aMonitor, bool* aDone);
// Take the preallocated process and transform it into a "real" app process,
// for the specified manifest URL. If there is no preallocated process (or
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -898,16 +898,22 @@ child:
uint32_t aTargetLocalIndex);
/**
* Notify that the session history asssociates to this PBrowser has become
* an inactive history in the grouped session history.
*/
async NotifyPartialSessionHistoryDeactive();
+ /**
+ * Tell the child that it is a fresh process created for a Large-Allocation
+ * load.
+ */
+ async SetFreshProcess();
+
/*
* FIXME: write protocol!
state LIVE:
send LoadURL goto LIVE;
//etc.
send Destroy goto DYING;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -114,16 +114,18 @@
#include "nsDeviceContext.h"
#include "nsSandboxFlags.h"
#include "FrameLayerBuilder.h"
#include "VRManagerChild.h"
#include "nsICommandParams.h"
#include "nsISHistory.h"
#include "nsQueryObject.h"
#include "GroupedSHistory.h"
+#include "nsIHttpChannel.h"
+#include "mozilla/dom/DocGroup.h"
#ifdef NS_PRINTING
#include "nsIPrintSession.h"
#include "nsIPrintSettings.h"
#include "nsIPrintSettingsService.h"
#include "nsIWebBrowserPrint.h"
#endif
@@ -550,16 +552,17 @@ TabChild::TabChild(nsIContentChild* aMan
, mDPI(0)
, mRounding(0)
, mDefaultScale(0)
, mIsTransparent(false)
, mIPCOpen(false)
, mParentIsActive(false)
, mDidSetRealShowInfo(false)
, mDidLoadURLInit(false)
+ , mIsFreshProcess(false)
, mLayerObserverEpoch(0)
#if defined(XP_WIN) && defined(ACCESSIBILITY)
, mNativeWindowHandle(0)
#endif
{
// In the general case having the TabParent tell us if APZ is enabled or not
// doesn't really work because the TabParent itself may not have a reference
// to the owning widget during initialization. Instead we assume that this
@@ -3334,16 +3337,23 @@ TabChild::RecvThemeChanged(nsTArray<Look
RefPtr<nsPresContext> presContext = presShell->GetPresContext();
if (presContext) {
presContext->ThemeChanged();
}
}
return true;
}
+bool
+TabChild::RecvSetFreshProcess()
+{
+ mIsFreshProcess = true;
+ return true;
+}
+
mozilla::plugins::PPluginWidgetChild*
TabChild::AllocPPluginWidgetChild()
{
return new mozilla::plugins::PluginWidgetChild();
}
bool
TabChild::DeallocPPluginWidgetChild(mozilla::plugins::PPluginWidgetChild* aActor)
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -38,16 +38,17 @@
#include "AudioChannelService.h"
#include "PuppetWidget.h"
#include "mozilla/layers/GeckoContentController.h"
#include "nsISHistoryListener.h"
#include "nsIPartialSHistoryListener.h"
class nsICachedFileDescriptorListener;
class nsIDOMWindowUtils;
+class nsIHttpChannel;
namespace mozilla {
namespace layout {
class RenderFrameChild;
} // namespace layout
namespace layers {
class APZChild;
@@ -670,16 +671,23 @@ public:
// Request that the docshell be marked as active.
void ForcePaint(uint64_t aLayerObserverEpoch);
#if defined(XP_WIN) && defined(ACCESSIBILITY)
uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
#endif
+ bool TakeIsFreshProcess()
+ {
+ bool wasFreshProcess = mIsFreshProcess;
+ mIsFreshProcess = false;
+ return wasFreshProcess;
+ }
+
protected:
virtual ~TabChild();
virtual PRenderFrameChild* AllocPRenderFrameChild() override;
virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
virtual bool RecvDestroy() override;
@@ -707,16 +715,18 @@ protected:
virtual bool RecvNotifyAttachGroupedSessionHistory(const uint32_t& aOffset) override;
virtual bool RecvNotifyPartialSessionHistoryActive(const uint32_t& aGlobalLength,
const uint32_t& aTargetLocalIndex) override;
virtual bool RecvNotifyPartialSessionHistoryDeactive() override;
+ virtual bool RecvSetFreshProcess() override;
+
private:
void HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
const ScrollableLayerGuid& aGuid);
// Notify others that our TabContext has been updated.
//
// You should call this after calling TabContext::SetTabContext(). We also
// call this during Init().
@@ -799,16 +809,17 @@ private:
bool mIsTransparent;
bool mIPCOpen;
bool mParentIsActive;
bool mAsyncPanZoomEnabled;
CSSSize mUnscaledInnerSize;
bool mDidSetRealShowInfo;
bool mDidLoadURLInit;
+ bool mIsFreshProcess;
AutoTArray<bool, NUMBER_OF_AUDIO_CHANNELS> mAudioChannelsActive;
RefPtr<layers::IAPZCTreeManager> mApzcTreeManager;
// APZChild clears this pointer from its destructor, so it shouldn't be a
// dangling pointer.
layers::APZChild* mAPZChild;
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -305,8 +305,18 @@ BiquadFilterChannelCountChangeWarning=Bi
UnanimatablePacedProperty=Paced property ā%1$Sā is not an animatable property.
# LOCALIZATION NOTE: Do not translate ".jpeg"
GenericImageNameJPEG=image.jpeg
# LOCALIZATION NOTE: Do not translate ".gif"
GenericImageNameGIF=image.gif
# LOCALIZATION NOTE: Do not translate ".png"
GenericImageNamePNG=image.png
GenericFileName=file
+# LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name
+LargeAllocationSuccess=This page was loaded in a new process due to a Large-Allocation header.
+# LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name. Do not translate GET.
+LargeAllocationNonGetRequest=A Large-Allocation header was ignored due to the load being triggered by a non-GET request.
+# LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name
+LargeAllocationRelatedBrowsingContexts=A Large-Allocation header was ignored due to the presence of windows which have a reference to this browsing context.
+# LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name
+LargeAllocationInIFrame=A Large-Allocation header was ignored due to the load occuring within an iframe.
+# LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name
+LargeAllocationNonE10S=A Large-Allocation header was ignored due to the document not being loaded out of process.
--- a/embedding/browser/nsIWebBrowserChrome3.idl
+++ b/embedding/browser/nsIWebBrowserChrome3.idl
@@ -42,9 +42,20 @@ interface nsIWebBrowserChrome3 : nsIWebB
* @param aURI
* The URI being loaded.
* @param aReferrer
* The referrer of the load.
*/
bool shouldLoadURI(in nsIDocShell aDocShell,
in nsIURI aURI,
in nsIURI aReferrer);
+
+ /**
+ * Attempts to load the currently loaded page into a fresh process to increase
+ * available memory.
+ *
+ * @param aDocShell
+ * The docshell performing the load.
+ */
+ bool reloadInFreshProcess(in nsIDocShell aDocShell,
+ in nsIURI aURI,
+ in nsIURI aReferrer);
};
--- a/uriloader/base/moz.build
+++ b/uriloader/base/moz.build
@@ -1,14 +1,16 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+include('/ipc/chromium/chromium-config.mozbuild')
+
XPIDL_SOURCES += [
'nsCURILoader.idl',
'nsIContentHandler.idl',
'nsIDocumentLoader.idl',
'nsITransfer.idl',
'nsIURIContentListener.idl',
'nsIURILoader.idl',
'nsIWebProgress.idl',
--- a/uriloader/base/nsURILoader.cpp
+++ b/uriloader/base/nsURILoader.cpp
@@ -44,16 +44,17 @@
#include "nsIMIMEHeaderParam.h"
#include "nsNetCID.h"
#include "nsMimeTypes.h"
#include "nsDocLoader.h"
#include "mozilla/Attributes.h"
#include "mozilla/Preferences.h"
+#include "nsContentUtils.h"
mozilla::LazyLogModule nsURILoader::mLog("URILoader");
#define LOG(args) MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Debug, args)
#define LOG_ERROR(args) MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Error, args)
#define LOG_ENABLED() MOZ_LOG_TEST(nsURILoader::mLog, mozilla::LogLevel::Debug)
#define NS_PREF_DISABLE_BACKGROUND_HANDLING \
@@ -229,16 +230,33 @@ NS_IMETHODIMP nsDocumentOpenInfo::OnStar
return NS_OK;
}
LOG((" HTTP response status: %d", responseCode));
if (204 == responseCode || 205 == responseCode) {
return NS_BINDING_ABORTED;
}
+
+ static bool sLargeAllocationHeaderEnabled = false;
+ static bool sCachedLargeAllocationPref = false;
+ if (!sCachedLargeAllocationPref) {
+ sCachedLargeAllocationPref = true;
+ mozilla::Preferences::AddBoolVarCache(&sLargeAllocationHeaderEnabled,
+ "dom.largeAllocationHeader.enabled");
+ }
+
+ if (sLargeAllocationHeaderEnabled) {
+ // If we have a Large-Allocation header, let's check if we should perform a process switch.
+ nsAutoCString largeAllocationHeader;
+ rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Large-Allocation"), largeAllocationHeader);
+ if (NS_SUCCEEDED(rv) && nsContentUtils::AttemptLargeAllocationLoad(httpChannel)) {
+ return NS_BINDING_ABORTED;
+ }
+ }
}
//
// Make sure that the transaction has succeeded, so far...
//
nsresult status;
rv = request->GetStatus(&status);
--- a/xpfe/appshell/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -399,16 +399,26 @@ NS_IMETHODIMP nsContentTreeOwner::Should
if (xulBrowserWindow)
return xulBrowserWindow->ShouldLoadURI(aDocShell, aURI, aReferrer, _retval);
*_retval = true;
return NS_OK;
}
+NS_IMETHODIMP nsContentTreeOwner::ReloadInFreshProcess(nsIDocShell* aDocShell,
+ nsIURI* aURI,
+ nsIURI* aReferrer,
+ bool* aRetVal)
+{
+ NS_WARNING("Cannot reload in fresh process from a nsContentTreeOwner!");
+ *aRetVal = false;
+ return NS_OK;
+}
+
//*****************************************************************************
// nsContentTreeOwner::nsIWebBrowserChrome2
//*****************************************************************************
NS_IMETHODIMP nsContentTreeOwner::SetStatusWithContext(uint32_t aStatusType,
const nsAString &aStatusText,
nsISupports *aStatusContext)
{