forked from kdashg/gecko-cinn
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bug 1861445 - Add the runtime.onPerformanceWarning WebExtension event…
… r=zombie,robwu When an extension's content script is very slow and causes a webpage to hang noticeably, a warning banner is displayed to the user. It would be useful to also notify the extension developer when that happens, so that they can address the issue. Let's add a new event runtime.onPerformanceWarning that can be dispatched when the browser needs to warn an extension of runtime performance issues. For now, let's just dispatch that event when the slow extension warning banner is displayed to the user. See also w3c/webextensions#456 Differential Revision: https://phabricator.services.mozilla.com/D194708
- Loading branch information
Showing
5 changed files
with
240 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
144 changes: 144 additions & 0 deletions
144
browser/components/extensions/test/browser/browser_ext_runtime_onPerformanceWarning.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ | ||
/* vim: set sts=2 sw=2 et tw=80: */ | ||
"use strict"; | ||
|
||
const { | ||
Management: { | ||
global: { tabTracker }, | ||
}, | ||
} = ChromeUtils.importESModule("resource://gre/modules/Extension.sys.mjs"); | ||
|
||
const { | ||
ExtensionUtils: { promiseObserved }, | ||
} = ChromeUtils.importESModule("resource://gre/modules/ExtensionUtils.sys.mjs"); | ||
|
||
class TestHangReport { | ||
constructor(addonId, scriptBrowser) { | ||
this.addonId = addonId; | ||
this.scriptBrowser = scriptBrowser; | ||
this.QueryInterface = ChromeUtils.generateQI(["nsIHangReport"]); | ||
} | ||
|
||
userCanceled() {} | ||
terminateScript() {} | ||
|
||
isReportForBrowserOrChildren(frameLoader) { | ||
return ( | ||
!this.scriptBrowser || this.scriptBrowser.frameLoader === frameLoader | ||
); | ||
} | ||
} | ||
|
||
function dispatchHangReport(extensionId, scriptBrowser) { | ||
const hangObserved = promiseObserved("process-hang-report"); | ||
|
||
Services.obs.notifyObservers( | ||
new TestHangReport(extensionId, scriptBrowser), | ||
"process-hang-report" | ||
); | ||
|
||
return hangObserved; | ||
} | ||
|
||
function background() { | ||
let onPerformanceWarningDetails = null; | ||
|
||
browser.runtime.onPerformanceWarning.addListener(details => { | ||
onPerformanceWarningDetails = details; | ||
}); | ||
|
||
browser.test.onMessage.addListener(message => { | ||
if (message === "get-on-performance-warning-details") { | ||
browser.test.sendMessage( | ||
"on-performance-warning-details", | ||
onPerformanceWarningDetails | ||
); | ||
onPerformanceWarningDetails = null; | ||
} | ||
}); | ||
} | ||
|
||
async function expectOnPerformanceWarningDetails( | ||
extension, | ||
expectedOnPerformanceWarningDetails | ||
) { | ||
extension.sendMessage("get-on-performance-warning-details"); | ||
|
||
let actualOnPerformanceWarningDetails = await extension.awaitMessage( | ||
"on-performance-warning-details" | ||
); | ||
Assert.deepEqual( | ||
actualOnPerformanceWarningDetails, | ||
expectedOnPerformanceWarningDetails, | ||
expectedOnPerformanceWarningDetails | ||
? "runtime.onPerformanceWarning fired with correct details" | ||
: "runtime.onPerformanceWarning didn't fire" | ||
); | ||
} | ||
|
||
add_task(async function test_should_fire_on_process_hang_report() { | ||
const description = | ||
"Slow extension content script caused a page hang, user was warned."; | ||
|
||
const extension = ExtensionTestUtils.loadExtension({ background }); | ||
await extension.startup(); | ||
|
||
const notificationPromise = BrowserTestUtils.waitForGlobalNotificationBar( | ||
window, | ||
"process-hang" | ||
); | ||
|
||
const tabs = await Promise.all([ | ||
BrowserTestUtils.openNewForegroundTab(gBrowser), | ||
BrowserTestUtils.openNewForegroundTab(gBrowser), | ||
]); | ||
|
||
// Warning event shouldn't have fired initially. | ||
await expectOnPerformanceWarningDetails(extension, null); | ||
|
||
// Hang report fired for the extension and first tab. Warning event with first | ||
// tab ID expected. | ||
await dispatchHangReport(extension.id, tabs[0].linkedBrowser); | ||
await expectOnPerformanceWarningDetails(extension, { | ||
category: "content_script", | ||
severity: "high", | ||
description, | ||
tabId: tabTracker.getId(tabs[0]), | ||
}); | ||
|
||
// Hang report fired for different extension, no warning event expected. | ||
await dispatchHangReport("wrong-addon-id", tabs[0].linkedBrowser); | ||
await expectOnPerformanceWarningDetails(extension, null); | ||
|
||
// Non-extension hang report fired, no warning event expected. | ||
await dispatchHangReport(null, tabs[0].linkedBrowser); | ||
await expectOnPerformanceWarningDetails(extension, null); | ||
|
||
// Hang report fired for the extension and second tab. Warning event with | ||
// second tab ID expected. | ||
await dispatchHangReport(extension.id, tabs[1].linkedBrowser); | ||
await expectOnPerformanceWarningDetails(extension, { | ||
category: "content_script", | ||
severity: "high", | ||
description, | ||
tabId: tabTracker.getId(tabs[1]), | ||
}); | ||
|
||
// Hang report fired for the extension with no associated tab. Warning event | ||
// with no tab ID expected. | ||
await dispatchHangReport(extension.id, null); | ||
await expectOnPerformanceWarningDetails(extension, { | ||
category: "content_script", | ||
severity: "high", | ||
description, | ||
}); | ||
|
||
await Promise.all(tabs.map(BrowserTestUtils.removeTab)); | ||
await extension.unload(); | ||
|
||
// Wait for the process-hang warning bar to be displayed, then ensure it's | ||
// cleared to avoid clobbering other tests. | ||
const notification = await notificationPromise; | ||
Assert.ok(notification.isConnected, "Notification still present"); | ||
notification.buttonContainer.querySelector("[label='Stop']").click(); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters