зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 71310d7e7e2c (bug 1110887)
--HG-- extra : rebase_source : e89fe430036926c1cc4077154958339c6dbfb3ca
This commit is contained in:
Родитель
01eaaf3593
Коммит
a808046b2f
|
@ -658,7 +658,6 @@ pref("plugins.update.url", "https://www.mozilla.org/%LOCALE%/plugincheck/?utm_so
|
|||
pref("plugins.update.notifyUser", false);
|
||||
|
||||
pref("plugins.click_to_play", true);
|
||||
pref("plugins.testmode", false);
|
||||
|
||||
pref("plugin.default.state", 1);
|
||||
|
||||
|
|
|
@ -78,4 +78,3 @@ skip-if = os == 'linux' || os == 'mac' # Bug 984821
|
|||
[browser_pluginCrashCommentAndURL.js]
|
||||
skip-if = !crashreporter
|
||||
[browser_plugins_added_dynamically.js]
|
||||
[browser_pluginCrashReportNonDeterminism.js]
|
||||
|
|
|
@ -1,259 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* With e10s, plugins must run in their own process. This means we have
|
||||
* three processes at a minimum when we're running a plugin:
|
||||
*
|
||||
* 1) The main browser, or "chrome" process
|
||||
* 2) The content process hosting the plugin instance
|
||||
* 3) The plugin process
|
||||
*
|
||||
* If the plugin process crashes, we cannot be sure if the chrome process
|
||||
* will hear about it first, or the content process will hear about it
|
||||
* first. Because of how IPC works, that's really up to the operating system,
|
||||
* and we assume any guarantees about it, so we have to account for both
|
||||
* possibilities.
|
||||
*
|
||||
* This test exercises the browser's reaction to both possibilities.
|
||||
*/
|
||||
|
||||
const CRASH_URL = "http://example.com/browser/browser/base/content/test/plugins/plugin_crashCommentAndURL.html";
|
||||
const CRASHED_MESSAGE = "BrowserPlugins:NPAPIPluginProcessCrashed";
|
||||
|
||||
/**
|
||||
* In order for our test to work, we need to be able to put a plugin
|
||||
* in a very specific state. Specifically, we need it to match the
|
||||
* :-moz-handler-crashed pseudoselector. The only way I can find to
|
||||
* do that is by actually crashing the plugin. So we wait for the
|
||||
* plugin to crash and show the "please" state (since that will
|
||||
* only show if both the message from the parent has been received
|
||||
* AND the PluginCrashed event has fired).
|
||||
*
|
||||
* Once in that state, we try to rewind the clock a little bit - we clear
|
||||
* out the crashData cache in the PluginContent with a message, and we also
|
||||
* override the pluginFallbackState of the <object> to fool PluginContent
|
||||
* into believing that the plugin is in a particular state.
|
||||
*
|
||||
* @param browser
|
||||
* The browser that has loaded the CRASH_URL that we need to
|
||||
* prepare to be in the special state.
|
||||
* @param pluginFallbackState
|
||||
* The value we should override the <object>'s pluginFallbackState
|
||||
* with.
|
||||
* @return Promise
|
||||
* The Promise resolves when the plugin has officially been put into
|
||||
* the crash reporter state, and then "rewound" to have the "status"
|
||||
* attribute of the statusDiv removed. The resolved Promise returns
|
||||
* the run ID for the crashed plugin. It rejects if we never get into
|
||||
* the crash reporter state.
|
||||
*/
|
||||
function preparePlugin(browser, pluginFallbackState) {
|
||||
return ContentTask.spawn(browser, pluginFallbackState, function* (pluginFallbackState) {
|
||||
let plugin = content.document.getElementById("plugin");
|
||||
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
// CRASH_URL will load a plugin that crashes immediately. We
|
||||
// wait until the plugin has finished being put into the crash
|
||||
// state.
|
||||
let statusDiv;
|
||||
yield ContentTaskUtils.waitForCondition(() => {
|
||||
statusDiv = plugin.ownerDocument
|
||||
.getAnonymousElementByAttribute(plugin, "anonid",
|
||||
"submitStatus");
|
||||
return statusDiv && statusDiv.getAttribute("status") == "please";
|
||||
}, "Timed out waiting for plugin to be in crash report state");
|
||||
|
||||
// "Rewind", by wiping out the status attribute...
|
||||
statusDiv.removeAttribute("status");
|
||||
// Somehow, I'm able to get away with overriding the getter for
|
||||
// this XPCOM object. Probably because I've got chrome privledges.
|
||||
Object.defineProperty(plugin, "pluginFallbackType", {
|
||||
get: function() {
|
||||
return pluginFallbackState;
|
||||
}
|
||||
});
|
||||
return plugin.runID;
|
||||
}).then((runID) => {
|
||||
browser.messageManager.sendAsyncMessage("BrowserPlugins:Test:ClearCrashData");
|
||||
return runID;
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* setup() {
|
||||
// Bypass click-to-play
|
||||
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED);
|
||||
|
||||
// Clear out any minidumps we create from plugins - we really don't care
|
||||
// about them.
|
||||
let crashObserver = (subject, topic, data) => {
|
||||
if (topic != "plugin-crashed") {
|
||||
return;
|
||||
}
|
||||
|
||||
let propBag = subject.QueryInterface(Ci.nsIPropertyBag2);
|
||||
let minidumpID = propBag.getPropertyAsAString("pluginDumpID");
|
||||
|
||||
let minidumpDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
minidumpDir.append("minidumps");
|
||||
|
||||
let pluginDumpFile = minidumpDir.clone();
|
||||
pluginDumpFile.append(minidumpID + ".dmp");
|
||||
|
||||
let extraFile = minidumpDir.clone();
|
||||
extraFile.append(minidumpID + ".extra");
|
||||
|
||||
ok(pluginDumpFile.exists(), "Found minidump");
|
||||
ok(extraFile.exists(), "Found extra file");
|
||||
|
||||
pluginDumpFile.remove(false);
|
||||
extraFile.remove(false);
|
||||
};
|
||||
|
||||
Services.obs.addObserver(crashObserver, "plugin-crashed");
|
||||
// plugins.testmode will make BrowserPlugins:Test:ClearCrashData work.
|
||||
Services.prefs.setBoolPref("plugins.testmode", true);
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("plugins.testmode");
|
||||
Services.obs.removeObserver(crashObserver, "plugin-crashed");
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* In this case, the chrome process hears about the crash first.
|
||||
*/
|
||||
add_task(function* testChromeHearsPluginCrashFirst() {
|
||||
// Open a remote window so that we can run this test even if e10s is not
|
||||
// enabled by default.
|
||||
let win = yield BrowserTestUtils.openNewBrowserWindow({remote: true});
|
||||
let browser = win.gBrowser.selectedBrowser;
|
||||
|
||||
browser.loadURI(CRASH_URL);
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
// In this case, we want the <object> to match the -moz-handler-crashed
|
||||
// pseudoselector, but we want it to seem still active, because the
|
||||
// content process is not yet supposed to know that the plugin has
|
||||
// crashed.
|
||||
let runID = yield preparePlugin(browser,
|
||||
Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE);
|
||||
|
||||
// Send the message down to PluginContent.jsm saying that the plugin has
|
||||
// crashed, and that we have a crash report.
|
||||
let mm = browser.messageManager;
|
||||
mm.sendAsyncMessage(CRASHED_MESSAGE,
|
||||
{ pluginName: "", runID, state: "please" });
|
||||
|
||||
let [gotExpected, msg] = yield ContentTask.spawn(browser, {}, function* () {
|
||||
// At this point, the content process should have heard the
|
||||
// plugin crash message from the parent, and we are OK to emit
|
||||
// the PluginCrashed event.
|
||||
let plugin = content.document.getElementById("plugin");
|
||||
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
let statusDiv = plugin.ownerDocument
|
||||
.getAnonymousElementByAttribute(plugin, "anonid",
|
||||
"submitStatus");
|
||||
|
||||
if (statusDiv.getAttribute("status") == "please") {
|
||||
return [false, "Did not expect plugin to be in crash report mode yet."];
|
||||
}
|
||||
|
||||
// Now we need the plugin to seem crashed to PluginContent.jsm, without
|
||||
// actually crashing the plugin again. We hack around this by overriding
|
||||
// the pluginFallbackType again.
|
||||
Object.defineProperty(plugin, "pluginFallbackType", {
|
||||
get: function() {
|
||||
return Ci.nsIObjectLoadingContent.PLUGIN_CRASHED;
|
||||
},
|
||||
});
|
||||
|
||||
let event = new content.PluginCrashedEvent("PluginCrashed", {
|
||||
pluginName: "",
|
||||
pluginDumpID: "",
|
||||
browserDumpID: "",
|
||||
submittedCrashReport: false,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
plugin.dispatchEvent(event);
|
||||
return [statusDiv.getAttribute("status") == "please",
|
||||
"Should have been showing crash report UI"];
|
||||
});
|
||||
|
||||
ok(gotExpected, msg);
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
});
|
||||
|
||||
/**
|
||||
* In this case, the content process hears about the crash first.
|
||||
*/
|
||||
add_task(function* testContentHearsCrashFirst() {
|
||||
// Open a remote window so that we can run this test even if e10s is not
|
||||
// enabled by default.
|
||||
let win = yield BrowserTestUtils.openNewBrowserWindow({remote: true});
|
||||
let browser = win.gBrowser.selectedBrowser;
|
||||
|
||||
browser.loadURI(CRASH_URL);
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
// In this case, we want the <object> to match the -moz-handler-crashed
|
||||
// pseudoselector, and we want the plugin to seem crashed, since the
|
||||
// content process in this case has heard about the crash first.
|
||||
let runID = yield preparePlugin(browser,
|
||||
Ci.nsIObjectLoadingContent.PLUGIN_CRASHED);
|
||||
|
||||
let [gotExpected, msg] = yield ContentTask.spawn(browser, null, function* () {
|
||||
// At this point, the content process has not yet heard from the
|
||||
// parent about the crash report. Let's ensure that by making sure
|
||||
// we're not showing the plugin crash report UI.
|
||||
let plugin = content.document.getElementById("plugin");
|
||||
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
let statusDiv = plugin.ownerDocument
|
||||
.getAnonymousElementByAttribute(plugin, "anonid",
|
||||
"submitStatus");
|
||||
|
||||
if (statusDiv.getAttribute("status") == "please") {
|
||||
return [false, "Did not expect plugin to be in crash report mode yet."];
|
||||
}
|
||||
|
||||
let event = new content.PluginCrashedEvent("PluginCrashed", {
|
||||
pluginName: "",
|
||||
pluginDumpID: "",
|
||||
browserDumpID: "",
|
||||
submittedCrashReport: false,
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
plugin.dispatchEvent(event);
|
||||
|
||||
return [statusDiv.getAttribute("status") != "please",
|
||||
"Should not yet be showing crash report UI"];
|
||||
});
|
||||
|
||||
ok(gotExpected, msg);
|
||||
|
||||
// Now send the message down to PluginContent.jsm that the plugin has
|
||||
// crashed...
|
||||
let mm = browser.messageManager;
|
||||
mm.sendAsyncMessage(CRASHED_MESSAGE,
|
||||
{ pluginName: "", runID, state: "please"});
|
||||
|
||||
[gotExpected, msg] = yield ContentTask.spawn(browser, null, function* () {
|
||||
// At this point, the content process will have heard the message
|
||||
// from the parent and reacted to it. We should be showing the plugin
|
||||
// crash report UI now.
|
||||
let plugin = content.document.getElementById("plugin");
|
||||
plugin.QueryInterface(Ci.nsIObjectLoadingContent);
|
||||
let statusDiv = plugin.ownerDocument
|
||||
.getAnonymousElementByAttribute(plugin, "anonid",
|
||||
"submitStatus");
|
||||
|
||||
return [statusDiv.getAttribute("status") == "please",
|
||||
"Should have been showing crash report UI"];
|
||||
});
|
||||
|
||||
ok(gotExpected, msg);
|
||||
|
||||
yield BrowserTestUtils.closeWindow(win);
|
||||
});
|
|
@ -52,7 +52,6 @@ PluginContent.prototype = {
|
|||
global.addMessageListener("BrowserPlugins:ContextMenuCommand", this);
|
||||
global.addMessageListener("BrowserPlugins:NPAPIPluginProcessCrashed", this);
|
||||
global.addMessageListener("BrowserPlugins:CrashReportSubmitted", this);
|
||||
global.addMessageListener("BrowserPlugins:Test:ClearCrashData", this);
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
|
@ -108,11 +107,6 @@ PluginContent.prototype = {
|
|||
state: msg.data.state,
|
||||
})
|
||||
break;
|
||||
case "BrowserPlugins:Test:ClearCrashData":
|
||||
// This message should ONLY ever be sent by automated tests.
|
||||
if (Services.prefs.getBoolPref("plugins.testmode")) {
|
||||
this.pluginCrashData.clear();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -112,28 +112,20 @@ this.BrowserTestUtils = {
|
|||
* {
|
||||
* private: A boolean indicating if the window should be
|
||||
* private
|
||||
* remote: A boolean indicating if the window should run
|
||||
* remote browser tabs or not. If omitted, the window
|
||||
* will choose the profile default state.
|
||||
* }
|
||||
* @return {Promise}
|
||||
* Resolves with the new window once it is loaded.
|
||||
*/
|
||||
openNewBrowserWindow(options={}) {
|
||||
openNewBrowserWindow(options) {
|
||||
let argString = Cc["@mozilla.org/supports-string;1"].
|
||||
createInstance(Ci.nsISupportsString);
|
||||
argString.data = "";
|
||||
let features = "chrome,dialog=no,all";
|
||||
|
||||
if (options.private) {
|
||||
if (options && options.private || false) {
|
||||
features += ",private";
|
||||
}
|
||||
|
||||
if (options.hasOwnProperty("remote")) {
|
||||
let remoteState = options.remote ? "remote" : "non-remote";
|
||||
features += `,${remoteState}`;
|
||||
}
|
||||
|
||||
let win = Services.ww.openWindow(
|
||||
null, Services.prefs.getCharPref("browser.chromeURL"), "_blank",
|
||||
features, argString);
|
||||
|
|
Загрузка…
Ссылка в новой задаче