dom/messages/SystemMessageInternal.js

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 "use strict";
michael@0 6
michael@0 7 const Cc = Components.classes;
michael@0 8 const Ci = Components.interfaces;
michael@0 9 const Cu = Components.utils;
michael@0 10 const Cr = Components.results;
michael@0 11
michael@0 12 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 13 Cu.import("resource://gre/modules/Services.jsm");
michael@0 14 Cu.import("resource://gre/modules/SystemMessagePermissionsChecker.jsm");
michael@0 15
michael@0 16 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
michael@0 17 "@mozilla.org/parentprocessmessagemanager;1",
michael@0 18 "nsIMessageBroadcaster");
michael@0 19
michael@0 20 XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
michael@0 21 "@mozilla.org/uuid-generator;1",
michael@0 22 "nsIUUIDGenerator");
michael@0 23
michael@0 24 XPCOMUtils.defineLazyServiceGetter(this, "powerManagerService",
michael@0 25 "@mozilla.org/power/powermanagerservice;1",
michael@0 26 "nsIPowerManagerService");
michael@0 27
michael@0 28 // Limit the number of pending messages for a given page.
michael@0 29 let kMaxPendingMessages;
michael@0 30 try {
michael@0 31 kMaxPendingMessages =
michael@0 32 Services.prefs.getIntPref("dom.messages.maxPendingMessages");
michael@0 33 } catch(e) {
michael@0 34 // getIntPref throws when the pref is not set.
michael@0 35 kMaxPendingMessages = 5;
michael@0 36 }
michael@0 37
michael@0 38 const kMessages =["SystemMessageManager:GetPendingMessages",
michael@0 39 "SystemMessageManager:HasPendingMessages",
michael@0 40 "SystemMessageManager:Register",
michael@0 41 "SystemMessageManager:Unregister",
michael@0 42 "SystemMessageManager:Message:Return:OK",
michael@0 43 "SystemMessageManager:AskReadyToRegister",
michael@0 44 "SystemMessageManager:HandleMessagesDone",
michael@0 45 "child-process-shutdown"]
michael@0 46
michael@0 47 function debug(aMsg) {
michael@0 48 // dump("-- SystemMessageInternal " + Date.now() + " : " + aMsg + "\n");
michael@0 49 }
michael@0 50
michael@0 51
michael@0 52 let defaultMessageConfigurator = {
michael@0 53 get mustShowRunningApp() {
michael@0 54 return false;
michael@0 55 }
michael@0 56 };
michael@0 57
michael@0 58 const MSG_SENT_SUCCESS = 0;
michael@0 59 const MSG_SENT_FAILURE_PERM_DENIED = 1;
michael@0 60 const MSG_SENT_FAILURE_APP_NOT_RUNNING = 2;
michael@0 61
michael@0 62 // Implementation of the component used by internal users.
michael@0 63
michael@0 64 function SystemMessageInternal() {
michael@0 65 // The set of pages registered by installed apps. We keep the
michael@0 66 // list of pending messages for each page here also.
michael@0 67 this._pages = [];
michael@0 68
michael@0 69 // The set of listeners. This is a multi-dimensional object. The _listeners
michael@0 70 // object itself is a map from manifest URL -> an array mapping proccesses to
michael@0 71 // windows. We do this so that we can track both what processes we have to
michael@0 72 // send system messages to as well as supporting the single-process case
michael@0 73 // where we track windows instead.
michael@0 74 this._listeners = {};
michael@0 75
michael@0 76 this._webappsRegistryReady = false;
michael@0 77 this._bufferedSysMsgs = [];
michael@0 78
michael@0 79 this._cpuWakeLocks = {};
michael@0 80
michael@0 81 this._configurators = {};
michael@0 82
michael@0 83 Services.obs.addObserver(this, "xpcom-shutdown", false);
michael@0 84 Services.obs.addObserver(this, "webapps-registry-start", false);
michael@0 85 Services.obs.addObserver(this, "webapps-registry-ready", false);
michael@0 86 kMessages.forEach(function(aMsg) {
michael@0 87 ppmm.addMessageListener(aMsg, this);
michael@0 88 }, this);
michael@0 89
michael@0 90 Services.obs.notifyObservers(this, "system-message-internal-ready", null);
michael@0 91 }
michael@0 92
michael@0 93 SystemMessageInternal.prototype = {
michael@0 94
michael@0 95 _getMessageConfigurator: function(aType) {
michael@0 96 debug("_getMessageConfigurator for type: " + aType);
michael@0 97 if (this._configurators[aType] === undefined) {
michael@0 98 let contractID =
michael@0 99 "@mozilla.org/dom/system-messages/configurator/" + aType + ";1";
michael@0 100 if (contractID in Cc) {
michael@0 101 debug(contractID + " is registered, creating an instance");
michael@0 102 this._configurators[aType] =
michael@0 103 Cc[contractID].createInstance(Ci.nsISystemMessagesConfigurator);
michael@0 104 } else {
michael@0 105 debug(contractID + "is not registered, caching the answer");
michael@0 106 this._configurators[aType] = null;
michael@0 107 }
michael@0 108 }
michael@0 109 return this._configurators[aType] || defaultMessageConfigurator;
michael@0 110 },
michael@0 111
michael@0 112 _cancelCpuWakeLock: function(aPageKey) {
michael@0 113 let cpuWakeLock = this._cpuWakeLocks[aPageKey];
michael@0 114 if (cpuWakeLock) {
michael@0 115 debug("Releasing the CPU wake lock for page key = " + aPageKey);
michael@0 116 cpuWakeLock.wakeLock.unlock();
michael@0 117 cpuWakeLock.timer.cancel();
michael@0 118 delete this._cpuWakeLocks[aPageKey];
michael@0 119 }
michael@0 120 },
michael@0 121
michael@0 122 _acquireCpuWakeLock: function(aPageKey) {
michael@0 123 let cpuWakeLock = this._cpuWakeLocks[aPageKey];
michael@0 124 if (!cpuWakeLock) {
michael@0 125 // We have to ensure the CPU doesn't sleep during the process of the page
michael@0 126 // handling the system messages, so that they can be handled on time.
michael@0 127 debug("Acquiring a CPU wake lock for page key = " + aPageKey);
michael@0 128 cpuWakeLock = this._cpuWakeLocks[aPageKey] = {
michael@0 129 wakeLock: powerManagerService.newWakeLock("cpu"),
michael@0 130 timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
michael@0 131 lockCount: 1
michael@0 132 };
michael@0 133 } else {
michael@0 134 // We've already acquired the CPU wake lock for this page,
michael@0 135 // so just add to the lock count and extend the timeout.
michael@0 136 cpuWakeLock.lockCount++;
michael@0 137 }
michael@0 138
michael@0 139 // Set a watchdog to avoid locking the CPU wake lock too long,
michael@0 140 // because it'd exhaust the battery quickly which is very bad.
michael@0 141 // This could probably happen if the app failed to launch or
michael@0 142 // handle the system messages due to any unexpected reasons.
michael@0 143 cpuWakeLock.timer.initWithCallback(function timerCb() {
michael@0 144 debug("Releasing the CPU wake lock because the system messages " +
michael@0 145 "were not handled by its registered page before time out.");
michael@0 146 this._cancelCpuWakeLock(aPageKey);
michael@0 147 }.bind(this), 30000, Ci.nsITimer.TYPE_ONE_SHOT);
michael@0 148 },
michael@0 149
michael@0 150 _releaseCpuWakeLock: function _releaseCpuWakeLock(aPageKey, aHandledCount) {
michael@0 151 let cpuWakeLock = this._cpuWakeLocks[aPageKey];
michael@0 152 if (cpuWakeLock) {
michael@0 153 cpuWakeLock.lockCount -= aHandledCount;
michael@0 154 if (cpuWakeLock.lockCount <= 0) {
michael@0 155 debug("Unlocking the CPU wake lock now that the system messages " +
michael@0 156 "have been successfully handled by its registered page.");
michael@0 157 this._cancelCpuWakeLock(aPageKey);
michael@0 158 }
michael@0 159 }
michael@0 160 },
michael@0 161
michael@0 162 _findPage: function(aType, aPageURL, aManifestURL) {
michael@0 163 let page = null;
michael@0 164 this._pages.some(function(aPage) {
michael@0 165 if (this._isPageMatched(aPage, aType, aPageURL, aManifestURL)) {
michael@0 166 page = aPage;
michael@0 167 }
michael@0 168 return page !== null;
michael@0 169 }, this);
michael@0 170 return page;
michael@0 171 },
michael@0 172
michael@0 173 sendMessage: function(aType, aMessage, aPageURI, aManifestURI, aExtra) {
michael@0 174 // Buffer system messages until the webapps' registration is ready,
michael@0 175 // so that we can know the correct pages registered to be sent.
michael@0 176 if (!this._webappsRegistryReady) {
michael@0 177 this._bufferedSysMsgs.push({ how: "send",
michael@0 178 type: aType,
michael@0 179 msg: aMessage,
michael@0 180 pageURI: aPageURI,
michael@0 181 manifestURI: aManifestURI,
michael@0 182 extra: aExtra });
michael@0 183 return;
michael@0 184 }
michael@0 185
michael@0 186 // Give this message an ID so that we can identify the message and
michael@0 187 // clean it up from the pending message queue when apps receive it.
michael@0 188 let messageID = gUUIDGenerator.generateUUID().toString();
michael@0 189
michael@0 190 debug("Sending " + aType + " " + JSON.stringify(aMessage) +
michael@0 191 " for " + aPageURI.spec + " @ " + aManifestURI.spec +
michael@0 192 '; extra: ' + JSON.stringify(aExtra));
michael@0 193
michael@0 194 let result = this._sendMessageCommon(aType,
michael@0 195 aMessage,
michael@0 196 messageID,
michael@0 197 aPageURI.spec,
michael@0 198 aManifestURI.spec,
michael@0 199 aExtra);
michael@0 200 debug("Returned status of sending message: " + result);
michael@0 201
michael@0 202 // Don't need to open the pages and queue the system message
michael@0 203 // which was not allowed to be sent.
michael@0 204 if (result === MSG_SENT_FAILURE_PERM_DENIED) {
michael@0 205 return;
michael@0 206 }
michael@0 207
michael@0 208 let page = this._findPage(aType, aPageURI.spec, aManifestURI.spec);
michael@0 209 if (page) {
michael@0 210 // Queue this message in the corresponding pages.
michael@0 211 this._queueMessage(page, aMessage, messageID);
michael@0 212
michael@0 213 this._openAppPage(page, aMessage, aExtra, result);
michael@0 214 }
michael@0 215 },
michael@0 216
michael@0 217 broadcastMessage: function(aType, aMessage, aExtra) {
michael@0 218 // Buffer system messages until the webapps' registration is ready,
michael@0 219 // so that we can know the correct pages registered to be broadcasted.
michael@0 220 if (!this._webappsRegistryReady) {
michael@0 221 this._bufferedSysMsgs.push({ how: "broadcast",
michael@0 222 type: aType,
michael@0 223 msg: aMessage,
michael@0 224 extra: aExtra });
michael@0 225 return;
michael@0 226 }
michael@0 227
michael@0 228 // Give this message an ID so that we can identify the message and
michael@0 229 // clean it up from the pending message queue when apps receive it.
michael@0 230 let messageID = gUUIDGenerator.generateUUID().toString();
michael@0 231
michael@0 232 debug("Broadcasting " + aType + " " + JSON.stringify(aMessage) +
michael@0 233 '; extra = ' + JSON.stringify(aExtra));
michael@0 234 // Find pages that registered an handler for this type.
michael@0 235 this._pages.forEach(function(aPage) {
michael@0 236 if (aPage.type == aType) {
michael@0 237 let result = this._sendMessageCommon(aType,
michael@0 238 aMessage,
michael@0 239 messageID,
michael@0 240 aPage.pageURL,
michael@0 241 aPage.manifestURL,
michael@0 242 aExtra);
michael@0 243 debug("Returned status of sending message: " + result);
michael@0 244
michael@0 245
michael@0 246 // Don't need to open the pages and queue the system message
michael@0 247 // which was not allowed to be sent.
michael@0 248 if (result === MSG_SENT_FAILURE_PERM_DENIED) {
michael@0 249 return;
michael@0 250 }
michael@0 251
michael@0 252 // Queue this message in the corresponding pages.
michael@0 253 this._queueMessage(aPage, aMessage, messageID);
michael@0 254
michael@0 255 this._openAppPage(aPage, aMessage, aExtra, result);
michael@0 256 }
michael@0 257 }, this);
michael@0 258 },
michael@0 259
michael@0 260 registerPage: function(aType, aPageURI, aManifestURI) {
michael@0 261 if (!aPageURI || !aManifestURI) {
michael@0 262 throw Cr.NS_ERROR_INVALID_ARG;
michael@0 263 }
michael@0 264
michael@0 265 let pageURL = aPageURI.spec;
michael@0 266 let manifestURL = aManifestURI.spec;
michael@0 267
michael@0 268 // Don't register duplicates for this tuple.
michael@0 269 let page = this._findPage(aType, pageURL, manifestURL);
michael@0 270 if (page) {
michael@0 271 debug("Ignoring duplicate registration of " +
michael@0 272 [aType, pageURL, manifestURL]);
michael@0 273 return;
michael@0 274 }
michael@0 275
michael@0 276 this._pages.push({ type: aType,
michael@0 277 pageURL: pageURL,
michael@0 278 manifestURL: manifestURL,
michael@0 279 pendingMessages: [] });
michael@0 280 },
michael@0 281
michael@0 282 _findTargetIndex: function(aTargets, aTarget) {
michael@0 283 if (!aTargets || !aTarget) {
michael@0 284 return -1;
michael@0 285 }
michael@0 286 for (let index = 0; index < aTargets.length; ++index) {
michael@0 287 let target = aTargets[index];
michael@0 288 if (target.target === aTarget) {
michael@0 289 return index;
michael@0 290 }
michael@0 291 }
michael@0 292 return -1;
michael@0 293 },
michael@0 294
michael@0 295 _isEmptyObject: function(aObj) {
michael@0 296 for (let name in aObj) {
michael@0 297 return false;
michael@0 298 }
michael@0 299 return true;
michael@0 300 },
michael@0 301
michael@0 302 _removeTargetFromListener: function(aTarget,
michael@0 303 aManifestURL,
michael@0 304 aRemoveListener,
michael@0 305 aPageURL) {
michael@0 306 let targets = this._listeners[aManifestURL];
michael@0 307 if (!targets) {
michael@0 308 return false;
michael@0 309 }
michael@0 310
michael@0 311 let index = this._findTargetIndex(targets, aTarget);
michael@0 312 if (index === -1) {
michael@0 313 return false;
michael@0 314 }
michael@0 315
michael@0 316 if (aRemoveListener) {
michael@0 317 debug("remove the listener for " + aManifestURL);
michael@0 318 delete this._listeners[aManifestURL];
michael@0 319 return true;
michael@0 320 }
michael@0 321
michael@0 322 let target = targets[index];
michael@0 323 if (aPageURL && target.winCounts[aPageURL] !== undefined &&
michael@0 324 --target.winCounts[aPageURL] === 0) {
michael@0 325 delete target.winCounts[aPageURL];
michael@0 326 }
michael@0 327
michael@0 328 if (this._isEmptyObject(target.winCounts)) {
michael@0 329 if (targets.length === 1) {
michael@0 330 // If it's the only one, get rid of the entry of manifest URL entirely.
michael@0 331 debug("remove the listener for " + aManifestURL);
michael@0 332 delete this._listeners[aManifestURL];
michael@0 333 } else {
michael@0 334 // If more than one left, remove this one and leave the rest.
michael@0 335 targets.splice(index, 1);
michael@0 336 }
michael@0 337 }
michael@0 338 return true;
michael@0 339 },
michael@0 340
michael@0 341 receiveMessage: function(aMessage) {
michael@0 342 let msg = aMessage.json;
michael@0 343
michael@0 344 // To prevent the hacked child process from sending commands to parent
michael@0 345 // to manage system messages, we need to check its manifest URL.
michael@0 346 if (["SystemMessageManager:Register",
michael@0 347 // TODO: fix bug 988142 to re-enable.
michael@0 348 // "SystemMessageManager:Unregister",
michael@0 349 "SystemMessageManager:GetPendingMessages",
michael@0 350 "SystemMessageManager:HasPendingMessages",
michael@0 351 "SystemMessageManager:Message:Return:OK",
michael@0 352 "SystemMessageManager:HandleMessagesDone"].indexOf(aMessage.name) != -1) {
michael@0 353 if (!aMessage.target.assertContainApp(msg.manifestURL)) {
michael@0 354 debug("Got message from a child process containing illegal manifest URL.");
michael@0 355 return null;
michael@0 356 }
michael@0 357 }
michael@0 358
michael@0 359 switch(aMessage.name) {
michael@0 360 case "SystemMessageManager:AskReadyToRegister":
michael@0 361 return true;
michael@0 362 break;
michael@0 363 case "SystemMessageManager:Register":
michael@0 364 {
michael@0 365 debug("Got Register from " + msg.pageURL + " @ " + msg.manifestURL);
michael@0 366 let pageURL = msg.pageURL;
michael@0 367 let targets, index;
michael@0 368 if (!(targets = this._listeners[msg.manifestURL])) {
michael@0 369 let winCounts = {};
michael@0 370 winCounts[pageURL] = 1;
michael@0 371 this._listeners[msg.manifestURL] = [{ target: aMessage.target,
michael@0 372 winCounts: winCounts }];
michael@0 373 } else if ((index = this._findTargetIndex(targets,
michael@0 374 aMessage.target)) === -1) {
michael@0 375 let winCounts = {};
michael@0 376 winCounts[pageURL] = 1;
michael@0 377 targets.push({ target: aMessage.target,
michael@0 378 winCounts: winCounts });
michael@0 379 } else {
michael@0 380 let winCounts = targets[index].winCounts;
michael@0 381 if (winCounts[pageURL] === undefined) {
michael@0 382 winCounts[pageURL] = 1;
michael@0 383 } else {
michael@0 384 winCounts[pageURL]++;
michael@0 385 }
michael@0 386 }
michael@0 387
michael@0 388 debug("listeners for " + msg.manifestURL +
michael@0 389 " innerWinID " + msg.innerWindowID);
michael@0 390 break;
michael@0 391 }
michael@0 392 case "child-process-shutdown":
michael@0 393 {
michael@0 394 debug("Got child-process-shutdown from " + aMessage.target);
michael@0 395 for (let manifestURL in this._listeners) {
michael@0 396 // See if any processes in this manifest URL have this target.
michael@0 397 this._removeTargetFromListener(aMessage.target,
michael@0 398 manifestURL,
michael@0 399 true,
michael@0 400 null);
michael@0 401 }
michael@0 402 break;
michael@0 403 }
michael@0 404 case "SystemMessageManager:Unregister":
michael@0 405 {
michael@0 406 debug("Got Unregister from " + aMessage.target +
michael@0 407 " innerWinID " + msg.innerWindowID);
michael@0 408 this._removeTargetFromListener(aMessage.target,
michael@0 409 msg.manifestURL,
michael@0 410 false,
michael@0 411 msg.pageURL);
michael@0 412 break;
michael@0 413 }
michael@0 414 case "SystemMessageManager:GetPendingMessages":
michael@0 415 {
michael@0 416 debug("received SystemMessageManager:GetPendingMessages " + msg.type +
michael@0 417 " for " + msg.pageURL + " @ " + msg.manifestURL);
michael@0 418
michael@0 419 // This is a sync call used to return the pending messages for a page.
michael@0 420 // Find the right page to get its corresponding pending messages.
michael@0 421 let page = this._findPage(msg.type, msg.pageURL, msg.manifestURL);
michael@0 422 if (!page) {
michael@0 423 return;
michael@0 424 }
michael@0 425
michael@0 426 // Return the |msg| of each pending message (drop the |msgID|).
michael@0 427 let pendingMessages = [];
michael@0 428 page.pendingMessages.forEach(function(aMessage) {
michael@0 429 pendingMessages.push(aMessage.msg);
michael@0 430 });
michael@0 431
michael@0 432 // Clear the pending queue for this page. This is OK since we'll store
michael@0 433 // pending messages in the content process (|SystemMessageManager|).
michael@0 434 page.pendingMessages.length = 0;
michael@0 435
michael@0 436 // Send the array of pending messages.
michael@0 437 aMessage.target
michael@0 438 .sendAsyncMessage("SystemMessageManager:GetPendingMessages:Return",
michael@0 439 { type: msg.type,
michael@0 440 manifestURL: msg.manifestURL,
michael@0 441 pageURL: msg.pageURL,
michael@0 442 msgQueue: pendingMessages });
michael@0 443 break;
michael@0 444 }
michael@0 445 case "SystemMessageManager:HasPendingMessages":
michael@0 446 {
michael@0 447 debug("received SystemMessageManager:HasPendingMessages " + msg.type +
michael@0 448 " for " + msg.pageURL + " @ " + msg.manifestURL);
michael@0 449
michael@0 450 // This is a sync call used to return if a page has pending messages.
michael@0 451 // Find the right page to get its corresponding pending messages.
michael@0 452 let page = this._findPage(msg.type, msg.pageURL, msg.manifestURL);
michael@0 453 if (!page) {
michael@0 454 return false;
michael@0 455 }
michael@0 456
michael@0 457 return page.pendingMessages.length != 0;
michael@0 458 break;
michael@0 459 }
michael@0 460 case "SystemMessageManager:Message:Return:OK":
michael@0 461 {
michael@0 462 debug("received SystemMessageManager:Message:Return:OK " + msg.type +
michael@0 463 " for " + msg.pageURL + " @ " + msg.manifestURL);
michael@0 464
michael@0 465 // We need to clean up the pending message since the app has already
michael@0 466 // received it, thus avoiding the re-lanunched app handling it again.
michael@0 467 let page = this._findPage(msg.type, msg.pageURL, msg.manifestURL);
michael@0 468 if (page) {
michael@0 469 let pendingMessages = page.pendingMessages;
michael@0 470 for (let i = 0; i < pendingMessages.length; i++) {
michael@0 471 if (pendingMessages[i].msgID === msg.msgID) {
michael@0 472 pendingMessages.splice(i, 1);
michael@0 473 break;
michael@0 474 }
michael@0 475 }
michael@0 476 }
michael@0 477 break;
michael@0 478 }
michael@0 479 case "SystemMessageManager:HandleMessagesDone":
michael@0 480 {
michael@0 481 debug("received SystemMessageManager:HandleMessagesDone " + msg.type +
michael@0 482 " with " + msg.handledCount + " for " + msg.pageURL +
michael@0 483 " @ " + msg.manifestURL);
michael@0 484
michael@0 485 // A page has finished handling some of its system messages, so we try
michael@0 486 // to release the CPU wake lock we acquired on behalf of that page.
michael@0 487 this._releaseCpuWakeLock(this._createKeyForPage(msg), msg.handledCount);
michael@0 488 break;
michael@0 489 }
michael@0 490 }
michael@0 491 },
michael@0 492
michael@0 493 observe: function(aSubject, aTopic, aData) {
michael@0 494 switch (aTopic) {
michael@0 495 case "xpcom-shutdown":
michael@0 496 kMessages.forEach(function(aMsg) {
michael@0 497 ppmm.removeMessageListener(aMsg, this);
michael@0 498 }, this);
michael@0 499 Services.obs.removeObserver(this, "xpcom-shutdown");
michael@0 500 Services.obs.removeObserver(this, "webapps-registry-start");
michael@0 501 Services.obs.removeObserver(this, "webapps-registry-ready");
michael@0 502 ppmm = null;
michael@0 503 this._pages = null;
michael@0 504 this._bufferedSysMsgs = null;
michael@0 505 break;
michael@0 506 case "webapps-registry-start":
michael@0 507 this._webappsRegistryReady = false;
michael@0 508 break;
michael@0 509 case "webapps-registry-ready":
michael@0 510 // After the webapps' registration has been done for sure,
michael@0 511 // re-fire the buffered system messages if there is any.
michael@0 512 this._webappsRegistryReady = true;
michael@0 513 this._bufferedSysMsgs.forEach(function(aSysMsg) {
michael@0 514 switch (aSysMsg.how) {
michael@0 515 case "send":
michael@0 516 this.sendMessage(
michael@0 517 aSysMsg.type, aSysMsg.msg,
michael@0 518 aSysMsg.pageURI, aSysMsg.manifestURI, aSysMsg.extra);
michael@0 519 break;
michael@0 520 case "broadcast":
michael@0 521 this.broadcastMessage(aSysMsg.type, aSysMsg.msg, aSysMsg.extra);
michael@0 522 break;
michael@0 523 }
michael@0 524 }, this);
michael@0 525 this._bufferedSysMsgs.length = 0;
michael@0 526 break;
michael@0 527 }
michael@0 528 },
michael@0 529
michael@0 530 _queueMessage: function(aPage, aMessage, aMessageID) {
michael@0 531 // Queue the message for this page because we've never known if an app is
michael@0 532 // opened or not. We'll clean it up when the app has already received it.
michael@0 533 aPage.pendingMessages.push({ msg: aMessage, msgID: aMessageID });
michael@0 534 if (aPage.pendingMessages.length > kMaxPendingMessages) {
michael@0 535 aPage.pendingMessages.splice(0, 1);
michael@0 536 }
michael@0 537 },
michael@0 538
michael@0 539 _openAppPage: function(aPage, aMessage, aExtra, aMsgSentStatus) {
michael@0 540 // This means the app must be brought to the foreground.
michael@0 541 let showApp = this._getMessageConfigurator(aPage.type).mustShowRunningApp;
michael@0 542
michael@0 543 // We should send the open-app message if the system message was
michael@0 544 // not sent, or if it was sent but we should show the app anyway.
michael@0 545 if ((aMsgSentStatus === MSG_SENT_SUCCESS) && !showApp) {
michael@0 546 return;
michael@0 547 }
michael@0 548
michael@0 549 // This flag means the app must *only* be brought to the foreground
michael@0 550 // and we don't need to load the app to handle messages.
michael@0 551 let onlyShowApp = (aMsgSentStatus === MSG_SENT_SUCCESS) && showApp;
michael@0 552
michael@0 553 // We don't need to send the full object to observers.
michael@0 554 let page = { pageURL: aPage.pageURL,
michael@0 555 manifestURL: aPage.manifestURL,
michael@0 556 type: aPage.type,
michael@0 557 extra: aExtra,
michael@0 558 target: aMessage.target,
michael@0 559 onlyShowApp: onlyShowApp,
michael@0 560 showApp: showApp };
michael@0 561 debug("Asking to open " + JSON.stringify(page));
michael@0 562 Services.obs.notifyObservers(this,
michael@0 563 "system-messages-open-app",
michael@0 564 JSON.stringify(page));
michael@0 565 },
michael@0 566
michael@0 567 _isPageMatched: function(aPage, aType, aPageURL, aManifestURL) {
michael@0 568 return (aPage.type === aType &&
michael@0 569 aPage.manifestURL === aManifestURL &&
michael@0 570 aPage.pageURL === aPageURL)
michael@0 571 },
michael@0 572
michael@0 573 _createKeyForPage: function _createKeyForPage(aPage) {
michael@0 574 let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
michael@0 575 .createInstance(Ci.nsIScriptableUnicodeConverter);
michael@0 576 converter.charset = "UTF-8";
michael@0 577
michael@0 578 let hasher = Cc["@mozilla.org/security/hash;1"]
michael@0 579 .createInstance(Ci.nsICryptoHash);
michael@0 580 hasher.init(hasher.SHA1);
michael@0 581
michael@0 582 // add manifest/page URL and action to the hash
michael@0 583 ["type", "manifestURL", "pageURL"].forEach(function(aProp) {
michael@0 584 let data = converter.convertToByteArray(aPage[aProp], {});
michael@0 585 hasher.update(data, data.length);
michael@0 586 });
michael@0 587
michael@0 588 return hasher.finish(true);
michael@0 589 },
michael@0 590
michael@0 591 _sendMessageCommon: function(aType, aMessage, aMessageID,
michael@0 592 aPageURL, aManifestURL, aExtra) {
michael@0 593 // Don't send the system message not granted by the app's permissions.
michael@0 594 if (!SystemMessagePermissionsChecker
michael@0 595 .isSystemMessagePermittedToSend(aType,
michael@0 596 aPageURL,
michael@0 597 aManifestURL)) {
michael@0 598 return MSG_SENT_FAILURE_PERM_DENIED;
michael@0 599 }
michael@0 600
michael@0 601 let appPageIsRunning = false;
michael@0 602 let pageKey = this._createKeyForPage({ type: aType,
michael@0 603 manifestURL: aManifestURL,
michael@0 604 pageURL: aPageURL });
michael@0 605
michael@0 606 let targets = this._listeners[aManifestURL];
michael@0 607 if (targets) {
michael@0 608 for (let index = 0; index < targets.length; ++index) {
michael@0 609 let target = targets[index];
michael@0 610 // We only need to send the system message to the targets (processes)
michael@0 611 // which contain the window page that matches the manifest/page URL of
michael@0 612 // the destination of system message.
michael@0 613 if (target.winCounts[aPageURL] === undefined) {
michael@0 614 continue;
michael@0 615 }
michael@0 616
michael@0 617 appPageIsRunning = true;
michael@0 618 // We need to acquire a CPU wake lock for that page and expect that
michael@0 619 // we'll receive a "SystemMessageManager:HandleMessagesDone" message
michael@0 620 // when the page finishes handling the system message. At that point,
michael@0 621 // we'll release the lock we acquired.
michael@0 622 this._acquireCpuWakeLock(pageKey);
michael@0 623
michael@0 624 // Multiple windows can share the same target (process), the content
michael@0 625 // window needs to check if the manifest/page URL is matched. Only
michael@0 626 // *one* window should handle the system message.
michael@0 627 let manager = target.target;
michael@0 628 manager.sendAsyncMessage("SystemMessageManager:Message",
michael@0 629 { type: aType,
michael@0 630 msg: aMessage,
michael@0 631 manifestURL: aManifestURL,
michael@0 632 pageURL: aPageURL,
michael@0 633 msgID: aMessageID });
michael@0 634 }
michael@0 635 }
michael@0 636
michael@0 637 if (!appPageIsRunning) {
michael@0 638 // The app page isn't running and relies on the 'open-app' chrome event to
michael@0 639 // wake it up. We still need to acquire a CPU wake lock for that page and
michael@0 640 // expect that we will receive a "SystemMessageManager:HandleMessagesDone"
michael@0 641 // message when the page finishes handling the system message with other
michael@0 642 // pending messages. At that point, we'll release the lock we acquired.
michael@0 643 this._acquireCpuWakeLock(pageKey);
michael@0 644 return MSG_SENT_FAILURE_APP_NOT_RUNNING;
michael@0 645 } else {
michael@0 646 return MSG_SENT_SUCCESS;
michael@0 647 }
michael@0 648
michael@0 649 },
michael@0 650
michael@0 651 classID: Components.ID("{70589ca5-91ac-4b9e-b839-d6a88167d714}"),
michael@0 652
michael@0 653 QueryInterface: XPCOMUtils.generateQI([Ci.nsISystemMessagesInternal,
michael@0 654 Ci.nsIObserver])
michael@0 655 }
michael@0 656
michael@0 657 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SystemMessageInternal]);

mercurial