зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2g-inbound
This commit is contained in:
Коммит
b355a3895d
|
@ -319,6 +319,26 @@
|
||||||
orient="horizontal"
|
orient="horizontal"
|
||||||
hidden="true"/>
|
hidden="true"/>
|
||||||
|
|
||||||
|
<menupopup id="processHangOptions"
|
||||||
|
onpopupshowing="ProcessHangMonitor.refreshMenu(window);">
|
||||||
|
<menuitem id="processHangTerminateScript"
|
||||||
|
oncommand="ProcessHangMonitor.terminateScript(window)"
|
||||||
|
accesskey="&processHang.terminateScript.accessKey;"
|
||||||
|
label="&processHang.terminateScript.label;"/>
|
||||||
|
<menuitem id="processHangDebugScript"
|
||||||
|
oncommand="ProcessHangMonitor.debugScript(window)"
|
||||||
|
accesskey="&processHang.debugScript.accessKey;"
|
||||||
|
label="&processHang.debugScript.label;"/>
|
||||||
|
<menuitem id="processHangTerminatePlugin"
|
||||||
|
oncommand="ProcessHangMonitor.terminatePlugin(window)"
|
||||||
|
accesskey="&processHang.terminatePlugin.accessKey;"
|
||||||
|
label="&processHang.terminatePlugin.label;"/>
|
||||||
|
<menuitem id="processHangTerminateProcess"
|
||||||
|
oncommand="ProcessHangMonitor.terminateProcess(window)"
|
||||||
|
accesskey="&processHang.terminateProcess.accessKey;"
|
||||||
|
label="&processHang.terminateProcess.label;"/>
|
||||||
|
</menupopup>
|
||||||
|
|
||||||
<menupopup id="toolbar-context-menu"
|
<menupopup id="toolbar-context-menu"
|
||||||
onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator'));">
|
onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator'));">
|
||||||
<menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
|
<menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
|
||||||
|
|
|
@ -925,6 +925,15 @@ you can use these alternative items. Otherwise, their values should be empty. -
|
||||||
<!ENTITY panicButton.thankyou.msg2 "Safe browsing!">
|
<!ENTITY panicButton.thankyou.msg2 "Safe browsing!">
|
||||||
<!ENTITY panicButton.thankyou.buttonlabel "Thanks!">
|
<!ENTITY panicButton.thankyou.buttonlabel "Thanks!">
|
||||||
|
|
||||||
|
<!ENTITY processHang.terminateScript.label "Stop Script">
|
||||||
|
<!ENTITY processHang.terminateScript.accessKey "S">
|
||||||
|
<!ENTITY processHang.debugScript.label "Debug Script">
|
||||||
|
<!ENTITY processHang.debugScript.accessKey "D">
|
||||||
|
<!ENTITY processHang.terminatePlugin.label "Kill Plugin">
|
||||||
|
<!ENTITY processHang.terminatePlugin.accessKey "P">
|
||||||
|
<!ENTITY processHang.terminateProcess.label "Kill Web Process">
|
||||||
|
<!ENTITY processHang.terminateProcess.accessKey "K">
|
||||||
|
|
||||||
<!ENTITY emeLearnMoreContextMenu.label "Learn more about DRM…">
|
<!ENTITY emeLearnMoreContextMenu.label "Learn more about DRM…">
|
||||||
<!ENTITY emeLearnMoreContextMenu.accesskey "D">
|
<!ENTITY emeLearnMoreContextMenu.accesskey "D">
|
||||||
<!ENTITY emeNotificationsNotNow.label "Not now">
|
<!ENTITY emeNotificationsNotNow.label "Not now">
|
||||||
|
|
|
@ -472,13 +472,9 @@ dataReportingNotification.button.label = Choose What I Share
|
||||||
dataReportingNotification.button.accessKey = C
|
dataReportingNotification.button.accessKey = C
|
||||||
|
|
||||||
# Process hang reporter
|
# Process hang reporter
|
||||||
processHang.label = A web page is slowing down your browser. What would you like to do?
|
processHang.message = A web page is causing %1$S to run slowly. What would you like to do?
|
||||||
processHang.button_stop.label = Stop It
|
processHang.button.label = Options
|
||||||
processHang.button_stop.accessKey = S
|
processHang.button.accessKey = O
|
||||||
processHang.button_wait.label = Wait
|
|
||||||
processHang.button_wait.accessKey = W
|
|
||||||
processHang.button_debug.label = Debug Script
|
|
||||||
processHang.button_debug.accessKey = D
|
|
||||||
|
|
||||||
# Webapps notification popup
|
# Webapps notification popup
|
||||||
webapps.install = Install
|
webapps.install = Install
|
||||||
|
|
|
@ -19,31 +19,13 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||||
* the platform interface.
|
* the platform interface.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a hang hasn't been reported for more than 10 seconds, assume the
|
||||||
|
* content process has gotten unstuck (and hide the hang notification).
|
||||||
|
*/
|
||||||
|
const HANG_EXPIRATION_TIME = 10000;
|
||||||
|
|
||||||
var ProcessHangMonitor = {
|
var ProcessHangMonitor = {
|
||||||
/**
|
|
||||||
* If a hang hasn't been reported for more than 10 seconds, assume the
|
|
||||||
* content process has gotten unstuck (and hide the hang notification).
|
|
||||||
*/
|
|
||||||
get HANG_EXPIRATION_TIME() {
|
|
||||||
try {
|
|
||||||
return Services.prefs.getIntPref("browser.hangNotification.expiration");
|
|
||||||
} catch (ex) {
|
|
||||||
return 10000;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This timeout is the wait period applied after a user selects "Wait" in
|
|
||||||
* an existing notification.
|
|
||||||
*/
|
|
||||||
get WAIT_EXPIRATION_TIME() {
|
|
||||||
try {
|
|
||||||
return Services.prefs.getIntPref("browser.hangNotification.waitPeriod");
|
|
||||||
} catch (ex) {
|
|
||||||
return 10000;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collection of hang reports that haven't expired or been dismissed
|
* Collection of hang reports that haven't expired or been dismissed
|
||||||
* by the user. The keys are nsIHangReports and values keys are
|
* by the user. The keys are nsIHangReports and values keys are
|
||||||
|
@ -52,12 +34,6 @@ var ProcessHangMonitor = {
|
||||||
*/
|
*/
|
||||||
_activeReports: new Map(),
|
_activeReports: new Map(),
|
||||||
|
|
||||||
/**
|
|
||||||
* Collection of hang reports that have been suppressed for a
|
|
||||||
* short period of time.
|
|
||||||
*/
|
|
||||||
_pausedReports: new Map(),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize hang reporting. Called once in the parent process.
|
* Initialize hang reporting. Called once in the parent process.
|
||||||
*/
|
*/
|
||||||
|
@ -94,78 +70,46 @@ var ProcessHangMonitor = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Terminate the plugin process associated with a hang being reported
|
* Kill the plugin process causing the hang being reported for the
|
||||||
* for the selected browser in |win|. Will attempt to generate a combined
|
* selected browser in |win|.
|
||||||
* crash report for all processes.
|
|
||||||
*/
|
*/
|
||||||
terminatePlugin: function(win) {
|
terminatePlugin: function(win) {
|
||||||
this.handleUserInput(win, report => report.terminatePlugin());
|
this.handleUserInput(win, report => report.terminatePlugin());
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dismiss the browser notification and invoke an appropriate action based on
|
* Kill the content process causing the hang being reported for the selected
|
||||||
* the hang type.
|
* browser in |win|.
|
||||||
*/
|
*/
|
||||||
stopIt: function (win) {
|
terminateProcess: function(win) {
|
||||||
let report = this.findActiveReport(win.gBrowser.selectedBrowser);
|
this.handleUserInput(win, report => report.terminateProcess());
|
||||||
if (!report) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (report.hangType) {
|
|
||||||
case report.SLOW_SCRIPT:
|
|
||||||
this.terminateScript(win);
|
|
||||||
break;
|
|
||||||
case report.PLUGIN_HANG:
|
|
||||||
this.terminatePlugin(win);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dismiss the notification, clear the report from the active list and set up
|
* Update the "Options" pop-up menu for the hang notification
|
||||||
* a new timer to track a wait period during which we won't notify.
|
* associated with the selected browser in |win|. The menu should
|
||||||
|
* display only options that are relevant to the given report.
|
||||||
*/
|
*/
|
||||||
waitLonger: function(win) {
|
refreshMenu: function(win) {
|
||||||
let report = this.findActiveReport(win.gBrowser.selectedBrowser);
|
let report = this.findReport(win.gBrowser.selectedBrowser);
|
||||||
if (!report) {
|
if (!report) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Remove the report from the active list and cancel its timer.
|
|
||||||
this.removeActiveReport(report);
|
|
||||||
|
|
||||||
// NOTE, we didn't call userCanceled on nsIHangReport here. This insures
|
function setVisible(id, visible) {
|
||||||
// we don't repeatedly generate and cache crash report data for this hang
|
let item = win.document.getElementById(id);
|
||||||
// in the process hang reporter. It already has one report for the browser
|
item.hidden = !visible;
|
||||||
// process we want it hold onto.
|
}
|
||||||
|
|
||||||
// Create a new wait timer with notify callback
|
if (report.hangType == report.SLOW_SCRIPT) {
|
||||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
setVisible("processHangTerminateScript", true);
|
||||||
timer.initWithCallback(() => {
|
setVisible("processHangDebugScript", true);
|
||||||
for (let [stashedReport, otherTimer] of this._pausedReports) {
|
setVisible("processHangTerminatePlugin", false);
|
||||||
if (otherTimer === timer) {
|
} else if (report.hangType == report.PLUGIN_HANG) {
|
||||||
this.removePausedReport(stashedReport);
|
setVisible("processHangTerminateScript", false);
|
||||||
|
setVisible("processHangDebugScript", false);
|
||||||
// Create a new notification display timeout timer
|
setVisible("processHangTerminatePlugin", true);
|
||||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
}
|
||||||
timer.initWithCallback(this, this.HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
|
|
||||||
|
|
||||||
// Store the timer in the active reports map. If we receive a new
|
|
||||||
// observer notification for this hang, we'll redisplay the browser
|
|
||||||
// notification in reportHang below. If we do not receive a new
|
|
||||||
// observer, timer will take care of cleaning up resources associated
|
|
||||||
// with this hang. The observer for active hangs fires about once
|
|
||||||
// a second.
|
|
||||||
this._activeReports.set(report, timer);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, this.WAIT_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
|
|
||||||
|
|
||||||
this._pausedReports.set(report, timer);
|
|
||||||
|
|
||||||
// remove the browser notification associated with this hang
|
|
||||||
this.updateWindows();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,11 +118,11 @@ var ProcessHangMonitor = {
|
||||||
* about it.
|
* about it.
|
||||||
*/
|
*/
|
||||||
handleUserInput: function(win, func) {
|
handleUserInput: function(win, func) {
|
||||||
let report = this.findActiveReport(win.gBrowser.selectedBrowser);
|
let report = this.findReport(win.gBrowser.selectedBrowser);
|
||||||
if (!report) {
|
if (!report) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.removeActiveReport(report);
|
this.removeReport(report);
|
||||||
|
|
||||||
return func(report);
|
return func(report);
|
||||||
},
|
},
|
||||||
|
@ -209,9 +153,9 @@ var ProcessHangMonitor = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a active hang report for the given <browser> element.
|
* Find any active hang reports for the given <browser> element.
|
||||||
*/
|
*/
|
||||||
findActiveReport: function(browser) {
|
findReport: function(browser) {
|
||||||
let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
|
let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
|
||||||
for (let [report, timer] of this._activeReports) {
|
for (let [report, timer] of this._activeReports) {
|
||||||
if (report.isReportForBrowser(frameLoader)) {
|
if (report.isReportForBrowser(frameLoader)) {
|
||||||
|
@ -221,44 +165,6 @@ var ProcessHangMonitor = {
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Find a paused hang report for the given <browser> element.
|
|
||||||
*/
|
|
||||||
findPausedReport: function(browser) {
|
|
||||||
let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
|
|
||||||
for (let [report, timer] of this._pausedReports) {
|
|
||||||
if (report.isReportForBrowser(frameLoader)) {
|
|
||||||
return report;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove an active hang report from the active list and cancel the timer
|
|
||||||
* associated with it.
|
|
||||||
*/
|
|
||||||
removeActiveReport: function(report) {
|
|
||||||
let timer = this._activeReports.get(report);
|
|
||||||
if (timer) {
|
|
||||||
timer.cancel();
|
|
||||||
}
|
|
||||||
this._activeReports.delete(report);
|
|
||||||
this.updateWindows();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a paused hang report from the paused list and cancel the timer
|
|
||||||
* associated with it.
|
|
||||||
*/
|
|
||||||
removePausedReport: function(report) {
|
|
||||||
let timer = this._pausedReports.get(report);
|
|
||||||
if (timer) {
|
|
||||||
timer.cancel();
|
|
||||||
}
|
|
||||||
this._pausedReports.delete(report);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterate over all XUL windows and ensure that the proper hang
|
* Iterate over all XUL windows and ensure that the proper hang
|
||||||
* reports are shown for each one. Also install event handlers in
|
* reports are shown for each one. Also install event handlers in
|
||||||
|
@ -285,7 +191,7 @@ var ProcessHangMonitor = {
|
||||||
* If there is a hang report for the current tab in |win|, display it.
|
* If there is a hang report for the current tab in |win|, display it.
|
||||||
*/
|
*/
|
||||||
updateWindow: function(win) {
|
updateWindow: function(win) {
|
||||||
let report = this.findActiveReport(win.gBrowser.selectedBrowser);
|
let report = this.findReport(win.gBrowser.selectedBrowser);
|
||||||
|
|
||||||
if (report) {
|
if (report) {
|
||||||
this.showNotification(win, report);
|
this.showNotification(win, report);
|
||||||
|
@ -306,36 +212,19 @@ var ProcessHangMonitor = {
|
||||||
|
|
||||||
let bundle = win.gNavigatorBundle;
|
let bundle = win.gNavigatorBundle;
|
||||||
let brandBundle = win.document.getElementById("bundle_brand");
|
let brandBundle = win.document.getElementById("bundle_brand");
|
||||||
|
let appName = brandBundle.getString("brandShortName");
|
||||||
|
let message = bundle.getFormattedString(
|
||||||
|
"processHang.message",
|
||||||
|
[appName]);
|
||||||
|
|
||||||
let buttons = [{
|
let buttons = [{
|
||||||
label: bundle.getString("processHang.button_stop.label"),
|
label: bundle.getString("processHang.button.label"),
|
||||||
accessKey: bundle.getString("processHang.button_stop.accessKey"),
|
accessKey: bundle.getString("processHang.button.accessKey"),
|
||||||
callback: function() {
|
popup: "processHangOptions",
|
||||||
ProcessHangMonitor.stopIt(win);
|
callback: null,
|
||||||
}
|
}];
|
||||||
},
|
|
||||||
{
|
|
||||||
label: bundle.getString("processHang.button_wait.label"),
|
|
||||||
accessKey: bundle.getString("processHang.button_wait.accessKey"),
|
|
||||||
callback: function() {
|
|
||||||
ProcessHangMonitor.waitLonger(win);
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
|
|
||||||
#ifdef MOZ_DEV_EDITION
|
nb.appendNotification(message, "process-hang",
|
||||||
if (report.hangType == report.SLOW_SCRIPT) {
|
|
||||||
buttons.push({
|
|
||||||
label: bundle.getString("processHang.button_debug.label"),
|
|
||||||
accessKey: bundle.getString("processHang.button_debug.accessKey"),
|
|
||||||
callback: function() {
|
|
||||||
ProcessHangMonitor.debugScript(win);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
nb.appendNotification(bundle.getString("processHang.label"),
|
|
||||||
"process-hang",
|
|
||||||
"chrome://browser/content/aboutRobots-icon.png",
|
"chrome://browser/content/aboutRobots-icon.png",
|
||||||
nb.PRIORITY_WARNING_HIGH, buttons);
|
nb.PRIORITY_WARNING_HIGH, buttons);
|
||||||
},
|
},
|
||||||
|
@ -381,19 +270,11 @@ var ProcessHangMonitor = {
|
||||||
* before, show a notification for it in all open XUL windows.
|
* before, show a notification for it in all open XUL windows.
|
||||||
*/
|
*/
|
||||||
reportHang: function(report) {
|
reportHang: function(report) {
|
||||||
// If this hang was already reported reset the timer for it.
|
// If this hang was already reported, then reset the timer for it.
|
||||||
if (this._activeReports.has(report)) {
|
if (this._activeReports.has(report)) {
|
||||||
let timer = this._activeReports.get(report);
|
let timer = this._activeReports.get(report);
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
timer.initWithCallback(this, this.HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
|
timer.initWithCallback(this, HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
|
||||||
// if this report is in active but doesn't have a notification associated
|
|
||||||
// with it, display a notification.
|
|
||||||
this.updateWindows();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this hang was already reported and paused by the user ignore it.
|
|
||||||
if (this._pausedReports.has(report)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,19 +291,28 @@ var ProcessHangMonitor = {
|
||||||
|
|
||||||
// Otherwise create a new timer and display the report.
|
// Otherwise create a new timer and display the report.
|
||||||
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||||
timer.initWithCallback(this, this.HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
|
timer.initWithCallback(this, HANG_EXPIRATION_TIME, timer.TYPE_ONE_SHOT);
|
||||||
|
|
||||||
this._activeReports.set(report, timer);
|
this._activeReports.set(report, timer);
|
||||||
this.updateWindows();
|
this.updateWindows();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dismiss a hang report because the user closed the notification
|
||||||
|
* for it or the report expired.
|
||||||
|
*/
|
||||||
|
removeReport: function(report) {
|
||||||
|
this._activeReports.delete(report);
|
||||||
|
this.updateWindows();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback for when HANG_EXPIRATION_TIME has elapsed.
|
* Callback for when HANG_EXPIRATION_TIME has elapsed.
|
||||||
*/
|
*/
|
||||||
notify: function(timer) {
|
notify: function(timer) {
|
||||||
for (let [otherReport, otherTimer] of this._activeReports) {
|
for (let [otherReport, otherTimer] of this._activeReports) {
|
||||||
if (otherTimer === timer) {
|
if (otherTimer === timer) {
|
||||||
this.removeActiveReport(otherReport);
|
this.removeReport(otherReport);
|
||||||
otherReport.userCanceled();
|
otherReport.userCanceled();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ EXTRA_JS_MODULES += [
|
||||||
'offlineAppCache.jsm',
|
'offlineAppCache.jsm',
|
||||||
'PanelFrame.jsm',
|
'PanelFrame.jsm',
|
||||||
'PluginContent.jsm',
|
'PluginContent.jsm',
|
||||||
|
'ProcessHangMonitor.jsm',
|
||||||
'ReaderParent.jsm',
|
'ReaderParent.jsm',
|
||||||
'RecentWindow.jsm',
|
'RecentWindow.jsm',
|
||||||
'RemotePrompt.jsm',
|
'RemotePrompt.jsm',
|
||||||
|
@ -45,10 +46,6 @@ EXTRA_JS_MODULES += [
|
||||||
'webrtcUI.jsm',
|
'webrtcUI.jsm',
|
||||||
]
|
]
|
||||||
|
|
||||||
EXTRA_PP_JS_MODULES += [
|
|
||||||
'ProcessHangMonitor.jsm'
|
|
||||||
]
|
|
||||||
|
|
||||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||||
EXTRA_JS_MODULES += [
|
EXTRA_JS_MODULES += [
|
||||||
'Windows8WindowFrameColor.jsm',
|
'Windows8WindowFrameColor.jsm',
|
||||||
|
|
|
@ -3,8 +3,6 @@ support-files =
|
||||||
head.js
|
head.js
|
||||||
|
|
||||||
[browser_BrowserUITelemetry_buckets.js]
|
[browser_BrowserUITelemetry_buckets.js]
|
||||||
[browser_ProcessHangNotifications.js]
|
|
||||||
skip-if = !e10s
|
|
||||||
[browser_ContentSearch.js]
|
[browser_ContentSearch.js]
|
||||||
skip-if = e10s
|
skip-if = e10s
|
||||||
support-files =
|
support-files =
|
||||||
|
|
|
@ -1,185 +0,0 @@
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
|
||||||
|
|
||||||
function getNotificationBox(aWindow) {
|
|
||||||
return aWindow.document.getElementById("high-priority-global-notificationbox");
|
|
||||||
}
|
|
||||||
|
|
||||||
function promiseNotificationShown(aWindow, aName) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
let notification = getNotificationBox(aWindow);
|
|
||||||
notification.addEventListener("AlertActive", function active() {
|
|
||||||
notification.removeEventListener("AlertActive", active, true);
|
|
||||||
is(notification.allNotifications.length, 1, "Notification Displayed.");
|
|
||||||
resolve(notification);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function promiseReportCallMade(aValue) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
let old = gTestHangReport.testCallback;
|
|
||||||
gTestHangReport.testCallback = function (val) {
|
|
||||||
gTestHangReport.testCallback = old;
|
|
||||||
is(aValue, val, "was the correct method call made on the hang report object?");
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function pushPrefs(...aPrefs) {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
SpecialPowers.pushPrefEnv({"set": aPrefs}, resolve);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function popPrefs() {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
SpecialPowers.popPrefEnv(resolve);
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let gTestHangReport = {
|
|
||||||
SLOW_SCRIPT: 1,
|
|
||||||
PLUGIN_HANG: 2,
|
|
||||||
|
|
||||||
TEST_CALLBACK_CANCELED: 1,
|
|
||||||
TEST_CALLBACK_TERMSCRIPT: 2,
|
|
||||||
TEST_CALLBACK_TERMPLUGIN: 3,
|
|
||||||
|
|
||||||
_hangType: 1,
|
|
||||||
_tcb: function (aCallbackType) {},
|
|
||||||
|
|
||||||
get hangType() {
|
|
||||||
return this._hangType;
|
|
||||||
},
|
|
||||||
|
|
||||||
set hangType(aValue) {
|
|
||||||
this._hangType = aValue;
|
|
||||||
},
|
|
||||||
|
|
||||||
set testCallback(aValue) {
|
|
||||||
this._tcb = aValue;
|
|
||||||
},
|
|
||||||
|
|
||||||
QueryInterface: function (aIID) {
|
|
||||||
if (aIID.equals(Components.interfaces.nsIHangReport) ||
|
|
||||||
aIID.equals(Components.interfaces.nsISupports))
|
|
||||||
return this;
|
|
||||||
throw Components.results.NS_NOINTERFACE;
|
|
||||||
},
|
|
||||||
|
|
||||||
userCanceled: function () {
|
|
||||||
this._tcb(this.TEST_CALLBACK_CANCELED);
|
|
||||||
},
|
|
||||||
|
|
||||||
terminateScript: function () {
|
|
||||||
this._tcb(this.TEST_CALLBACK_TERMSCRIPT);
|
|
||||||
},
|
|
||||||
|
|
||||||
terminatePlugin: function () {
|
|
||||||
this._tcb(this.TEST_CALLBACK_TERMPLUGIN);
|
|
||||||
},
|
|
||||||
|
|
||||||
isReportForBrowser: function(aFrameLoader) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// on dev edition we add a button for js debugging of hung scripts.
|
|
||||||
let buttonCount = (UpdateUtils.UpdateChannel == "aurora" ? 3 : 2);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if hang reports receive a terminate script callback when the user selects
|
|
||||||
* stop in response to a script hang.
|
|
||||||
*/
|
|
||||||
|
|
||||||
add_task(function* terminateScriptTest() {
|
|
||||||
let promise = promiseNotificationShown(window, "process-hang");
|
|
||||||
Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
|
|
||||||
let notification = yield promise;
|
|
||||||
|
|
||||||
let buttons = notification.currentNotification.getElementsByTagName("button");
|
|
||||||
is(buttons.length, buttonCount, "proper number of buttons");
|
|
||||||
|
|
||||||
// Click the "Stop It" button, we should get a terminate script callback
|
|
||||||
gTestHangReport.hangType = gTestHangReport.SLOW_SCRIPT;
|
|
||||||
promise = promiseReportCallMade(gTestHangReport.TEST_CALLBACK_TERMSCRIPT);
|
|
||||||
buttons[0].click();
|
|
||||||
yield promise;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if hang reports receive user canceled callbacks after a user selects wait
|
|
||||||
* and the browser frees up from a script hang on its own.
|
|
||||||
*/
|
|
||||||
|
|
||||||
add_task(function* waitForScriptTest() {
|
|
||||||
let promise = promiseNotificationShown(window, "process-hang");
|
|
||||||
Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
|
|
||||||
let notification = yield promise;
|
|
||||||
|
|
||||||
let buttons = notification.currentNotification.getElementsByTagName("button");
|
|
||||||
is(buttons.length, buttonCount, "proper number of buttons");
|
|
||||||
|
|
||||||
yield pushPrefs(["browser.hangNotification.waitPeriod", 1000],
|
|
||||||
["browser.hangNotification.expiration", 2000]);
|
|
||||||
|
|
||||||
function nocbcheck() {
|
|
||||||
ok(false, "received a callback?");
|
|
||||||
}
|
|
||||||
let oldcb = gTestHangReport.testCallback;
|
|
||||||
gTestHangReport.testCallback = nocbcheck;
|
|
||||||
// Click the "Wait" button this time, we shouldn't get a callback at all.
|
|
||||||
buttons[1].click();
|
|
||||||
gTestHangReport.testCallback = oldcb;
|
|
||||||
|
|
||||||
// send another hang pulse, we should not get a notification here
|
|
||||||
Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
|
|
||||||
is(notification.currentNotification, null, "no notification should be visible");
|
|
||||||
|
|
||||||
// After selecting Wait, we should get a userCanceled callback after
|
|
||||||
// HANG_EXPIRATION_TIME.
|
|
||||||
yield promiseReportCallMade(gTestHangReport.TEST_CALLBACK_CANCELED);
|
|
||||||
|
|
||||||
yield popPrefs();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if hang reports receive user canceled callbacks after the content
|
|
||||||
* process stops sending hang notifications.
|
|
||||||
*/
|
|
||||||
|
|
||||||
add_task(function* hangGoesAwayTest() {
|
|
||||||
yield pushPrefs(["browser.hangNotification.expiration", 1000]);
|
|
||||||
|
|
||||||
let promise = promiseNotificationShown(window, "process-hang");
|
|
||||||
Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
|
|
||||||
yield promise;
|
|
||||||
|
|
||||||
yield promiseReportCallMade(gTestHangReport.TEST_CALLBACK_CANCELED);
|
|
||||||
|
|
||||||
yield popPrefs();
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests if hang reports receive a terminate plugin callback when the user selects
|
|
||||||
* stop in response to a plugin hang.
|
|
||||||
*/
|
|
||||||
|
|
||||||
add_task(function* terminatePluginTest() {
|
|
||||||
let promise = promiseNotificationShown(window, "process-hang");
|
|
||||||
Services.obs.notifyObservers(gTestHangReport, "process-hang-report", null);
|
|
||||||
let notification = yield promise;
|
|
||||||
|
|
||||||
let buttons = notification.currentNotification.getElementsByTagName("button");
|
|
||||||
is(buttons.length, buttonCount, "proper number of buttons");
|
|
||||||
|
|
||||||
// Click the "Stop It" button, we should get a terminate script callback
|
|
||||||
gTestHangReport.hangType = gTestHangReport.PLUGIN_HANG;
|
|
||||||
promise = promiseReportCallMade(gTestHangReport.TEST_CALLBACK_TERMPLUGIN);
|
|
||||||
buttons[0].click();
|
|
||||||
yield promise;
|
|
||||||
});
|
|
|
@ -151,6 +151,7 @@ public:
|
||||||
NS_IMETHOD BeginStartingDebugger() override;
|
NS_IMETHOD BeginStartingDebugger() override;
|
||||||
NS_IMETHOD EndStartingDebugger() override;
|
NS_IMETHOD EndStartingDebugger() override;
|
||||||
NS_IMETHOD TerminatePlugin() override;
|
NS_IMETHOD TerminatePlugin() override;
|
||||||
|
NS_IMETHOD TerminateProcess() override;
|
||||||
NS_IMETHOD UserCanceled() override;
|
NS_IMETHOD UserCanceled() override;
|
||||||
|
|
||||||
NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) override;
|
NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) override;
|
||||||
|
@ -819,8 +820,6 @@ HangMonitoredProcess::TerminatePlugin()
|
||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// generates a crash report that includes a browser report taken here
|
|
||||||
// earlier, the content process, and any plugin process(es).
|
|
||||||
uint32_t id = mHangData.get_PluginHangData().pluginId();
|
uint32_t id = mHangData.get_PluginHangData().pluginId();
|
||||||
plugins::TerminatePlugin(id, NS_LITERAL_CSTRING("HangMonitor"),
|
plugins::TerminatePlugin(id, NS_LITERAL_CSTRING("HangMonitor"),
|
||||||
mBrowserDumpId);
|
mBrowserDumpId);
|
||||||
|
@ -831,6 +830,24 @@ HangMonitoredProcess::TerminatePlugin()
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
HangMonitoredProcess::TerminateProcess()
|
||||||
|
{
|
||||||
|
MOZ_RELEASE_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
if (!mContentParent) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mActor && mHangData.type() == HangData::TPluginHangData) {
|
||||||
|
uint32_t id = mHangData.get_PluginHangData().pluginId();
|
||||||
|
mActor->CleanupPluginHang(id, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
mContentParent->KillHard("HangMonitor");
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
HangMonitoredProcess::IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult)
|
HangMonitoredProcess::IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult)
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,7 +18,7 @@ interface nsIFrameLoader;
|
||||||
* process will continue to run uninhibitedly during this time.
|
* process will continue to run uninhibitedly during this time.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
[scriptable, uuid(5fcffbb9-be62-49b1-b8a1-36e820787a74)]
|
[scriptable, uuid(90cea731-dd3e-459e-b017-f9a14697b56e)]
|
||||||
interface nsIHangReport : nsISupports
|
interface nsIHangReport : nsISupports
|
||||||
{
|
{
|
||||||
const unsigned long SLOW_SCRIPT = 1;
|
const unsigned long SLOW_SCRIPT = 1;
|
||||||
|
@ -50,6 +50,10 @@ interface nsIHangReport : nsISupports
|
||||||
// Only valid for PLUGIN_HANG reports.
|
// Only valid for PLUGIN_HANG reports.
|
||||||
void terminatePlugin();
|
void terminatePlugin();
|
||||||
|
|
||||||
|
// Terminate the hung content process unconditionally.
|
||||||
|
// Valid for any type of hang.
|
||||||
|
void terminateProcess();
|
||||||
|
|
||||||
// Ask the content process to start up the slow script debugger.
|
// Ask the content process to start up the slow script debugger.
|
||||||
// Only valid for SLOW_SCRIPT reports.
|
// Only valid for SLOW_SCRIPT reports.
|
||||||
void beginStartingDebugger();
|
void beginStartingDebugger();
|
||||||
|
|
Загрузка…
Ссылка в новой задаче