Bug 1334210 - Handle Large-Allocation process exhaustion more smoothly, r=ehsan

MozReview-Commit-ID: KxnFTSXxWVi
This commit is contained in:
Michael Layzell 2017-01-26 14:20:44 -05:00
Родитель 149c8c2a40
Коммит 7a66b1dc95
11 изменённых файлов: 144 добавлений и 71 удалений

Просмотреть файл

@ -157,10 +157,12 @@ this.E10SUtils = {
if (aDocShell.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeParent)
return true;
// If we are in a fresh process, and it wouldn't be content visible to
// change processes, we want to load into a new process so that we can throw
// If we are in a Large-Allocation process, and it wouldn't be content visible
// to change processes, we want to load into a new process so that we can throw
// this one out.
if (aDocShell.inFreshProcess && aDocShell.isOnlyToplevelInTabGroup) {
if (aDocShell.inLargeAllocProcess &&
!aDocShell.awaitingLargeAlloc &&
aDocShell.isOnlyToplevelInTabGroup) {
return false;
}

Просмотреть файл

@ -14854,8 +14854,22 @@ nsDocShell::GetIsOnlyToplevelInTabGroup(bool* aResult)
}
NS_IMETHODIMP
nsDocShell::GetInFreshProcess(bool* aResult)
nsDocShell::GetAwaitingLargeAlloc(bool* aResult)
{
*aResult = TabChild::GetWasFreshProcess();
MOZ_ASSERT(aResult);
nsCOMPtr<nsITabChild> tabChild = GetTabChild();
if (!tabChild) {
*aResult = false;
return NS_OK;
}
*aResult = static_cast<TabChild*>(tabChild.get())->IsAwaitingLargeAlloc();
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetInLargeAllocProcess(bool* aResult)
{
MOZ_ASSERT(aResult);
*aResult = TabChild::InLargeAllocProcess();
return NS_OK;
}

Просмотреть файл

@ -1127,7 +1127,14 @@ interface nsIDocShell : nsIDocShellTreeItem
[infallible] readonly attribute boolean isOnlyToplevelInTabGroup;
/**
* Returns `true` if this docshell was created by a Large-Allocation load.
* Returns `true` if this docshell is loaded within a Large-Allocation
* process.
*/
[infallible] readonly attribute boolean inFreshProcess;
[infallible] readonly attribute boolean inLargeAllocProcess;
/**
* Returns `true` if this docshell was created due to a Large-Allocation
* header, and has not seen the initiating load yet.
*/
[infallible] readonly attribute boolean awaitingLargeAlloc;
};

Просмотреть файл

@ -9709,8 +9709,8 @@ nsContentUtils::AttemptLargeAllocationLoad(nsIHttpChannel* aChannel)
TabChild* tabChild = TabChild::GetFrom(outer->AsOuter());
NS_ENSURE_TRUE(tabChild, false);
if (tabChild->TakeIsFreshProcess()) {
NS_WARNING("Already in a fresh process, ignoring Large-Allocation header!");
if (tabChild->TakeAwaitingLargeAlloc()) {
NS_WARNING("In a Large-Allocation TabChild, ignoring Large-Allocation header!");
outer->SetLargeAllocStatus(LargeAllocStatus::SUCCESS);
return false;
}

Просмотреть файл

@ -34,7 +34,5 @@ interface nsITabChild : nsISupports
[array, size_is(linksCount)] in nsIDroppedLinkItem links);
readonly attribute uint64_t tabId;
readonly attribute bool isInFreshProcess;
};

Просмотреть файл

@ -580,8 +580,13 @@ ContentParent::JoinAllSubprocesses()
ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
ProcessPriority aPriority,
ContentParent* aOpener,
bool aLargeAllocationProcess)
bool aLargeAllocationProcess,
bool* aNew)
{
if (aNew) {
*aNew = false;
}
if (!sBrowserContentParents) {
sBrowserContentParents =
new nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>;
@ -622,6 +627,9 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
}
RefPtr<ContentParent> p = new ContentParent(aOpener, contentProcessType);
if (aNew) {
*aNew = true;
}
if (!p->LaunchSubprocess(aPriority)) {
return nullptr;
@ -901,6 +909,7 @@ ContentParent::CreateBrowser(const TabContext& aContext,
openerTabId = TabParent::GetTabIdFrom(docShell);
}
bool newProcess = false;
RefPtr<nsIContentParent> constructorSender;
if (isInContentProcess) {
MOZ_ASSERT(aContext.IsMozBrowserElement());
@ -918,7 +927,7 @@ ContentParent::CreateBrowser(const TabContext& aContext,
constructorSender =
GetNewOrUsedBrowserProcess(remoteType, initialPriority, nullptr,
aFreshProcess);
aFreshProcess, &newProcess);
if (!constructorSender) {
return nullptr;
}
@ -966,7 +975,10 @@ ContentParent::CreateBrowser(const TabContext& aContext,
constructorSender->IsForBrowser());
if (aFreshProcess) {
Unused << browser->SendSetFreshProcess();
// Tell the TabChild object that it was created due to a Large-Allocation
// request, and whether or not that Large-Allocation request succeeded at
// creating a new content process.
Unused << browser->SendSetIsLargeAllocation(true, newProcess);
}
if (browser) {

Просмотреть файл

@ -148,7 +148,8 @@ public:
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
ContentParent* aOpener = nullptr,
bool aLargeAllocationProcess = false);
bool aLargeAllocationProcess = false,
bool* anew = nullptr);
/**
* Get or create a content process for the given TabContext. aFrameElement

Просмотреть файл

@ -908,7 +908,7 @@ child:
* Tell the child that it is a fresh process created for a Large-Allocation
* load.
*/
async SetFreshProcess();
async SetIsLargeAllocation(bool aIsLA, bool aNewProcess);
/*
* FIXME: write protocol!

Просмотреть файл

@ -155,7 +155,7 @@ static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
static TabChildMap* sTabChildren;
bool TabChild::sWasFreshProcess = false;
bool TabChild::sInLargeAllocProcess = false;
TabChildBase::TabChildBase()
: mTabChildGlobal(nullptr)
@ -381,7 +381,7 @@ TabChild::TabChild(nsIContentChild* aManager,
, mParentIsActive(false)
, mDidSetRealShowInfo(false)
, mDidLoadURLInit(false)
, mIsFreshProcess(false)
, mAwaitingLA(false)
, mSkipKeyPress(false)
, mLayerObserverEpoch(0)
#if defined(XP_WIN) && defined(ACCESSIBILITY)
@ -3100,19 +3100,25 @@ TabChild::RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache)
}
mozilla::ipc::IPCResult
TabChild::RecvSetFreshProcess()
TabChild::RecvSetIsLargeAllocation(const bool& aIsLA, const bool& aNewProcess)
{
MOZ_ASSERT(!sWasFreshProcess, "Can only be a fresh process once!");
mIsFreshProcess = true;
mAwaitingLA = aIsLA;
sInLargeAllocProcess = aIsLA && aNewProcess;
return IPC_OK();
}
NS_IMETHODIMP
TabChild::GetIsInFreshProcess(bool* aResult)
bool
TabChild::IsAwaitingLargeAlloc()
{
MOZ_ASSERT(aResult);
*aResult = mIsFreshProcess || sWasFreshProcess;
return NS_OK;
return mAwaitingLA;
}
bool
TabChild::TakeAwaitingLargeAlloc()
{
bool awaiting = mAwaitingLA;
mAwaitingLA = false;
return awaiting;
}
mozilla::plugins::PPluginWidgetChild*

Просмотреть файл

@ -661,30 +661,23 @@ public:
uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
#endif
bool TakeIsFreshProcess()
// These methods return `true` if this TabChild is currently awaiting a
// Large-Allocation header.
bool TakeAwaitingLargeAlloc();
bool IsAwaitingLargeAlloc();
// Returns `true` if this this process was created to load a docshell in a
// "Fresh Process". This value is initialized to `false`, and is set to `true`
// in RecvSetFreshProcess.
static bool InLargeAllocProcess()
{
if (mIsFreshProcess) {
MOZ_ASSERT(!sWasFreshProcess,
"At most one tabGroup may be a fresh process per process");
sWasFreshProcess = true;
mIsFreshProcess = false;
return true;
}
return false;
return sInLargeAllocProcess;
}
already_AddRefed<nsISHistory> GetRelatedSHistory();
mozilla::dom::TabGroup* TabGroup();
// Returns `true` if this this process was created to load a docshell in a
// "Fresh Process". This value is initialized to `false`, and is set to `true`
// in RecvSetFreshProcess.
static bool GetWasFreshProcess()
{
return sWasFreshProcess;
}
protected:
virtual ~TabChild();
@ -722,7 +715,8 @@ protected:
virtual mozilla::ipc::IPCResult RecvNotifyPartialSHistoryDeactive() override;
virtual mozilla::ipc::IPCResult RecvSetFreshProcess() override;
virtual mozilla::ipc::IPCResult RecvSetIsLargeAllocation(const bool& aIsLA,
const bool& aNewProcess) override;
private:
void HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
@ -813,7 +807,7 @@ private:
CSSSize mUnscaledInnerSize;
bool mDidSetRealShowInfo;
bool mDidLoadURLInit;
bool mIsFreshProcess;
bool mAwaitingLA;
bool mSkipKeyPress;
@ -834,7 +828,7 @@ private:
uintptr_t mNativeWindowHandle;
#endif // defined(XP_WIN)
static bool sWasFreshProcess;
static bool sInLargeAllocProcess;
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
};

Просмотреть файл

@ -46,12 +46,10 @@ function getPID(aBrowser) {
});
}
function getIsFreshProcess(aBrowser) {
function getInLAProc(aBrowser) {
return ContentTask.spawn(aBrowser, null, () => {
try {
return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsITabChild)
.isInFreshProcess;
return docShell.inLargeAllocProcess;
} catch (e) {
// This must be a non-remote browser, which means it is not fresh
return false;
@ -76,7 +74,7 @@ add_task(function*() {
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 0");
let pid1 = yield getPID(aBrowser);
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
let epc = expectProcessCreated();
yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
@ -89,7 +87,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2, "The pids should be different between the initial load and the new load");
is(true, yield getIsFreshProcess(aBrowser));
is(true, yield getInLAProc(aBrowser));
});
// When a Large-Allocation document is loaded in an iframe, the header should
@ -97,7 +95,7 @@ add_task(function*() {
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 1");
let pid1 = yield getPID(aBrowser);
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
// Fail the test if we create a process
let stopExpectNoProcess = expectNoProcess();
@ -116,7 +114,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
is(pid1, pid2, "The PID should not have changed");
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
stopExpectNoProcess();
});
@ -125,7 +123,7 @@ add_task(function*() {
yield BrowserTestUtils.withNewTab("http://example.com", function*(aBrowser) {
info("Starting test 2");
let pid1 = yield getPID(aBrowser);
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
// Fail the test if we create a process
let stopExpectNoProcess = expectNoProcess();
@ -152,7 +150,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
is(pid1, pid2, "The PID should not have changed");
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
stopExpectNoProcess();
});
@ -161,7 +159,7 @@ add_task(function*() {
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 3");
let pid1 = yield getPID(aBrowser);
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
let epc = expectProcessCreated();
@ -174,7 +172,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2);
is(true, yield getIsFreshProcess(aBrowser));
is(true, yield getInLAProc(aBrowser));
yield BrowserTestUtils.browserLoaded(aBrowser);
@ -186,7 +184,7 @@ add_task(function*() {
// We should have been kicked out of the large-allocation process by the
// load, meaning we're back in a non-fresh process
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
epc = expectProcessCreated();
@ -200,14 +198,14 @@ add_task(function*() {
isnot(pid1, pid4);
isnot(pid2, pid4);
is(true, yield getIsFreshProcess(aBrowser));
is(true, yield getInLAProc(aBrowser));
});
// Load Large-Allocation then about:blank load, then back button press should load from bfcache.
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 4");
let pid1 = yield getPID(aBrowser);
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
let epc = expectProcessCreated();
@ -220,7 +218,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2, "PIDs 1 and 2 should not match");
is(true, yield getIsFreshProcess(aBrowser));
is(true, yield getInLAProc(aBrowser));
yield BrowserTestUtils.browserLoaded(aBrowser);
@ -235,7 +233,7 @@ add_task(function*() {
// We should have been kicked out of the large-allocation process by the
// load, meaning we're back in a non-large-allocation process.
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
epc = expectProcessCreated();
@ -252,14 +250,14 @@ add_task(function*() {
isnot(pid1, pid4, "PID 4 shouldn't match PID 1");
isnot(pid2, pid4, "PID 4 shouldn't match PID 2");
isnot(pid3, pid4, "PID 4 shouldn't match PID 3");
is(true, yield getIsFreshProcess(aBrowser));
is(true, yield getInLAProc(aBrowser));
});
// Two consecutive large-allocation loads should create two processes.
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 5");
let pid1 = yield getPID(aBrowser);
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
let ready = Promise.all([expectProcessCreated(),
BrowserTestUtils.browserLoaded(aBrowser)]);
@ -273,7 +271,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2, "PIDs 1 and 2 should not match");
is(true, yield getIsFreshProcess(aBrowser));
is(true, yield getInLAProc(aBrowser));
let epc = expectProcessCreated();
@ -290,7 +288,7 @@ add_task(function*() {
// this was not a fresh process, we'll need to wait for another process.
// Start listening now.
epc = expectProcessCreated();
if (!(yield getIsFreshProcess(aBrowser))) {
if (!(yield getInLAProc(aBrowser))) {
yield epc;
} else {
epc.kill();
@ -300,14 +298,14 @@ add_task(function*() {
isnot(pid1, pid3, "PIDs 1 and 3 should not match");
isnot(pid2, pid3, "PIDs 2 and 3 should not match");
is(true, yield getIsFreshProcess(aBrowser));
is(true, yield getInLAProc(aBrowser));
});
// Opening a window from the large-allocation window should prevent the process switch.
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 6");
let pid1 = yield getPID(aBrowser);
is(false, yield getIsFreshProcess(aBrowser));
is(false, yield getInLAProc(aBrowser));
let ready = Promise.all([expectProcessCreated(),
BrowserTestUtils.browserLoaded(aBrowser)]);
@ -321,7 +319,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2, "PIDs 1 and 2 should not match");
is(true, yield getIsFreshProcess(aBrowser));
is(true, yield getInLAProc(aBrowser));
let stopExpectNoProcess = expectNoProcess();
@ -335,7 +333,7 @@ add_task(function*() {
let pid3 = yield getPID(aBrowser);
is(pid3, pid2, "PIDs 2 and 3 should match");
is(true, yield getIsFreshProcess(aBrowser));
is(true, yield getInLAProc(aBrowser));
stopExpectNoProcess();
@ -344,4 +342,45 @@ add_task(function*() {
this.__newWindow.close();
});
});
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 7");
yield SpecialPowers.pushPrefEnv({
set: [
["dom.ipc.processCount.webLargeAllocation", 1]
],
});
// Loading the first Large-Allocation tab should succeed as normal
let pid1 = yield getPID(aBrowser);
is(false, yield getInLAProc(aBrowser));
let ready = Promise.all([expectProcessCreated(),
BrowserTestUtils.browserLoaded(aBrowser)]);
yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
content.document.location = TEST_URI;
});
yield ready;
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2, "PIDs 1 and 2 should not match");
is(true, yield getInLAProc(aBrowser));
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
// The second one should load in a non-LA proc because the
// webLargeAllocation processes have been exhausted.
is(false, yield getInLAProc(aBrowser));
let ready = Promise.all([BrowserTestUtils.browserLoaded(aBrowser)]);
yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
content.document.location = TEST_URI;
});
yield ready;
is(false, yield getInLAProc(aBrowser));
});
});
});