michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // This tests our recovery if a child content process hosting providers michael@0: // crashes. michael@0: michael@0: // A content script we inject into one of our browsers michael@0: const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/browser/base/content/test/social/social_crash_content_helper.js"; michael@0: michael@0: let {getFrameWorkerHandle} = Cu.import("resource://gre/modules/FrameWorker.jsm", {}); michael@0: let {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise; michael@0: michael@0: function test() { michael@0: waitForExplicitFinish(); michael@0: michael@0: // We need to ensure all our workers are in the same content process. michael@0: Services.prefs.setIntPref("dom.ipc.processCount", 1); michael@0: michael@0: // This test generates many uncaught promises that should not cause failures. michael@0: Promise.Debugging.clearUncaughtErrorObservers(); michael@0: michael@0: runSocialTestWithProvider(gProviders, function (finishcb) { michael@0: runSocialTests(tests, undefined, undefined, function() { michael@0: Services.prefs.clearUserPref("dom.ipc.processCount"); michael@0: finishcb(); michael@0: }); michael@0: }); michael@0: } michael@0: michael@0: let gProviders = [ michael@0: { michael@0: name: "provider 1", michael@0: origin: "https://example.com", michael@0: sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1", michael@0: workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js", michael@0: iconURL: "chrome://branding/content/icon48.png" michael@0: }, michael@0: { michael@0: name: "provider 2", michael@0: origin: "https://test1.example.com", michael@0: sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2", michael@0: workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js", michael@0: iconURL: "chrome://branding/content/icon48.png" michael@0: } michael@0: ]; michael@0: michael@0: var tests = { michael@0: testCrash: function(next) { michael@0: // open the sidebar, then crash the child. michael@0: let sbrowser = document.getElementById("social-sidebar-browser"); michael@0: onSidebarLoad(function() { michael@0: // get the browser element for our provider. michael@0: let fw = getFrameWorkerHandle(gProviders[0].workerURL); michael@0: fw.port.close(); michael@0: fw._worker.browserPromise.then(browser => { michael@0: let mm = browser.messageManager; michael@0: mm.loadFrameScript(TEST_CONTENT_HELPER, false); michael@0: // add an observer for the crash - after it sees the crash we attempt michael@0: // a reload. michael@0: let observer = new crashObserver(function() { michael@0: info("Saw the process crash.") michael@0: Services.obs.removeObserver(observer, 'ipc:content-shutdown'); michael@0: // Add another sidebar load listener - it should be the error page. michael@0: onSidebarLoad(function() { michael@0: ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page"); michael@0: // after reloading, the sidebar should reload michael@0: onSidebarLoad(function() { michael@0: // now ping both workers - they should both be alive. michael@0: ensureWorkerLoaded(gProviders[0], function() { michael@0: ensureWorkerLoaded(gProviders[1], function() { michael@0: // and we are done! michael@0: next(); michael@0: }); michael@0: }); michael@0: }); michael@0: // click the try-again button. michael@0: sbrowser.contentDocument.getElementById("btnTryAgain").click(); michael@0: }); michael@0: }); michael@0: Services.obs.addObserver(observer, 'ipc:content-shutdown', false); michael@0: // and cause the crash. michael@0: mm.sendAsyncMessage("social-test:crash"); michael@0: }); michael@0: }) michael@0: SocialSidebar.show(); michael@0: }, michael@0: } michael@0: michael@0: function onSidebarLoad(callback) { michael@0: let sbrowser = document.getElementById("social-sidebar-browser"); michael@0: sbrowser.addEventListener("load", function load() { michael@0: sbrowser.removeEventListener("load", load, true); michael@0: callback(); michael@0: }, true); michael@0: } michael@0: michael@0: function ensureWorkerLoaded(manifest, callback) { michael@0: let fw = getFrameWorkerHandle(manifest.workerURL); michael@0: // once the worker responds to a ping we know it must be up. michael@0: let port = fw.port; michael@0: port.onmessage = function(msg) { michael@0: if (msg.data.topic == "pong") { michael@0: port.close(); michael@0: callback(); michael@0: } michael@0: } michael@0: port.postMessage({topic: "ping"}) michael@0: } michael@0: michael@0: // More duplicated code from browser_thumbnails_brackground_crash. michael@0: // Bug 915518 exists to unify these. michael@0: michael@0: // This observer is needed so we can clean up all evidence of the crash so michael@0: // the testrunner thinks things are peachy. michael@0: let crashObserver = function(callback) { michael@0: this.callback = callback; michael@0: } michael@0: crashObserver.prototype = { michael@0: observe: function(subject, topic, data) { michael@0: is(topic, 'ipc:content-shutdown', 'Received correct observer topic.'); michael@0: ok(subject instanceof Components.interfaces.nsIPropertyBag2, michael@0: 'Subject implements nsIPropertyBag2.'); michael@0: // we might see this called as the process terminates due to previous tests. michael@0: // We are only looking for "abnormal" exits... michael@0: if (!subject.hasKey("abnormal")) { michael@0: info("This is a normal termination and isn't the one we are looking for..."); michael@0: return; michael@0: } michael@0: michael@0: var dumpID; michael@0: if ('nsICrashReporter' in Components.interfaces) { michael@0: dumpID = subject.getPropertyAsAString('dumpID'); michael@0: ok(dumpID, "dumpID is present and not an empty string"); michael@0: } michael@0: michael@0: if (dumpID) { michael@0: var minidumpDirectory = getMinidumpDirectory(); michael@0: removeFile(minidumpDirectory, dumpID + '.dmp'); michael@0: removeFile(minidumpDirectory, dumpID + '.extra'); michael@0: } michael@0: this.callback(); michael@0: } michael@0: } michael@0: michael@0: function getMinidumpDirectory() { michael@0: var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile); michael@0: dir.append("minidumps"); michael@0: return dir; michael@0: } michael@0: function removeFile(directory, filename) { michael@0: var file = directory.clone(); michael@0: file.append(filename); michael@0: if (file.exists()) { michael@0: file.remove(false); michael@0: } michael@0: }