diff --git a/build/pgo/index.html b/build/pgo/index.html
index 7e6ce301d9e..6bba02400cf 100644
--- a/build/pgo/index.html
+++ b/build/pgo/index.html
@@ -2,117 +2,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
- function quitHook()
- {
- var xhr = new XMLHttpRequest();
- xhr.open("GET", "http://" + location.host + "/server/shutdown", true);
- xhr.onreadystatechange = function (event)
- {
- if (xhr.readyState == 4)
- goQuitApplication();
- };
- xhr.send(null);
- }
-
- function canQuitApplication()
- {
- var os = Components.classes["@mozilla.org/observer-service;1"]
- .getService(Components.interfaces.nsIObserverService);
- if (!os)
- {
- return true;
- }
-
- try
- {
- var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
- .createInstance(Components.interfaces.nsISupportsPRBool);
- os.notifyObservers(cancelQuit, "quit-application-requested", null);
-
- // Something aborted the quit process.
- if (cancelQuit.data)
- {
- return false;
- }
- }
- catch (ex)
- {
- }
- os.notifyObservers(null, "quit-application-granted", null);
- return true;
- }
-
- function goQuitApplication()
- {
- const privs = 'UniversalXPConnect';
-
- try
- {
- netscape.security.PrivilegeManager.enablePrivilege(privs);
- }
- catch(ex)
- {
- throw('goQuitApplication: privilege failure ' + ex);
- }
-
- if (!canQuitApplication())
- {
- return false;
- }
-
- const kAppStartup = '@mozilla.org/toolkit/app-startup;1';
- const kAppShell = '@mozilla.org/appshell/appShellService;1';
- var appService;
- var forceQuit;
-
- if (kAppStartup in Components.classes)
- {
- appService = Components.classes[kAppStartup].
- getService(Components.interfaces.nsIAppStartup);
- forceQuit = Components.interfaces.nsIAppStartup.eForceQuit;
-
- }
- else if (kAppShell in Components.classes)
- {
- appService = Components.classes[kAppShell].
- getService(Components.interfaces.nsIAppShellService);
- forceQuit = Components.interfaces.nsIAppShellService.eForceQuit;
- }
- else
- {
- throw 'goQuitApplication: no AppStartup/appShell';
- }
-
- var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService();
-
- var windowManagerInterface = windowManager.
- QueryInterface(Components.interfaces.nsIWindowMediator);
-
- var enumerator = windowManagerInterface.getEnumerator(null);
-
- while (enumerator.hasMoreElements())
- {
- var domWindow = enumerator.getNext();
- if (("tryToClose" in domWindow) && !domWindow.tryToClose())
- {
- return false;
- }
- domWindow.close();
- }
-
- try
- {
- appService.quit(forceQuit);
- }
- catch(ex)
- {
- throw('goQuitApplication: ' + ex);
- }
-
- return true;
- }
-
+
var list =
[
"blueprint/sample.html",
@@ -162,7 +52,7 @@
if (idx < list.length) {
window.setTimeout(loadURL, interval);
} else {
- window.setTimeout(goQuitApplication, interval);
+ window.setTimeout(Quitter.quit, interval);
}
}
var i;
diff --git a/build/pgo/profileserver.py b/build/pgo/profileserver.py
index fae1775e37d..912684a6a27 100644
--- a/build/pgo/profileserver.py
+++ b/build/pgo/profileserver.py
@@ -48,7 +48,7 @@ if __name__ == '__main__':
prefs[pref] = Preferences.cast(prefs[pref])
profile = FirefoxProfile(profile=profilePath,
preferences=prefs,
- #addons=[os.path.join(here, 'extension')],
+ addons=[os.path.join(build.distdir, 'xpi-stage', 'quitter')],
locations=locations)
env = os.environ.copy()
diff --git a/toolkit/toolkit.mozbuild b/toolkit/toolkit.mozbuild
index fc0bf8a1f26..ac6a81e62e8 100644
--- a/toolkit/toolkit.mozbuild
+++ b/toolkit/toolkit.mozbuild
@@ -181,6 +181,8 @@ add_tier_dir('platform', 'addon-sdk')
if CONFIG['ENABLE_MARIONETTE'] or CONFIG['MOZ_WIDGET_TOOLKIT'] not in ('gonk', 'android'):
add_tier_dir('platform', 'testing/marionette')
+add_tier_dir('platform', 'tools/quitter')
+
if CONFIG['ENABLE_TESTS']:
add_tier_dir('platform', [
'testing/mochitest',
diff --git a/tools/quitter/Makefile.in b/tools/quitter/Makefile.in
new file mode 100644
index 00000000000..8a4fd6e1ba2
--- /dev/null
+++ b/tools/quitter/Makefile.in
@@ -0,0 +1,20 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+include $(DEPTH)/config/autoconf.mk
+
+XPI_NAME = quitter
+XPI_PKGNAME = quitter@mozilla.org
+NO_JS_MANIFEST = 1
+
+DIST_FILES = \
+ install.rdf \
+ chrome.manifest \
+ $(NULL)
+
+# Used in install.rdf
+USE_EXTENSION_MANIFEST=1
+
+include $(topsrcdir)/config/rules.mk
diff --git a/tools/quitter/QuitterObserver.js b/tools/quitter/QuitterObserver.js
new file mode 100644
index 00000000000..fe2a810c93d
--- /dev/null
+++ b/tools/quitter/QuitterObserver.js
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const CHILD_SCRIPT = "chrome://quitter/content/contentscript.js";
+
+/* XPCOM gunk */
+function QuitterObserver() {}
+
+QuitterObserver.prototype = {
+ classDescription: "Quitter Observer for use in testing.",
+ classID: Components.ID("{c235a986-5ac1-4f28-ad73-825dae9bad90}"),
+ contractID: "@mozilla.org/quitter-observer;1",
+ QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIObserver]),
+ _xpcom_categories: [{category: "profile-after-change", service: true }],
+ isFrameScriptLoaded: false,
+
+ observe: function(aSubject, aTopic, aData)
+ {
+ if (aTopic == "profile-after-change") {
+ this.init();
+ } else if (!this.isFrameScriptLoaded &&
+ aTopic == "chrome-document-global-created") {
+
+ var messageManager = Cc["@mozilla.org/globalmessagemanager;1"].
+ getService(Ci.nsIMessageBroadcaster);
+ // Register for any messages our API needs us to handle
+ messageManager.addMessageListener("Quitter.Quit", this);
+
+ messageManager.loadFrameScript(CHILD_SCRIPT, true);
+ this.isFrameScriptLoaded = true;
+ } else if (aTopic == "xpcom-shutdown") {
+ this.uninit();
+ }
+ },
+
+ init: function()
+ {
+ var obs = Services.obs;
+ obs.addObserver(this, "xpcom-shutdown", false);
+ obs.addObserver(this, "chrome-document-global-created", false);
+ },
+
+ uninit: function()
+ {
+ var obs = Services.obs;
+ obs.removeObserver(this, "chrome-document-global-created", false);
+ },
+
+ /**
+ * messageManager callback function
+ * This will get requests from our API in the window and process them in chrome for it
+ **/
+ receiveMessage: function(aMessage) {
+ switch(aMessage.name) {
+ case "Quitter.Quit":
+ let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
+ appStartup.quit(Ci.nsIAppStartup.eForceQuit);
+ break;
+ }
+ }
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([QuitterObserver]);
diff --git a/tools/quitter/chrome.manifest b/tools/quitter/chrome.manifest
new file mode 100644
index 00000000000..feffe54c427
--- /dev/null
+++ b/tools/quitter/chrome.manifest
@@ -0,0 +1,4 @@
+content quitter chrome/quitter/content/
+component {c235a986-5ac1-4f28-ad73-825dae9bad90} components/QuitterObserver.js
+contract @mozilla.org/quitter-observer;1 {c235a986-5ac1-4f28-ad73-825dae9bad90}
+category profile-after-change @mozilla.org/quitter-observer;1 @mozilla.org/quitter-observer;1
diff --git a/tools/quitter/contentscript.js b/tools/quitter/contentscript.js
new file mode 100644
index 00000000000..a870a6623a8
--- /dev/null
+++ b/tools/quitter/contentscript.js
@@ -0,0 +1,38 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
+function Quitter() {
+}
+
+Quitter.prototype = {
+ toString: function() { return "[Quitter]"; },
+ quit: function() {
+ sendSyncMessage('Quitter.Quit', {});
+ },
+ __exposedProps__: {
+ 'toString': 'r',
+ 'quit': 'r'
+ }
+};
+
+// This is a frame script, so it may be running in a content process.
+// In any event, it is targeted at a specific "tab", so we listen for
+// the DOMWindowCreated event to be notified about content windows
+// being created in this context.
+
+function QuitterManager() {
+ addEventListener("DOMWindowCreated", this, false);
+}
+
+QuitterManager.prototype = {
+ handleEvent: function handleEvent(aEvent) {
+ var window = aEvent.target.defaultView;
+ window.wrappedJSObject.Quitter = new Quitter(window);
+ }
+};
+
+var quittermanager = new QuitterManager();
diff --git a/tools/quitter/install.rdf b/tools/quitter/install.rdf
new file mode 100644
index 00000000000..dfeba946257
--- /dev/null
+++ b/tools/quitter/install.rdf
@@ -0,0 +1,26 @@
+
+
+
+
+
+ quitter@mozilla.org
+ 2013.09.14
+ 2
+
+
+
+
+ toolkit@mozilla.org
+#expand __MOZILLA_VERSION_U__
+#expand __MOZILLA_VERSION_U__
+
+
+
+
+ Quitter
+ Adds a quit method that content pages can use to quit the application.
+ Mozilla
+
+
diff --git a/tools/quitter/jar.mn b/tools/quitter/jar.mn
new file mode 100644
index 00000000000..4467f923b24
--- /dev/null
+++ b/tools/quitter/jar.mn
@@ -0,0 +1,3 @@
+quitter.jar:
+% content quitter %content/
+ content/contentscript.js (contentscript.js)
diff --git a/tools/quitter/moz.build b/tools/quitter/moz.build
new file mode 100644
index 00000000000..d8de684a414
--- /dev/null
+++ b/tools/quitter/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXTRA_COMPONENTS += [
+ 'QuitterObserver.js',
+]