1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/apps/src/Webapps.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,889 @@ 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 file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +const Cc = Components.classes; 1.9 +const Ci = Components.interfaces; 1.10 +const Cu = Components.utils; 1.11 +const Cr = Components.results; 1.12 + 1.13 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.14 +Cu.import("resource://gre/modules/Services.jsm"); 1.15 +Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); 1.16 +Cu.import("resource://gre/modules/AppsUtils.jsm"); 1.17 +Cu.import("resource://gre/modules/BrowserElementPromptService.jsm"); 1.18 + 1.19 +XPCOMUtils.defineLazyServiceGetter(this, "cpmm", 1.20 + "@mozilla.org/childprocessmessagemanager;1", 1.21 + "nsIMessageSender"); 1.22 + 1.23 +function convertAppsArray(aApps, aWindow) { 1.24 + let apps = new aWindow.Array(); 1.25 + for (let i = 0; i < aApps.length; i++) { 1.26 + let app = aApps[i]; 1.27 + apps.push(createApplicationObject(aWindow, app)); 1.28 + } 1.29 + 1.30 + return apps; 1.31 +} 1.32 + 1.33 +function WebappsRegistry() { 1.34 +} 1.35 + 1.36 +WebappsRegistry.prototype = { 1.37 + __proto__: DOMRequestIpcHelper.prototype, 1.38 + 1.39 + receiveMessage: function(aMessage) { 1.40 + let msg = aMessage.json; 1.41 + if (msg.oid != this._id) 1.42 + return 1.43 + let req = this.getRequest(msg.requestID); 1.44 + if (!req) 1.45 + return; 1.46 + let app = msg.app; 1.47 + switch (aMessage.name) { 1.48 + case "Webapps:Install:Return:OK": 1.49 + this.removeMessageListeners("Webapps:Install:Return:KO"); 1.50 + Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app)); 1.51 + cpmm.sendAsyncMessage("Webapps:Install:Return:Ack", 1.52 + { manifestURL : app.manifestURL }); 1.53 + break; 1.54 + case "Webapps:Install:Return:KO": 1.55 + this.removeMessageListeners(aMessage.name); 1.56 + Services.DOMRequest.fireError(req, msg.error || "DENIED"); 1.57 + break; 1.58 + case "Webapps:GetSelf:Return:OK": 1.59 + this.removeMessageListeners(aMessage.name); 1.60 + if (msg.apps.length) { 1.61 + app = msg.apps[0]; 1.62 + Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app)); 1.63 + } else { 1.64 + Services.DOMRequest.fireSuccess(req, null); 1.65 + } 1.66 + break; 1.67 + case "Webapps:CheckInstalled:Return:OK": 1.68 + this.removeMessageListeners(aMessage.name); 1.69 + Services.DOMRequest.fireSuccess(req, msg.app); 1.70 + break; 1.71 + case "Webapps:GetInstalled:Return:OK": 1.72 + this.removeMessageListeners(aMessage.name); 1.73 + Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window)); 1.74 + break; 1.75 + } 1.76 + this.removeRequest(msg.requestID); 1.77 + }, 1.78 + 1.79 + _getOrigin: function(aURL) { 1.80 + let uri = Services.io.newURI(aURL, null, null); 1.81 + return uri.prePath; 1.82 + }, 1.83 + 1.84 + // Checks that the URL scheme is appropriate (http or https) and 1.85 + // asynchronously fire an error on the DOM Request if it isn't. 1.86 + _validateURL: function(aURL, aRequest) { 1.87 + let uri; 1.88 + let res; 1.89 + 1.90 + try { 1.91 + uri = Services.io.newURI(aURL, null, null); 1.92 + if (uri.schemeIs("http") || uri.schemeIs("https")) { 1.93 + res = uri.spec; 1.94 + } 1.95 + } catch(e) { 1.96 + Services.DOMRequest.fireErrorAsync(aRequest, "INVALID_URL"); 1.97 + return false; 1.98 + } 1.99 + 1.100 + // The scheme is incorrect, fire DOMRequest error. 1.101 + if (!res) { 1.102 + Services.DOMRequest.fireErrorAsync(aRequest, "INVALID_URL"); 1.103 + return false; 1.104 + } 1.105 + 1.106 + return uri.spec; 1.107 + }, 1.108 + 1.109 + // Checks that we run as a foreground page, and fire an error on the 1.110 + // DOM Request if we aren't. 1.111 + _ensureForeground: function(aRequest) { 1.112 + let docShell = this._window.QueryInterface(Ci.nsIInterfaceRequestor) 1.113 + .getInterface(Ci.nsIWebNavigation) 1.114 + .QueryInterface(Ci.nsIDocShell); 1.115 + if (docShell.isActive) { 1.116 + return true; 1.117 + } 1.118 + 1.119 + Services.DOMRequest.fireErrorAsync(aRequest, "BACKGROUND_APP"); 1.120 + return false; 1.121 + }, 1.122 + 1.123 + _prepareInstall: function(aURL, aRequest, aParams, isPackage) { 1.124 + let installURL = this._window.location.href; 1.125 + let requestID = this.getRequestId(aRequest); 1.126 + let receipts = (aParams && aParams.receipts && 1.127 + Array.isArray(aParams.receipts)) ? aParams.receipts 1.128 + : []; 1.129 + let categories = (aParams && aParams.categories && 1.130 + Array.isArray(aParams.categories)) ? aParams.categories 1.131 + : []; 1.132 + 1.133 + let principal = this._window.document.nodePrincipal; 1.134 + 1.135 + return { app: { 1.136 + installOrigin: this._getOrigin(installURL), 1.137 + origin: this._getOrigin(aURL), 1.138 + manifestURL: aURL, 1.139 + receipts: receipts, 1.140 + categories: categories 1.141 + }, 1.142 + 1.143 + from: installURL, 1.144 + oid: this._id, 1.145 + requestID: requestID, 1.146 + appId: principal.appId, 1.147 + isBrowser: principal.isInBrowserElement, 1.148 + isPackage: isPackage 1.149 + }; 1.150 + }, 1.151 + 1.152 + // mozIDOMApplicationRegistry implementation 1.153 + 1.154 + install: function(aURL, aParams) { 1.155 + let request = this.createRequest(); 1.156 + 1.157 + let uri = this._validateURL(aURL, request); 1.158 + 1.159 + if (uri && this._ensureForeground(request)) { 1.160 + this.addMessageListeners("Webapps:Install:Return:KO"); 1.161 + cpmm.sendAsyncMessage("Webapps:Install", 1.162 + this._prepareInstall(uri, request, aParams, false)); 1.163 + } 1.164 + 1.165 + return request; 1.166 + }, 1.167 + 1.168 + getSelf: function() { 1.169 + let request = this.createRequest(); 1.170 + this.addMessageListeners("Webapps:GetSelf:Return:OK"); 1.171 + cpmm.sendAsyncMessage("Webapps:GetSelf", { origin: this._getOrigin(this._window.location.href), 1.172 + appId: this._window.document.nodePrincipal.appId, 1.173 + oid: this._id, 1.174 + requestID: this.getRequestId(request) }); 1.175 + return request; 1.176 + }, 1.177 + 1.178 + checkInstalled: function(aManifestURL) { 1.179 + let manifestURL = Services.io.newURI(aManifestURL, null, this._window.document.baseURIObject); 1.180 + this._window.document.nodePrincipal.checkMayLoad(manifestURL, true, false); 1.181 + 1.182 + let request = this.createRequest(); 1.183 + 1.184 + this.addMessageListeners("Webapps:CheckInstalled:Return:OK"); 1.185 + cpmm.sendAsyncMessage("Webapps:CheckInstalled", { origin: this._getOrigin(this._window.location.href), 1.186 + manifestURL: manifestURL.spec, 1.187 + oid: this._id, 1.188 + requestID: this.getRequestId(request) }); 1.189 + return request; 1.190 + }, 1.191 + 1.192 + getInstalled: function() { 1.193 + let request = this.createRequest(); 1.194 + this.addMessageListeners("Webapps:GetInstalled:Return:OK"); 1.195 + cpmm.sendAsyncMessage("Webapps:GetInstalled", { origin: this._getOrigin(this._window.location.href), 1.196 + oid: this._id, 1.197 + requestID: this.getRequestId(request) }); 1.198 + return request; 1.199 + }, 1.200 + 1.201 + get mgmt() { 1.202 + if (!this.hasMgmtPrivilege) { 1.203 + return null; 1.204 + } 1.205 + 1.206 + if (!this._mgmt) 1.207 + this._mgmt = new WebappsApplicationMgmt(this._window); 1.208 + return this._mgmt; 1.209 + }, 1.210 + 1.211 + uninit: function() { 1.212 + this._mgmt = null; 1.213 + cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", 1.214 + ["Webapps:Install:Return:OK"]); 1.215 + }, 1.216 + 1.217 + installPackage: function(aURL, aParams) { 1.218 + let request = this.createRequest(); 1.219 + 1.220 + let uri = this._validateURL(aURL, request); 1.221 + 1.222 + if (uri && this._ensureForeground(request)) { 1.223 + this.addMessageListeners("Webapps:Install:Return:KO"); 1.224 + cpmm.sendAsyncMessage("Webapps:InstallPackage", 1.225 + this._prepareInstall(uri, request, aParams, true)); 1.226 + } 1.227 + 1.228 + return request; 1.229 + }, 1.230 + 1.231 + // nsIDOMGlobalPropertyInitializer implementation 1.232 + init: function(aWindow) { 1.233 + this.initDOMRequestHelper(aWindow, "Webapps:Install:Return:OK"); 1.234 + 1.235 + let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor) 1.236 + .getInterface(Ci.nsIDOMWindowUtils); 1.237 + this._id = util.outerWindowID; 1.238 + cpmm.sendAsyncMessage("Webapps:RegisterForMessages", 1.239 + { messages: ["Webapps:Install:Return:OK"]}); 1.240 + 1.241 + let principal = aWindow.document.nodePrincipal; 1.242 + let perm = Services.perms 1.243 + .testExactPermissionFromPrincipal(principal, "webapps-manage"); 1.244 + 1.245 + // Only pages with the webapps-manage permission set can get access to 1.246 + // the mgmt object. 1.247 + this.hasMgmtPrivilege = perm == Ci.nsIPermissionManager.ALLOW_ACTION; 1.248 + }, 1.249 + 1.250 + classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"), 1.251 + 1.252 + QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, 1.253 + Ci.nsIObserver, 1.254 + Ci.mozIDOMApplicationRegistry, 1.255 + Ci.mozIDOMApplicationRegistry2, 1.256 + Ci.nsIDOMGlobalPropertyInitializer]), 1.257 + 1.258 + classInfo: XPCOMUtils.generateCI({classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"), 1.259 + contractID: "@mozilla.org/webapps;1", 1.260 + interfaces: [Ci.mozIDOMApplicationRegistry, 1.261 + Ci.mozIDOMApplicationRegistry2], 1.262 + flags: Ci.nsIClassInfo.DOM_OBJECT, 1.263 + classDescription: "Webapps Registry"}) 1.264 +} 1.265 + 1.266 +/** 1.267 + * mozIDOMApplication object 1.268 + */ 1.269 + 1.270 +// A simple cache for the wrapped manifests. 1.271 +let manifestCache = { 1.272 + _cache: { }, 1.273 + 1.274 + // Gets an entry from the cache, and populates the cache if needed. 1.275 + get: function mcache_get(aManifestURL, aManifest, aWindow, aInnerWindowID) { 1.276 + if (!(aManifestURL in this._cache)) { 1.277 + this._cache[aManifestURL] = { }; 1.278 + } 1.279 + 1.280 + let winObjs = this._cache[aManifestURL]; 1.281 + if (!(aInnerWindowID in winObjs)) { 1.282 + winObjs[aInnerWindowID] = Cu.cloneInto(aManifest, aWindow); 1.283 + } 1.284 + 1.285 + return winObjs[aInnerWindowID]; 1.286 + }, 1.287 + 1.288 + // Invalidates an entry in the cache. 1.289 + evict: function mcache_evict(aManifestURL, aInnerWindowID) { 1.290 + if (aManifestURL in this._cache) { 1.291 + let winObjs = this._cache[aManifestURL]; 1.292 + if (aInnerWindowID in winObjs) { 1.293 + delete winObjs[aInnerWindowID]; 1.294 + } 1.295 + 1.296 + if (Object.keys(winObjs).length == 0) { 1.297 + delete this._cache[aManifestURL]; 1.298 + } 1.299 + } 1.300 + }, 1.301 + 1.302 + observe: function(aSubject, aTopic, aData) { 1.303 + // Clear the cache on memory pressure. 1.304 + this._cache = { }; 1.305 + }, 1.306 + 1.307 + init: function() { 1.308 + Services.obs.addObserver(this, "memory-pressure", false); 1.309 + } 1.310 +}; 1.311 + 1.312 +function createApplicationObject(aWindow, aApp) { 1.313 + let app = Cc["@mozilla.org/webapps/application;1"].createInstance(Ci.mozIDOMApplication); 1.314 + app.wrappedJSObject.init(aWindow, aApp); 1.315 + return app; 1.316 +} 1.317 + 1.318 +function WebappsApplication() { 1.319 + this.wrappedJSObject = this; 1.320 +} 1.321 + 1.322 +WebappsApplication.prototype = { 1.323 + __proto__: DOMRequestIpcHelper.prototype, 1.324 + 1.325 + init: function(aWindow, aApp) { 1.326 + this._window = aWindow; 1.327 + let principal = this._window.document.nodePrincipal; 1.328 + this._appStatus = principal.appStatus; 1.329 + this.origin = aApp.origin; 1.330 + this._manifest = aApp.manifest; 1.331 + this._updateManifest = aApp.updateManifest; 1.332 + this.manifestURL = aApp.manifestURL; 1.333 + this.receipts = aApp.receipts; 1.334 + this.installOrigin = aApp.installOrigin; 1.335 + this.installTime = aApp.installTime; 1.336 + this.installState = aApp.installState || "installed"; 1.337 + this.removable = aApp.removable; 1.338 + this.lastUpdateCheck = aApp.lastUpdateCheck ? aApp.lastUpdateCheck 1.339 + : Date.now(); 1.340 + this.updateTime = aApp.updateTime ? aApp.updateTime 1.341 + : aApp.installTime; 1.342 + this.progress = NaN; 1.343 + this.downloadAvailable = aApp.downloadAvailable; 1.344 + this.downloading = aApp.downloading; 1.345 + this.readyToApplyDownload = aApp.readyToApplyDownload; 1.346 + this.downloadSize = aApp.downloadSize || 0; 1.347 + 1.348 + this._onprogress = null; 1.349 + this._ondownloadsuccess = null; 1.350 + this._ondownloaderror = null; 1.351 + this._ondownloadavailable = null; 1.352 + this._ondownloadapplied = null; 1.353 + 1.354 + this._downloadError = null; 1.355 + 1.356 + this.initDOMRequestHelper(aWindow, [ 1.357 + { name: "Webapps:CheckForUpdate:Return:KO", weakRef: true }, 1.358 + { name: "Webapps:Connect:Return:OK", weakRef: true }, 1.359 + { name: "Webapps:Connect:Return:KO", weakRef: true }, 1.360 + { name: "Webapps:FireEvent", weakRef: true }, 1.361 + { name: "Webapps:GetConnections:Return:OK", weakRef: true }, 1.362 + { name: "Webapps:UpdateState", weakRef: true } 1.363 + ]); 1.364 + 1.365 + cpmm.sendAsyncMessage("Webapps:RegisterForMessages", { 1.366 + messages: ["Webapps:FireEvent", 1.367 + "Webapps:UpdateState"], 1.368 + app: { 1.369 + id: this.id, 1.370 + manifestURL: this.manifestURL, 1.371 + installState: this.installState, 1.372 + downloading: this.downloading 1.373 + } 1.374 + }); 1.375 + }, 1.376 + 1.377 + get manifest() { 1.378 + return manifestCache.get(this.manifestURL, 1.379 + this._manifest, 1.380 + this._window, 1.381 + this.innerWindowID); 1.382 + }, 1.383 + 1.384 + get updateManifest() { 1.385 + return this.updateManifest = 1.386 + this._updateManifest ? Cu.cloneInto(this._updateManifest, this._window) 1.387 + : null; 1.388 + }, 1.389 + 1.390 + set onprogress(aCallback) { 1.391 + this._onprogress = aCallback; 1.392 + }, 1.393 + 1.394 + get onprogress() { 1.395 + return this._onprogress; 1.396 + }, 1.397 + 1.398 + set ondownloadsuccess(aCallback) { 1.399 + this._ondownloadsuccess = aCallback; 1.400 + }, 1.401 + 1.402 + get ondownloadsuccess() { 1.403 + return this._ondownloadsuccess; 1.404 + }, 1.405 + 1.406 + set ondownloaderror(aCallback) { 1.407 + this._ondownloaderror = aCallback; 1.408 + }, 1.409 + 1.410 + get ondownloaderror() { 1.411 + return this._ondownloaderror; 1.412 + }, 1.413 + 1.414 + set ondownloadavailable(aCallback) { 1.415 + this._ondownloadavailable = aCallback; 1.416 + }, 1.417 + 1.418 + get ondownloadavailable() { 1.419 + return this._ondownloadavailable; 1.420 + }, 1.421 + 1.422 + set ondownloadapplied(aCallback) { 1.423 + this._ondownloadapplied = aCallback; 1.424 + }, 1.425 + 1.426 + get ondownloadapplied() { 1.427 + return this._ondownloadapplied; 1.428 + }, 1.429 + 1.430 + get downloadError() { 1.431 + return new this._window.DOMError(this._downloadError || ''); 1.432 + }, 1.433 + 1.434 + download: function() { 1.435 + cpmm.sendAsyncMessage("Webapps:Download", 1.436 + { manifestURL: this.manifestURL }); 1.437 + }, 1.438 + 1.439 + cancelDownload: function() { 1.440 + cpmm.sendAsyncMessage("Webapps:CancelDownload", 1.441 + { manifestURL: this.manifestURL }); 1.442 + }, 1.443 + 1.444 + checkForUpdate: function() { 1.445 + let request = this.createRequest(); 1.446 + 1.447 + cpmm.sendAsyncMessage("Webapps:CheckForUpdate", 1.448 + { manifestURL: this.manifestURL, 1.449 + oid: this._id, 1.450 + requestID: this.getRequestId(request) }); 1.451 + return request; 1.452 + }, 1.453 + 1.454 + launch: function(aStartPoint) { 1.455 + let request = this.createRequest(); 1.456 + this.addMessageListeners(["Webapps:Launch:Return:OK", 1.457 + "Webapps:Launch:Return:KO"]); 1.458 + cpmm.sendAsyncMessage("Webapps:Launch", { origin: this.origin, 1.459 + manifestURL: this.manifestURL, 1.460 + startPoint: aStartPoint || "", 1.461 + oid: this._id, 1.462 + timestamp: Date.now(), 1.463 + requestID: this.getRequestId(request) }); 1.464 + return request; 1.465 + }, 1.466 + 1.467 + clearBrowserData: function() { 1.468 + let request = this.createRequest(); 1.469 + let browserChild = 1.470 + BrowserElementPromptService.getBrowserElementChildForWindow(this._window); 1.471 + if (browserChild) { 1.472 + this.addMessageListeners("Webapps:ClearBrowserData:Return"); 1.473 + browserChild.messageManager.sendAsyncMessage( 1.474 + "Webapps:ClearBrowserData", 1.475 + { manifestURL: this.manifestURL, 1.476 + oid: this._id, 1.477 + requestID: this.getRequestId(request) } 1.478 + ); 1.479 + } else { 1.480 + Services.DOMRequest.fireErrorAsync(request, "NO_CLEARABLE_BROWSER"); 1.481 + } 1.482 + return request; 1.483 + }, 1.484 + 1.485 + connect: function(aKeyword, aRules) { 1.486 + return this.createPromise(function (aResolve, aReject) { 1.487 + cpmm.sendAsyncMessage("Webapps:Connect", 1.488 + { keyword: aKeyword, 1.489 + rules: aRules, 1.490 + manifestURL: this.manifestURL, 1.491 + outerWindowID: this._id, 1.492 + requestID: this.getPromiseResolverId({ 1.493 + resolve: aResolve, 1.494 + reject: aReject 1.495 + })}); 1.496 + }.bind(this)); 1.497 + }, 1.498 + 1.499 + getConnections: function() { 1.500 + return this.createPromise(function (aResolve, aReject) { 1.501 + cpmm.sendAsyncMessage("Webapps:GetConnections", 1.502 + { manifestURL: this.manifestURL, 1.503 + outerWindowID: this._id, 1.504 + requestID: this.getPromiseResolverId({ 1.505 + resolve: aResolve, 1.506 + reject: aReject 1.507 + })}); 1.508 + }.bind(this)); 1.509 + }, 1.510 + 1.511 + addReceipt: function(receipt) { 1.512 + let request = this.createRequest(); 1.513 + 1.514 + this.addMessageListeners(["Webapps:AddReceipt:Return:OK", 1.515 + "Webapps:AddReceipt:Return:KO"]); 1.516 + 1.517 + cpmm.sendAsyncMessage("Webapps:AddReceipt", { manifestURL: this.manifestURL, 1.518 + receipt: receipt, 1.519 + oid: this._id, 1.520 + requestID: this.getRequestId(request) }); 1.521 + 1.522 + return request; 1.523 + }, 1.524 + 1.525 + removeReceipt: function(receipt) { 1.526 + let request = this.createRequest(); 1.527 + 1.528 + this.addMessageListeners(["Webapps:RemoveReceipt:Return:OK", 1.529 + "Webapps:RemoveReceipt:Return:KO"]); 1.530 + 1.531 + cpmm.sendAsyncMessage("Webapps:RemoveReceipt", { manifestURL: this.manifestURL, 1.532 + receipt: receipt, 1.533 + oid: this._id, 1.534 + requestID: this.getRequestId(request) }); 1.535 + 1.536 + return request; 1.537 + }, 1.538 + 1.539 + replaceReceipt: function(oldReceipt, newReceipt) { 1.540 + let request = this.createRequest(); 1.541 + 1.542 + this.addMessageListeners(["Webapps:ReplaceReceipt:Return:OK", 1.543 + "Webapps:ReplaceReceipt:Return:KO"]); 1.544 + 1.545 + cpmm.sendAsyncMessage("Webapps:ReplaceReceipt", { manifestURL: this.manifestURL, 1.546 + newReceipt: newReceipt, 1.547 + oldReceipt: oldReceipt, 1.548 + oid: this._id, 1.549 + requestID: this.getRequestId(request) }); 1.550 + 1.551 + return request; 1.552 + }, 1.553 + 1.554 + uninit: function() { 1.555 + this._onprogress = null; 1.556 + cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", [ 1.557 + "Webapps:FireEvent", 1.558 + "Webapps:UpdateState" 1.559 + ]); 1.560 + 1.561 + manifestCache.evict(this.manifestURL, this.innerWindowID); 1.562 + }, 1.563 + 1.564 + _fireEvent: function(aName) { 1.565 + let handler = this["_on" + aName]; 1.566 + if (handler) { 1.567 + let event = new this._window.MozApplicationEvent(aName, { 1.568 + application: this 1.569 + }); 1.570 + try { 1.571 + handler.handleEvent(event); 1.572 + } catch (ex) { 1.573 + dump("Event handler expection " + ex + "\n"); 1.574 + } 1.575 + } 1.576 + }, 1.577 + 1.578 + _updateState: function(aMsg) { 1.579 + if (aMsg.app) { 1.580 + for (let prop in aMsg.app) { 1.581 + this[prop] = aMsg.app[prop]; 1.582 + } 1.583 + } 1.584 + 1.585 + if (aMsg.error) { 1.586 + this._downloadError = aMsg.error; 1.587 + } 1.588 + 1.589 + if (aMsg.manifest) { 1.590 + this._manifest = aMsg.manifest; 1.591 + manifestCache.evict(this.manifestURL, this.innerWindowID); 1.592 + } 1.593 + }, 1.594 + 1.595 + receiveMessage: function(aMessage) { 1.596 + let msg = aMessage.json; 1.597 + let req; 1.598 + if (aMessage.name == "Webapps:Connect:Return:OK" || 1.599 + aMessage.name == "Webapps:Connect:Return:KO" || 1.600 + aMessage.name == "Webapps:GetConnections:Return:OK") { 1.601 + req = this.takePromiseResolver(msg.requestID); 1.602 + } else { 1.603 + req = this.takeRequest(msg.requestID); 1.604 + } 1.605 + 1.606 + // ondownload* callbacks should be triggered on all app instances 1.607 + if ((msg.oid != this._id || !req) && 1.608 + aMessage.name !== "Webapps:FireEvent" && 1.609 + aMessage.name !== "Webapps:UpdateState") { 1.610 + return; 1.611 + } 1.612 + 1.613 + switch (aMessage.name) { 1.614 + case "Webapps:Launch:Return:KO": 1.615 + this.removeMessageListeners(["Webapps:Launch:Return:OK", 1.616 + "Webapps:Launch:Return:KO"]); 1.617 + Services.DOMRequest.fireError(req, "APP_INSTALL_PENDING"); 1.618 + break; 1.619 + case "Webapps:Launch:Return:OK": 1.620 + this.removeMessageListeners(["Webapps:Launch:Return:OK", 1.621 + "Webapps:Launch:Return:KO"]); 1.622 + Services.DOMRequest.fireSuccess(req, null); 1.623 + break; 1.624 + case "Webapps:CheckForUpdate:Return:KO": 1.625 + Services.DOMRequest.fireError(req, msg.error); 1.626 + break; 1.627 + case "Webapps:FireEvent": 1.628 + if (msg.manifestURL != this.manifestURL) { 1.629 + return; 1.630 + } 1.631 + 1.632 + // The parent might ask childs to trigger more than one event in one 1.633 + // shot, so in order to avoid needless IPC we allow an array for the 1.634 + // 'eventType' IPC message field. 1.635 + if (!Array.isArray(msg.eventType)) { 1.636 + msg.eventType = [msg.eventType]; 1.637 + } 1.638 + 1.639 + msg.eventType.forEach((aEventType) => { 1.640 + if ("_on" + aEventType in this) { 1.641 + this._fireEvent(aEventType); 1.642 + } else { 1.643 + dump("Unsupported event type " + aEventType + "\n"); 1.644 + } 1.645 + }); 1.646 + 1.647 + if (req) { 1.648 + Services.DOMRequest.fireSuccess(req, this.manifestURL); 1.649 + } 1.650 + break; 1.651 + case "Webapps:UpdateState": 1.652 + if (msg.manifestURL != this.manifestURL) { 1.653 + return; 1.654 + } 1.655 + 1.656 + this._updateState(msg); 1.657 + break; 1.658 + case "Webapps:ClearBrowserData:Return": 1.659 + this.removeMessageListeners(aMessage.name); 1.660 + Services.DOMRequest.fireSuccess(req, null); 1.661 + break; 1.662 + case "Webapps:Connect:Return:OK": 1.663 + let messagePorts = []; 1.664 + msg.messagePortIDs.forEach((aPortID) => { 1.665 + let port = new this._window.MozInterAppMessagePort(aPortID); 1.666 + messagePorts.push(port); 1.667 + }); 1.668 + req.resolve(messagePorts); 1.669 + break; 1.670 + case "Webapps:Connect:Return:KO": 1.671 + req.reject("No connections registered"); 1.672 + break; 1.673 + case "Webapps:GetConnections:Return:OK": 1.674 + let connections = []; 1.675 + msg.connections.forEach((aConnection) => { 1.676 + let connection = 1.677 + new this._window.MozInterAppConnection(aConnection.keyword, 1.678 + aConnection.pubAppManifestURL, 1.679 + aConnection.subAppManifestURL); 1.680 + connections.push(connection); 1.681 + }); 1.682 + req.resolve(connections); 1.683 + break; 1.684 + case "Webapps:AddReceipt:Return:OK": 1.685 + this.removeMessageListeners(["Webapps:AddReceipt:Return:OK", 1.686 + "Webapps:AddReceipt:Return:KO"]); 1.687 + this.receipts = msg.receipts; 1.688 + Services.DOMRequest.fireSuccess(req, null); 1.689 + break; 1.690 + case "Webapps:AddReceipt:Return:KO": 1.691 + this.removeMessageListeners(["Webapps:AddReceipt:Return:OK", 1.692 + "Webapps:AddReceipt:Return:KO"]); 1.693 + Services.DOMRequest.fireError(req, msg.error); 1.694 + break; 1.695 + case "Webapps:RemoveReceipt:Return:OK": 1.696 + this.removeMessageListeners(["Webapps:RemoveReceipt:Return:OK", 1.697 + "Webapps:RemoveReceipt:Return:KO"]); 1.698 + this.receipts = msg.receipts; 1.699 + Services.DOMRequest.fireSuccess(req, null); 1.700 + break; 1.701 + case "Webapps:RemoveReceipt:Return:KO": 1.702 + this.removeMessageListeners(["Webapps:RemoveReceipt:Return:OK", 1.703 + "Webapps:RemoveReceipt:Return:KO"]); 1.704 + Services.DOMRequest.fireError(req, msg.error); 1.705 + break; 1.706 + case "Webapps:ReplaceReceipt:Return:OK": 1.707 + this.removeMessageListeners(["Webapps:ReplaceReceipt:Return:OK", 1.708 + "Webapps:ReplaceReceipt:Return:KO"]); 1.709 + this.receipts = msg.receipts; 1.710 + Services.DOMRequest.fireSuccess(req, null); 1.711 + break; 1.712 + case "Webapps:ReplaceReceipt:Return:KO": 1.713 + this.removeMessageListeners(["Webapps:ReplaceReceipt:Return:OK", 1.714 + "Webapps:ReplaceReceipt:Return:KO"]); 1.715 + Services.DOMRequest.fireError(req, msg.error); 1.716 + break; 1.717 + } 1.718 + }, 1.719 + 1.720 + classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"), 1.721 + 1.722 + QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication, 1.723 + Ci.nsISupportsWeakReference, 1.724 + Ci.nsIObserver]), 1.725 + 1.726 + classInfo: XPCOMUtils.generateCI({classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"), 1.727 + contractID: "@mozilla.org/webapps/application;1", 1.728 + interfaces: [Ci.mozIDOMApplication], 1.729 + flags: Ci.nsIClassInfo.DOM_OBJECT, 1.730 + classDescription: "Webapps Application"}) 1.731 +} 1.732 + 1.733 +/** 1.734 + * mozIDOMApplicationMgmt object 1.735 + */ 1.736 +function WebappsApplicationMgmt(aWindow) { 1.737 + this.initDOMRequestHelper(aWindow, ["Webapps:GetAll:Return:OK", 1.738 + "Webapps:GetAll:Return:KO", 1.739 + "Webapps:Uninstall:Return:OK", 1.740 + "Webapps:Uninstall:Broadcast:Return:OK", 1.741 + "Webapps:Uninstall:Return:KO", 1.742 + "Webapps:Install:Return:OK", 1.743 + "Webapps:GetNotInstalled:Return:OK"]); 1.744 + 1.745 + cpmm.sendAsyncMessage("Webapps:RegisterForMessages", 1.746 + { 1.747 + messages: ["Webapps:Install:Return:OK", 1.748 + "Webapps:Uninstall:Return:OK", 1.749 + "Webapps:Uninstall:Broadcast:Return:OK"] 1.750 + } 1.751 + ); 1.752 + 1.753 + this._oninstall = null; 1.754 + this._onuninstall = null; 1.755 +} 1.756 + 1.757 +WebappsApplicationMgmt.prototype = { 1.758 + __proto__: DOMRequestIpcHelper.prototype, 1.759 + __exposedProps__: { 1.760 + applyDownload: "r", 1.761 + getAll: "r", 1.762 + getNotInstalled: "r", 1.763 + oninstall: "rw", 1.764 + onuninstall: "rw" 1.765 + }, 1.766 + 1.767 + uninit: function() { 1.768 + this._oninstall = null; 1.769 + this._onuninstall = null; 1.770 + cpmm.sendAsyncMessage("Webapps:UnregisterForMessages", 1.771 + ["Webapps:Install:Return:OK", 1.772 + "Webapps:Uninstall:Return:OK", 1.773 + "Webapps:Uninstall:Broadcast:Return:OK"]); 1.774 + }, 1.775 + 1.776 + applyDownload: function(aApp) { 1.777 + if (!aApp.readyToApplyDownload) { 1.778 + return; 1.779 + } 1.780 + 1.781 + cpmm.sendAsyncMessage("Webapps:ApplyDownload", 1.782 + { manifestURL: aApp.manifestURL }); 1.783 + }, 1.784 + 1.785 + uninstall: function(aApp) { 1.786 + dump("-- webapps.js uninstall " + aApp.manifestURL + "\n"); 1.787 + let request = this.createRequest(); 1.788 + cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: aApp.origin, 1.789 + manifestURL: aApp.manifestURL, 1.790 + oid: this._id, 1.791 + requestID: this.getRequestId(request) }); 1.792 + return request; 1.793 + }, 1.794 + 1.795 + getAll: function() { 1.796 + let request = this.createRequest(); 1.797 + cpmm.sendAsyncMessage("Webapps:GetAll", { oid: this._id, 1.798 + requestID: this.getRequestId(request) }); 1.799 + return request; 1.800 + }, 1.801 + 1.802 + getNotInstalled: function() { 1.803 + let request = this.createRequest(); 1.804 + cpmm.sendAsyncMessage("Webapps:GetNotInstalled", { oid: this._id, 1.805 + requestID: this.getRequestId(request) }); 1.806 + return request; 1.807 + }, 1.808 + 1.809 + get oninstall() { 1.810 + return this._oninstall; 1.811 + }, 1.812 + 1.813 + get onuninstall() { 1.814 + return this._onuninstall; 1.815 + }, 1.816 + 1.817 + set oninstall(aCallback) { 1.818 + this._oninstall = aCallback; 1.819 + }, 1.820 + 1.821 + set onuninstall(aCallback) { 1.822 + this._onuninstall = aCallback; 1.823 + }, 1.824 + 1.825 + receiveMessage: function(aMessage) { 1.826 + var msg = aMessage.json; 1.827 + let req = this.getRequest(msg.requestID); 1.828 + // We want Webapps:Install:Return:OK and Webapps:Uninstall:Broadcast:Return:OK 1.829 + // to be broadcasted to all instances of mozApps.mgmt. 1.830 + if (!((msg.oid == this._id && req) || 1.831 + aMessage.name == "Webapps:Install:Return:OK" || 1.832 + aMessage.name == "Webapps:Uninstall:Broadcast:Return:OK")) { 1.833 + return; 1.834 + } 1.835 + switch (aMessage.name) { 1.836 + case "Webapps:GetAll:Return:OK": 1.837 + Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window)); 1.838 + break; 1.839 + case "Webapps:GetAll:Return:KO": 1.840 + Services.DOMRequest.fireError(req, "DENIED"); 1.841 + break; 1.842 + case "Webapps:GetNotInstalled:Return:OK": 1.843 + Services.DOMRequest.fireSuccess(req, convertAppsArray(msg.apps, this._window)); 1.844 + break; 1.845 + case "Webapps:Install:Return:OK": 1.846 + if (this._oninstall) { 1.847 + let app = msg.app; 1.848 + let event = new this._window.MozApplicationEvent("applicationinstall", 1.849 + { application : createApplicationObject(this._window, app) }); 1.850 + this._oninstall.handleEvent(event); 1.851 + } 1.852 + break; 1.853 + case "Webapps:Uninstall:Broadcast:Return:OK": 1.854 + if (this._onuninstall) { 1.855 + let detail = { 1.856 + manifestURL: msg.manifestURL, 1.857 + origin: msg.origin 1.858 + }; 1.859 + let event = new this._window.MozApplicationEvent("applicationuninstall", 1.860 + { application : createApplicationObject(this._window, detail) }); 1.861 + this._onuninstall.handleEvent(event); 1.862 + } 1.863 + break; 1.864 + case "Webapps:Uninstall:Return:OK": 1.865 + Services.DOMRequest.fireSuccess(req, msg.origin); 1.866 + break; 1.867 + case "Webapps:Uninstall:Return:KO": 1.868 + Services.DOMRequest.fireError(req, "NOT_INSTALLED"); 1.869 + break; 1.870 + } 1.871 + if (aMessage.name !== "Webapps:Uninstall:Broadcast:Return:OK") { 1.872 + this.removeRequest(msg.requestID); 1.873 + } 1.874 + }, 1.875 + 1.876 + classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"), 1.877 + 1.878 + QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationMgmt, 1.879 + Ci.nsISupportsWeakReference, 1.880 + Ci.nsIObserver]), 1.881 + 1.882 + classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"), 1.883 + contractID: "@mozilla.org/webapps/application-mgmt;1", 1.884 + interfaces: [Ci.mozIDOMApplicationMgmt], 1.885 + flags: Ci.nsIClassInfo.DOM_OBJECT, 1.886 + classDescription: "Webapps Application Mgmt"}) 1.887 +} 1.888 + 1.889 +manifestCache.init(); 1.890 + 1.891 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, 1.892 + WebappsApplication]);