b2g/chrome/content/shell.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
michael@0 2 /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 Cu.import('resource://gre/modules/ContactService.jsm');
michael@0 8 Cu.import('resource://gre/modules/SettingsChangeNotifier.jsm');
michael@0 9 Cu.import('resource://gre/modules/DataStoreChangeNotifier.jsm');
michael@0 10 Cu.import('resource://gre/modules/AlarmService.jsm');
michael@0 11 Cu.import('resource://gre/modules/ActivitiesService.jsm');
michael@0 12 Cu.import('resource://gre/modules/PermissionPromptHelper.jsm');
michael@0 13 Cu.import('resource://gre/modules/NotificationDB.jsm');
michael@0 14 Cu.import('resource://gre/modules/Payment.jsm');
michael@0 15 Cu.import("resource://gre/modules/AppsUtils.jsm");
michael@0 16 Cu.import('resource://gre/modules/UserAgentOverrides.jsm');
michael@0 17 Cu.import('resource://gre/modules/Keyboard.jsm');
michael@0 18 Cu.import('resource://gre/modules/ErrorPage.jsm');
michael@0 19 #ifdef MOZ_WIDGET_GONK
michael@0 20 Cu.import('resource://gre/modules/NetworkStatsService.jsm');
michael@0 21 #endif
michael@0 22
michael@0 23 // Identity
michael@0 24 Cu.import('resource://gre/modules/SignInToWebsite.jsm');
michael@0 25 SignInToWebsiteController.init();
michael@0 26
michael@0 27 #ifdef MOZ_SERVICES_FXACCOUNTS
michael@0 28 Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
michael@0 29 #endif
michael@0 30
michael@0 31 Cu.import('resource://gre/modules/DownloadsAPI.jsm');
michael@0 32
michael@0 33 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
michael@0 34 "resource://gre/modules/SystemAppProxy.jsm");
michael@0 35
michael@0 36 Cu.import('resource://gre/modules/Webapps.jsm');
michael@0 37 DOMApplicationRegistry.allAppsLaunchable = true;
michael@0 38
michael@0 39 XPCOMUtils.defineLazyServiceGetter(Services, 'env',
michael@0 40 '@mozilla.org/process/environment;1',
michael@0 41 'nsIEnvironment');
michael@0 42
michael@0 43 XPCOMUtils.defineLazyServiceGetter(Services, 'ss',
michael@0 44 '@mozilla.org/content/style-sheet-service;1',
michael@0 45 'nsIStyleSheetService');
michael@0 46
michael@0 47 XPCOMUtils.defineLazyServiceGetter(this, 'gSystemMessenger',
michael@0 48 '@mozilla.org/system-message-internal;1',
michael@0 49 'nsISystemMessagesInternal');
michael@0 50
michael@0 51 XPCOMUtils.defineLazyServiceGetter(Services, 'fm',
michael@0 52 '@mozilla.org/focus-manager;1',
michael@0 53 'nsIFocusManager');
michael@0 54
michael@0 55 XPCOMUtils.defineLazyGetter(this, 'DebuggerServer', function() {
michael@0 56 Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
michael@0 57 return DebuggerServer;
michael@0 58 });
michael@0 59
michael@0 60 XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
michael@0 61 return Cc["@mozilla.org/parentprocessmessagemanager;1"]
michael@0 62 .getService(Ci.nsIMessageListenerManager);
michael@0 63 });
michael@0 64
michael@0 65 #ifdef MOZ_WIDGET_GONK
michael@0 66 XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
michael@0 67 Cu.import("resource://gre/modules/systemlibs.js");
michael@0 68 return libcutils;
michael@0 69 });
michael@0 70 #endif
michael@0 71
michael@0 72 #ifdef MOZ_CAPTIVEDETECT
michael@0 73 XPCOMUtils.defineLazyServiceGetter(Services, 'captivePortalDetector',
michael@0 74 '@mozilla.org/toolkit/captive-detector;1',
michael@0 75 'nsICaptivePortalDetector');
michael@0 76 #endif
michael@0 77
michael@0 78 function getContentWindow() {
michael@0 79 return shell.contentBrowser.contentWindow;
michael@0 80 }
michael@0 81
michael@0 82 function debug(str) {
michael@0 83 dump(' -*- Shell.js: ' + str + '\n');
michael@0 84 }
michael@0 85
michael@0 86 #ifdef MOZ_CRASHREPORTER
michael@0 87 function debugCrashReport(aStr) {
michael@0 88 dump('Crash reporter : ' + aStr);
michael@0 89 }
michael@0 90 #else
michael@0 91 function debugCrashReport(aStr) {}
michael@0 92 #endif
michael@0 93
michael@0 94 var shell = {
michael@0 95
michael@0 96 get CrashSubmit() {
michael@0 97 delete this.CrashSubmit;
michael@0 98 #ifdef MOZ_CRASHREPORTER
michael@0 99 Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
michael@0 100 return this.CrashSubmit;
michael@0 101 #else
michael@0 102 dump('Crash reporter : disabled at build time.');
michael@0 103 return this.CrashSubmit = null;
michael@0 104 #endif
michael@0 105 },
michael@0 106
michael@0 107 onlineForCrashReport: function shell_onlineForCrashReport() {
michael@0 108 let wifiManager = navigator.mozWifiManager;
michael@0 109 let onWifi = (wifiManager &&
michael@0 110 (wifiManager.connection.status == 'connected'));
michael@0 111 return !Services.io.offline && onWifi;
michael@0 112 },
michael@0 113
michael@0 114 reportCrash: function shell_reportCrash(isChrome, aCrashID) {
michael@0 115 let crashID = aCrashID;
michael@0 116 try {
michael@0 117 // For chrome crashes, we want to report the lastRunCrashID.
michael@0 118 if (isChrome) {
michael@0 119 crashID = Cc["@mozilla.org/xre/app-info;1"]
michael@0 120 .getService(Ci.nsIXULRuntime).lastRunCrashID;
michael@0 121 }
michael@0 122 } catch(e) {
michael@0 123 debugCrashReport('Failed to fetch crash id. Crash ID is "' + crashID
michael@0 124 + '" Exception: ' + e);
michael@0 125 }
michael@0 126
michael@0 127 // Bail if there isn't a valid crashID.
michael@0 128 if (!this.CrashSubmit || !crashID && !this.CrashSubmit.pendingIDs().length) {
michael@0 129 return;
michael@0 130 }
michael@0 131
michael@0 132 // purge the queue.
michael@0 133 this.CrashSubmit.pruneSavedDumps();
michael@0 134
michael@0 135 // check for environment affecting crash reporting
michael@0 136 let env = Cc["@mozilla.org/process/environment;1"]
michael@0 137 .getService(Ci.nsIEnvironment);
michael@0 138 let shutdown = env.get("MOZ_CRASHREPORTER_SHUTDOWN");
michael@0 139 if (shutdown) {
michael@0 140 let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
michael@0 141 .getService(Ci.nsIAppStartup);
michael@0 142 appStartup.quit(Ci.nsIAppStartup.eForceQuit);
michael@0 143 }
michael@0 144
michael@0 145 let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT");
michael@0 146 if (noReport) {
michael@0 147 return;
michael@0 148 }
michael@0 149
michael@0 150 try {
michael@0 151 // Check if we should automatically submit this crash.
michael@0 152 if (Services.prefs.getBoolPref('app.reportCrashes')) {
michael@0 153 this.submitCrash(crashID);
michael@0 154 } else {
michael@0 155 this.deleteCrash(crashID);
michael@0 156 }
michael@0 157 } catch (e) {
michael@0 158 debugCrashReport('Can\'t fetch app.reportCrashes. Exception: ' + e);
michael@0 159 }
michael@0 160
michael@0 161 // We can get here if we're just submitting old pending crashes.
michael@0 162 // Check that there's a valid crashID so that we only notify the
michael@0 163 // user if a crash just happened and not when we OOM. Bug 829477
michael@0 164 if (crashID) {
michael@0 165 this.sendChromeEvent({
michael@0 166 type: "handle-crash",
michael@0 167 crashID: crashID,
michael@0 168 chrome: isChrome
michael@0 169 });
michael@0 170 }
michael@0 171 },
michael@0 172
michael@0 173 deleteCrash: function shell_deleteCrash(aCrashID) {
michael@0 174 if (aCrashID) {
michael@0 175 debugCrashReport('Deleting pending crash: ' + aCrashID);
michael@0 176 shell.CrashSubmit.delete(aCrashID);
michael@0 177 }
michael@0 178 },
michael@0 179
michael@0 180 // this function submit the pending crashes.
michael@0 181 // make sure you are online.
michael@0 182 submitQueuedCrashes: function shell_submitQueuedCrashes() {
michael@0 183 // submit the pending queue.
michael@0 184 let pending = shell.CrashSubmit.pendingIDs();
michael@0 185 for (let crashid of pending) {
michael@0 186 debugCrashReport('Submitting crash: ' + crashid);
michael@0 187 shell.CrashSubmit.submit(crashid);
michael@0 188 }
michael@0 189 },
michael@0 190
michael@0 191 // This function submits a crash when we're online.
michael@0 192 submitCrash: function shell_submitCrash(aCrashID) {
michael@0 193 if (this.onlineForCrashReport()) {
michael@0 194 this.submitQueuedCrashes();
michael@0 195 return;
michael@0 196 }
michael@0 197
michael@0 198 debugCrashReport('Not online, postponing.');
michael@0 199
michael@0 200 Services.obs.addObserver(function observer(subject, topic, state) {
michael@0 201 let network = subject.QueryInterface(Ci.nsINetworkInterface);
michael@0 202 if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED
michael@0 203 && network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
michael@0 204 shell.submitQueuedCrashes();
michael@0 205
michael@0 206 Services.obs.removeObserver(observer, topic);
michael@0 207 }
michael@0 208 }, "network-connection-state-changed", false);
michael@0 209 },
michael@0 210
michael@0 211 get contentBrowser() {
michael@0 212 delete this.contentBrowser;
michael@0 213 return this.contentBrowser = document.getElementById('systemapp');
michael@0 214 },
michael@0 215
michael@0 216 get homeURL() {
michael@0 217 try {
michael@0 218 let homeSrc = Services.env.get('B2G_HOMESCREEN');
michael@0 219 if (homeSrc)
michael@0 220 return homeSrc;
michael@0 221 } catch (e) {}
michael@0 222
michael@0 223 return Services.prefs.getCharPref('browser.homescreenURL');
michael@0 224 },
michael@0 225
michael@0 226 get manifestURL() {
michael@0 227 return Services.prefs.getCharPref('browser.manifestURL');
michael@0 228 },
michael@0 229
michael@0 230 _started: false,
michael@0 231 hasStarted: function shell_hasStarted() {
michael@0 232 return this._started;
michael@0 233 },
michael@0 234
michael@0 235 start: function shell_start() {
michael@0 236 this._started = true;
michael@0 237
michael@0 238 // This forces the initialization of the cookie service before we hit the
michael@0 239 // network.
michael@0 240 // See bug 810209
michael@0 241 let cookies = Cc["@mozilla.org/cookieService;1"];
michael@0 242
michael@0 243 try {
michael@0 244 let cr = Cc["@mozilla.org/xre/app-info;1"]
michael@0 245 .getService(Ci.nsICrashReporter);
michael@0 246 // Dogfood id. We might want to remove it in the future.
michael@0 247 // see bug 789466
michael@0 248 try {
michael@0 249 let dogfoodId = Services.prefs.getCharPref('prerelease.dogfood.id');
michael@0 250 if (dogfoodId != "") {
michael@0 251 cr.annotateCrashReport("Email", dogfoodId);
michael@0 252 }
michael@0 253 }
michael@0 254 catch (e) { }
michael@0 255
michael@0 256 #ifdef MOZ_WIDGET_GONK
michael@0 257 // Annotate crash report
michael@0 258 let annotations = [ [ "Android_Hardware", "ro.hardware" ],
michael@0 259 [ "Android_Device", "ro.product.device" ],
michael@0 260 [ "Android_CPU_ABI2", "ro.product.cpu.abi2" ],
michael@0 261 [ "Android_CPU_ABI", "ro.product.cpu.abi" ],
michael@0 262 [ "Android_Manufacturer", "ro.product.manufacturer" ],
michael@0 263 [ "Android_Brand", "ro.product.brand" ],
michael@0 264 [ "Android_Model", "ro.product.model" ],
michael@0 265 [ "Android_Board", "ro.product.board" ],
michael@0 266 ];
michael@0 267
michael@0 268 annotations.forEach(function (element) {
michael@0 269 cr.annotateCrashReport(element[0], libcutils.property_get(element[1]));
michael@0 270 });
michael@0 271
michael@0 272 let androidVersion = libcutils.property_get("ro.build.version.sdk") +
michael@0 273 "(" + libcutils.property_get("ro.build.version.codename") + ")";
michael@0 274 cr.annotateCrashReport("Android_Version", androidVersion);
michael@0 275
michael@0 276 SettingsListener.observe("deviceinfo.os", "", function(value) {
michael@0 277 try {
michael@0 278 let cr = Cc["@mozilla.org/xre/app-info;1"]
michael@0 279 .getService(Ci.nsICrashReporter);
michael@0 280 cr.annotateCrashReport("B2G_OS_Version", value);
michael@0 281 } catch(e) { }
michael@0 282 });
michael@0 283 #endif
michael@0 284 } catch(e) {
michael@0 285 debugCrashReport('exception: ' + e);
michael@0 286 }
michael@0 287
michael@0 288 let homeURL = this.homeURL;
michael@0 289 if (!homeURL) {
michael@0 290 let msg = 'Fatal error during startup: No homescreen found: try setting B2G_HOMESCREEN';
michael@0 291 alert(msg);
michael@0 292 return;
michael@0 293 }
michael@0 294 let manifestURL = this.manifestURL;
michael@0 295 // <html:iframe id="systemapp"
michael@0 296 // mozbrowser="true" allowfullscreen="true"
michael@0 297 // style="overflow: hidden; height: 100%; width: 100%; border: none;"
michael@0 298 // src="data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;'>"/>
michael@0 299 let systemAppFrame =
michael@0 300 document.createElementNS('http://www.w3.org/1999/xhtml', 'html:iframe');
michael@0 301 systemAppFrame.setAttribute('id', 'systemapp');
michael@0 302 systemAppFrame.setAttribute('mozbrowser', 'true');
michael@0 303 systemAppFrame.setAttribute('mozapp', manifestURL);
michael@0 304 systemAppFrame.setAttribute('allowfullscreen', 'true');
michael@0 305 systemAppFrame.setAttribute('style', "overflow: hidden; height: 100%; width: 100%; border: none;");
michael@0 306 systemAppFrame.setAttribute('src', "data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;");
michael@0 307 let container = document.getElementById('container');
michael@0 308 #ifdef MOZ_WIDGET_COCOA
michael@0 309 // See shell.html
michael@0 310 let hotfix = document.getElementById('placeholder');
michael@0 311 if (hotfix) {
michael@0 312 container.removeChild(hotfix);
michael@0 313 }
michael@0 314 #endif
michael@0 315 container.appendChild(systemAppFrame);
michael@0 316
michael@0 317 systemAppFrame.contentWindow
michael@0 318 .QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 319 .getInterface(Ci.nsIWebNavigation)
michael@0 320 .sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
michael@0 321 .createInstance(Ci.nsISHistory);
michael@0 322
michael@0 323 // On firefox mulet, shell.html is loaded in a tab
michael@0 324 // and we have to listen on the chrome event handler
michael@0 325 // to catch key events
michael@0 326 let chromeEventHandler = window.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 327 .getInterface(Ci.nsIWebNavigation)
michael@0 328 .QueryInterface(Ci.nsIDocShell)
michael@0 329 .chromeEventHandler || window;
michael@0 330 // Capture all key events so we can filter out hardware buttons
michael@0 331 // And send them to Gaia via mozChromeEvents.
michael@0 332 // Ideally, hardware buttons wouldn't generate key events at all, or
michael@0 333 // if they did, they would use keycodes that conform to DOM 3 Events.
michael@0 334 // See discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=762362
michael@0 335 chromeEventHandler.addEventListener('keydown', this, true);
michael@0 336 chromeEventHandler.addEventListener('keypress', this, true);
michael@0 337 chromeEventHandler.addEventListener('keyup', this, true);
michael@0 338
michael@0 339 window.addEventListener('MozApplicationManifest', this);
michael@0 340 window.addEventListener('mozfullscreenchange', this);
michael@0 341 window.addEventListener('MozAfterPaint', this);
michael@0 342 window.addEventListener('sizemodechange', this);
michael@0 343 window.addEventListener('unload', this);
michael@0 344 this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
michael@0 345
michael@0 346 SystemAppProxy.registerFrame(this.contentBrowser);
michael@0 347
michael@0 348 CustomEventManager.init();
michael@0 349 WebappsHelper.init();
michael@0 350 UserAgentOverrides.init();
michael@0 351 IndexedDBPromptHelper.init();
michael@0 352 CaptivePortalLoginHelper.init();
michael@0 353
michael@0 354 this.contentBrowser.src = homeURL;
michael@0 355 this.isHomeLoaded = false;
michael@0 356
michael@0 357 ppmm.addMessageListener("content-handler", this);
michael@0 358 ppmm.addMessageListener("dial-handler", this);
michael@0 359 ppmm.addMessageListener("sms-handler", this);
michael@0 360 ppmm.addMessageListener("mail-handler", this);
michael@0 361 ppmm.addMessageListener("app-notification-send", AlertsHelper);
michael@0 362 ppmm.addMessageListener("file-picker", this);
michael@0 363 ppmm.addMessageListener("getProfD", function(message) {
michael@0 364 return Services.dirsvc.get("ProfD", Ci.nsIFile).path;
michael@0 365 });
michael@0 366 },
michael@0 367
michael@0 368 stop: function shell_stop() {
michael@0 369 window.removeEventListener('unload', this);
michael@0 370 window.removeEventListener('keydown', this, true);
michael@0 371 window.removeEventListener('keypress', this, true);
michael@0 372 window.removeEventListener('keyup', this, true);
michael@0 373 window.removeEventListener('MozApplicationManifest', this);
michael@0 374 window.removeEventListener('mozfullscreenchange', this);
michael@0 375 window.removeEventListener('sizemodechange', this);
michael@0 376 this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
michael@0 377 ppmm.removeMessageListener("content-handler", this);
michael@0 378 if (this.timer) {
michael@0 379 this.timer.cancel();
michael@0 380 this.timer = null;
michael@0 381 }
michael@0 382
michael@0 383 UserAgentOverrides.uninit();
michael@0 384 IndexedDBPromptHelper.uninit();
michael@0 385 },
michael@0 386
michael@0 387 // If this key event actually represents a hardware button, filter it here
michael@0 388 // and send a mozChromeEvent with detail.type set to xxx-button-press or
michael@0 389 // xxx-button-release instead.
michael@0 390 filterHardwareKeys: function shell_filterHardwareKeys(evt) {
michael@0 391 var type;
michael@0 392 switch (evt.keyCode) {
michael@0 393 case evt.DOM_VK_HOME: // Home button
michael@0 394 type = 'home-button';
michael@0 395 break;
michael@0 396 case evt.DOM_VK_SLEEP: // Sleep button
michael@0 397 case evt.DOM_VK_END: // On desktop we don't have a sleep button
michael@0 398 type = 'sleep-button';
michael@0 399 break;
michael@0 400 case evt.DOM_VK_PAGE_UP: // Volume up button
michael@0 401 type = 'volume-up-button';
michael@0 402 break;
michael@0 403 case evt.DOM_VK_PAGE_DOWN: // Volume down button
michael@0 404 type = 'volume-down-button';
michael@0 405 break;
michael@0 406 case evt.DOM_VK_ESCAPE: // Back button (should be disabled)
michael@0 407 type = 'back-button';
michael@0 408 break;
michael@0 409 case evt.DOM_VK_CONTEXT_MENU: // Menu button
michael@0 410 type = 'menu-button';
michael@0 411 break;
michael@0 412 case evt.DOM_VK_F1: // headset button
michael@0 413 type = 'headset-button';
michael@0 414 break;
michael@0 415 }
michael@0 416
michael@0 417 let mediaKeys = {
michael@0 418 'MediaNextTrack': 'media-next-track-button',
michael@0 419 'MediaPreviousTrack': 'media-previous-track-button',
michael@0 420 'MediaPause': 'media-pause-button',
michael@0 421 'MediaPlay': 'media-play-button',
michael@0 422 'MediaPlayPause': 'media-play-pause-button',
michael@0 423 'MediaStop': 'media-stop-button',
michael@0 424 'MediaRewind': 'media-rewind-button',
michael@0 425 'FastFwd': 'media-fast-forward-button'
michael@0 426 };
michael@0 427
michael@0 428 let isMediaKey = false;
michael@0 429 if (mediaKeys[evt.key]) {
michael@0 430 isMediaKey = true;
michael@0 431 type = mediaKeys[evt.key];
michael@0 432 }
michael@0 433
michael@0 434 if (!type) {
michael@0 435 return;
michael@0 436 }
michael@0 437
michael@0 438 // If we didn't return, then the key event represents a hardware key
michael@0 439 // and we need to prevent it from propagating to Gaia
michael@0 440 evt.stopImmediatePropagation();
michael@0 441 evt.preventDefault(); // Prevent keypress events (when #501496 is fixed).
michael@0 442
michael@0 443 // If it is a key down or key up event, we send a chrome event to Gaia.
michael@0 444 // If it is a keypress event we just ignore it.
michael@0 445 switch (evt.type) {
michael@0 446 case 'keydown':
michael@0 447 type = type + '-press';
michael@0 448 break;
michael@0 449 case 'keyup':
michael@0 450 type = type + '-release';
michael@0 451 break;
michael@0 452 case 'keypress':
michael@0 453 return;
michael@0 454 }
michael@0 455
michael@0 456 // Let applications receive the headset button key press/release event.
michael@0 457 if (evt.keyCode == evt.DOM_VK_F1 && type !== this.lastHardwareButtonEventType) {
michael@0 458 this.lastHardwareButtonEventType = type;
michael@0 459 gSystemMessenger.broadcastMessage('headset-button', type);
michael@0 460 return;
michael@0 461 }
michael@0 462
michael@0 463 if (isMediaKey) {
michael@0 464 this.lastHardwareButtonEventType = type;
michael@0 465 gSystemMessenger.broadcastMessage('media-button', type);
michael@0 466 return;
michael@0 467 }
michael@0 468
michael@0 469 // On my device, the physical hardware buttons (sleep and volume)
michael@0 470 // send multiple events (press press release release), but the
michael@0 471 // soft home button just sends one. This hack is to manually
michael@0 472 // "debounce" the keys. If the type of this event is the same as
michael@0 473 // the type of the last one, then don't send it. We'll never send
michael@0 474 // two presses or two releases in a row.
michael@0 475 // FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=761067
michael@0 476 if (type !== this.lastHardwareButtonEventType) {
michael@0 477 this.lastHardwareButtonEventType = type;
michael@0 478 this.sendChromeEvent({type: type});
michael@0 479 }
michael@0 480 },
michael@0 481
michael@0 482 lastHardwareButtonEventType: null, // property for the hack above
michael@0 483 needBufferOpenAppReq: true,
michael@0 484 bufferedOpenAppReqs: [],
michael@0 485 timer: null,
michael@0 486 visibleNormalAudioActive: false,
michael@0 487
michael@0 488 handleEvent: function shell_handleEvent(evt) {
michael@0 489 let content = this.contentBrowser.contentWindow;
michael@0 490 switch (evt.type) {
michael@0 491 case 'keydown':
michael@0 492 case 'keyup':
michael@0 493 case 'keypress':
michael@0 494 this.filterHardwareKeys(evt);
michael@0 495 break;
michael@0 496 case 'mozfullscreenchange':
michael@0 497 // When the screen goes fullscreen make sure to set the focus to the
michael@0 498 // main window so noboby can prevent the ESC key to get out fullscreen
michael@0 499 // mode
michael@0 500 if (document.mozFullScreen)
michael@0 501 Services.fm.focusedWindow = window;
michael@0 502 break;
michael@0 503 case 'sizemodechange':
michael@0 504 if (window.windowState == window.STATE_MINIMIZED && !this.visibleNormalAudioActive) {
michael@0 505 this.contentBrowser.setVisible(false);
michael@0 506 } else {
michael@0 507 this.contentBrowser.setVisible(true);
michael@0 508 }
michael@0 509 break;
michael@0 510 case 'mozbrowserloadstart':
michael@0 511 if (content.document.location == 'about:blank') {
michael@0 512 this.contentBrowser.addEventListener('mozbrowserlocationchange', this, true);
michael@0 513 return;
michael@0 514 }
michael@0 515
michael@0 516 this.notifyContentStart();
michael@0 517 break;
michael@0 518 case 'mozbrowserlocationchange':
michael@0 519 if (content.document.location == 'about:blank') {
michael@0 520 return;
michael@0 521 }
michael@0 522
michael@0 523 this.notifyContentStart();
michael@0 524 break;
michael@0 525
michael@0 526 case 'MozApplicationManifest':
michael@0 527 try {
michael@0 528 if (!Services.prefs.getBoolPref('browser.cache.offline.enable'))
michael@0 529 return;
michael@0 530
michael@0 531 let contentWindow = evt.originalTarget.defaultView;
michael@0 532 let documentElement = contentWindow.document.documentElement;
michael@0 533 if (!documentElement)
michael@0 534 return;
michael@0 535
michael@0 536 let manifest = documentElement.getAttribute('manifest');
michael@0 537 if (!manifest)
michael@0 538 return;
michael@0 539
michael@0 540 let principal = contentWindow.document.nodePrincipal;
michael@0 541 if (Services.perms.testPermissionFromPrincipal(principal, 'offline-app') == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
michael@0 542 if (Services.prefs.getBoolPref('browser.offline-apps.notify')) {
michael@0 543 // FIXME Bug 710729 - Add a UI for offline cache notifications
michael@0 544 return;
michael@0 545 }
michael@0 546 return;
michael@0 547 }
michael@0 548
michael@0 549 Services.perms.addFromPrincipal(principal, 'offline-app',
michael@0 550 Ci.nsIPermissionManager.ALLOW_ACTION);
michael@0 551
michael@0 552 let documentURI = Services.io.newURI(contentWindow.document.documentURI,
michael@0 553 null,
michael@0 554 null);
michael@0 555 let manifestURI = Services.io.newURI(manifest, null, documentURI);
michael@0 556 let updateService = Cc['@mozilla.org/offlinecacheupdate-service;1']
michael@0 557 .getService(Ci.nsIOfflineCacheUpdateService);
michael@0 558 updateService.scheduleUpdate(manifestURI, documentURI, window);
michael@0 559 } catch (e) {
michael@0 560 dump('Error while creating offline cache: ' + e + '\n');
michael@0 561 }
michael@0 562 break;
michael@0 563 case 'MozAfterPaint':
michael@0 564 window.removeEventListener('MozAfterPaint', this);
michael@0 565 this.sendChromeEvent({
michael@0 566 type: 'system-first-paint'
michael@0 567 });
michael@0 568 break;
michael@0 569 case 'unload':
michael@0 570 this.stop();
michael@0 571 break;
michael@0 572 }
michael@0 573 },
michael@0 574
michael@0 575 // Send an event to a specific window, document or element.
michael@0 576 sendEvent: function shell_sendEvent(target, type, details) {
michael@0 577 let doc = target.document || target.ownerDocument || target;
michael@0 578 let event = doc.createEvent('CustomEvent');
michael@0 579 event.initCustomEvent(type, true, true, details ? details : {});
michael@0 580 target.dispatchEvent(event);
michael@0 581 },
michael@0 582
michael@0 583 sendCustomEvent: function shell_sendCustomEvent(type, details) {
michael@0 584 let target = getContentWindow();
michael@0 585 let payload = details ? Cu.cloneInto(details, target) : {};
michael@0 586 this.sendEvent(target, type, payload);
michael@0 587 },
michael@0 588
michael@0 589 sendChromeEvent: function shell_sendChromeEvent(details) {
michael@0 590 if (!this.isHomeLoaded) {
michael@0 591 if (!('pendingChromeEvents' in this)) {
michael@0 592 this.pendingChromeEvents = [];
michael@0 593 }
michael@0 594
michael@0 595 this.pendingChromeEvents.push(details);
michael@0 596 return;
michael@0 597 }
michael@0 598
michael@0 599 this.sendEvent(getContentWindow(), "mozChromeEvent",
michael@0 600 Cu.cloneInto(details, getContentWindow()));
michael@0 601 },
michael@0 602
michael@0 603 openAppForSystemMessage: function shell_openAppForSystemMessage(msg) {
michael@0 604 let payload = {
michael@0 605 url: msg.pageURL,
michael@0 606 manifestURL: msg.manifestURL,
michael@0 607 isActivity: (msg.type == 'activity'),
michael@0 608 onlyShowApp: msg.onlyShowApp,
michael@0 609 showApp: msg.showApp,
michael@0 610 target: msg.target,
michael@0 611 expectingSystemMessage: true,
michael@0 612 extra: msg.extra
michael@0 613 }
michael@0 614 this.sendCustomEvent('open-app', payload);
michael@0 615 },
michael@0 616
michael@0 617 receiveMessage: function shell_receiveMessage(message) {
michael@0 618 var activities = { 'content-handler': { name: 'view', response: null },
michael@0 619 'dial-handler': { name: 'dial', response: null },
michael@0 620 'mail-handler': { name: 'new', response: null },
michael@0 621 'sms-handler': { name: 'new', response: null },
michael@0 622 'file-picker': { name: 'pick', response: 'file-picked' } };
michael@0 623
michael@0 624 if (!(message.name in activities))
michael@0 625 return;
michael@0 626
michael@0 627 let data = message.data;
michael@0 628 let activity = activities[message.name];
michael@0 629
michael@0 630 let a = new MozActivity({
michael@0 631 name: activity.name,
michael@0 632 data: data
michael@0 633 });
michael@0 634
michael@0 635 if (activity.response) {
michael@0 636 a.onsuccess = function() {
michael@0 637 let sender = message.target.QueryInterface(Ci.nsIMessageSender);
michael@0 638 sender.sendAsyncMessage(activity.response, { success: true,
michael@0 639 result: a.result });
michael@0 640 }
michael@0 641 a.onerror = function() {
michael@0 642 let sender = message.target.QueryInterface(Ci.nsIMessageSender);
michael@0 643 sender.sendAsyncMessage(activity.response, { success: false });
michael@0 644 }
michael@0 645 }
michael@0 646 },
michael@0 647
michael@0 648 notifyContentStart: function shell_notifyContentStart() {
michael@0 649 this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
michael@0 650 this.contentBrowser.removeEventListener('mozbrowserlocationchange', this, true);
michael@0 651
michael@0 652 let content = this.contentBrowser.contentWindow;
michael@0 653
michael@0 654 this.reportCrash(true);
michael@0 655
michael@0 656 this.sendEvent(window, 'ContentStart');
michael@0 657
michael@0 658 Services.obs.notifyObservers(null, 'content-start', null);
michael@0 659
michael@0 660 #ifdef MOZ_WIDGET_GONK
michael@0 661 Cu.import('resource://gre/modules/OperatorApps.jsm');
michael@0 662 #endif
michael@0 663
michael@0 664 content.addEventListener('load', function shell_homeLoaded() {
michael@0 665 content.removeEventListener('load', shell_homeLoaded);
michael@0 666 shell.isHomeLoaded = true;
michael@0 667
michael@0 668 #ifdef MOZ_WIDGET_GONK
michael@0 669 libcutils.property_set('sys.boot_completed', '1');
michael@0 670 #endif
michael@0 671
michael@0 672 Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
michael@0 673
michael@0 674 SystemAppProxy.setIsReady();
michael@0 675 if ('pendingChromeEvents' in shell) {
michael@0 676 shell.pendingChromeEvents.forEach((shell.sendChromeEvent).bind(shell));
michael@0 677 }
michael@0 678 delete shell.pendingChromeEvents;
michael@0 679 });
michael@0 680 }
michael@0 681 };
michael@0 682
michael@0 683 // Listen for the request of opening app and relay them to Gaia.
michael@0 684 Services.obs.addObserver(function onSystemMessageOpenApp(subject, topic, data) {
michael@0 685 let msg = JSON.parse(data);
michael@0 686 // Buffer non-activity request until content starts to load for 10 seconds.
michael@0 687 // We'll revisit this later if new kind of requests don't need to be cached.
michael@0 688 if (shell.needBufferOpenAppReq && msg.type !== 'activity') {
michael@0 689 shell.bufferedOpenAppReqs.push(msg);
michael@0 690 return;
michael@0 691 }
michael@0 692 shell.openAppForSystemMessage(msg);
michael@0 693 }, 'system-messages-open-app', false);
michael@0 694
michael@0 695 Services.obs.addObserver(function onInterAppCommConnect(subject, topic, data) {
michael@0 696 data = JSON.parse(data);
michael@0 697 shell.sendChromeEvent({ type: "inter-app-comm-permission",
michael@0 698 chromeEventID: data.callerID,
michael@0 699 manifestURL: data.manifestURL,
michael@0 700 keyword: data.keyword,
michael@0 701 peers: data.appsToSelect });
michael@0 702 }, 'inter-app-comm-select-app', false);
michael@0 703
michael@0 704 Services.obs.addObserver(function onFullscreenOriginChange(subject, topic, data) {
michael@0 705 shell.sendChromeEvent({ type: "fullscreenoriginchange",
michael@0 706 fullscreenorigin: data });
michael@0 707 }, "fullscreen-origin-change", false);
michael@0 708
michael@0 709 DOMApplicationRegistry.registryStarted.then(function () {
michael@0 710 shell.sendChromeEvent({ type: 'webapps-registry-start' });
michael@0 711 });
michael@0 712 DOMApplicationRegistry.registryReady.then(function () {
michael@0 713 shell.sendChromeEvent({ type: 'webapps-registry-ready' });
michael@0 714 });
michael@0 715
michael@0 716 Services.obs.addObserver(function onBluetoothVolumeChange(subject, topic, data) {
michael@0 717 shell.sendChromeEvent({
michael@0 718 type: "bluetooth-volumeset",
michael@0 719 value: data
michael@0 720 });
michael@0 721 }, 'bluetooth-volume-change', false);
michael@0 722
michael@0 723 Services.obs.addObserver(function(subject, topic, data) {
michael@0 724 shell.sendCustomEvent('mozmemorypressure');
michael@0 725 }, 'memory-pressure', false);
michael@0 726
michael@0 727 var CustomEventManager = {
michael@0 728 init: function custevt_init() {
michael@0 729 window.addEventListener("ContentStart", (function(evt) {
michael@0 730 let content = shell.contentBrowser.contentWindow;
michael@0 731 content.addEventListener("mozContentEvent", this, false, true);
michael@0 732
michael@0 733 // After content starts to load for 10 seconds, send and
michael@0 734 // clean up the buffered open-app requests if there is any.
michael@0 735 //
michael@0 736 // TODO: Bug 793420 - Remove the waiting timer for the 'open-app'
michael@0 737 // mozChromeEvents requested by System Message
michael@0 738 shell.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
michael@0 739 shell.timer.initWithCallback(function timerCallback() {
michael@0 740 shell.bufferedOpenAppReqs.forEach(function bufferOpenAppReq(msg) {
michael@0 741 shell.openAppForSystemMessage(msg);
michael@0 742 });
michael@0 743 shell.bufferedOpenAppReqs.length = 0;
michael@0 744 shell.needBufferOpenAppReq = false;
michael@0 745 shell.timer = null;
michael@0 746 }, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
michael@0 747 }).bind(this), false);
michael@0 748 },
michael@0 749
michael@0 750 handleEvent: function custevt_handleEvent(evt) {
michael@0 751 let detail = evt.detail;
michael@0 752 dump('XXX FIXME : Got a mozContentEvent: ' + detail.type + "\n");
michael@0 753
michael@0 754 switch(detail.type) {
michael@0 755 case 'desktop-notification-show':
michael@0 756 case 'desktop-notification-click':
michael@0 757 case 'desktop-notification-close':
michael@0 758 AlertsHelper.handleEvent(detail);
michael@0 759 break;
michael@0 760 case 'webapps-install-granted':
michael@0 761 case 'webapps-install-denied':
michael@0 762 WebappsHelper.handleEvent(detail);
michael@0 763 break;
michael@0 764 case 'select-choicechange':
michael@0 765 FormsHelper.handleEvent(detail);
michael@0 766 break;
michael@0 767 case 'system-message-listener-ready':
michael@0 768 Services.obs.notifyObservers(null, 'system-message-listener-ready', null);
michael@0 769 break;
michael@0 770 case 'remote-debugger-prompt':
michael@0 771 RemoteDebugger.handleEvent(detail);
michael@0 772 break;
michael@0 773 case 'captive-portal-login-cancel':
michael@0 774 CaptivePortalLoginHelper.handleEvent(detail);
michael@0 775 break;
michael@0 776 case 'inter-app-comm-permission':
michael@0 777 Services.obs.notifyObservers(null, 'inter-app-comm-select-app-result',
michael@0 778 JSON.stringify({ callerID: detail.chromeEventID,
michael@0 779 keyword: detail.keyword,
michael@0 780 manifestURL: detail.manifestURL,
michael@0 781 selectedApps: detail.peers }));
michael@0 782 break;
michael@0 783 case 'inputmethod-update-layouts':
michael@0 784 KeyboardHelper.handleEvent(detail);
michael@0 785 break;
michael@0 786 }
michael@0 787 }
michael@0 788 }
michael@0 789
michael@0 790 var AlertsHelper = {
michael@0 791 _listeners: {},
michael@0 792 _count: 0,
michael@0 793
michael@0 794 handleEvent: function alert_handleEvent(detail) {
michael@0 795 if (!detail || !detail.id)
michael@0 796 return;
michael@0 797
michael@0 798 let uid = detail.id;
michael@0 799 let listener = this._listeners[uid];
michael@0 800 if (!listener)
michael@0 801 return;
michael@0 802
michael@0 803 let topic;
michael@0 804 if (detail.type == "desktop-notification-click") {
michael@0 805 topic = "alertclickcallback";
michael@0 806 } else if (detail.type == "desktop-notification-show") {
michael@0 807 topic = "alertshow";
michael@0 808 } else {
michael@0 809 /* desktop-notification-close */
michael@0 810 topic = "alertfinished";
michael@0 811 }
michael@0 812
michael@0 813 if (uid.startsWith("alert")) {
michael@0 814 try {
michael@0 815 listener.observer.observe(null, topic, listener.cookie);
michael@0 816 } catch (e) { }
michael@0 817 } else {
michael@0 818 try {
michael@0 819 listener.mm.sendAsyncMessage("app-notification-return", {
michael@0 820 uid: uid,
michael@0 821 topic: topic,
michael@0 822 target: listener.target
michael@0 823 });
michael@0 824 } catch (e) {
michael@0 825 // we get an exception if the app is not launched yet
michael@0 826 gSystemMessenger.sendMessage("notification", {
michael@0 827 clicked: (detail.type === "desktop-notification-click"),
michael@0 828 title: listener.title,
michael@0 829 body: listener.text,
michael@0 830 imageURL: listener.imageURL,
michael@0 831 lang: listener.lang,
michael@0 832 dir: listener.dir,
michael@0 833 id: listener.id,
michael@0 834 tag: listener.tag
michael@0 835 },
michael@0 836 Services.io.newURI(listener.target, null, null),
michael@0 837 Services.io.newURI(listener.manifestURL, null, null)
michael@0 838 );
michael@0 839 }
michael@0 840 }
michael@0 841
michael@0 842 // we're done with this notification
michael@0 843 if (topic === "alertfinished") {
michael@0 844 delete this._listeners[uid];
michael@0 845 }
michael@0 846 },
michael@0 847
michael@0 848 registerListener: function alert_registerListener(alertId, cookie, alertListener) {
michael@0 849 this._listeners[alertId] = { observer: alertListener, cookie: cookie };
michael@0 850 },
michael@0 851
michael@0 852 registerAppListener: function alert_registerAppListener(uid, listener) {
michael@0 853 this._listeners[uid] = listener;
michael@0 854
michael@0 855 let app = DOMApplicationRegistry.getAppByManifestURL(listener.manifestURL);
michael@0 856 DOMApplicationRegistry.getManifestFor(app.manifestURL).then((manifest) => {
michael@0 857 let helper = new ManifestHelper(manifest, app.origin);
michael@0 858 let getNotificationURLFor = function(messages) {
michael@0 859 if (!messages)
michael@0 860 return null;
michael@0 861
michael@0 862 for (let i = 0; i < messages.length; i++) {
michael@0 863 let message = messages[i];
michael@0 864 if (message === "notification") {
michael@0 865 return helper.fullLaunchPath();
michael@0 866 } else if (typeof message == "object" && "notification" in message) {
michael@0 867 return helper.resolveFromOrigin(message["notification"]);
michael@0 868 }
michael@0 869 }
michael@0 870
michael@0 871 // No message found...
michael@0 872 return null;
michael@0 873 }
michael@0 874
michael@0 875 listener.target = getNotificationURLFor(manifest.messages);
michael@0 876
michael@0 877 // Bug 816944 - Support notification messages for entry_points.
michael@0 878 });
michael@0 879 },
michael@0 880
michael@0 881 showNotification: function alert_showNotification(imageURL,
michael@0 882 title,
michael@0 883 text,
michael@0 884 textClickable,
michael@0 885 cookie,
michael@0 886 uid,
michael@0 887 bidi,
michael@0 888 lang,
michael@0 889 manifestURL) {
michael@0 890 function send(appName, appIcon) {
michael@0 891 shell.sendChromeEvent({
michael@0 892 type: "desktop-notification",
michael@0 893 id: uid,
michael@0 894 icon: imageURL,
michael@0 895 title: title,
michael@0 896 text: text,
michael@0 897 bidi: bidi,
michael@0 898 lang: lang,
michael@0 899 appName: appName,
michael@0 900 appIcon: appIcon,
michael@0 901 manifestURL: manifestURL
michael@0 902 });
michael@0 903 }
michael@0 904
michael@0 905 if (!manifestURL || !manifestURL.length) {
michael@0 906 send(null, null);
michael@0 907 return;
michael@0 908 }
michael@0 909
michael@0 910 // If we have a manifest URL, get the icon and title from the manifest
michael@0 911 // to prevent spoofing.
michael@0 912 let app = DOMApplicationRegistry.getAppByManifestURL(manifestURL);
michael@0 913 DOMApplicationRegistry.getManifestFor(manifestURL).then((aManifest) => {
michael@0 914 let helper = new ManifestHelper(aManifest, app.origin);
michael@0 915 send(helper.name, helper.iconURLForSize(128));
michael@0 916 });
michael@0 917 },
michael@0 918
michael@0 919 showAlertNotification: function alert_showAlertNotification(imageURL,
michael@0 920 title,
michael@0 921 text,
michael@0 922 textClickable,
michael@0 923 cookie,
michael@0 924 alertListener,
michael@0 925 name,
michael@0 926 bidi,
michael@0 927 lang) {
michael@0 928 let currentListener = this._listeners[name];
michael@0 929 if (currentListener) {
michael@0 930 currentListener.observer.observe(null, "alertfinished", currentListener.cookie);
michael@0 931 }
michael@0 932
michael@0 933 this.registerListener(name, cookie, alertListener);
michael@0 934 this.showNotification(imageURL, title, text, textClickable, cookie,
michael@0 935 name, bidi, lang, null);
michael@0 936 },
michael@0 937
michael@0 938 closeAlert: function alert_closeAlert(name) {
michael@0 939 shell.sendChromeEvent({
michael@0 940 type: "desktop-notification-close",
michael@0 941 id: name
michael@0 942 });
michael@0 943 },
michael@0 944
michael@0 945 receiveMessage: function alert_receiveMessage(aMessage) {
michael@0 946 if (!aMessage.target.assertAppHasPermission("desktop-notification")) {
michael@0 947 Cu.reportError("Desktop-notification message " + aMessage.name +
michael@0 948 " from a content process with no desktop-notification privileges.");
michael@0 949 return;
michael@0 950 }
michael@0 951
michael@0 952 let data = aMessage.data;
michael@0 953 let details = data.details;
michael@0 954 let listener = {
michael@0 955 mm: aMessage.target,
michael@0 956 title: data.title,
michael@0 957 text: data.text,
michael@0 958 manifestURL: details.manifestURL,
michael@0 959 imageURL: data.imageURL,
michael@0 960 lang: details.lang || undefined,
michael@0 961 id: details.id || undefined,
michael@0 962 dir: details.dir || undefined,
michael@0 963 tag: details.tag || undefined
michael@0 964 };
michael@0 965 this.registerAppListener(data.uid, listener);
michael@0 966
michael@0 967 this.showNotification(data.imageURL, data.title, data.text,
michael@0 968 details.textClickable, null,
michael@0 969 data.uid, details.dir,
michael@0 970 details.lang, details.manifestURL);
michael@0 971 },
michael@0 972 }
michael@0 973
michael@0 974 var WebappsHelper = {
michael@0 975 _installers: {},
michael@0 976 _count: 0,
michael@0 977
michael@0 978 init: function webapps_init() {
michael@0 979 Services.obs.addObserver(this, "webapps-launch", false);
michael@0 980 Services.obs.addObserver(this, "webapps-ask-install", false);
michael@0 981 Services.obs.addObserver(this, "webapps-close", false);
michael@0 982 },
michael@0 983
michael@0 984 registerInstaller: function webapps_registerInstaller(data) {
michael@0 985 let id = "installer" + this._count++;
michael@0 986 this._installers[id] = data;
michael@0 987 return id;
michael@0 988 },
michael@0 989
michael@0 990 handleEvent: function webapps_handleEvent(detail) {
michael@0 991 if (!detail || !detail.id)
michael@0 992 return;
michael@0 993
michael@0 994 let installer = this._installers[detail.id];
michael@0 995 delete this._installers[detail.id];
michael@0 996 switch (detail.type) {
michael@0 997 case "webapps-install-granted":
michael@0 998 DOMApplicationRegistry.confirmInstall(installer);
michael@0 999 break;
michael@0 1000 case "webapps-install-denied":
michael@0 1001 DOMApplicationRegistry.denyInstall(installer);
michael@0 1002 break;
michael@0 1003 }
michael@0 1004 },
michael@0 1005
michael@0 1006 observe: function webapps_observe(subject, topic, data) {
michael@0 1007 let json = JSON.parse(data);
michael@0 1008 json.mm = subject;
michael@0 1009
michael@0 1010 switch(topic) {
michael@0 1011 case "webapps-launch":
michael@0 1012 DOMApplicationRegistry.getManifestFor(json.manifestURL).then((aManifest) => {
michael@0 1013 if (!aManifest)
michael@0 1014 return;
michael@0 1015
michael@0 1016 let manifest = new ManifestHelper(aManifest, json.origin);
michael@0 1017 let payload = {
michael@0 1018 __exposedProps__: {
michael@0 1019 timestamp: "r",
michael@0 1020 url: "r",
michael@0 1021 manifestURL: "r"
michael@0 1022 },
michael@0 1023 timestamp: json.timestamp,
michael@0 1024 url: manifest.fullLaunchPath(json.startPoint),
michael@0 1025 manifestURL: json.manifestURL
michael@0 1026 }
michael@0 1027 shell.sendEvent(getContentWindow(), "webapps-launch", payload);
michael@0 1028 });
michael@0 1029 break;
michael@0 1030 case "webapps-ask-install":
michael@0 1031 let id = this.registerInstaller(json);
michael@0 1032 shell.sendChromeEvent({
michael@0 1033 type: "webapps-ask-install",
michael@0 1034 id: id,
michael@0 1035 app: json.app
michael@0 1036 });
michael@0 1037 break;
michael@0 1038 case "webapps-close":
michael@0 1039 shell.sendEvent(getContentWindow(), "webapps-close",
michael@0 1040 {
michael@0 1041 __exposedProps__: { "manifestURL": "r" },
michael@0 1042 "manifestURL": json.manifestURL
michael@0 1043 });
michael@0 1044 break;
michael@0 1045 }
michael@0 1046 }
michael@0 1047 }
michael@0 1048
michael@0 1049 let IndexedDBPromptHelper = {
michael@0 1050 _quotaPrompt: "indexedDB-quota-prompt",
michael@0 1051 _quotaResponse: "indexedDB-quota-response",
michael@0 1052
michael@0 1053 init:
michael@0 1054 function IndexedDBPromptHelper_init() {
michael@0 1055 Services.obs.addObserver(this, this._quotaPrompt, false);
michael@0 1056 },
michael@0 1057
michael@0 1058 uninit:
michael@0 1059 function IndexedDBPromptHelper_uninit() {
michael@0 1060 Services.obs.removeObserver(this, this._quotaPrompt);
michael@0 1061 },
michael@0 1062
michael@0 1063 observe:
michael@0 1064 function IndexedDBPromptHelper_observe(subject, topic, data) {
michael@0 1065 if (topic != this._quotaPrompt) {
michael@0 1066 throw new Error("Unexpected topic!");
michael@0 1067 }
michael@0 1068
michael@0 1069 let observer = subject.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 1070 .getInterface(Ci.nsIObserver);
michael@0 1071 let responseTopic = this._quotaResponse;
michael@0 1072
michael@0 1073 setTimeout(function() {
michael@0 1074 observer.observe(null, responseTopic,
michael@0 1075 Ci.nsIPermissionManager.DENY_ACTION);
michael@0 1076 }, 0);
michael@0 1077 }
michael@0 1078 }
michael@0 1079
michael@0 1080 let RemoteDebugger = {
michael@0 1081 _promptDone: false,
michael@0 1082 _promptAnswer: false,
michael@0 1083 _running: false,
michael@0 1084
michael@0 1085 prompt: function debugger_prompt() {
michael@0 1086 this._promptDone = false;
michael@0 1087
michael@0 1088 shell.sendChromeEvent({
michael@0 1089 "type": "remote-debugger-prompt"
michael@0 1090 });
michael@0 1091
michael@0 1092 while(!this._promptDone) {
michael@0 1093 Services.tm.currentThread.processNextEvent(true);
michael@0 1094 }
michael@0 1095
michael@0 1096 return this._promptAnswer;
michael@0 1097 },
michael@0 1098
michael@0 1099 handleEvent: function debugger_handleEvent(detail) {
michael@0 1100 this._promptAnswer = detail.value;
michael@0 1101 this._promptDone = true;
michael@0 1102 },
michael@0 1103
michael@0 1104 get isDebugging() {
michael@0 1105 if (!this._running) {
michael@0 1106 return false;
michael@0 1107 }
michael@0 1108
michael@0 1109 return DebuggerServer._connections &&
michael@0 1110 Object.keys(DebuggerServer._connections).length > 0;
michael@0 1111 },
michael@0 1112
michael@0 1113 // Start the debugger server.
michael@0 1114 start: function debugger_start() {
michael@0 1115 if (this._running) {
michael@0 1116 return;
michael@0 1117 }
michael@0 1118
michael@0 1119 if (!DebuggerServer.initialized) {
michael@0 1120 // Ask for remote connections.
michael@0 1121 DebuggerServer.init(this.prompt.bind(this));
michael@0 1122
michael@0 1123 // /!\ Be careful when adding a new actor, especially global actors.
michael@0 1124 // Any new global actor will be exposed and returned by the root actor.
michael@0 1125
michael@0 1126 // Add Firefox-specific actors, but prevent tab actors to be loaded in
michael@0 1127 // the parent process, unless we enable certified apps debugging.
michael@0 1128 let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
michael@0 1129 DebuggerServer.addBrowserActors("navigator:browser", restrictPrivileges);
michael@0 1130
michael@0 1131 /**
michael@0 1132 * Construct a root actor appropriate for use in a server running in B2G.
michael@0 1133 * The returned root actor respects the factories registered with
michael@0 1134 * DebuggerServer.addGlobalActor only if certified apps debugging is on,
michael@0 1135 * otherwise we used an explicit limited list of global actors
michael@0 1136 *
michael@0 1137 * * @param connection DebuggerServerConnection
michael@0 1138 * The conection to the client.
michael@0 1139 */
michael@0 1140 DebuggerServer.createRootActor = function createRootActor(connection)
michael@0 1141 {
michael@0 1142 let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
michael@0 1143 let parameters = {
michael@0 1144 // We do not expose browser tab actors yet,
michael@0 1145 // but we still have to define tabList.getList(),
michael@0 1146 // otherwise, client won't be able to fetch global actors
michael@0 1147 // from listTabs request!
michael@0 1148 tabList: {
michael@0 1149 getList: function() {
michael@0 1150 return promise.resolve([]);
michael@0 1151 }
michael@0 1152 },
michael@0 1153 // Use an explicit global actor list to prevent exposing
michael@0 1154 // unexpected actors
michael@0 1155 globalActorFactories: restrictPrivileges ? {
michael@0 1156 webappsActor: DebuggerServer.globalActorFactories.webappsActor,
michael@0 1157 deviceActor: DebuggerServer.globalActorFactories.deviceActor,
michael@0 1158 } : DebuggerServer.globalActorFactories
michael@0 1159 };
michael@0 1160 let root = new DebuggerServer.RootActor(connection, parameters);
michael@0 1161 root.applicationType = "operating-system";
michael@0 1162 return root;
michael@0 1163 };
michael@0 1164
michael@0 1165 #ifdef MOZ_WIDGET_GONK
michael@0 1166 DebuggerServer.on("connectionchange", function() {
michael@0 1167 AdbController.updateState();
michael@0 1168 });
michael@0 1169 #endif
michael@0 1170 }
michael@0 1171
michael@0 1172 let path = Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
michael@0 1173 "/data/local/debugger-socket";
michael@0 1174 try {
michael@0 1175 DebuggerServer.openListener(path);
michael@0 1176 // Temporary event, until bug 942756 lands and offers a way to know
michael@0 1177 // when the server is up and running.
michael@0 1178 Services.obs.notifyObservers(null, 'debugger-server-started', null);
michael@0 1179 this._running = true;
michael@0 1180 } catch (e) {
michael@0 1181 dump('Unable to start debugger server: ' + e + '\n');
michael@0 1182 }
michael@0 1183 },
michael@0 1184
michael@0 1185 stop: function debugger_stop() {
michael@0 1186 if (!this._running) {
michael@0 1187 return;
michael@0 1188 }
michael@0 1189
michael@0 1190 if (!DebuggerServer.initialized) {
michael@0 1191 // Can this really happen if we are running?
michael@0 1192 this._running = false;
michael@0 1193 return;
michael@0 1194 }
michael@0 1195
michael@0 1196 try {
michael@0 1197 DebuggerServer.closeListener();
michael@0 1198 } catch (e) {
michael@0 1199 dump('Unable to stop debugger server: ' + e + '\n');
michael@0 1200 }
michael@0 1201 this._running = false;
michael@0 1202 }
michael@0 1203 }
michael@0 1204
michael@0 1205 let KeyboardHelper = {
michael@0 1206 handleEvent: function keyboard_handleEvent(detail) {
michael@0 1207 Keyboard.setLayouts(detail.layouts);
michael@0 1208 }
michael@0 1209 };
michael@0 1210
michael@0 1211 // This is the backend for Gaia's screenshot feature. Gaia requests a
michael@0 1212 // screenshot by sending a mozContentEvent with detail.type set to
michael@0 1213 // 'take-screenshot'. Then we take a screenshot and send a
michael@0 1214 // mozChromeEvent with detail.type set to 'take-screenshot-success'
michael@0 1215 // and detail.file set to the an image/png blob
michael@0 1216 window.addEventListener('ContentStart', function ss_onContentStart() {
michael@0 1217 let content = shell.contentBrowser.contentWindow;
michael@0 1218 content.addEventListener('mozContentEvent', function ss_onMozContentEvent(e) {
michael@0 1219 if (e.detail.type !== 'take-screenshot')
michael@0 1220 return;
michael@0 1221
michael@0 1222 try {
michael@0 1223 var canvas = document.createElementNS('http://www.w3.org/1999/xhtml',
michael@0 1224 'canvas');
michael@0 1225 var width = window.innerWidth;
michael@0 1226 var height = window.innerHeight;
michael@0 1227 var scale = window.devicePixelRatio;
michael@0 1228 canvas.setAttribute('width', width * scale);
michael@0 1229 canvas.setAttribute('height', height * scale);
michael@0 1230
michael@0 1231 var context = canvas.getContext('2d');
michael@0 1232 var flags =
michael@0 1233 context.DRAWWINDOW_DRAW_CARET |
michael@0 1234 context.DRAWWINDOW_DRAW_VIEW |
michael@0 1235 context.DRAWWINDOW_USE_WIDGET_LAYERS;
michael@0 1236 context.scale(scale, scale);
michael@0 1237 context.drawWindow(window, 0, 0, width, height,
michael@0 1238 'rgb(255,255,255)', flags);
michael@0 1239
michael@0 1240 // I can't use sendChromeEvent() here because it doesn't wrap
michael@0 1241 // the blob in the detail object correctly. So I use __exposedProps__
michael@0 1242 // instead to safely send the chrome detail object to content.
michael@0 1243 shell.sendEvent(getContentWindow(), 'mozChromeEvent', {
michael@0 1244 __exposedProps__: { type: 'r', file: 'r' },
michael@0 1245 type: 'take-screenshot-success',
michael@0 1246 file: canvas.mozGetAsFile('screenshot', 'image/png')
michael@0 1247 });
michael@0 1248 } catch (e) {
michael@0 1249 dump('exception while creating screenshot: ' + e + '\n');
michael@0 1250 shell.sendChromeEvent({
michael@0 1251 type: 'take-screenshot-error',
michael@0 1252 error: String(e)
michael@0 1253 });
michael@0 1254 }
michael@0 1255 });
michael@0 1256 });
michael@0 1257
michael@0 1258 (function contentCrashTracker() {
michael@0 1259 Services.obs.addObserver(function(aSubject, aTopic, aData) {
michael@0 1260 let props = aSubject.QueryInterface(Ci.nsIPropertyBag2);
michael@0 1261 if (props.hasKey("abnormal") && props.hasKey("dumpID")) {
michael@0 1262 shell.reportCrash(false, props.getProperty("dumpID"));
michael@0 1263 }
michael@0 1264 },
michael@0 1265 "ipc:content-shutdown", false);
michael@0 1266 })();
michael@0 1267
michael@0 1268 var CaptivePortalLoginHelper = {
michael@0 1269 init: function init() {
michael@0 1270 Services.obs.addObserver(this, 'captive-portal-login', false);
michael@0 1271 Services.obs.addObserver(this, 'captive-portal-login-abort', false);
michael@0 1272 },
michael@0 1273 handleEvent: function handleEvent(detail) {
michael@0 1274 Services.captivePortalDetector.cancelLogin(detail.id);
michael@0 1275 },
michael@0 1276 observe: function observe(subject, topic, data) {
michael@0 1277 shell.sendChromeEvent(JSON.parse(data));
michael@0 1278 }
michael@0 1279 }
michael@0 1280
michael@0 1281 // Listen for crashes submitted through the crash reporter UI.
michael@0 1282 window.addEventListener('ContentStart', function cr_onContentStart() {
michael@0 1283 let content = shell.contentBrowser.contentWindow;
michael@0 1284 content.addEventListener("mozContentEvent", function cr_onMozContentEvent(e) {
michael@0 1285 if (e.detail.type == "submit-crash" && e.detail.crashID) {
michael@0 1286 debugCrashReport("submitting crash at user request ", e.detail.crashID);
michael@0 1287 shell.submitCrash(e.detail.crashID);
michael@0 1288 } else if (e.detail.type == "delete-crash" && e.detail.crashID) {
michael@0 1289 debugCrashReport("deleting crash at user request ", e.detail.crashID);
michael@0 1290 shell.deleteCrash(e.detail.crashID);
michael@0 1291 }
michael@0 1292 });
michael@0 1293 });
michael@0 1294
michael@0 1295 window.addEventListener('ContentStart', function update_onContentStart() {
michael@0 1296 Cu.import('resource://gre/modules/WebappsUpdater.jsm');
michael@0 1297 WebappsUpdater.handleContentStart(shell);
michael@0 1298
michael@0 1299 let promptCc = Cc["@mozilla.org/updates/update-prompt;1"];
michael@0 1300 if (!promptCc) {
michael@0 1301 return;
michael@0 1302 }
michael@0 1303
michael@0 1304 let updatePrompt = promptCc.createInstance(Ci.nsIUpdatePrompt);
michael@0 1305 if (!updatePrompt) {
michael@0 1306 return;
michael@0 1307 }
michael@0 1308
michael@0 1309 updatePrompt.wrappedJSObject.handleContentStart(shell);
michael@0 1310 });
michael@0 1311
michael@0 1312 (function geolocationStatusTracker() {
michael@0 1313 let gGeolocationActive = false;
michael@0 1314
michael@0 1315 Services.obs.addObserver(function(aSubject, aTopic, aData) {
michael@0 1316 let oldState = gGeolocationActive;
michael@0 1317 if (aData == "starting") {
michael@0 1318 gGeolocationActive = true;
michael@0 1319 } else if (aData == "shutdown") {
michael@0 1320 gGeolocationActive = false;
michael@0 1321 }
michael@0 1322
michael@0 1323 if (gGeolocationActive != oldState) {
michael@0 1324 shell.sendChromeEvent({
michael@0 1325 type: 'geolocation-status',
michael@0 1326 active: gGeolocationActive
michael@0 1327 });
michael@0 1328 }
michael@0 1329 }, "geolocation-device-events", false);
michael@0 1330 })();
michael@0 1331
michael@0 1332 (function headphonesStatusTracker() {
michael@0 1333 Services.obs.addObserver(function(aSubject, aTopic, aData) {
michael@0 1334 shell.sendChromeEvent({
michael@0 1335 type: 'headphones-status-changed',
michael@0 1336 state: aData
michael@0 1337 });
michael@0 1338 }, "headphones-status-changed", false);
michael@0 1339 })();
michael@0 1340
michael@0 1341 (function audioChannelChangedTracker() {
michael@0 1342 Services.obs.addObserver(function(aSubject, aTopic, aData) {
michael@0 1343 shell.sendChromeEvent({
michael@0 1344 type: 'audio-channel-changed',
michael@0 1345 channel: aData
michael@0 1346 });
michael@0 1347 }, "audio-channel-changed", false);
michael@0 1348 })();
michael@0 1349
michael@0 1350 (function defaultVolumeChannelChangedTracker() {
michael@0 1351 Services.obs.addObserver(function(aSubject, aTopic, aData) {
michael@0 1352 shell.sendChromeEvent({
michael@0 1353 type: 'default-volume-channel-changed',
michael@0 1354 channel: aData
michael@0 1355 });
michael@0 1356 }, "default-volume-channel-changed", false);
michael@0 1357 })();
michael@0 1358
michael@0 1359 (function visibleAudioChannelChangedTracker() {
michael@0 1360 Services.obs.addObserver(function(aSubject, aTopic, aData) {
michael@0 1361 shell.sendChromeEvent({
michael@0 1362 type: 'visible-audio-channel-changed',
michael@0 1363 channel: aData
michael@0 1364 });
michael@0 1365 shell.visibleNormalAudioActive = (aData == 'normal');
michael@0 1366 }, "visible-audio-channel-changed", false);
michael@0 1367 })();
michael@0 1368
michael@0 1369 (function recordingStatusTracker() {
michael@0 1370 // Recording status is tracked per process with following data structure:
michael@0 1371 // {<processId>: {<requestURL>: {isApp: <isApp>,
michael@0 1372 // count: <N>,
michael@0 1373 // audioCount: <N>,
michael@0 1374 // videoCount: <N>}}
michael@0 1375 let gRecordingActiveProcesses = {};
michael@0 1376
michael@0 1377 let recordingHandler = function(aSubject, aTopic, aData) {
michael@0 1378 let props = aSubject.QueryInterface(Ci.nsIPropertyBag2);
michael@0 1379 let processId = (props.hasKey('childID')) ? props.get('childID')
michael@0 1380 : 'main';
michael@0 1381 if (processId && !gRecordingActiveProcesses.hasOwnProperty(processId)) {
michael@0 1382 gRecordingActiveProcesses[processId] = {};
michael@0 1383 }
michael@0 1384
michael@0 1385 let commandHandler = function (requestURL, command) {
michael@0 1386 let currentProcess = gRecordingActiveProcesses[processId];
michael@0 1387 let currentActive = currentProcess[requestURL];
michael@0 1388 let wasActive = (currentActive['count'] > 0);
michael@0 1389 let wasAudioActive = (currentActive['audioCount'] > 0);
michael@0 1390 let wasVideoActive = (currentActive['videoCount'] > 0);
michael@0 1391
michael@0 1392 switch (command.type) {
michael@0 1393 case 'starting':
michael@0 1394 currentActive['count']++;
michael@0 1395 currentActive['audioCount'] += (command.isAudio) ? 1 : 0;
michael@0 1396 currentActive['videoCount'] += (command.isVideo) ? 1 : 0;
michael@0 1397 break;
michael@0 1398 case 'shutdown':
michael@0 1399 currentActive['count']--;
michael@0 1400 currentActive['audioCount'] -= (command.isAudio) ? 1 : 0;
michael@0 1401 currentActive['videoCount'] -= (command.isVideo) ? 1 : 0;
michael@0 1402 break;
michael@0 1403 case 'content-shutdown':
michael@0 1404 currentActive['count'] = 0;
michael@0 1405 currentActive['audioCount'] = 0;
michael@0 1406 currentActive['videoCount'] = 0;
michael@0 1407 break;
michael@0 1408 }
michael@0 1409
michael@0 1410 if (currentActive['count'] > 0) {
michael@0 1411 currentProcess[requestURL] = currentActive;
michael@0 1412 } else {
michael@0 1413 delete currentProcess[requestURL];
michael@0 1414 }
michael@0 1415
michael@0 1416 // We need to track changes if any active state is changed.
michael@0 1417 let isActive = (currentActive['count'] > 0);
michael@0 1418 let isAudioActive = (currentActive['audioCount'] > 0);
michael@0 1419 let isVideoActive = (currentActive['videoCount'] > 0);
michael@0 1420 if ((isActive != wasActive) ||
michael@0 1421 (isAudioActive != wasAudioActive) ||
michael@0 1422 (isVideoActive != wasVideoActive)) {
michael@0 1423 shell.sendChromeEvent({
michael@0 1424 type: 'recording-status',
michael@0 1425 active: isActive,
michael@0 1426 requestURL: requestURL,
michael@0 1427 isApp: currentActive['isApp'],
michael@0 1428 isAudio: isAudioActive,
michael@0 1429 isVideo: isVideoActive
michael@0 1430 });
michael@0 1431 }
michael@0 1432 };
michael@0 1433
michael@0 1434 switch (aData) {
michael@0 1435 case 'starting':
michael@0 1436 case 'shutdown':
michael@0 1437 // create page record if it is not existed yet.
michael@0 1438 let requestURL = props.get('requestURL');
michael@0 1439 if (requestURL &&
michael@0 1440 !gRecordingActiveProcesses[processId].hasOwnProperty(requestURL)) {
michael@0 1441 gRecordingActiveProcesses[processId][requestURL] = {isApp: props.get('isApp'),
michael@0 1442 count: 0,
michael@0 1443 audioCount: 0,
michael@0 1444 videoCount: 0};
michael@0 1445 }
michael@0 1446 commandHandler(requestURL, { type: aData,
michael@0 1447 isAudio: props.get('isAudio'),
michael@0 1448 isVideo: props.get('isVideo')});
michael@0 1449 break;
michael@0 1450 case 'content-shutdown':
michael@0 1451 // iterate through all the existing active processes
michael@0 1452 Object.keys(gRecordingActiveProcesses[processId]).forEach(function(requestURL) {
michael@0 1453 commandHandler(requestURL, { type: aData,
michael@0 1454 isAudio: true,
michael@0 1455 isVideo: true});
michael@0 1456 });
michael@0 1457 break;
michael@0 1458 }
michael@0 1459
michael@0 1460 // clean up process record if no page record in it.
michael@0 1461 if (Object.keys(gRecordingActiveProcesses[processId]).length == 0) {
michael@0 1462 delete gRecordingActiveProcesses[processId];
michael@0 1463 }
michael@0 1464 };
michael@0 1465 Services.obs.addObserver(recordingHandler, 'recording-device-events', false);
michael@0 1466 Services.obs.addObserver(recordingHandler, 'recording-device-ipc-events', false);
michael@0 1467
michael@0 1468 Services.obs.addObserver(function(aSubject, aTopic, aData) {
michael@0 1469 // send additional recording events if content process is being killed
michael@0 1470 let processId = aSubject.QueryInterface(Ci.nsIPropertyBag2).get('childID');
michael@0 1471 if (gRecordingActiveProcesses.hasOwnProperty(processId)) {
michael@0 1472 Services.obs.notifyObservers(aSubject, 'recording-device-ipc-events', 'content-shutdown');
michael@0 1473 }
michael@0 1474 }, 'ipc:content-shutdown', false);
michael@0 1475 })();
michael@0 1476
michael@0 1477 (function volumeStateTracker() {
michael@0 1478 Services.obs.addObserver(function(aSubject, aTopic, aData) {
michael@0 1479 shell.sendChromeEvent({
michael@0 1480 type: 'volume-state-changed',
michael@0 1481 active: (aData == 'Shared')
michael@0 1482 });
michael@0 1483 }, 'volume-state-changed', false);
michael@0 1484 })();
michael@0 1485
michael@0 1486 #ifdef MOZ_WIDGET_GONK
michael@0 1487 // Devices don't have all the same partition size for /cache where we
michael@0 1488 // store the http cache.
michael@0 1489 (function setHTTPCacheSize() {
michael@0 1490 let path = Services.prefs.getCharPref("browser.cache.disk.parent_directory");
michael@0 1491 let volumeService = Cc["@mozilla.org/telephony/volume-service;1"]
michael@0 1492 .getService(Ci.nsIVolumeService);
michael@0 1493
michael@0 1494 let stats = volumeService.createOrGetVolumeByPath(path).getStats();
michael@0 1495
michael@0 1496 // We must set the size in KB, and keep a bit of free space.
michael@0 1497 let size = Math.floor(stats.totalBytes / 1024) - 1024;
michael@0 1498 Services.prefs.setIntPref("browser.cache.disk.capacity", size);
michael@0 1499 }) ()
michael@0 1500 #endif
michael@0 1501
michael@0 1502 #ifdef MOZ_WIDGET_GONK
michael@0 1503 let SensorsListener = {
michael@0 1504 sensorsListenerDevices: ['crespo'],
michael@0 1505 device: libcutils.property_get("ro.product.device"),
michael@0 1506
michael@0 1507 deviceNeedsWorkaround: function SensorsListener_deviceNeedsWorkaround() {
michael@0 1508 return (this.sensorsListenerDevices.indexOf(this.device) != -1);
michael@0 1509 },
michael@0 1510
michael@0 1511 handleEvent: function SensorsListener_handleEvent(evt) {
michael@0 1512 switch(evt.type) {
michael@0 1513 case 'devicemotion':
michael@0 1514 // Listener that does nothing, we need this to have the sensor being
michael@0 1515 // able to report correct values, as explained in bug 753245, comment 6
michael@0 1516 // and in bug 871916
michael@0 1517 break;
michael@0 1518
michael@0 1519 default:
michael@0 1520 break;
michael@0 1521 }
michael@0 1522 },
michael@0 1523
michael@0 1524 observe: function SensorsListener_observe(subject, topic, data) {
michael@0 1525 // We remove the listener when the screen is off, otherwise sensor will
michael@0 1526 // continue to bother us with data and we won't be able to get the
michael@0 1527 // system into suspend state, thus draining battery.
michael@0 1528 if (data === 'on') {
michael@0 1529 window.addEventListener('devicemotion', this);
michael@0 1530 } else {
michael@0 1531 window.removeEventListener('devicemotion', this);
michael@0 1532 }
michael@0 1533 },
michael@0 1534
michael@0 1535 init: function SensorsListener_init() {
michael@0 1536 if (this.deviceNeedsWorkaround()) {
michael@0 1537 // On boot, enable the listener, screen will be on.
michael@0 1538 window.addEventListener('devicemotion', this);
michael@0 1539
michael@0 1540 // Then listen for further screen state changes
michael@0 1541 Services.obs.addObserver(this, 'screen-state-changed', false);
michael@0 1542 }
michael@0 1543 }
michael@0 1544 }
michael@0 1545
michael@0 1546 SensorsListener.init();
michael@0 1547 #endif
michael@0 1548
michael@0 1549 // Calling this observer will cause a shutdown an a profile reset.
michael@0 1550 // Use eg. : Services.obs.notifyObservers(null, 'b2g-reset-profile', null);
michael@0 1551 Services.obs.addObserver(function resetProfile(subject, topic, data) {
michael@0 1552 Services.obs.removeObserver(resetProfile, topic);
michael@0 1553
michael@0 1554 // Listening for 'profile-before-change2' which is late in the shutdown
michael@0 1555 // sequence, but still has xpcom access.
michael@0 1556 Services.obs.addObserver(function clearProfile(subject, topic, data) {
michael@0 1557 Services.obs.removeObserver(clearProfile, topic);
michael@0 1558 #ifdef MOZ_WIDGET_GONK
michael@0 1559 let json = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
michael@0 1560 json.initWithPath('/system/b2g/webapps/webapps.json');
michael@0 1561 let toRemove = json.exists()
michael@0 1562 // This is a user build, just rm -r /data/local /data/b2g/mozilla
michael@0 1563 ? ['/data/local', '/data/b2g/mozilla']
michael@0 1564 // This is an eng build. We clear the profile and a set of files
michael@0 1565 // under /data/local.
michael@0 1566 : ['/data/b2g/mozilla',
michael@0 1567 '/data/local/permissions.sqlite',
michael@0 1568 '/data/local/storage',
michael@0 1569 '/data/local/OfflineCache'];
michael@0 1570
michael@0 1571 toRemove.forEach(function(dir) {
michael@0 1572 try {
michael@0 1573 let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
michael@0 1574 file.initWithPath(dir);
michael@0 1575 file.remove(true);
michael@0 1576 } catch(e) { dump(e); }
michael@0 1577 });
michael@0 1578 #else
michael@0 1579 // Desktop builds.
michael@0 1580 let profile = Services.dirsvc.get('ProfD', Ci.nsIFile);
michael@0 1581
michael@0 1582 // We don't want to remove everything from the profile, since this
michael@0 1583 // would prevent us from starting up.
michael@0 1584 let whitelist = ['defaults', 'extensions', 'settings.json',
michael@0 1585 'user.js', 'webapps'];
michael@0 1586 let enumerator = profile.directoryEntries;
michael@0 1587 while (enumerator.hasMoreElements()) {
michael@0 1588 let file = enumerator.getNext().QueryInterface(Ci.nsIFile);
michael@0 1589 if (whitelist.indexOf(file.leafName) == -1) {
michael@0 1590 file.remove(true);
michael@0 1591 }
michael@0 1592 }
michael@0 1593 #endif
michael@0 1594 },
michael@0 1595 'profile-before-change2', false);
michael@0 1596
michael@0 1597 let appStartup = Cc['@mozilla.org/toolkit/app-startup;1']
michael@0 1598 .getService(Ci.nsIAppStartup);
michael@0 1599 appStartup.quit(Ci.nsIAppStartup.eForceQuit);
michael@0 1600 }, 'b2g-reset-profile', false);
michael@0 1601
michael@0 1602 /**
michael@0 1603 * CID of our implementation of nsIDownloadManagerUI.
michael@0 1604 */
michael@0 1605 const kTransferCid = Components.ID("{1b4c85df-cbdd-4bb6-b04e-613caece083c}");
michael@0 1606
michael@0 1607 /**
michael@0 1608 * Contract ID of the service implementing nsITransfer.
michael@0 1609 */
michael@0 1610 const kTransferContractId = "@mozilla.org/transfer;1";
michael@0 1611
michael@0 1612 // Override Toolkit's nsITransfer implementation with the one from the
michael@0 1613 // JavaScript API for downloads. This will eventually be removed when
michael@0 1614 // nsIDownloadManager will not be available anymore (bug 851471). The
michael@0 1615 // old code in this module will be removed in bug 899110.
michael@0 1616 Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
michael@0 1617 .registerFactory(kTransferCid, "",
michael@0 1618 kTransferContractId, null);

mercurial