author | Myk Melez <myk@mozilla.org> |
Tue, 14 Aug 2012 15:27:26 -0700 | |
changeset 102403 | 07b53bdc212ac3876cea6c2c7906e3106985043e |
parent 102402 | 1fa3735f14ed8a90af762f9c26d66d0af01458f8 |
child 102404 | d0ca290ec99c53c7a9e2ada76455c605f307a64b |
push id | 13452 |
push user | myk@mozilla.com |
push date | Wed, 15 Aug 2012 15:28:52 +0000 |
treeherder | mozilla-inbound@07b53bdc212a [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | adw |
bugs | 770770 |
milestone | 17.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -107,20 +107,17 @@ Tester.prototype = { }, waitForWindowsState: function Tester_waitForWindowsState(aCallback) { let timedOut = this.currentTest && this.currentTest.timedOut; let baseMsg = timedOut ? "Found a {elt} after previous test timed out" : this.currentTest ? "Found an unexpected {elt} at the end of test run" : "Found an unexpected {elt}"; - if (gConfig.testRoot == "browser" && - this.currentTest && - window.gBrowser && - gBrowser.tabs.length > 1) { + if (this.currentTest && window.gBrowser && gBrowser.tabs.length > 1) { while (gBrowser.tabs.length > 1) { let lastTab = gBrowser.tabContainer.lastChild; let msg = baseMsg.replace("{elt}", "tab") + ": " + lastTab.linkedBrowser.currentURI.spec; this.currentTest.addResult(new testResult(false, msg, "", false)); gBrowser.removeTab(lastTab); } }
--- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -631,17 +631,17 @@ class Mochitest(object): self.urlOpts.append("testname=%s" % ("/").join([self.TEST_PATH, options.testPath])) if options.testManifest: self.urlOpts.append("testManifest=%s" % options.testManifest) if hasattr(options, 'runOnly') and options.runOnly: self.urlOpts.append("runOnly=true") else: self.urlOpts.append("runOnly=false") if options.failureFile: - self.urlOpts.append("failureFile=%s" % options.failureFile) + self.urlOpts.append("failureFile=%s" % self.getFullPath(options.failureFile)) def cleanup(self, manifest, options): """ remove temporary files and profile """ os.remove(manifest) shutil.rmtree(options.profilePath) def startVMwareRecording(self, options): """ starts recording inside VMware VM using the recording helper dll """
--- a/testing/mochitest/tests/SimpleTest/setup.js +++ b/testing/mochitest/tests/SimpleTest/setup.js @@ -82,22 +82,22 @@ if (params.repeat) { } // closeWhenDone tells us to close the browser when complete if (params.closeWhenDone) { TestRunner.onComplete = SpecialPowers.quit; } if (params.failureFile) { - TestRunner.setFailureFile(params.failureFile); + TestRunner.setFailureFile(params.failureFile[0]); } // logFile to write our results if (params.logFile) { - var spl = new SpecialPowersLogger(params.logFile); + var spl = new SpecialPowersLogger(params.logFile[0]); TestRunner.logger.addListener("mozLogger", fileLevel + "", spl.getLogCallback()); } // if we get a quiet param, don't log to the console if (!params.quiet) { function dumpListener(msg) { dump(msg.num + " " + msg.level + " " + msg.info.join(' ') + "\n"); }
--- a/webapprt/CommandLineHandler.js +++ b/webapprt/CommandLineHandler.js @@ -3,127 +3,69 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://webapprt/modules/WebappRT.jsm"); function CommandLineHandler() {} CommandLineHandler.prototype = { classID: Components.ID("{6d69c782-40a3-469b-8bfd-3ee366105a4a}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]), handle: function handle(cmdLine) { let args = Cc["@mozilla.org/hash-property-bag;1"]. createInstance(Ci.nsIWritablePropertyBag); let inTestMode = this._handleTestMode(cmdLine, args); - Services.obs.notifyObservers(args, "webapprt-command-line", null); - // Initialize DOMApplicationRegistry by importing Webapps.jsm, but only - // after broadcasting webapprt-command-line. Webapps.jsm calls - // DOMApplicationRegistry.init() when it's first imported. init() accesses - // the WebappRegD directory, which in test mode is special-cased by - // DirectoryProvider.js after it observes webapprt-command-line. - Cu.import("resource://gre/modules/Webapps.jsm"); - - if (!inTestMode) { - startUp(inTestMode); + if (inTestMode) { + // Open the mochitest shim window, which configures the runtime for tests. + Services.ww.openWindow(null, + "chrome://webapprt/content/mochitest.xul", + "_blank", + "chrome,dialog=no", + args); } else { - DOMApplicationRegistry.allAppsLaunchable = true; - - // startUp() accesses WebappRT.config, which in test mode is not valid - // until WebappRT.jsm observes an app installation. - Services.obs.addObserver(function onInstall(subj, topic, data) { - Services.obs.removeObserver(onInstall, "webapprt-test-did-install"); - startUp(inTestMode); - }, "webapprt-test-did-install", false); + args.setProperty("url", WebappRT.launchURI.spec); + Services.ww.openWindow(null, + "chrome://webapprt/content/webapp.xul", + "_blank", + "chrome,dialog=no,resizable,scrollbars,centerscreen", + args); } - - // Open the window with arguments to identify it as the main window - Services.ww.openWindow(null, - "chrome://webapprt/content/webapp.xul", - "_blank", - "chrome,dialog=no,resizable,scrollbars,centerscreen", - args); }, _handleTestMode: function _handleTestMode(cmdLine, args) { // -test-mode [url] let idx = cmdLine.findFlag("test-mode", true); if (idx < 0) return false; - let url = null; + let url; let urlIdx = idx + 1; if (urlIdx < cmdLine.length) { let potentialURL = cmdLine.getArgument(urlIdx); if (potentialURL && potentialURL[0] != "-") { - url = potentialURL; try { - Services.io.newURI(url, null, null); + url = Services.io.newURI(potentialURL, null, null); } catch (err) { throw Components.Exception( - "-test-mode argument is not a valid URL: " + url, + "-test-mode argument is not a valid URL: " + potentialURL, Components.results.NS_ERROR_INVALID_ARG); } cmdLine.removeArguments(urlIdx, urlIdx); + args.setProperty("url", url.spec); } } cmdLine.removeArguments(idx, idx); - args.setProperty("test-mode", url); return true; }, helpInfo : "", }; let components = [CommandLineHandler]; let NSGetFactory = XPCOMUtils.generateNSGetFactory(components); - -/* There's some work we need to do on startup, before we load the webapp, - * and this seems as good a place as any to do it, although it's possible - * that in the future we will find a lazier place to do it. - * - * NOTE: it's very important that the stuff we do here doesn't prevent - * the command-line handler from being registered/accessible, since otherwise - * the app won't start, which is catastrophic failure. That's why it's all - * wrapped in a try/catch block. */ -function startUp(inTestMode) { - try { - if (!inTestMode) { - // Initialize window-independent handling of webapps- notifications. Skip - // this in test mode, since it interferes with test app installations. - // We'll have to revisit this when we actually want to test installations - // and other functionality provided by WebappsHandler. - Cu.import("resource://webapprt/modules/WebappsHandler.jsm"); - WebappsHandler.init(); - } - - // On firstrun, set permissions to their default values. - if (!Services.prefs.getBoolPref("webapprt.firstrun")) { - Cu.import("resource://webapprt/modules/WebappRT.jsm"); - let uri = Services.io.newURI(WebappRT.config.app.origin, null, null); - - // Set AppCache-related permissions. - Services.perms.add(uri, "pin-app", - Ci.nsIPermissionManager.ALLOW_ACTION); - Services.perms.add(uri, "offline-app", - Ci.nsIPermissionManager.ALLOW_ACTION); - - Services.perms.add(uri, "indexedDB", - Ci.nsIPermissionManager.ALLOW_ACTION); - Services.perms.add(uri, "indexedDB-unlimited", - Ci.nsIPermissionManager.ALLOW_ACTION); - - // Now that we've set the appropriate permissions, twiddle the firstrun - // flag so we don't try to do so again. - Services.prefs.setBoolPref("webapprt.firstrun", true); - } - } catch(ex) { -#ifdef MOZ_DEBUG - dump(ex + "\n"); -#endif - } -}
--- a/webapprt/DirectoryProvider.js +++ b/webapprt/DirectoryProvider.js @@ -9,40 +9,26 @@ const Cu = Components.utils; const WEBAPP_REGISTRY_DIR = "WebappRegD"; const NS_APP_CHROME_DIR_LIST = "AChromDL"; Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://webapprt/modules/WebappRT.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -let gInTestMode = false; -Services.obs.addObserver(function observe(subj, topic, data) { - Services.obs.removeObserver(observe, "webapprt-command-line"); - let args = subj.QueryInterface(Ci.nsIPropertyBag2); - gInTestMode = args.hasKey("test-mode"); -}, "webapprt-command-line", false); - function DirectoryProvider() {} DirectoryProvider.prototype = { classID: Components.ID("{e1799fda-4b2f-4457-b671-e0641d95698d}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider, Ci.nsIDirectoryServiceProvider2]), getFile: function(prop, persistent) { if (prop == WEBAPP_REGISTRY_DIR) { - if (gInTestMode) { - // In test mode, apps are registered in the runtime's profile. Note - // that in test mode WebappRT.config may not be valid at this point. - // It's only valid after WebappRT.jsm observes an app installation, and - // we can get here before any app is installed. - return FileUtils.getDir("ProfD", []); - } let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); dir.initWithPath(WebappRT.config.registryDir); return dir; } // We return null to show failure instead of throwing an error, // which works with the way the interface is called (per bug 529077). return null;
--- a/webapprt/Makefile.in +++ b/webapprt/Makefile.in @@ -34,16 +34,17 @@ EXTRA_PP_COMPONENTS = \ components.manifest \ CommandLineHandler.js \ ContentPermission.js \ ContentPolicy.js \ DirectoryProvider.js \ $(NULL) EXTRA_JS_MODULES = \ + Startup.jsm \ WebappRT.jsm \ WebappsHandler.jsm \ $(NULL) PREF_JS_EXPORTS = $(srcdir)/prefs.js \ $(NULL) TEST_DIRS += \
new file mode 100644 --- /dev/null +++ b/webapprt/Startup.jsm @@ -0,0 +1,44 @@ +/* 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/. */ + +/* This is imported by each new webapp window but is only evaluated the first + * time it is imported. Put stuff here that you want to happen once on startup + * before the webapp is loaded. But note that the "stuff" happens immediately + * the first time this module is imported. So only put stuff here that must + * happen before the webapp is loaded. */ + +const EXPORTED_SYMBOLS = []; + +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); + +// Initialize DOMApplicationRegistry by importing Webapps.jsm. +Cu.import("resource://gre/modules/Webapps.jsm"); + +// Initialize window-independent handling of webapps- notifications. +Cu.import("resource://webapprt/modules/WebappsHandler.jsm"); +WebappsHandler.init(); + +// On firstrun, set permissions to their default values. +if (!Services.prefs.getBoolPref("webapprt.firstrun")) { + Cu.import("resource://webapprt/modules/WebappRT.jsm"); + let uri = Services.io.newURI(WebappRT.config.app.origin, null, null); + + // Set AppCache-related permissions. + Services.perms.add(uri, "pin-app", + Ci.nsIPermissionManager.ALLOW_ACTION); + Services.perms.add(uri, "offline-app", + Ci.nsIPermissionManager.ALLOW_ACTION); + + Services.perms.add(uri, "indexedDB", + Ci.nsIPermissionManager.ALLOW_ACTION); + Services.perms.add(uri, "indexedDB-unlimited", + Ci.nsIPermissionManager.ALLOW_ACTION); + + // Now that we've set the appropriate permissions, twiddle the firstrun + // flag so we don't try to do so again. + Services.prefs.setBoolPref("webapprt.firstrun", true); +}
--- a/webapprt/WebappRT.jsm +++ b/webapprt/WebappRT.jsm @@ -16,64 +16,45 @@ XPCOMUtils.defineLazyGetter(this, "FileU return FileUtils; }); XPCOMUtils.defineLazyGetter(this, "DOMApplicationRegistry", function() { Cu.import("resource://gre/modules/Webapps.jsm"); return DOMApplicationRegistry; }); -// In test mode, observe webapps-ask-install so tests can install apps. -Services.obs.addObserver(function observeCmdLine(subj, topic, data) { - Services.obs.removeObserver(observeCmdLine, "webapprt-command-line"); - let args = subj.QueryInterface(Ci.nsIPropertyBag2); - if (!args.hasKey("test-mode")) - return; - Services.obs.addObserver(function observeInstall(subj, topic, data) { - // observeInstall is present for the lifetime of the runtime. - let config = JSON.parse(data); - config.registryDir = Services.dirsvc.get("ProfD", Ci.nsIFile).path; - DOMApplicationRegistry.confirmInstall(config); - delete WebappRT.config; - WebappRT.config = deepFreeze(config); - Services.obs.notifyObservers(null, "webapprt-test-did-install", - JSON.stringify(config)); - }, "webapps-ask-install", false); -}, "webapprt-command-line", false); +let WebappRT = { + _config: null, -let WebappRT = { get config() { + if (this._config) + return this._config; + + let config; let webappFile = FileUtils.getFile("AppRegD", ["webapp.json"]); + let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]. createInstance(Ci.nsIFileInputStream); inputStream.init(webappFile, -1, 0, 0); let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON); - let config = json.decodeFromStream(inputStream, webappFile.fileSize); + config = json.decodeFromStream(inputStream, webappFile.fileSize); + + return this._config = config; + }, - // Memoize the getter, freezing the `config` object in the meantime so - // consumers don't inadvertently (or intentionally) change it, as the object - // is meant to be a read-only representation of the webapp's configuration. - config = deepFreeze(config); - delete this.config; - Object.defineProperty(this, "config", { get: function getConfig() config }); - return this.config; + // This exists to support test mode, which installs webapps after startup. + // Ideally we wouldn't have to have a setter, as tests can just delete + // the getter and then set the property. But the object to which they set it + // will have a reference to its global object, so our reference to it + // will leak that object (per bug 780674). The setter enables us to clone + // the new value so we don't actually retain a reference to it. + set config(newVal) { + this._config = JSON.parse(JSON.stringify(newVal)); + }, + + get launchURI() { + let url = Services.io.newURI(this.config.app.origin, null, null); + if (this.config.app.manifest.launch_path) { + url = Services.io.newURI(this.config.app.manifest.launch_path, null, url); + } + return url; } }; - -function deepFreeze(o) { - // First, freeze the object. - Object.freeze(o); - - // Then recursively call deepFreeze() to freeze its properties. - for (let p in o) { - // If the object is on the prototype, not an object, or is already frozen, - // skip it. Note that this might leave an unfrozen reference somewhere in - // the object if there is an already frozen object containing an unfrozen - // object. - if (!o.hasOwnProperty(p) || !(typeof o[p] == "object") || - Object.isFrozen(o[p])) - continue; - - deepFreeze(o[p]); - } - - return o; -}
new file mode 100644 --- /dev/null +++ b/webapprt/content/mochitest.js @@ -0,0 +1,74 @@ +/* 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/. */ + +/* Note: this script is loaded by both mochitest.xul and head.js, so make sure + * the code you put here can be evaluated by both! */ + +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://webapprt/modules/WebappRT.jsm"); + +const MANIFEST_URL_BASE = Services.io.newURI( + "http://mochi.test:8888/webapprtChrome/webapprt/test/chrome/", null, null); + +// When WebappsHandler opens an install confirmation dialog for apps we install, +// close it, which will be seen as the equivalent of cancelling the install. +// This doesn't prevent us from installing those apps, as we listen for the same +// notification as WebappsHandler and do the install ourselves. It just +// prevents the modal installation confirmation dialogs from hanging tests. +Services.ww.registerNotification({ + observe: function(win, topic) { + if (topic == "domwindowopened") { + // Wait for load because the window is not yet sufficiently initialized. + win.addEventListener("load", function onLoadWindow() { + win.removeEventListener("load", onLoadWindow, false); + if (win.location == "chrome://global/content/commonDialog.xul" && + win.opener == window) { + win.close(); + } + }, false); + } + } +}); + +/** + * Transmogrify the runtime session into one for the given webapp. + * + * @param {String} manifestURL + * The URL of the webapp's manifest, relative to the base URL. + * Note that the base URL points to the *chrome* WebappRT mochitests, + * so you must supply an absolute URL to manifests elsewhere. + * @param {Object} parameters + * The value to pass as the "parameters" argument to + * mozIDOMApplicationRegistry.install, e.g., { receipts: ... }. + * Use undefined to pass nothing. + * @param {Function} onBecome + * The callback to call once the transmogrification is complete. + */ +function becomeWebapp(manifestURL, parameters, onBecome) { + function observeInstall(subj, topic, data) { + Services.obs.removeObserver(observeInstall, "webapps-ask-install"); + + // Step 2: Configure the runtime session to represent the app. + // We load DOMApplicationRegistry into a local scope to avoid appearing + // to leak it. + + let scope = {}; + Cu.import("resource://gre/modules/Webapps.jsm", scope); + scope.DOMApplicationRegistry.confirmInstall(JSON.parse(data)); + + let installRecord = JSON.parse(data); + installRecord.registryDir = Services.dirsvc.get("ProfD", Ci.nsIFile).path; + WebappRT.config = installRecord; + + onBecome(); + } + Services.obs.addObserver(observeInstall, "webapps-ask-install", false); + + // Step 1: Install the app at the URL specified by the manifest. + let url = Services.io.newURI(manifestURL, null, MANIFEST_URL_BASE); + navigator.mozApps.install(url.spec, parameters); +}
new file mode 100644 --- /dev/null +++ b/webapprt/content/mochitest.xul @@ -0,0 +1,44 @@ +<?xml version="1.0"?> + +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> + +<window windowtype="webapprt:mochitest" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + +<script type="application/javascript" src="chrome://webapprt/content/mochitest.js"/> + +<script type="application/javascript"> + Cu.import("resource://webapprt/modules/WebappRT.jsm"); + + // In test mode, the runtime isn't configured until we tell it to become + // an app, which requires us to use DOMApplicationRegistry to install one. + // But DOMApplicationRegistry needs to know the location of its registry dir, + // so we need to configure the runtime with at least that information. + WebappRT.config = { + registryDir: Services.dirsvc.get("ProfD", Ci.nsIFile).path, + }; + + Cu.import("resource://gre/modules/Webapps.jsm"); + + DOMApplicationRegistry.allAppsLaunchable = true; + + becomeWebapp("http://mochi.test:8888/tests/webapprt/test/content/test.webapp", + undefined, function onBecome() { + Services.ww.openWindow( + null, + "chrome://webapprt/content/webapp.xul", + "_blank", + "chrome,dialog=no,resizable,scrollbars,centerscreen", + window.arguments[0] + ); + window.close(); + }); +</script> + +<description value="WebappRT Test Shim"/> + +</window>
--- a/webapprt/content/webapp.js +++ b/webapprt/content/webapp.js @@ -1,16 +1,17 @@ /* 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/. */ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; +Cu.import("resource://webapprt/modules/Startup.jsm"); Cu.import("resource://webapprt/modules/WebappRT.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); XPCOMUtils.defineLazyGetter(this, "gAppBrowser", function() document.getElementById("content")); #ifdef MOZ_CRASHREPORTER @@ -43,79 +44,45 @@ let progressListener = { updateCrashReportURL(aRequest.URI); } } }; function onLoad() { window.removeEventListener("load", onLoad, false); - let cmdLineArgs = window.arguments && window.arguments[0] ? - window.arguments[0].QueryInterface(Ci.nsIPropertyBag2) : - null; - - // In test mode, listen for test app installations and load the -test-mode URL - // if present. - if (cmdLineArgs && cmdLineArgs.hasKey("test-mode")) { - // This observer is only present until the first app gets installed. - // It adds the progress listener, which can't happen until then because - // the progress listener needs to access the app manifest, which isn't - // available beforehand. - Services.obs.addObserver(function observeOnce(subj, topic, data) { - Services.obs.removeObserver(observeOnce, "webapprt-test-did-install"); - gAppBrowser.addProgressListener(progressListener, - Ci.nsIWebProgress.NOTIFY_LOCATION); - }, "webapprt-test-did-install", false); - - // This observer is present for the lifetime of the runtime. - Services.obs.addObserver(function observe(subj, topic, data) { - initWindow(false); - }, "webapprt-test-did-install", false); + let args = window.arguments && window.arguments[0] ? + window.arguments[0].QueryInterface(Ci.nsIPropertyBag2) : + null; - let testURL = cmdLineArgs.get("test-mode"); - if (testURL) { - gAppBrowser.loadURI(testURL); - } - - return; - } - - gAppBrowser.webProgress. - addProgressListener(progressListener, Ci.nsIWebProgress.NOTIFY_LOCATION | - Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); - - initWindow(!!cmdLineArgs); -} -window.addEventListener("load", onLoad, false); - -function onUnload() { - gAppBrowser.removeProgressListener(progressListener); -} -window.addEventListener("unload", onUnload, false); - -function initWindow(isMainWindow) { - let manifest = WebappRT.config.app.manifest; + gAppBrowser.addProgressListener(progressListener, + Ci.nsIWebProgress.NOTIFY_LOCATION | + Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); updateMenuItems(); // Listen for clicks to redirect <a target="_blank"> to the browser. // This doesn't capture clicks so content can capture them itself and do // something different if it doesn't want the default behavior. gAppBrowser.addEventListener("click", onContentClick, false, true); - // Only load the webapp on the initially launched main window - if (isMainWindow) { - // Load the webapp's launch URL - let installRecord = WebappRT.config.app; - let url = Services.io.newURI(installRecord.origin, null, null); - if (manifest.launch_path) - url = Services.io.newURI(manifest.launch_path, null, url); - gAppBrowser.setAttribute("src", url.spec); + // This is not the only way that a URL gets loaded in the app browser. + // When content calls openWindow(), there are no window.arguments, + // but something in the platform loads the URL specified by the content. + if (args && args.hasKey("url")) { + gAppBrowser.setAttribute("src", args.get("url")); } + } +window.addEventListener("load", onLoad, false); + +function onUnload() { + gAppBrowser.removeProgressListener(progressListener); +} +window.addEventListener("unload", onUnload, false); /** * Direct a click on <a target="_blank"> to the user's default browser. * * In the long run, it might be cleaner to move this to an extension of * nsIWebBrowserChrome3::onBeforeLinkTraversal. * * @param {DOMEvent} event the DOM event @@ -194,17 +161,19 @@ function updateEditUIVisibility() { } function updateCrashReportURL(aURI) { #ifdef MOZ_CRASHREPORTER if (!gCrashReporter.enabled) return; let uri = aURI.clone(); - if (uri.userPass != "") { - try { + // uri.userPass throws on protocols without the concept of authentication, + // like about:, which tests can load, so we catch and ignore an exception. + try { + if (uri.userPass != "") { uri.userPass = ""; - } catch (e) {} - } + } + } catch (e) {} gCrashReporter.annotateCrashReport("URL", uri.spec); #endif }
--- a/webapprt/jar.mn +++ b/webapprt/jar.mn @@ -1,8 +1,10 @@ # 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/. webapprt.jar: % content webapprt %content/ * content/webapp.js (content/webapp.js) * content/webapp.xul (content/webapp.xul) + content/mochitest.js (content/mochitest.js) + content/mochitest.xul (content/mochitest.xul)
--- a/webapprt/test/chrome/Makefile.in +++ b/webapprt/test/chrome/Makefile.in @@ -7,17 +7,16 @@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk MOCHITEST_WEBAPPRT_CHROME_FILES = \ head.js \ - install.html \ browser_sample.js \ sample.webapp \ sample.html \ browser_window-title.js \ window-title.webapp \ window-title.html \ $(NULL)
--- a/webapprt/test/chrome/browser_sample.js +++ b/webapprt/test/chrome/browser_sample.js @@ -1,19 +1,20 @@ // This is a sample WebappRT chrome test. It's just a browser-chrome mochitest. +Cu.import("resource://webapprt/modules/WebappRT.jsm"); + function test() { waitForExplicitFinish(); ok(true, "true is true!"); - installWebapp("sample.webapp", undefined, function onInstall(appConfig) { + loadWebapp("sample.webapp", undefined, function onLoad() { is(document.documentElement.getAttribute("title"), - appConfig.app.manifest.name, + WebappRT.config.app.manifest.name, "Window title should be webapp name"); - let content = document.getElementById("content"); - let msg = content.contentDocument.getElementById("msg"); + let msg = gAppBrowser.contentDocument.getElementById("msg"); var observer = new MutationObserver(function (mutations) { ok(/^Webapp getSelf OK:/.test(msg.textContent), "The webapp should have successfully installed and updated its msg"); finish(); }); observer.observe(msg, { childList: true }); }); }
--- a/webapprt/test/chrome/browser_window-title.js +++ b/webapprt/test/chrome/browser_window-title.js @@ -1,28 +1,26 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://webapprt/modules/WebappRT.jsm"); function test() { waitForExplicitFinish(); - installWebapp("window-title.webapp", undefined, - function onInstall(appConfig) { + loadWebapp("window-title.webapp", undefined, function onLoad() { is(document.documentElement.getAttribute("title"), - appConfig.app.manifest.name, + WebappRT.config.app.manifest.name, "initial window title should be webapp name"); - let appBrowser = document.getElementById("content"); - // Tests are triples of [URL to load, expected window title, test message]. let tests = [ ["http://example.com/webapprtChrome/webapprt/test/chrome/window-title.html", - "http://example.com" + " - " + appConfig.app.manifest.name, + "http://example.com" + " - " + WebappRT.config.app.manifest.name, "window title should show origin of page at different origin"], ["http://mochi.test:8888/webapprtChrome/webapprt/test/chrome/window-title.html", - appConfig.app.manifest.name, + WebappRT.config.app.manifest.name, "after returning to app origin, window title should no longer show origin"], ]; let title, message; let progressListener = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener, Ci.nsISupportsWeakReference]), @@ -31,25 +29,25 @@ function test() { // Do test in timeout to give runtime time to change title. window.setTimeout(function() { is(document.documentElement.getAttribute("title"), title, message); testNext(); }, 0); } }; - appBrowser.addProgressListener(progressListener, - Ci.nsIWebProgress.NOTIFY_LOCATION); + gAppBrowser.addProgressListener(progressListener, + Ci.nsIWebProgress.NOTIFY_LOCATION); function testNext() { if (!tests.length) { - appBrowser.removeProgressListener(progressListener); - appBrowser.stop(); + gAppBrowser.removeProgressListener(progressListener); + gAppBrowser.stop(); finish(); return; } - [appBrowser.contentDocument.location, title, message] = tests.shift(); + [gAppBrowser.contentDocument.location, title, message] = tests.shift(); } testNext(); }); }
--- a/webapprt/test/chrome/head.js +++ b/webapprt/test/chrome/head.js @@ -1,63 +1,31 @@ /* 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/. */ - -const INSTALL_URL = - "http://mochi.test:8888/webapprtChrome/webapprt/test/chrome/install.html"; + * 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/. */ Cu.import("resource://gre/modules/Services.jsm"); -/** - * Installs the given webapp and navigates to it. - * - * @param manifestPath - * The path of the webapp's manifest relative to - * http://mochi.test:8888/webapprtChrome/webapprt/test/chrome/. - * @param parameters - * The value to pass as the "parameters" argument to - * mozIDOMApplicationRegistry.install, e.g., { receipts: ... }. Use - * undefined to pass nothing. - * @param callback - * Called when the newly installed webapp is navigated to. It's passed - * the webapp's config object. - */ -function installWebapp(manifestPath, parameters, onInstall) { - // Three steps: (1) Load install.html, (2) listen for webapprt-test-did- - // install to get the app config object, and then (3) listen for load of the - // webapp page to call the callback. webapprt-test-did-install will be - // broadcasted before install.html navigates to the webapp page. (This is due - // to some implementation details: WebappRT.jsm confirms the installation by - // calling DOMApplicationRegistry.confirmInstall, and then it immediately - // broadcasts webapprt-test-did-install. confirmInstall asynchronously - // notifies the mozApps consumer via onsuccess, which is when install.html - // navigates to the webapp page.) - - let content = document.getElementById("content"); +// Some of the code we want to provide to chrome mochitests is in another file +// so we can share it with the mochitest shim window, thus we need to load it. +Services.scriptloader.loadSubScript("chrome://webapprt/content/mochitest.js", + this); - Services.obs.addObserver(function observe(subj, topic, data) { - // step 2 - Services.obs.removeObserver(observe, "webapprt-test-did-install"); - let appConfig = JSON.parse(data); - - content.addEventListener("load", function onLoad(event) { - // step 3 - content.removeEventListener("load", onLoad, true); - let webappURL = appConfig.app.origin + appConfig.app.manifest.launch_path; - is(event.target.URL, webappURL, - "No other page should have loaded between installation and " + - "the webapp's page load: " + event.target.URL); - onInstall(appConfig); - }, true); - }, "webapprt-test-did-install", false); - - // step 1 - let args = [["manifestPath", manifestPath]]; - if (parameters !== undefined) { - args.push(["parameters", parameters]); - } - let queryStr = args.map(function ([key, val]) - key + "=" + encodeURIComponent(JSON.stringify(val))). - join("&"); - let installURL = INSTALL_URL + "?" + queryStr; - content.loadURI(installURL); +/** + * Load the webapp in the app browser. + * + * @param {String} manifestURL + * @see becomeWebapp + * @param {Object} parameters + * @see becomeWebapp + * @param {Function} onLoad + * The callback to call once the webapp is loaded. + */ +function loadWebapp(manifest, parameters, onLoad) { + becomeWebapp(manifest, parameters, function onBecome() { + function onLoadApp() { + gAppBrowser.removeEventListener("load", onLoadApp, true); + onLoad(); + } + gAppBrowser.addEventListener("load", onLoadApp, true); + gAppBrowser.setAttribute("src", WebappRT.launchURI.spec); + }); }
deleted file mode 100644 --- a/webapprt/test/chrome/install.html +++ /dev/null @@ -1,56 +0,0 @@ -<!DOCTYPE HTML> - -<!-- - Chrome tests load this file to install their webapps. Pass manifestPath=path - in the query string to install the app with the manifest at - http://mochi.test:8888/webapprtChrome/webapprt/test/chrome/<path>. ---> - -<html> - <head> - <meta charset="utf-8"> - <script> - -function parseQueryStr() { - return window.location.search.substr(1).split("&"). - map(function (pairStr) pairStr.split("=")). - reduce(function (memo, [key, val]) { - memo[key] = JSON.parse(decodeURIComponent(val)); - return memo; - }, {}); -} - -function msg(str) { - document.getElementById("msg").textContent = str; -} - -function onLoad() { - var args = parseQueryStr(); - if (!args.manifestPath) { - msg("No manifest path given, so standing by."); - return; - } - var manifestURL = - "http://mochi.test:8888/webapprtChrome/webapprt/test/chrome/" + - args.manifestPath; - var installArgs = [manifestURL, args.parameters]; - msg("Installing webapp with arguments " + installArgs.toSource() + "..."); - var install = navigator.mozApps.install.apply(navigator.mozApps, installArgs); - install.onsuccess = function (event) { - msg("Webapp installed, now navigating to it."); - var testAppURL = install.result.origin + - install.result.manifest.launch_path; - window.location = testAppURL; - }; - install.onerror = function () { - msg("Webapp installation failed with " + install.error.name + - " for manifest " + manifestURL); - }; -} - - </script> - </head> - <body onload="onLoad();" onunload=""> - <p id="msg">Installation page waiting for page load...</p> - </body> -</html>
--- a/webapprt/test/content/Makefile.in +++ b/webapprt/test/content/Makefile.in @@ -6,15 +6,13 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk MOCHITEST_FILES = \ - helpers.js \ + test.webapp \ webapprt_sample.html \ - sample.webapp \ - sample.html \ $(NULL) include $(topsrcdir)/config/rules.mk
deleted file mode 100644 --- a/webapprt/test/content/helpers.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Shows a message on the page. - * - * @param str - * The message to show. - */ -function msg(str) { - document.getElementById("msg").textContent = str; -} - -/** - * Installs the specified webapp and navigates to it in the iframe. - * - * @param manifestPath - * The path of the manifest relative to - * http://mochi.test:8888/tests/webapprt/test/content/. - * @param parameters - * The value to pass as the "parameters" argument to - * mozIDOMApplicationRegistry.install, e.g., { receipts: ... }. Use - * undefined to pass nothing. - */ -function installWebapp(manifestPath, parameters) { - var manifestURL = "http://mochi.test:8888/tests/webapprt/test/content/" + - manifestPath; - var installArgs = [manifestURL, parameters]; - msg("Installing webapp with arguments " + installArgs.toSource() + "..."); - var install = navigator.mozApps.install.apply(navigator.mozApps, installArgs); - install.onsuccess = function (event) { - msg("Webapp installed."); - var testAppURL = install.result.origin + - install.result.manifest.launch_path + - window.location.search; - document.getElementById("webapp-iframe").src = testAppURL; - }; - install.onerror = function () { - msg("Webapp installation failed with " + install.error.name + - " for manifest " + manifestURL); - }; -} - -/** - * If webapprt_foo.html is loaded in the window, this function installs the - * webapp whose manifest is named foo.webapp. - * - * @param parameters - * The value to pass as the "parameters" argument to - * mozIDOMApplicationRegistry.install, e.g., { receipts: ... }. Use - * undefined to pass nothing. - */ -function installOwnWebapp(parameters) { - var match = /webapprt_(.+)\.html$/.exec(window.location.pathname); - if (!match) { - throw new Error("Test URL is unconventional, so could not derive a " + - "manifest URL from it: " + window.location); - } - installWebapp(match[1] + ".webapp", parameters); -}
deleted file mode 100644 --- a/webapprt/test/content/sample.html +++ /dev/null @@ -1,42 +0,0 @@ -<!DOCTYPE HTML> - -<!-- - This file is the actual test. It's a webapp installed by webapprt_sample.html - and loaded into its iframe. It's just a plain mochitest. ---> - -<html> - <head> - <meta charset="utf-8"> - <script src="/tests/SimpleTest/SimpleTest.js"></script> - <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> - </head> - <body> - <p id="display"> - This is the test webapp. - </p> - <div id="content" style="display: none"></div> - <pre id="test"> - <script> - -SimpleTest.waitForExplicitFinish(); - -ok(true, "true is true!"); - -var self = navigator.mozApps.getSelf(); -self.onsuccess = function () { - ok(true, "onsuccess should be called"); - ok(self.result, "result should be nonnull"); - ok(self.result.manifest, "manifest should be nonnull"); - is(self.result.manifest.name, "Sample Test Webapp", "manifest.name"); - SimpleTest.finish(); -}; -self.onerror = function () { - ok(false, "onerror should not be called"); - SimpleTest.finish(); -}; - - </script> - </pre> - </body> -</html>
deleted file mode 100644 --- a/webapprt/test/content/sample.webapp +++ /dev/null @@ -1,1 +0,0 @@ -{"name": "Sample Test Webapp", "description": "A webapp that demonstrates how to make a WebappRT test.", "launch_path": "/tests/webapprt/test/content/sample.html" } \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/webapprt/test/content/test.webapp @@ -0,0 +1,4 @@ +{ + "name": "WebappRT Mochitest Webapp", + "description": "a webapp for running WebappRT mochitests" +}
--- a/webapprt/test/content/webapprt_sample.html +++ b/webapprt/test/content/webapprt_sample.html @@ -1,25 +1,44 @@ <!DOCTYPE HTML> <!-- - Since its name is prefixed with webapprt_, this file is picked up by the - mochitest harness. Once loaded, it installs the webapp, and when the - installation completes, it loads the webapp into an iframe. The webapp is - the actual test; see sample.html. + This is a sample WebappRT content mochitest. Since its name is prefixed with + webapprt_, this file is picked up by the Mochitest harness. It's just a plain + mochitest that runs in the app browser within an app window. --> <html> <head> <meta charset="utf-8"> - <script src="helpers.js"></script> - <script> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + </head> + <body> + <p id="display"> + This is the sample WebappRT content mochitest. + </p> + <div id="content" style="display: none"></div> + <pre id="test"> + <script> -// If your installation page needs to do anything other than call -// installOwnWebapp, you can do it here. +SimpleTest.waitForExplicitFinish(); + +ok(true, "true is true!"); - </script> - </head> - <body onload="installOwnWebapp();"> - <p id="msg">Installation page waiting for page load...</p> - <iframe id="webapp-iframe" width="100%" height="93%"></iframe> +var self = navigator.mozApps.getSelf(); +self.onsuccess = function () { + ok(true, "onsuccess should be called"); + ok(self.result, "result should be nonnull"); + ok(self.result.manifest, "manifest should be nonnull"); + is(self.result.manifest.name, "WebappRT Mochitest Webapp", + "manifest.name"); + SimpleTest.finish(); +}; +self.onerror = function () { + ok(false, "onerror should not be called"); + SimpleTest.finish(); +}; + + </script> + </pre> </body> </html>