gecko/dom/downloads/tests/shim_app_as_test_chrome.js
Andrew Sutherland 91dbc0ce1f Bug 825318 - Implement adoptDownload for mozDownloadManager, r=aus, r=sicking
Implement mozDownloadManager.adoptDownload as a certified-only API.

This also fixes and re-enables many of the existing dom/downloads tests
failures by virtue of cleanup and not running them on non-gonk toolkits
where exceptions will be thrown and things will fail.  This should
resolve bug 979446 about re-enabling the tests.
2015-02-24 11:06:59 -05:00

179 lines
6.1 KiB
JavaScript

/**
* This is the chrome helper for shim_app_as_test.js. Its load is triggered by
* shim_app_as_test.js by a call to SpecialPowers.loadChromeScript and runs
* in the parent process in a sandbox created with the system principal. (Which
* seems like it can never get collected because it's reachable via the
* apparently singleton SpecialPowersObserverAPI instance and there's no logic
* to support reaping. Wuh-oh.)
*
* It exists to help install fake privileged/certified applications. It needs
* to exist because:
* - We need to poke at DOMApplicationRegistry directly.
* - By using SpecialPowers.loadChromeScript we are able to ensure this file
* is run in the parent process. This is important because
* DOMApplicationRegistry only lives in the parent process!
* - By running entirely in a chrome privileged compartment, we avoid crazy
* wrapper problems that we would otherwise face with our shenanigans of
* directly meddling with DOMApplicationRegistry. (And hopefully save
* anyone changing DOMApplicationRegistry from frustration/hating us if
* things were just barely working.)
* - Bug 1097479 means that embed-webapps doesn't work when the content process
* that is telling us to do things is itself OOP. So it falls upon us to
* handle the running of the app by creating a sibling mozbrowser/mozapp
* iframe to the one running the mochitests.
*
* Note that in this file we try to do *only* those things that can't otherwise
* be cleanly done using SpecialPowers.
*
* Want to better understand our execution context? Check out
* SpecialPowersObserverAPI.js and search on SPLoadChromeScript.
*/
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const CC = Components.Constructor;
Cu.import('resource://gre/modules/Webapps.jsm'); // for DOMApplicationRegistry
Cu.import('resource://gre/modules/AppsUtils.jsm'); // for AppUtils
Cu.import('resource://gre/modules/Services.jsm'); // for AppUtils
// Yes, you would think there was something like this already exposed easily
// in a JSM somewhere. No.
function fetchManifest(manifestURL) {
return new Promise(function(resolve, reject) {
let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
xhr.open("GET", manifestURL, true);
xhr.responseType = "json";
xhr.addEventListener("load", function() {
if (xhr.status == 200) {
resolve(xhr.response);
} else {
reject();
}
});
xhr.addEventListener("error", function() {
reject();
});
xhr.send(null);
});
}
/**
* Install an app using confirmInstall using pre-chewed data. This avoids the
* check in the normal installApp flow that gets all judgemental about the
* installation of privileged and certified apps.
*/
function installApp(req) {
fetchManifest(req.manifestURL).then(function(manifestObj) {
var data = {
// cloneAppObj normalizes the representation for us
app: AppsUtils.cloneAppObject({
installOrigin: req.origin,
origin: req.origin,
manifestURL: req.manifestURL,
appStatus: AppsUtils.getAppManifestStatus(manifestObj),
receipts: [],
categories: []
}),
from: req.origin, // unused?
oid: 0, // unused?
requestID: 0, // unused-ish
appId: 0, // unused
isBrowser: false,
isPackage: false, // used
// magic to auto-ack... don't think we care about this...
forceSuccessAck: false
// stuff that probably doesn't matter: 'mm', 'apkInstall',
};
// cloneAppObject does not propagate the manifest
data.app.manifest = manifestObj;
return DOMApplicationRegistry.confirmInstall(data).then(
function() {
var appId =
DOMApplicationRegistry.getAppLocalIdByManifestURL(req.manifestURL);
// act like this is a privileged app having all of its permissions
// authorized at first run.
DOMApplicationRegistry.updatePermissionsForApp(
appId,
/* preinstalled */ true,
/* system update? */ true);
sendAsyncMessage(
'installed',
{
appId: appId,
manifestURL: req.manifestURL,
manifest: manifestObj
});
},
function(err) {
sendAsyncMessage('installed', false);
});
});
}
function uninstallApp(appInfo) {
DOMApplicationRegistry.uninstall(appInfo.manifestURL).then(
function() {
sendAsyncMessage('uninstalled', true);
},
function() {
sendAsyncMessage('uninstalled', false);
});
}
var activeIframe = null;
/**
* Run our app in a sibling mozbrowser/mozapp iframe to the mochitest iframe.
* This is needed because we can't nest mozbrowser/mozapp iframes inside our
* already-OOP iframe until bug 1097479 is resolved.
*/
function runApp(appInfo) {
let shellDomWindow = Services.wm.getMostRecentWindow('navigator:browser');
let sysAppFrame = shellDomWindow.document.body.querySelector('#systemapp');
let sysAppDoc = sysAppFrame.contentDocument;
let siblingFrame = sysAppDoc.body.querySelector('#test-container');
let ifr = activeIframe = sysAppDoc.createElement('iframe');
ifr.setAttribute('mozbrowser', 'true');
ifr.setAttribute('remote', 'true');
ifr.setAttribute('mozapp', appInfo.manifestURL);
ifr.addEventListener('mozbrowsershowmodalprompt', function(evt) {
var message = evt.detail.message;
// only send the message as long as we haven't been told to clean up.
if (activeIframe) {
sendAsyncMessage('appMessage', message);
}
}, false);
ifr.addEventListener('mozbrowsererror', function(evt) {
if (activeIframe) {
sendAsyncMessage('appError', { message: '' + evt.detail });
}
});
ifr.setAttribute('src', appInfo.manifest.launch_path);
siblingFrame.parentElement.appendChild(ifr);
}
function closeApp() {
if (activeIframe) {
activeIframe.parentElement.removeChild(activeIframe);
activeIframe = null;
}
}
addMessageListener('install', installApp);
addMessageListener('uninstall', uninstallApp);
addMessageListener('run', runApp);
addMessageListener('close', closeApp);