1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/base/content/test/social/browser_social_workercrash.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,157 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +// This tests our recovery if a child content process hosting providers 1.9 +// crashes. 1.10 + 1.11 +// A content script we inject into one of our browsers 1.12 +const TEST_CONTENT_HELPER = "chrome://mochitests/content/browser/browser/base/content/test/social/social_crash_content_helper.js"; 1.13 + 1.14 +let {getFrameWorkerHandle} = Cu.import("resource://gre/modules/FrameWorker.jsm", {}); 1.15 +let {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise; 1.16 + 1.17 +function test() { 1.18 + waitForExplicitFinish(); 1.19 + 1.20 + // We need to ensure all our workers are in the same content process. 1.21 + Services.prefs.setIntPref("dom.ipc.processCount", 1); 1.22 + 1.23 + // This test generates many uncaught promises that should not cause failures. 1.24 + Promise.Debugging.clearUncaughtErrorObservers(); 1.25 + 1.26 + runSocialTestWithProvider(gProviders, function (finishcb) { 1.27 + runSocialTests(tests, undefined, undefined, function() { 1.28 + Services.prefs.clearUserPref("dom.ipc.processCount"); 1.29 + finishcb(); 1.30 + }); 1.31 + }); 1.32 +} 1.33 + 1.34 +let gProviders = [ 1.35 + { 1.36 + name: "provider 1", 1.37 + origin: "https://example.com", 1.38 + sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1", 1.39 + workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js", 1.40 + iconURL: "chrome://branding/content/icon48.png" 1.41 + }, 1.42 + { 1.43 + name: "provider 2", 1.44 + origin: "https://test1.example.com", 1.45 + sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2", 1.46 + workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js", 1.47 + iconURL: "chrome://branding/content/icon48.png" 1.48 + } 1.49 +]; 1.50 + 1.51 +var tests = { 1.52 + testCrash: function(next) { 1.53 + // open the sidebar, then crash the child. 1.54 + let sbrowser = document.getElementById("social-sidebar-browser"); 1.55 + onSidebarLoad(function() { 1.56 + // get the browser element for our provider. 1.57 + let fw = getFrameWorkerHandle(gProviders[0].workerURL); 1.58 + fw.port.close(); 1.59 + fw._worker.browserPromise.then(browser => { 1.60 + let mm = browser.messageManager; 1.61 + mm.loadFrameScript(TEST_CONTENT_HELPER, false); 1.62 + // add an observer for the crash - after it sees the crash we attempt 1.63 + // a reload. 1.64 + let observer = new crashObserver(function() { 1.65 + info("Saw the process crash.") 1.66 + Services.obs.removeObserver(observer, 'ipc:content-shutdown'); 1.67 + // Add another sidebar load listener - it should be the error page. 1.68 + onSidebarLoad(function() { 1.69 + ok(sbrowser.contentDocument.location.href.indexOf("about:socialerror?")==0, "is on social error page"); 1.70 + // after reloading, the sidebar should reload 1.71 + onSidebarLoad(function() { 1.72 + // now ping both workers - they should both be alive. 1.73 + ensureWorkerLoaded(gProviders[0], function() { 1.74 + ensureWorkerLoaded(gProviders[1], function() { 1.75 + // and we are done! 1.76 + next(); 1.77 + }); 1.78 + }); 1.79 + }); 1.80 + // click the try-again button. 1.81 + sbrowser.contentDocument.getElementById("btnTryAgain").click(); 1.82 + }); 1.83 + }); 1.84 + Services.obs.addObserver(observer, 'ipc:content-shutdown', false); 1.85 + // and cause the crash. 1.86 + mm.sendAsyncMessage("social-test:crash"); 1.87 + }); 1.88 + }) 1.89 + SocialSidebar.show(); 1.90 + }, 1.91 +} 1.92 + 1.93 +function onSidebarLoad(callback) { 1.94 + let sbrowser = document.getElementById("social-sidebar-browser"); 1.95 + sbrowser.addEventListener("load", function load() { 1.96 + sbrowser.removeEventListener("load", load, true); 1.97 + callback(); 1.98 + }, true); 1.99 +} 1.100 + 1.101 +function ensureWorkerLoaded(manifest, callback) { 1.102 + let fw = getFrameWorkerHandle(manifest.workerURL); 1.103 + // once the worker responds to a ping we know it must be up. 1.104 + let port = fw.port; 1.105 + port.onmessage = function(msg) { 1.106 + if (msg.data.topic == "pong") { 1.107 + port.close(); 1.108 + callback(); 1.109 + } 1.110 + } 1.111 + port.postMessage({topic: "ping"}) 1.112 +} 1.113 + 1.114 +// More duplicated code from browser_thumbnails_brackground_crash. 1.115 +// Bug 915518 exists to unify these. 1.116 + 1.117 +// This observer is needed so we can clean up all evidence of the crash so 1.118 +// the testrunner thinks things are peachy. 1.119 +let crashObserver = function(callback) { 1.120 + this.callback = callback; 1.121 +} 1.122 +crashObserver.prototype = { 1.123 + observe: function(subject, topic, data) { 1.124 + is(topic, 'ipc:content-shutdown', 'Received correct observer topic.'); 1.125 + ok(subject instanceof Components.interfaces.nsIPropertyBag2, 1.126 + 'Subject implements nsIPropertyBag2.'); 1.127 + // we might see this called as the process terminates due to previous tests. 1.128 + // We are only looking for "abnormal" exits... 1.129 + if (!subject.hasKey("abnormal")) { 1.130 + info("This is a normal termination and isn't the one we are looking for..."); 1.131 + return; 1.132 + } 1.133 + 1.134 + var dumpID; 1.135 + if ('nsICrashReporter' in Components.interfaces) { 1.136 + dumpID = subject.getPropertyAsAString('dumpID'); 1.137 + ok(dumpID, "dumpID is present and not an empty string"); 1.138 + } 1.139 + 1.140 + if (dumpID) { 1.141 + var minidumpDirectory = getMinidumpDirectory(); 1.142 + removeFile(minidumpDirectory, dumpID + '.dmp'); 1.143 + removeFile(minidumpDirectory, dumpID + '.extra'); 1.144 + } 1.145 + this.callback(); 1.146 + } 1.147 +} 1.148 + 1.149 +function getMinidumpDirectory() { 1.150 + var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile); 1.151 + dir.append("minidumps"); 1.152 + return dir; 1.153 +} 1.154 +function removeFile(directory, filename) { 1.155 + var file = directory.clone(); 1.156 + file.append(filename); 1.157 + if (file.exists()) { 1.158 + file.remove(false); 1.159 + } 1.160 +}