author | Dale Harvey <dale@arandomurl.com> |
Thu, 04 Apr 2013 17:58:44 -0700 | |
changeset 127737 | e63cb4c3e06320521b32cf08465f66fa728d35f3 |
parent 127736 | 8161280e740e290a8082a9e2a6c6766b23460a05 |
child 127738 | 503dea706f82dcc671e73aae1b2ce8226bec8bb9 |
push id | 24512 |
push user | ryanvm@gmail.com |
push date | Fri, 05 Apr 2013 20:13:49 +0000 |
treeherder | mozilla-central@139b6ba547fa [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | fabrice, ted |
bugs | 826058 |
milestone | 23.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/dom/apps/tests/Makefile.in +++ b/dom/apps/tests/Makefile.in @@ -6,13 +6,22 @@ DEPTH = @DEPTH@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk +MOCHITEST_FILES = \ + test_app_update.html \ + file_app.sjs \ + file_app.template.html \ + file_hosted_app.template.webapp \ + file_cached_app.template.webapp \ + file_cached_app.template.appcache \ + $(NULL) + MOCHITEST_CHROME_FILES = \ test_apps_service.xul \ $(NULL) include $(topsrcdir)/config/rules.mk
new file mode 100644 --- /dev/null +++ b/dom/apps/tests/file_app.sjs @@ -0,0 +1,119 @@ +var gBasePath = "tests/dom/apps/tests/"; +var gAppTemplatePath = "tests/dom/apps/tests/file_app.template.html"; +var gAppcacheTemplatePath = "tests/dom/apps/tests/file_cached_app.template.appcache"; + +function makeResource(templatePath, version, apptype) { + var res = readTemplate(templatePath).replace(/VERSIONTOKEN/g, version) + .replace(/APPTYPETOKEN/g, apptype); + + // Hack - This is necessary to make the tests pass, but hbambas says it + // shouldn't be necessary. Comment it out and watch the tests fail. + if (templatePath == gAppTemplatePath && apptype == 'cached') { + res = res.replace('<html>', '<html manifest="file_app.sjs?apptype=cached&getappcache=true">'); + } + + return res; +} + +function handleRequest(request, response) { + var query = getQuery(request); + + // If this is a version update, update state and return. + if ("setVersion" in query) { + setState('version', query.setVersion); + response.setHeader("Content-Type", "text/html", false); + response.setHeader("Access-Control-Allow-Origin", "*", false); + response.write('OK'); + return; + } + + // Get the app type. + var apptype = query.apptype; + if (apptype != 'hosted' && apptype != 'cached') + throw "Invalid app type: " + apptype; + + // Get the version from server state and handle the etag. + var version = Number(getState('version')); + var etag = getEtag(request, version); + dump("Server Etag: " + etag + "\n"); + + if (etagMatches(request, etag)) { + dump("Etags Match. Sending 304\n"); + response.setStatusLine(request.httpVersion, "304", "Not Modified"); + return; + } + + response.setHeader("Etag", etag, false); + if (request.hasHeader("If-None-Match")) + dump("Client Etag: " + request.getHeader("If-None-Match") + "\n"); + else + dump("No Client Etag\n"); + + // Check if we're generating a webapp manifest. + if ('getmanifest' in query) { + var template = gBasePath + 'file_' + apptype + '_app.template.webapp'; + response.setHeader("Content-Type", "application/x-web-app-manifest+json", false); + response.write(makeResource(template, version, apptype)); + return; + } + + // If apptype==cached, we might be generating the appcache manifest. + // + // NB: Among other reasons, we use the same sjs file here so that the version + // state is shared. + if (apptype == 'cached' && 'getappcache' in query) { + response.setHeader("Content-Type", "text/cache-manifest", false); + response.write(makeResource(gAppcacheTemplatePath, version, apptype)); + return; + } + + // Generate the app. + response.setHeader("Content-Type", "text/html", false); + response.write(makeResource(gAppTemplatePath, version, apptype)); +} + +function getEtag(request, version) { + return request.queryString.replace(/&/g, '-').replace(/=/g, '-') + '-' + version; +} + +function etagMatches(request, etag) { + return request.hasHeader("If-None-Match") && request.getHeader("If-None-Match") == etag; +} + +function getQuery(request) { + var query = {}; + request.queryString.split('&').forEach(function (val) { + var [name, value] = val.split('='); + query[name] = unescape(value); + }); + return query; +} + +// Copy-pasted incantations. There ought to be a better way to synchronously read +// a file into a string, but I guess we're trying to discourage that. +function readTemplate(path) { + var file = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("CurWorkD", Components.interfaces.nsILocalFile); + var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. + createInstance(Components.interfaces.nsIFileInputStream); + var cis = Components.classes["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Components.interfaces.nsIConverterInputStream); + var split = path.split("/"); + for(var i = 0; i < split.length; ++i) { + file.append(split[i]); + } + fis.init(file, -1, -1, false); + cis.init(fis, "UTF-8", 0, 0); + + var data = ""; + let (str = {}) { + let read = 0; + do { + read = cis.readString(0xffffffff, str); // read as much as we can and put it in str.value + data += str.value; + } while (read != 0); + } + cis.close(); + return data; +}
new file mode 100644 --- /dev/null +++ b/dom/apps/tests/file_app.template.html @@ -0,0 +1,63 @@ +<html> +<head> +<script> + +function sendMessage(msg) { + alert(msg); +} + +function ok(p, msg) { + if (p) + sendMessage("OK: " + msg); + else + sendMessage("KO: " + msg); +} + +function is(a, b, msg) { + if (a == b) + sendMessage("OK: " + a + " == " + b + " - " + msg); + else + sendMessage("KO: " + a + " != " + b + " - " + msg); +} + +function installed(p) { + if (p) + sendMessage("IS_INSTALLED"); + else + sendMessage("NOT_INSTALLED"); +} + +function finish() { + sendMessage("VERSION: MyWebApp vVERSIONTOKEN"); + sendMessage("DONE"); +} + +function cbError() { + ok(false, "Error callback invoked"); + finish(); +} + +function go() { + ok(true, "Launched app"); + var request = window.navigator.mozApps.getSelf(); + request.onsuccess = function() { var app = request.result; checkApp(app); } + request.onerror = cbError; +} + +function checkApp(app) { + // If the app is installed, |app| will be non-null. If it is, verify its state. + installed(!!app); + if (app) { + var appName = "Really Rapid Release (APPTYPETOKEN)"; + is(app.manifest.name, appName, "Manifest name should be correct"); + is(app.origin, "http://test", "App origin should be correct"); + is(app.installOrigin, "http://mochi.test:8888", "Install origin should be correct"); + } + finish(); +} + +</script> +</head> +<body onload="go();"> +App Body. Version: VERSIONTOKEN +</body></html>
new file mode 100644 --- /dev/null +++ b/dom/apps/tests/file_cached_app.template.appcache @@ -0,0 +1,3 @@ +CACHE MANIFEST +# Version VERSIONTOKEN +/tests/dom/apps/tests/file_app.sjs?apptype=cached
new file mode 100644 --- /dev/null +++ b/dom/apps/tests/file_cached_app.template.webapp @@ -0,0 +1,6 @@ +{ + "name": "Really Rapid Release (cached)", + "description": "Updated even faster than Firefox, just to annoy slashdotters.", + "launch_path": "/tests/dom/apps/tests/file_app.sjs?apptype=cached", + "appcache_path": "/tests/dom/apps/tests/file_app.sjs?apptype=cached&getappcache=true" +}
new file mode 100644 --- /dev/null +++ b/dom/apps/tests/file_hosted_app.template.webapp @@ -0,0 +1,5 @@ +{ + "name": "Really Rapid Release (hosted)", + "description": "Updated even faster than Firefox, just to annoy slashdotters.", + "launch_path": "/tests/dom/apps/tests/file_app.sjs?apptype=hosted" +}
new file mode 100644 --- /dev/null +++ b/dom/apps/tests/test_app_update.html @@ -0,0 +1,237 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=826058 +--> +<head> + <meta charset="utf-8"> + <title>Test for Bug 826058</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript;version=1.7"> + + /** Test for Bug 826058 **/ + + SimpleTest.waitForExplicitFinish(); + + var gBaseURL = 'http://test/tests/dom/apps/tests/'; + var gHostedManifestURL = gBaseURL + 'file_app.sjs?apptype=hosted&getmanifest=true'; + var gCachedManifestURL = gBaseURL + 'file_app.sjs?apptype=cached&getmanifest=true'; + var gGenerator = runTest(); + var mozBrowserFramesEnabledValue = undefined; + var launchableValue = undefined; + + function go() { + SpecialPowers.pushPermissions( + [{ "type": "browser", "allow": 1, "context": document }, + { "type": "embed-apps", "allow": 1, "context": document }, + { "type": "webapps-manage", "allow": 1, "context": document }], + function() { gGenerator.next() }); + } + + function continueTest() { + try { gGenerator.next(); } + catch (e) { dump("Got exception: " + e + "\n"); } + } + + function cbError() { + ok(false, "Error callback invoked: " + this.error.name); + finish(); + } + + function runTest() { + // Set up. + + try { + mozBrowserFramesEnabledValue = + SpecialPowers.getBoolPref("dom.mozBrowserFramesEnabled"); + } catch(e) {} + + launchableValue = SpecialPowers.setAllAppsLaunchable(true); + SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true); + + setAppVersion(1, continueTest); + yield; + SpecialPowers.autoConfirmAppInstall(continueTest); + yield; + + // Load the app, uninstalled. + checkAppState(null, false, 1, continueTest); + yield; + + // Bump the version and install the app. + setAppVersion(2, continueTest); + yield; + + var request = navigator.mozApps.install(gHostedManifestURL); + request.onerror = cbError; + request.onsuccess = continueTest; + yield; + var app = request.result; + ok(app, "App is non-null"); + + // Check the app a few times. + checkAppState(app, true, 2, continueTest); + yield; + checkAppState(app, true, 2, continueTest); + yield; + + // Bump the version and check the app again. The app is not cached, so the + // version bump takes effect. + setAppVersion(3, continueTest); + yield; + checkAppState(app, true, 3, continueTest); + yield; + + // Uninstall the app. + request = navigator.mozApps.mgmt.uninstall(app); + request.onerror = cbError; + request.onsuccess = continueTest; + yield; + + // Check the uninstalled app. + checkAppState(app, false, 3, continueTest); + yield; + + // Install the cached app. + setAppVersion(3, continueTest); + yield; + ok(true, "Installing cached app"); + var request = navigator.mozApps.install(gCachedManifestURL); + request.onerror = cbError; + request.onsuccess = continueTest; + yield; + var app = request.result; + ok(app, "App is non-null"); + if (app.installState == "pending") { + ok(true, "App is pending. Waiting for progress"); + app.onprogress = function() ok(true, "Got download progress"); + app.ondownloadsuccess = continueTest; + app.ondownloaderror = cbError; + yield; + } + is(app.installState, "installed", "App is installed"); + + // Check the cached app. + checkAppState(app, true, 3, continueTest); + yield; + + // Check for updates. The current infrastructure always returns a new appcache + // manifest, so there should always be an update. + var lastCheck = app.lastUpdateCheck; + ok(true, "Setting callbacks"); + app.ondownloadapplied = function() ok(true, "downloadapplied fired."); + app.ondownloadavailable = function() ok(false, "downloadavailable fired"); + ok(true, "Checking for updates"); + var request = app.checkForUpdate(); + request.onerror = cbError; + request.onsuccess = continueTest; + yield; + todo(app.lastUpdateCheck > lastCheck, "lastUpdateCheck updated appropriately"); + + + // Uninstall the app. + request = navigator.mozApps.mgmt.uninstall(app); + request.onerror = cbError; + request.onsuccess = continueTest; + yield; + ok(true, "Uninstalled app"); + + // Check the uninstalled app. + checkAppState(app, false, 3, continueTest); + yield; + + // All done. + ok(true, "All done"); + finish(); + } + + function setAppVersion(version, cb) { + var xhr = new XMLHttpRequest(); + xhr.addEventListener("load", function() { is(xhr.responseText, "OK", "setVersion OK"); cb(); }); + xhr.addEventListener("error", cbError); + xhr.addEventListener("abort", cbError); + xhr.open('GET', gBaseURL + 'file_app.sjs?setVersion=' + version, true); + xhr.send(); + } + + // This function checks the state of an installed app. It does the following: + // + // * Check various state on the app object itself. + // * Launch the app. + // * Listen for messages from the app, verifying state. + // * Close the app. + // * Invoke the callback. + function checkAppState(app, installed, version, cb) { + // Check state on the app object. + if (installed) + is(app.installState, "installed", "Checking installed app"); + else + ok(true, "Checking uninstalled app"); + + // Set up the app. We need to set the attributes before the app is inserted + // into the DOM. + var ifr = document.createElement('iframe'); + ifr.setAttribute('mozbrowser', 'true'); + ifr.setAttribute('mozapp', app ? app.manifestURL : gHostedManifestURL); + ifr.setAttribute('src', getAppURL(app)); + var domParent = document.getElementById('container'); + + // Set us up to listen for messages from the app. + var listener = function(e) { + var message = e.detail.message; + if (/OK/.exec(message)) { + ok(true, "Message from app: " + message); + } else if (/KO/.exec(message)) { + ok(false, "Message from app: " + message); + } else if (/IS_INSTALLED/.exec(message)) { + ok(installed, "App is installed"); + } else if (/NOT_INSTALLED/.exec(message)) { + ok(!installed, "App is not installed"); + } else if (/VERSION/.exec(message)) { + is(message, "VERSION: MyWebApp v" + version, "Version should be correct"); + } else if (/DONE/.exec(message)) { + ok(true, "Messaging from app complete"); + ifr.removeEventListener('mozbrowsershowmodalprompt', listener); + domParent.removeChild(ifr); + cb(); + } + } + ifr.addEventListener('mozbrowsershowmodalprompt', listener, false); + + // Add the iframe to the DOM, triggering the launch. + domParent.appendChild(ifr); + } + + // Returns that appropriate path for the app associated with the manifest, + // or the base sjs file if app is null. + function getAppURL(app) { + if (!app) + return gBaseURL + "file_app.sjs?apptype=hosted"; + return app.origin + app.manifest.launch_path; + } + + function finish() { + SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", mozBrowserFramesEnabledValue); + //SpecialPowers.setAllAppsLaunchable(launchableValue); + SimpleTest.finish(); + } + + function doReload() { + window.location.reload(true); + } + + </script> +</head> +<body onload="go()"> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=826058">Mozilla Bug 826058</a> +<p id="display"></p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +<div id="container"></div> +<button onclick="doReload()">Reload Page</button> +</body> +</html>
--- a/dom/browser-element/mochitest/browserElement_AppFramePermission.js +++ b/dom/browser-element/mochitest/browserElement_AppFramePermission.js @@ -4,28 +4,23 @@ // Bug 777384 - Test mozapp permission. "use strict"; SimpleTest.waitForExplicitFinish(); browserElementTestHelpers.setEnabledPref(true); browserElementTestHelpers.addPermission(); function makeAllAppsLaunchable() { - var Webapps = {}; - SpecialPowers.Cu.import("resource://gre/modules/Webapps.jsm", Webapps); - var appRegistry = SpecialPowers.wrap(Webapps.DOMApplicationRegistry); - - var originalValue = appRegistry.allAppsLaunchable; - appRegistry.allAppsLaunchable = true; + var originalValue = SpecialPowers.setAllAppsLaunchable(true); // Clean up after ourselves once tests are done so the test page is unloaded. window.addEventListener("unload", function restoreAllAppsLaunchable(event) { if (event.target == window.document) { window.removeEventListener("unload", restoreAllAppsLaunchable, false); - appRegistry.allAppsLaunchable = originalValue; + SpecialPowers.setAllAppsLaunchable(originalValue); } }, false); } makeAllAppsLaunchable(); function testAppElement(expectAnApp, callback) { var iframe = document.createElement('iframe'); SpecialPowers.wrap(iframe).mozbrowser = true;
--- a/dom/indexedDB/test/webapp_clearBrowserData.js +++ b/dom/indexedDB/test/webapp_clearBrowserData.js @@ -116,32 +116,26 @@ function start() return; } SpecialPowers.addPermission("browser", true, document); SpecialPowers.addPermission("browser", true, { manifestURL: manifestURL, isInBrowserElement: false }); SpecialPowers.addPermission("embed-apps", true, document); - let Webapps = {}; - SpecialPowers.wrap(Components) - .utils.import("resource://gre/modules/Webapps.jsm", Webapps); - let appRegistry = SpecialPowers.wrap(Webapps.DOMApplicationRegistry); - - let originalAllAppsLaunchable = appRegistry.allAppsLaunchable; - appRegistry.allAppsLaunchable = true; + let originalAllAppsLaunchable = SpecialPowers.setAllAppsLaunchable(true); window.addEventListener("unload", function cleanup(event) { if (event.target == document) { window.removeEventListener("unload", cleanup, false); SpecialPowers.removePermission("browser", location.href); SpecialPowers.removePermission("browser", location.protocol + "//" + appDomain); SpecialPowers.removePermission("embed-apps", location.href); - appRegistry.allAppsLaunchable = originalAllAppsLaunchable; + SpecialPowers.setAllAppsLaunchable(originalAllAppsLaunchable); } }, false); SpecialPowers.pushPrefEnv({ "set": [["dom.mozBrowserFramesEnabled", true]] }, runTest); }
--- a/testing/mochitest/b2g.json +++ b/testing/mochitest/b2g.json @@ -1,14 +1,15 @@ { "runtests": { "caps": "", "content": "", "docshell": "", "dom": "", + "dom/apps": "", "layout": "" }, "excludetests": { "caps/tests/mochitest/test_app_principal_equality.html": " should not be able to access the other frames - got true, expected false", "content/xbl/":"", "content/xul" : "", "content/html/content/test/" : "", "content/media/" : "",
--- a/testing/specialpowers/components/SpecialPowersObserver.js +++ b/testing/specialpowers/components/SpecialPowersObserver.js @@ -52,16 +52,17 @@ SpecialPowersObserver.prototype = new Sp case "chrome-document-global-created": if (!this._isFrameScriptLoaded) { // Register for any messages our API needs us to handle this._messageManager.addMessageListener("SPPrefService", this); this._messageManager.addMessageListener("SPProcessCrashService", this); this._messageManager.addMessageListener("SPPingService", this); this._messageManager.addMessageListener("SpecialPowers.Quit", this); this._messageManager.addMessageListener("SPPermissionManager", this); + this._messageManager.addMessageListener("SPWebAppService", this); this._messageManager.loadFrameScript(CHILD_LOGGER_SCRIPT, true); this._messageManager.loadFrameScript(CHILD_SCRIPT_API, true); this._messageManager.loadFrameScript(CHILD_SCRIPT, true); this._isFrameScriptLoaded = true; } break;
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js +++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js @@ -250,14 +250,27 @@ SpecialPowersObserverAPI.prototype = { return false; break; default: throw new SpecialPowersException("Invalid operation for " + "SPPermissionManager"); } break; + case "SPWebAppService": + let Webapps = {}; + Components.utils.import("resource://gre/modules/Webapps.jsm", Webapps); + switch (aMessage.json.op) { + case "set-launchable": + let val = Webapps.DOMApplicationRegistry.allAppsLaunchable; + Webapps.DOMApplicationRegistry.allAppsLaunchable = aMessage.json.launchable; + return val; + default: + throw new SpecialPowersException("Invalid operation for SPWebAppsService"); + } + break; + default: throw new SpecialPowersException("Unrecognized Special Powers API"); } } };
--- a/testing/specialpowers/content/specialpowersAPI.js +++ b/testing/specialpowers/content/specialpowersAPI.js @@ -829,16 +829,26 @@ SpecialPowersAPI.prototype = { // Disables the app install prompt for the duration of this test. There is // no need to re-enable the prompt at the end of the test. // // The provided callback is invoked once the prompt is disabled. autoConfirmAppInstall: function(cb) { this.pushPrefEnv({set: [['dom.mozApps.auto_confirm_install', true]]}, cb); }, + // Allow tests to disable the per platform app validity checks so we can + // test higher level WebApp functionality without full platform support. + setAllAppsLaunchable: function(launchable) { + var message = { + op: "set-launchable", + launchable: launchable + }; + return this._sendSyncMessage("SPWebAppService", message); + }, + addObserver: function(obs, notification, weak) { var obsvc = Cc['@mozilla.org/observer-service;1'] .getService(Ci.nsIObserverService); obsvc.addObserver(obs, notification, weak); }, removeObserver: function(obs, notification) { var obsvc = Cc['@mozilla.org/observer-service;1'] .getService(Ci.nsIObserverService);