Bug 1493225, part 3 - Cancel content JS when navigating through history to prevent hangs r=smaug

This patch adds an ID to ensure that we avoid canceling content JS if the next
page already started loading by the time we're ready to try canceling the JS.

Differential Revision: https://phabricator.services.mozilla.com/D25164

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jim Porter 2019-04-18 18:39:45 +00:00
Родитель dcf852821e
Коммит 7f3f2e49dc
9 изменённых файлов: 74 добавлений и 26 удалений

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

@ -670,6 +670,15 @@ nsDocShell::GetInterface(const nsIID& aIID, void** aSink) {
return *aSink ? NS_OK : NS_NOINTERFACE;
}
NS_IMETHODIMP
nsDocShell::SetCancelContentJSEpoch(int32_t aEpoch) {
// Note: this gets called fairly early (before a pageload actually starts).
// We could probably defer this even longer.
nsCOMPtr<nsITabChild> tabChild = GetTabChild();
static_cast<TabChild*>(tabChild.get())->SetCancelContentJSEpoch(aEpoch);
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::LoadURI(nsDocShellLoadState* aLoadState) {
MOZ_ASSERT(aLoadState, "Must have a valid load state!");

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

@ -75,6 +75,8 @@ webidl Document;
[scriptable, builtinclass, uuid(049234fe-da10-478b-bc5d-bc6f9a1ba63d)]
interface nsIDocShell : nsIDocShellTreeItem
{
void setCancelContentJSEpoch(in long aEpoch);
/**
* Loads a given URI. This will give priority to loading the requested URI
* in the object implementing this interface. If it can't be loaded here

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

@ -50,7 +50,7 @@ child:
async CancelContentJSExecutionIfRunning(
TabId tabId, NavigationType aNavigationType,
int32_t aNavigationIndex, nsCString? aNavigationURI);
int32_t aNavigationIndex, nsCString? aNavigationURI, int32_t aEpoch);
};
} // namespace mozilla

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

@ -118,7 +118,8 @@ class HangMonitorChild : public PProcessHangMonitorChild,
mozilla::ipc::IPCResult RecvCancelContentJSExecutionIfRunning(
const TabId& aTabId, const nsITabParent::NavigationType& aNavigationType,
const int32_t& aNavigationIndex,
const mozilla::Maybe<nsCString>& aNavigationURI) override;
const mozilla::Maybe<nsCString>& aNavigationURI,
const int32_t& aEpoch) override;
void ActorDestroy(ActorDestroyReason aWhy) override;
@ -163,6 +164,7 @@ class HangMonitorChild : public PProcessHangMonitorChild,
nsITabParent::NavigationType mCancelContentJSNavigationType;
int32_t mCancelContentJSNavigationIndex;
mozilla::Maybe<nsCString> mCancelContentJSNavigationURI;
int32_t mCancelContentJSEpoch;
JSContext* mContext;
bool mShutdownDone;
@ -279,7 +281,7 @@ class HangMonitorParent : public PProcessHangMonitorParent,
const LayersObserverEpoch& aEpoch);
void CancelContentJSExecutionIfRunningOnThread(
TabId aTabId, nsITabParent::NavigationType aNavigationType,
int32_t aNavigationIndex, nsIURI* aNavigationURI);
int32_t aNavigationIndex, nsIURI* aNavigationURI, int32_t aEpoch);
void ShutdownOnThread();
@ -326,6 +328,7 @@ HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
mCancelContentJS(false),
mCancelContentJSNavigationType(nsITabParent::NAVIGATE_BACK),
mCancelContentJSNavigationIndex(0),
mCancelContentJSEpoch(0),
mShutdownDone(false),
mIPCOpen(true),
mPaintWhileInterruptingJSActive(false) {
@ -354,6 +357,7 @@ void HangMonitorChild::InterruptCallback() {
nsITabParent::NavigationType cancelContentJSNavigationType;
int32_t cancelContentJSNavigationIndex;
mozilla::Maybe<nsCString> cancelContentJSNavigationURI;
int32_t cancelContentJSEpoch;
{
MonitorAutoLock lock(mMonitor);
@ -367,6 +371,7 @@ void HangMonitorChild::InterruptCallback() {
cancelContentJSNavigationType = mCancelContentJSNavigationType;
cancelContentJSNavigationIndex = mCancelContentJSNavigationIndex;
cancelContentJSNavigationURI = std::move(mCancelContentJSNavigationURI);
cancelContentJSEpoch = mCancelContentJSEpoch;
mPaintWhileInterruptingJS = false;
mCancelContentJS = false;
@ -402,7 +407,7 @@ void HangMonitorChild::InterruptCallback() {
bool canCancel;
rv = tabChild->CanCancelContentJS(cancelContentJSNavigationType,
cancelContentJSNavigationIndex, uri,
&canCancel);
cancelContentJSEpoch, &canCancel);
if (NS_SUCCEEDED(rv) && canCancel) {
// Tell xpconnect that we want to cancel the content JS in this tab
// during the next interrupt callback.
@ -513,7 +518,7 @@ void HangMonitorChild::ClearPaintWhileInterruptingJS(
mozilla::ipc::IPCResult HangMonitorChild::RecvCancelContentJSExecutionIfRunning(
const TabId& aTabId, const nsITabParent::NavigationType& aNavigationType,
const int32_t& aNavigationIndex,
const mozilla::Maybe<nsCString>& aNavigationURI) {
const mozilla::Maybe<nsCString>& aNavigationURI, const int32_t& aEpoch) {
MOZ_RELEASE_ASSERT(IsOnThread());
{
@ -523,6 +528,7 @@ mozilla::ipc::IPCResult HangMonitorChild::RecvCancelContentJSExecutionIfRunning(
mCancelContentJSNavigationType = aNavigationType;
mCancelContentJSNavigationIndex = aNavigationIndex;
mCancelContentJSNavigationURI = aNavigationURI;
mCancelContentJSEpoch = aEpoch;
}
JS_RequestInterruptCallback(mContext);
@ -744,16 +750,16 @@ void HangMonitorParent::CancelContentJSExecutionIfRunning(
TabId id = aTab->GetTabId();
Dispatch(NewNonOwningRunnableMethod<TabId, nsITabParent::NavigationType,
int32_t, nsIURI*>(
int32_t, nsIURI*, int32_t>(
"HangMonitorParent::CancelContentJSExecutionIfRunningOnThread", this,
&HangMonitorParent::CancelContentJSExecutionIfRunningOnThread, id,
aNavigationType, aCancelContentJSOptions.mIndex,
aCancelContentJSOptions.mUri));
aCancelContentJSOptions.mUri, aCancelContentJSOptions.mEpoch));
}
void HangMonitorParent::CancelContentJSExecutionIfRunningOnThread(
TabId aTabId, nsITabParent::NavigationType aNavigationType,
int32_t aNavigationIndex, nsIURI* aNavigationURI) {
int32_t aNavigationIndex, nsIURI* aNavigationURI, int32_t aEpoch) {
MOZ_RELEASE_ASSERT(IsOnThread());
mozilla::Maybe<nsCString> spec;
@ -766,8 +772,8 @@ void HangMonitorParent::CancelContentJSExecutionIfRunningOnThread(
}
if (mIPCOpen) {
Unused << SendCancelContentJSExecutionIfRunning(aTabId, aNavigationType,
aNavigationIndex, spec);
Unused << SendCancelContentJSExecutionIfRunning(
aTabId, aNavigationType, aNavigationIndex, spec, aEpoch);
}
}

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

@ -405,7 +405,8 @@ TabChild::TabChild(ContentChild* aManager, const TabId& aTabId,
mPendingDocShellIsActive(false), mPendingDocShellReceivedMessage(false),
mPendingRenderLayers(false),
mPendingRenderLayersReceivedMessage(false), mPendingLayersObserverEpoch{0},
mPendingDocShellBlockers(0), mWidgetNativeData(0) {
mPendingDocShellBlockers(0), mCancelContentJSEpoch(0),
mWidgetNativeData(0) {
mozilla::HoldJSObjects(this);
nsWeakPtr weakPtrThis(do_GetWeakReference(
@ -3290,10 +3291,17 @@ void TabChild::PaintWhileInterruptingJS(
nsresult TabChild::CanCancelContentJS(
nsITabParent::NavigationType aNavigationType, int32_t aNavigationIndex,
nsIURI* aNavigationURI, bool* aCanCancel) {
nsIURI* aNavigationURI, int32_t aEpoch, bool* aCanCancel) {
nsresult rv;
*aCanCancel = false;
if (aEpoch <= mCancelContentJSEpoch) {
// The next page loaded before we got here, so we shouldn't try to cancel
// the content JS.
TABC_LOG("Unable to cancel content JS; the next page is already loaded!\n");
return NS_OK;
}
nsCOMPtr<nsISHistory> history = do_GetInterface(WebNavigation());
if (!history) {
return NS_ERROR_FAILURE;

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

@ -615,7 +615,7 @@ class TabChild final : public TabChildBase,
nsresult CanCancelContentJS(nsITabParent::NavigationType aNavigationType,
int32_t aNavigationIndex, nsIURI* aNavigationURI,
bool* aCanCancel);
int32_t aEpoch, bool* aCanCancel);
layers::LayersObserverEpoch LayersObserverEpoch() const {
return mLayersObserverEpoch;
@ -665,6 +665,10 @@ class TabChild final : public TabChildBase,
const ScrollableLayerGuid& aGuid,
const uint64_t& aInputBlockId);
void SetCancelContentJSEpoch(int32_t aEpoch) {
mCancelContentJSEpoch = aEpoch;
}
static bool HasVisibleTabs() {
return sVisibleTabs && !sVisibleTabs->IsEmpty();
}
@ -921,6 +925,7 @@ class TabChild final : public TabChildBase,
// When mPendingDocShellBlockers is greater than 0, the DocShell is blocked,
// and once it reaches 0, it is no longer blocked.
uint32_t mPendingDocShellBlockers;
int32_t mCancelContentJSEpoch;
WindowsHandle mWidgetNativeData;

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

@ -5,4 +5,5 @@
dictionary CancelContentJSOptions {
long index = 0;
URI? uri = null;
long epoch = 0;
};

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

@ -27,13 +27,13 @@ class WebNavigationChild extends ActorChild {
receiveMessage(message) {
switch (message.name) {
case "WebNavigation:GoBack":
this.goBack();
this.goBack(message.data);
break;
case "WebNavigation:GoForward":
this.goForward();
this.goForward(message.data);
break;
case "WebNavigation:GotoIndex":
this.gotoIndex(message.data.index);
this.gotoIndex(message.data);
break;
case "WebNavigation:LoadURI":
let histogram = Services.telemetry.getKeyedHistogramById("FX_TAB_REMOTE_NAVIGATION_DELAY_MS");
@ -65,19 +65,26 @@ class WebNavigationChild extends ActorChild {
}
}
goBack() {
goBack(params) {
if (this.webNavigation.canGoBack) {
this.mm.docShell.setCancelContentJSEpoch(params.cancelContentJSEpoch);
this._wrapURIChangeCall(() => this.webNavigation.goBack());
}
}
goForward() {
goForward(params) {
if (this.webNavigation.canGoForward) {
this.mm.docShell.setCancelContentJSEpoch(params.cancelContentJSEpoch);
this._wrapURIChangeCall(() => this.webNavigation.goForward());
}
}
gotoIndex(index) {
gotoIndex(params) {
let {
index,
cancelContentJSEpoch,
} = params || {};
this.mm.docShell.setCancelContentJSEpoch(cancelContentJSEpoch);
this._wrapURIChangeCall(() => this.webNavigation.gotoIndex(index));
}
@ -91,6 +98,7 @@ class WebNavigationChild extends ActorChild {
baseURI,
triggeringPrincipal,
csp,
cancelContentJSEpoch,
} = params || {};
if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
@ -131,6 +139,7 @@ class WebNavigationChild extends ActorChild {
headers,
baseURI,
};
this.mm.docShell.setCancelContentJSEpoch(cancelContentJSEpoch);
this._wrapURIChangeCall(() => {
return this.webNavigation.loadURI(uri, loadURIOptions);
});

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

@ -18,6 +18,7 @@ function makeURI(url) {
function RemoteWebNavigation() {
this.wrappedJSObject = this;
this._cancelContentJSEpoch = 1;
}
RemoteWebNavigation.prototype = {
@ -55,19 +56,24 @@ RemoteWebNavigation.prototype = {
canGoBack: false,
canGoForward: false,
goBack() {
let cancelContentJSEpoch = this._cancelContentJSEpoch++;
this._browser.frameLoader.tabParent.maybeCancelContentJSExecution(
Ci.nsITabParent.NAVIGATE_BACK);
this._sendMessage("WebNavigation:GoBack", {});
Ci.nsITabParent.NAVIGATE_BACK, {epoch: cancelContentJSEpoch});
this._sendMessage("WebNavigation:GoBack", {cancelContentJSEpoch});
},
goForward() {
let cancelContentJSEpoch = this._cancelContentJSEpoch++;
this._browser.frameLoader.tabParent.maybeCancelContentJSExecution(
Ci.nsITabParent.NAVIGATE_FORWARD);
this._sendMessage("WebNavigation:GoForward", {});
Ci.nsITabParent.NAVIGATE_FORWARD, {epoch: cancelContentJSEpoch});
this._sendMessage("WebNavigation:GoForward", {cancelContentJSEpoch});
},
gotoIndex(aIndex) {
let cancelContentJSEpoch = this._cancelContentJSEpoch++;
this._browser.frameLoader.tabParent.maybeCancelContentJSExecution(
Ci.nsITabParent.NAVIGATE_INDEX, {index: aIndex});
this._sendMessage("WebNavigation:GotoIndex", {index: aIndex});
Ci.nsITabParent.NAVIGATE_INDEX,
{index: aIndex, epoch: cancelContentJSEpoch});
this._sendMessage("WebNavigation:GotoIndex", {index: aIndex,
cancelContentJSEpoch});
},
loadURI(aURI, aLoadURIOptions) {
let uri;
@ -97,8 +103,9 @@ RemoteWebNavigation.prototype = {
}
}
let cancelContentJSEpoch = this._cancelContentJSEpoch++;
this._browser.frameLoader.tabParent.maybeCancelContentJSExecution(
Ci.nsITabParent.NAVIGATE_URL, {uri});
Ci.nsITabParent.NAVIGATE_URL, {uri, epoch: cancelContentJSEpoch});
this._sendMessage("WebNavigation:LoadURI", {
uri: aURI,
flags: aLoadURIOptions.loadFlags,
@ -110,6 +117,7 @@ RemoteWebNavigation.prototype = {
aLoadURIOptions.triggeringPrincipal || Services.scriptSecurityManager.createNullPrincipal({})),
csp: aLoadURIOptions.csp ? E10SUtils.serializeCSP(aLoadURIOptions.csp) : null,
requestTime: Services.telemetry.msSystemNow(),
cancelContentJSEpoch,
});
},
setOriginAttributesBeforeLoading(aOriginAttributes) {