dom/plugins/test/mochitest/test_hangui.xul
author Gabriele Svelto <gsvelto@mozilla.com>
Thu, 11 May 2017 14:03:50 +0200
changeset 412268 001d49708a355c9b127fc338722959874cfc7552
parent 308825 744faf917eebb624f26febc118451ed3467d2e84
child 455769 d4a7c018420e408fbe0a13ffddd2861623fda5a7
permissions -rw-r--r--
Bug 1359326 - Run the minidump analyzer directly from the CrashService code; r=bsmedberg This patch removes the C++ code used to run the minidump analyzer when a content process crashes, and replaces it with JS code within the CrashService object. This removes the need for a separate shutdown blocker in C++ code and allows end-to-end testing of the crash service functionality. Additionally the exception handler code can be simplified since it's now only used to run the crash reporter client. The test added to test_crash_service.js covers computing the minidump SHA256 hash (bug 1322611) and of the minidump analyzer itself (bug 1280477). MozReview-Commit-ID: LO5w839NHev

<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                 type="text/css"?>
<window title="Basic Plugin Tests"
  xmlns:html="http://www.w3.org/1999/xhtml"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
  <title>Plugin Hang UI Test</title>
  <script type="application/javascript"
          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
  <script type="application/javascript"
          src="plugin-utils.js" />
  <script type="application/javascript"
          src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hang_test.js" />
  <script type="application/javascript"
          src="http://mochi.test:8888/chrome/dom/plugins/test/mochitest/hangui_common.js" />

<body xmlns="http://www.w3.org/1999/xhtml">
  <iframe id="iframe1" src="hangui_subpage.html" width="400" height="400"></iframe>
</body>
<script class="testbody" type="application/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
SimpleTest.expectChildProcessCrash();
setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);

Components.utils.import("resource://gre/modules/Services.jsm");

const hangUITimeoutPref = "dom.ipc.plugins.hangUITimeoutSecs";
const hangUIMinDisplayPref = "dom.ipc.plugins.hangUIMinDisplaySecs";
const timeoutPref = "dom.ipc.plugins.timeoutSecs";

var worker = new ChromeWorker("hangui_iface.js");
worker.onmessage = function(event) {
  var result = event.data;
  var params = result.params;
  var output = params.testName;
  if (result.msg) {
    output += ": " + result.msg;
  }
  ok(result.status, output);
  if (params.callback) {
    var cb = eval(params.callback);
    var timeout = setTimeout(function() { clearTimeout(timeout); cb(); }, 100);
  }
};
worker.onerror = function(event) {
  var output = "Error: " + event.message + " at " + event.filename + ":" + event.lineno;
  ok(false, output);
};

var iframe;
var p;
var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);

function hanguiOperation(testName, timeoutSec, expectFind, expectClose, opCode,
                         commandId, check, cb) {
  var timeoutMs = timeoutSec * 1000 + EPSILON_MS;
  worker.postMessage({ "timeoutMs": timeoutMs, "expectToFind": expectFind,
                       "expectToClose": expectClose, "opCode": opCode,
                       "commandId": commandId, "check": check,
                       "testName": testName, "callback": cb });
}

function hanguiExpect(testName, shouldBeShowing, shouldClose, cb) {
  var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
  if (!shouldBeShowing && !timeoutSec) {
    timeoutSec = Services.prefs.getIntPref(timeoutPref);
  }
  hanguiOperation(testName, timeoutSec, shouldBeShowing, shouldClose, HANGUIOP_NOTHING, 0, false, cb);
}

function hanguiContinue(testName, check, cb) {
  var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
  hanguiOperation(testName, timeoutSec, true, true, HANGUIOP_COMMAND, IDC_CONTINUE, check, cb);
}

function hanguiStop(testName, check, cb) {
  var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
  hanguiOperation(testName, timeoutSec, true, true, HANGUIOP_COMMAND, IDC_STOP, check, cb);
}

function hanguiCancel(testName, cb) {
  var timeoutSec = Services.prefs.getIntPref(hangUITimeoutPref);
  hanguiOperation(testName, timeoutSec, true, true, HANGUIOP_CANCEL, 0, false, cb);
}

function finishTest() {
  if (obsCount > 0) {
    os.removeObserver(testObserver, "plugin-crashed");
    --obsCount;
  }
  SpecialPowers.clearUserPref(hangUITimeoutPref);
  SpecialPowers.clearUserPref(hangUIMinDisplayPref);
  SpecialPowers.clearUserPref(timeoutPref);
  SimpleTest.finish();
}

function runTests() {
  resetVars();

  hanguiOperation("Prime ChromeWorker", 0, false, false, HANGUIOP_NOTHING, 0,
                  false, "test1");
}

window.frameLoaded = runTests;

var obsCount = 0;

function onPluginCrashedHangUI(aEvent) {
  ok(true, "Plugin crashed notification received");
  is(aEvent.type, "PluginCrashed", "event is correct type");

  is(p, aEvent.target, "Plugin crashed event target is plugin element");

  ok(aEvent instanceof PluginCrashedEvent,
     "plugin crashed event has the right interface");

  is(typeof aEvent.pluginDumpID, "string", "pluginDumpID is correct type");
  isnot(aEvent.pluginDumpID, "", "got a non-empty dump ID");
  is(typeof aEvent.pluginName, "string", "pluginName is correct type");
  is(aEvent.pluginName, "Test Plug-in", "got correct plugin name");
  is(typeof aEvent.pluginFilename, "string", "pluginFilename is correct type");
  isnot(aEvent.pluginFilename, "", "got a non-empty filename");
  // The app itself may or may not have decided to submit the report, so
  // allow either true or false here.
  ok("submittedCrashReport" in aEvent, "submittedCrashReport is a property of event");
  is(typeof aEvent.submittedCrashReport, "boolean", "submittedCrashReport is correct type");

  os.removeObserver(testObserver, "plugin-crashed");
  --obsCount;
}

function resetVars() {
  iframe = document.getElementById('iframe1');
  p = iframe.contentDocument.getElementById("plugin1");
  if (obsCount == 0) {
    os.addObserver(testObserver, "plugin-crashed", true);
    ++obsCount;
  }
  iframe.contentDocument.addEventListener("PluginCrashed",
                                          onPluginCrashedHangUI,
                                          false);
}

function test9b() {
  hanguiExpect("test9b: Plugin Hang UI is not showing (checkbox)", false);
  p.stall(STALL_DURATION);
  hanguiExpect("test9b: Plugin Hang UI is still not showing (checkbox)", false, false, "finishTest");
  p.stall(STALL_DURATION);
}

function test9a() {
  resetVars();
  SpecialPowers.setIntPref(hangUITimeoutPref, 1);
  SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
  SpecialPowers.setIntPref(timeoutPref, 45);
  hanguiContinue("test9a: Continue button works with checkbox", true, "test9b");
  p.stall(STALL_DURATION);
}

function test9() {
  window.frameLoaded = test9a;
  iframe.contentWindow.location.reload();
}

function test8a() {
  resetVars();
  SpecialPowers.setIntPref(hangUITimeoutPref, 1);
  SpecialPowers.setIntPref(hangUIMinDisplayPref, 4);
  hanguiExpect("test8a: Plugin Hang UI is not showing (disabled due to hangUIMinDisplaySecs)", false, false, "test9");
  var exceptionThrown = false;
  try {
    p.hang();
  } catch(e) {
    exceptionThrown = true;
  }
  ok(exceptionThrown, "test8a: Exception thrown from hang() when plugin was terminated");
}

function test8() {
  window.frameLoaded = test8a;
  iframe.contentWindow.location.reload();
}

function test7a() {
  resetVars();
  SpecialPowers.setIntPref(hangUITimeoutPref, 0);
  hanguiExpect("test7a: Plugin Hang UI is not showing (disabled)", false, false, "test8");
  var exceptionThrown = false;
  try {
    p.hang();
  } catch(e) {
    exceptionThrown = true;
  }
  ok(exceptionThrown, "test7a: Exception thrown from hang() when plugin was terminated");
}

function test7() {
  window.frameLoaded = test7a;
  iframe.contentWindow.location.reload();
}

function test6() {
  SpecialPowers.setIntPref(hangUITimeoutPref, 1);
  SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
  SpecialPowers.setIntPref(timeoutPref, 3);
  hanguiExpect("test6: Plugin Hang UI is showing", true, true, "test7");
  var exceptionThrown = false;
  try {
    p.hang();
  } catch(e) {
    exceptionThrown = true;
  }
  ok(exceptionThrown, "test6: Exception thrown from hang() when plugin was terminated (child timeout)");
}

function test5a() {
  resetVars();
  hanguiCancel("test5a: Close button works", "test6");
  p.stall(STALL_DURATION);
}

function test5() {
  window.frameLoaded = test5a;
  iframe.contentWindow.location.reload();
}

function test4() {
  hanguiStop("test4: Stop button works", false, "test5");
  // We'll get an exception here because the plugin was terminated
  var exceptionThrown = false;
  try {
    p.hang();
  } catch(e) {
    exceptionThrown = true;
  }
  ok(exceptionThrown, "test4: Exception thrown from hang() when plugin was terminated");
}

function test3() {
  hanguiContinue("test3: Continue button works", false, "test4");
  p.stall(STALL_DURATION);
}

function test2() {
  // This test is identical to test1 because there were some bugs where the
  // Hang UI would show on the first hang but not on subsequent hangs
  hanguiExpect("test2: Plugin Hang UI is showing", true, true, "test3");
  p.stall(STALL_DURATION);
}

function test1() {
  SpecialPowers.setIntPref(hangUITimeoutPref, 1);
  SpecialPowers.setIntPref(hangUIMinDisplayPref, 1);
  SpecialPowers.setIntPref(timeoutPref, 45);
  hanguiExpect("test1: Plugin Hang UI is showing", true, true, "test2");
  p.stall(STALL_DURATION);
}

]]>
</script>
</window>