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; 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 NS_IMETHODIMP
nsDocShell::LoadURI(nsDocShellLoadState* aLoadState) { nsDocShell::LoadURI(nsDocShellLoadState* aLoadState) {
MOZ_ASSERT(aLoadState, "Must have a valid load state!"); MOZ_ASSERT(aLoadState, "Must have a valid load state!");

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

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

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

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

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

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

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

@ -405,7 +405,8 @@ TabChild::TabChild(ContentChild* aManager, const TabId& aTabId,
mPendingDocShellIsActive(false), mPendingDocShellReceivedMessage(false), mPendingDocShellIsActive(false), mPendingDocShellReceivedMessage(false),
mPendingRenderLayers(false), mPendingRenderLayers(false),
mPendingRenderLayersReceivedMessage(false), mPendingLayersObserverEpoch{0}, mPendingRenderLayersReceivedMessage(false), mPendingLayersObserverEpoch{0},
mPendingDocShellBlockers(0), mWidgetNativeData(0) { mPendingDocShellBlockers(0), mCancelContentJSEpoch(0),
mWidgetNativeData(0) {
mozilla::HoldJSObjects(this); mozilla::HoldJSObjects(this);
nsWeakPtr weakPtrThis(do_GetWeakReference( nsWeakPtr weakPtrThis(do_GetWeakReference(
@ -3290,10 +3291,17 @@ void TabChild::PaintWhileInterruptingJS(
nsresult TabChild::CanCancelContentJS( nsresult TabChild::CanCancelContentJS(
nsITabParent::NavigationType aNavigationType, int32_t aNavigationIndex, nsITabParent::NavigationType aNavigationType, int32_t aNavigationIndex,
nsIURI* aNavigationURI, bool* aCanCancel) { nsIURI* aNavigationURI, int32_t aEpoch, bool* aCanCancel) {
nsresult rv; nsresult rv;
*aCanCancel = false; *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()); nsCOMPtr<nsISHistory> history = do_GetInterface(WebNavigation());
if (!history) { if (!history) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;

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

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

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

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

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

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

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

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