summaryrefslogtreecommitdiffstats
path: root/b2g/chrome/content/shell.js
diff options
context:
space:
mode:
Diffstat (limited to 'b2g/chrome/content/shell.js')
-rw-r--r--b2g/chrome/content/shell.js1308
1 files changed, 0 insertions, 1308 deletions
diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js
deleted file mode 100644
index d483f9a64..000000000
--- a/b2g/chrome/content/shell.js
+++ /dev/null
@@ -1,1308 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
-/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-window.performance.mark('gecko-shell-loadstart');
-
-Cu.import('resource://gre/modules/NotificationDB.jsm');
-Cu.import("resource://gre/modules/AppsUtils.jsm");
-Cu.import('resource://gre/modules/UserAgentOverrides.jsm');
-Cu.import('resource://gre/modules/Keyboard.jsm');
-Cu.import('resource://gre/modules/ErrorPage.jsm');
-Cu.import('resource://gre/modules/AlertsHelper.jsm');
-Cu.import('resource://gre/modules/SystemUpdateService.jsm');
-
-if (isGonk) {
- Cu.import('resource://gre/modules/NetworkStatsService.jsm');
- Cu.import('resource://gre/modules/ResourceStatsService.jsm');
-}
-
-// Identity
-Cu.import('resource://gre/modules/SignInToWebsite.jsm');
-SignInToWebsiteController.init();
-
-Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
-Cu.import('resource://gre/modules/DownloadsAPI.jsm');
-Cu.import('resource://gre/modules/PresentationDeviceInfoManager.jsm');
-Cu.import('resource://gre/modules/AboutServiceWorkers.jsm');
-
-XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
- "resource://gre/modules/SystemAppProxy.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "Screenshot",
- "resource://gre/modules/Screenshot.jsm");
-
-XPCOMUtils.defineLazyServiceGetter(Services, 'env',
- '@mozilla.org/process/environment;1',
- 'nsIEnvironment');
-
-XPCOMUtils.defineLazyServiceGetter(Services, 'ss',
- '@mozilla.org/content/style-sheet-service;1',
- 'nsIStyleSheetService');
-
-XPCOMUtils.defineLazyServiceGetter(this, 'gSystemMessenger',
- '@mozilla.org/system-message-internal;1',
- 'nsISystemMessagesInternal');
-
-XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
- return Cc["@mozilla.org/parentprocessmessagemanager;1"]
- .getService(Ci.nsIMessageListenerManager);
-});
-
-if (isGonk) {
- XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
- Cu.import("resource://gre/modules/systemlibs.js");
- return libcutils;
- });
-}
-
-XPCOMUtils.defineLazyServiceGetter(Services, 'captivePortalDetector',
- '@mozilla.org/toolkit/captive-detector;1',
- 'nsICaptivePortalDetector');
-
-XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
- "resource://gre/modules/SafeBrowsing.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "SafeMode",
- "resource://gre/modules/SafeMode.jsm");
-
-window.performance.measure('gecko-shell-jsm-loaded', 'gecko-shell-loadstart');
-
-function debug(str) {
- dump(' -*- Shell.js: ' + str + '\n');
-}
-
-const once = event => {
- let target = shell.contentBrowser;
- return new Promise((resolve, reject) => {
- target.addEventListener(event, function gotEvent(evt) {
- target.removeEventListener(event, gotEvent, false);
- resolve(evt);
- }, false);
- });
-}
-
-function clearCache() {
- let cache = Cc["@mozilla.org/netwerk/cache-storage-service;1"]
- .getService(Ci.nsICacheStorageService);
- cache.clear();
-}
-
-function clearCacheAndReload() {
- // Reload the main frame with a cleared cache.
- debug('Reloading ' + shell.contentBrowser.contentWindow.location);
- clearCache();
- shell.contentBrowser.contentWindow.location.reload(true);
- once('mozbrowserlocationchange').then(
- evt => {
- shell.sendEvent(window, "ContentStart");
- });
-}
-
-function restart() {
- let appStartup = Cc['@mozilla.org/toolkit/app-startup;1']
- .getService(Ci.nsIAppStartup);
- appStartup.quit(Ci.nsIAppStartup.eForceQuit | Ci.nsIAppStartup.eRestart);
-}
-
-function debugCrashReport(aStr) {
- AppConstants.MOZ_CRASHREPORTER && dump('Crash reporter : ' + aStr);
-}
-
-var shell = {
-
- get CrashSubmit() {
- delete this.CrashSubmit;
- if (AppConstants.MOZ_CRASHREPORTER) {
- Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
- return this.CrashSubmit;
- } else {
- dump('Crash reporter : disabled at build time.');
- return this.CrashSubmit = null;
- }
- },
-
- onlineForCrashReport: function shell_onlineForCrashReport() {
- let wifiManager = navigator.mozWifiManager;
- let onWifi = (wifiManager &&
- (wifiManager.connection.status == 'connected'));
- return !Services.io.offline && onWifi;
- },
-
- reportCrash: function shell_reportCrash(isChrome, aCrashID) {
- let crashID = aCrashID;
- try {
- // For chrome crashes, we want to report the lastRunCrashID.
- if (isChrome) {
- crashID = Cc["@mozilla.org/xre/app-info;1"]
- .getService(Ci.nsIXULRuntime).lastRunCrashID;
- }
- } catch(e) {
- debugCrashReport('Failed to fetch crash id. Crash ID is "' + crashID
- + '" Exception: ' + e);
- }
-
- // Bail if there isn't a valid crashID.
- if (!this.CrashSubmit || !crashID && !this.CrashSubmit.pendingIDs().length) {
- return;
- }
-
- // purge the queue.
- this.CrashSubmit.pruneSavedDumps();
-
- // check for environment affecting crash reporting
- let env = Cc["@mozilla.org/process/environment;1"]
- .getService(Ci.nsIEnvironment);
- let shutdown = env.get("MOZ_CRASHREPORTER_SHUTDOWN");
- if (shutdown) {
- let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]
- .getService(Ci.nsIAppStartup);
- appStartup.quit(Ci.nsIAppStartup.eForceQuit);
- }
-
- let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT");
- if (noReport) {
- return;
- }
-
- try {
- // Check if we should automatically submit this crash.
- if (Services.prefs.getBoolPref('app.reportCrashes')) {
- this.submitCrash(crashID);
- } else {
- this.deleteCrash(crashID);
- }
- } catch (e) {
- debugCrashReport('Can\'t fetch app.reportCrashes. Exception: ' + e);
- }
-
- // We can get here if we're just submitting old pending crashes.
- // Check that there's a valid crashID so that we only notify the
- // user if a crash just happened and not when we OOM. Bug 829477
- if (crashID) {
- this.sendChromeEvent({
- type: "handle-crash",
- crashID: crashID,
- chrome: isChrome
- });
- }
- },
-
- deleteCrash: function shell_deleteCrash(aCrashID) {
- if (aCrashID) {
- debugCrashReport('Deleting pending crash: ' + aCrashID);
- shell.CrashSubmit.delete(aCrashID);
- }
- },
-
- // this function submit the pending crashes.
- // make sure you are online.
- submitQueuedCrashes: function shell_submitQueuedCrashes() {
- // submit the pending queue.
- let pending = shell.CrashSubmit.pendingIDs();
- for (let crashid of pending) {
- debugCrashReport('Submitting crash: ' + crashid);
- shell.CrashSubmit.submit(crashid);
- }
- },
-
- // This function submits a crash when we're online.
- submitCrash: function shell_submitCrash(aCrashID) {
- if (this.onlineForCrashReport()) {
- this.submitQueuedCrashes();
- return;
- }
-
- debugCrashReport('Not online, postponing.');
-
- Services.obs.addObserver(function observer(subject, topic, state) {
- let network = subject.QueryInterface(Ci.nsINetworkInfo);
- if (network.state == Ci.nsINetworkInfo.NETWORK_STATE_CONNECTED
- && network.type == Ci.nsINetworkInfo.NETWORK_TYPE_WIFI) {
- shell.submitQueuedCrashes();
-
- Services.obs.removeObserver(observer, topic);
- }
- }, "network-connection-state-changed", false);
- },
-
- get homeURL() {
- try {
- let homeSrc = Services.env.get('B2G_HOMESCREEN');
- if (homeSrc)
- return homeSrc;
- } catch (e) {}
-
- return Services.prefs.getCharPref('b2g.system_startup_url');
- },
-
- get manifestURL() {
- return Services.prefs.getCharPref('b2g.system_manifest_url');
- },
-
- _started: false,
- hasStarted: function shell_hasStarted() {
- return this._started;
- },
-
- bootstrap: function() {
- window.performance.mark('gecko-shell-bootstrap');
-
- // Before anything, check if we want to start in safe mode.
- SafeMode.check(window).then(() => {
- let startManifestURL =
- Cc['@mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap']
- .getService(Ci.nsISupports).wrappedJSObject.startManifestURL;
-
- // If --start-manifest hasn't been specified, we re-use the latest specified manifest.
- // If it's the first launch, we will fallback to b2g.default.start_manifest_url
- if (AppConstants.MOZ_GRAPHENE && !startManifestURL) {
- try {
- startManifestURL = Services.prefs.getCharPref("b2g.system_manifest_url");
- } catch(e) {}
- }
-
- if (!startManifestURL) {
- try {
- startManifestURL = Services.prefs.getCharPref("b2g.default.start_manifest_url");
- } catch(e) {}
- }
-
- if (startManifestURL) {
- Cu.import('resource://gre/modules/Bootstraper.jsm');
-
- if (AppConstants.MOZ_GRAPHENE && Bootstraper.isInstallRequired(startManifestURL)) {
- // Installing the app my take some time. We don't want to keep the
- // native window hidden.
- showInstallScreen();
- }
-
- Bootstraper.ensureSystemAppInstall(startManifestURL)
- .then(this.start.bind(this))
- .catch(Bootstraper.bailout);
- } else {
- this.start();
- }
- });
- },
-
- start: function shell_start() {
- window.performance.mark('gecko-shell-start');
- this._started = true;
-
- // This forces the initialization of the cookie service before we hit the
- // network.
- // See bug 810209
- let cookies = Cc["@mozilla.org/cookieService;1"];
-
- try {
- let cr = Cc["@mozilla.org/xre/app-info;1"]
- .getService(Ci.nsICrashReporter);
- // Dogfood id. We might want to remove it in the future.
- // see bug 789466
- try {
- let dogfoodId = Services.prefs.getCharPref('prerelease.dogfood.id');
- if (dogfoodId != "") {
- cr.annotateCrashReport("Email", dogfoodId);
- }
- }
- catch (e) { }
-
- if (isGonk) {
- // Annotate crash report
- let annotations = [ [ "Android_Hardware", "ro.hardware" ],
- [ "Android_Device", "ro.product.device" ],
- [ "Android_CPU_ABI2", "ro.product.cpu.abi2" ],
- [ "Android_CPU_ABI", "ro.product.cpu.abi" ],
- [ "Android_Manufacturer", "ro.product.manufacturer" ],
- [ "Android_Brand", "ro.product.brand" ],
- [ "Android_Model", "ro.product.model" ],
- [ "Android_Board", "ro.product.board" ],
- ];
-
- annotations.forEach(function (element) {
- cr.annotateCrashReport(element[0], libcutils.property_get(element[1]));
- });
-
- let androidVersion = libcutils.property_get("ro.build.version.sdk") +
- "(" + libcutils.property_get("ro.build.version.codename") + ")";
- cr.annotateCrashReport("Android_Version", androidVersion);
-
- SettingsListener.observe("deviceinfo.os", "", function(value) {
- try {
- let cr = Cc["@mozilla.org/xre/app-info;1"]
- .getService(Ci.nsICrashReporter);
- cr.annotateCrashReport("B2G_OS_Version", value);
- } catch(e) { }
- });
- }
- } catch(e) {
- debugCrashReport('exception: ' + e);
- }
-
- let homeURL = this.homeURL;
- if (!homeURL) {
- let msg = 'Fatal error during startup: No homescreen found: try setting B2G_HOMESCREEN';
- alert(msg);
- return;
- }
-
- let manifestURL = this.manifestURL;
- // <html:iframe id="systemapp"
- // mozbrowser="true" allowfullscreen="true"
- // style="overflow: hidden; height: 100%; width: 100%; border: none;"
- // src="data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;'>"/>
- let systemAppFrame =
- document.createElementNS('http://www.w3.org/1999/xhtml', 'html:iframe');
- systemAppFrame.setAttribute('id', 'systemapp');
- systemAppFrame.setAttribute('mozbrowser', 'true');
- systemAppFrame.setAttribute('mozapp', manifestURL);
- systemAppFrame.setAttribute('allowfullscreen', 'true');
- systemAppFrame.setAttribute('src', 'blank.html');
- let container = document.getElementById('container');
-
- if (AppConstants.platform == 'macosx') {
- // See shell.html
- let hotfix = document.getElementById('placeholder');
- if (hotfix) {
- container.removeChild(hotfix);
- }
- }
-
- this.contentBrowser = container.appendChild(systemAppFrame);
-
- let webNav = systemAppFrame.contentWindow
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation);
- webNav.sessionHistory = Cc["@mozilla.org/browser/shistory;1"].createInstance(Ci.nsISHistory);
-
- if (AppConstants.MOZ_GRAPHENE) {
- webNav.QueryInterface(Ci.nsIDocShell).windowDraggingAllowed = true;
- }
-
- let audioChannels = systemAppFrame.allowedAudioChannels;
- audioChannels && audioChannels.forEach(function(audioChannel) {
- // Set all audio channels as unmuted by default
- // because some audio in System app will be played
- // before AudioChannelService[1] is Gaia is loaded.
- // [1]: https://github.com/mozilla-b2g/gaia/blob/master/apps/system/js/audio_channel_service.js
- audioChannel.setMuted(false);
- });
-
- // On firefox mulet, shell.html is loaded in a tab
- // and we have to listen on the chrome event handler
- // to catch key events
- let chromeEventHandler = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShell)
- .chromeEventHandler || window;
- // Capture all key events so we can filter out hardware buttons
- // And send them to Gaia via mozChromeEvents.
- // Ideally, hardware buttons wouldn't generate key events at all, or
- // if they did, they would use keycodes that conform to DOM 3 Events.
- // See discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=762362
- chromeEventHandler.addEventListener('keydown', this, true);
- chromeEventHandler.addEventListener('keyup', this, true);
-
- window.addEventListener('MozApplicationManifest', this);
- window.addEventListener('MozAfterPaint', this);
- window.addEventListener('sizemodechange', this);
- window.addEventListener('unload', this);
- this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
- this.contentBrowser.addEventListener('mozbrowserscrollviewchange', this, true);
- this.contentBrowser.addEventListener('mozbrowsercaretstatechanged', this);
-
- CustomEventManager.init();
- UserAgentOverrides.init();
- CaptivePortalLoginHelper.init();
-
- this.contentBrowser.src = homeURL;
-
- this._isEventListenerReady = false;
-
- window.performance.mark('gecko-shell-system-frame-set');
-
- ppmm.addMessageListener("content-handler", this);
- ppmm.addMessageListener("dial-handler", this);
- ppmm.addMessageListener("sms-handler", this);
- ppmm.addMessageListener("mail-handler", this);
- ppmm.addMessageListener("file-picker", this);
-
- setTimeout(function() {
- SafeBrowsing.init();
- }, 5000);
- },
-
- stop: function shell_stop() {
- window.removeEventListener('unload', this);
- window.removeEventListener('keydown', this, true);
- window.removeEventListener('keyup', this, true);
- window.removeEventListener('MozApplicationManifest', this);
- window.removeEventListener('sizemodechange', this);
- this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
- this.contentBrowser.removeEventListener('mozbrowserscrollviewchange', this, true);
- this.contentBrowser.removeEventListener('mozbrowsercaretstatechanged', this);
- ppmm.removeMessageListener("content-handler", this);
-
- UserAgentOverrides.uninit();
- },
-
- // If this key event represents a hardware button which needs to be send as
- // a message, broadcasts it with the message set to 'xxx-button-press' or
- // 'xxx-button-release'.
- broadcastHardwareKeys: function shell_broadcastHardwareKeys(evt) {
- let type;
- let message;
-
- let mediaKeys = {
- 'MediaTrackNext': 'media-next-track-button',
- 'MediaTrackPrevious': 'media-previous-track-button',
- 'MediaPause': 'media-pause-button',
- 'MediaPlay': 'media-play-button',
- 'MediaPlayPause': 'media-play-pause-button',
- 'MediaStop': 'media-stop-button',
- 'MediaRewind': 'media-rewind-button',
- 'MediaFastForward': 'media-fast-forward-button'
- };
-
- if (evt.keyCode == evt.DOM_VK_F1) {
- type = 'headset-button';
- message = 'headset-button';
- } else if (mediaKeys[evt.key]) {
- type = 'media-button';
- message = mediaKeys[evt.key];
- } else {
- return;
- }
-
- switch (evt.type) {
- case 'keydown':
- message = message + '-press';
- break;
- case 'keyup':
- message = message + '-release';
- break;
- }
-
- // Let applications receive the headset button and media key press/release message.
- if (message !== this.lastHardwareButtonMessage) {
- this.lastHardwareButtonMessage = message;
- gSystemMessenger.broadcastMessage(type, message);
- }
- },
-
- lastHardwareButtonMessage: null, // property for the hack above
- visibleNormalAudioActive: false,
-
- handleEvent: function shell_handleEvent(evt) {
- function checkReloadKey() {
- if (evt.type !== 'keyup') {
- return false;
- }
-
- try {
- let key = JSON.parse(Services.prefs.getCharPref('b2g.reload_key'));
- return (evt.keyCode == key.key &&
- evt.ctrlKey == key.ctrl &&
- evt.altKey == key.alt &&
- evt.shiftKey == key.shift &&
- evt.metaKey == key.meta);
- } catch(e) {
- debug('Failed to get key: ' + e);
- }
-
- return false;
- }
-
- let content = this.contentBrowser.contentWindow;
- switch (evt.type) {
- case 'keydown':
- case 'keyup':
- if (checkReloadKey()) {
- clearCacheAndReload();
- } else {
- this.broadcastHardwareKeys(evt);
- }
- break;
- case 'sizemodechange':
- if (window.windowState == window.STATE_MINIMIZED && !this.visibleNormalAudioActive) {
- this.contentBrowser.setVisible(false);
- } else {
- this.contentBrowser.setVisible(true);
- }
- break;
- case 'load':
- if (content.document.location == 'about:blank') {
- return;
- }
- content.removeEventListener('load', this, true);
- this.notifyContentWindowLoaded();
- break;
- case 'mozbrowserloadstart':
- if (content.document.location == 'about:blank') {
- this.contentBrowser.addEventListener('mozbrowserlocationchange', this, true);
- return;
- }
-
- this.notifyContentStart();
- break;
- case 'mozbrowserlocationchange':
- if (content.document.location == 'about:blank') {
- return;
- }
-
- this.notifyContentStart();
- break;
- case 'mozbrowserscrollviewchange':
- this.sendChromeEvent({
- type: 'scrollviewchange',
- detail: evt.detail,
- });
- break;
- case 'mozbrowsercaretstatechanged':
- {
- let elt = evt.target;
- let win = elt.ownerDocument.defaultView;
- let offsetX = win.mozInnerScreenX - window.mozInnerScreenX;
- let offsetY = win.mozInnerScreenY - window.mozInnerScreenY;
-
- let rect = elt.getBoundingClientRect();
- offsetX += rect.left;
- offsetY += rect.top;
-
- let data = evt.detail;
- data.offsetX = offsetX;
- data.offsetY = offsetY;
- data.sendDoCommandMsg = null;
-
- shell.sendChromeEvent({
- type: 'caretstatechanged',
- detail: data,
- });
- }
- break;
-
- case 'MozApplicationManifest':
- try {
- if (!Services.prefs.getBoolPref('browser.cache.offline.enable'))
- return;
-
- let contentWindow = evt.originalTarget.defaultView;
- let documentElement = contentWindow.document.documentElement;
- if (!documentElement)
- return;
-
- let manifest = documentElement.getAttribute('manifest');
- if (!manifest)
- return;
-
- let principal = contentWindow.document.nodePrincipal;
- if (Services.perms.testPermissionFromPrincipal(principal, 'offline-app') == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
- if (Services.prefs.getBoolPref('browser.offline-apps.notify')) {
- // FIXME Bug 710729 - Add a UI for offline cache notifications
- return;
- }
- return;
- }
-
- Services.perms.addFromPrincipal(principal, 'offline-app',
- Ci.nsIPermissionManager.ALLOW_ACTION);
-
- let documentURI = Services.io.newURI(contentWindow.document.documentURI,
- null,
- null);
- let manifestURI = Services.io.newURI(manifest, null, documentURI);
- let updateService = Cc['@mozilla.org/offlinecacheupdate-service;1']
- .getService(Ci.nsIOfflineCacheUpdateService);
- updateService.scheduleUpdate(manifestURI, documentURI, principal, window);
- } catch (e) {
- dump('Error while creating offline cache: ' + e + '\n');
- }
- break;
- case 'MozAfterPaint':
- window.removeEventListener('MozAfterPaint', this);
- // This event should be sent before System app returns with
- // system-message-listener-ready mozContentEvent, because it's on
- // the critical launch path of the app.
- SystemAppProxy._sendCustomEvent('mozChromeEvent', {
- type: 'system-first-paint'
- }, /* noPending */ true);
- break;
- case 'unload':
- this.stop();
- break;
- }
- },
-
- // Send an event to a specific window, document or element.
- sendEvent: function shell_sendEvent(target, type, details) {
- if (target === this.contentBrowser) {
- // We must ask SystemAppProxy to send the event in this case so
- // that event would be dispatched from frame.contentWindow instead of
- // on the System app frame.
- SystemAppProxy._sendCustomEvent(type, details);
- return;
- }
-
- let doc = target.document || target.ownerDocument || target;
- let event = doc.createEvent('CustomEvent');
- event.initCustomEvent(type, true, true, details ? details : {});
- target.dispatchEvent(event);
- },
-
- sendCustomEvent: function shell_sendCustomEvent(type, details) {
- SystemAppProxy._sendCustomEvent(type, details);
- },
-
- sendChromeEvent: function shell_sendChromeEvent(details) {
- this.sendCustomEvent("mozChromeEvent", details);
- },
-
- receiveMessage: function shell_receiveMessage(message) {
- var activities = { 'content-handler': { name: 'view', response: null },
- 'dial-handler': { name: 'dial', response: null },
- 'mail-handler': { name: 'new', response: null },
- 'sms-handler': { name: 'new', response: null },
- 'file-picker': { name: 'pick', response: 'file-picked' } };
-
- if (!(message.name in activities))
- return;
-
- let data = message.data;
- let activity = activities[message.name];
-
- let a = new MozActivity({
- name: activity.name,
- data: data
- });
-
- if (activity.response) {
- a.onsuccess = function() {
- let sender = message.target.QueryInterface(Ci.nsIMessageSender);
- sender.sendAsyncMessage(activity.response, { success: true,
- result: a.result });
- }
- a.onerror = function() {
- let sender = message.target.QueryInterface(Ci.nsIMessageSender);
- sender.sendAsyncMessage(activity.response, { success: false });
- }
- }
- },
-
- notifyContentStart: function shell_notifyContentStart() {
- window.performance.mark('gecko-shell-notify-content-start');
- this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
- this.contentBrowser.removeEventListener('mozbrowserlocationchange', this, true);
-
- let content = this.contentBrowser.contentWindow;
- content.addEventListener('load', this, true);
-
- this.reportCrash(true);
-
- SystemAppProxy.registerFrame(shell.contentBrowser);
-
- this.sendEvent(window, 'ContentStart');
-
- Services.obs.notifyObservers(null, 'content-start', null);
-
- if (AppConstants.MOZ_GRAPHENE &&
- Services.prefs.getBoolPref("b2g.nativeWindowGeometry.fullscreen")) {
- window.fullScreen = true;
- }
-
- shell.handleCmdLine();
- },
-
- handleCmdLine: function() {
- // This isn't supported on devices.
- if (!isGonk) {
- let b2gcmds = Cc["@mozilla.org/commandlinehandler/general-startup;1?type=b2gcmds"]
- .getService(Ci.nsISupports);
- let args = b2gcmds.wrappedJSObject.cmdLine;
- try {
- // Returns null if -url is not present.
- let url = args.handleFlagWithParam("url", false);
- if (url) {
- this.sendChromeEvent({type: "mozbrowseropenwindow", url});
- args.preventDefault = true;
- }
- } catch(e) {
- // Throws if -url is present with no params.
- }
- }
- },
-
- // This gets called when window.onload fires on the System app content window,
- // which means things in <html> are parsed and statically referenced <script>s
- // and <script defer>s are loaded and run.
- notifyContentWindowLoaded: function shell_notifyContentWindowLoaded() {
- isGonk && libcutils.property_set('sys.boot_completed', '1');
-
- // This will cause Gonk Widget to remove boot animation from the screen
- // and reveals the page.
- Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
-
- SystemAppProxy.setIsLoaded();
- },
-
- // This gets called when the content sends us system-message-listener-ready
- // mozContentEvent, OR when an observer message tell us we should consider
- // the content as ready.
- notifyEventListenerReady: function shell_notifyEventListenerReady() {
- if (this._isEventListenerReady) {
- Cu.reportError('shell.js: SystemApp has already been declared as being ready.');
- return;
- }
- this._isEventListenerReady = true;
-
- if (Services.prefs.getBoolPref('b2g.orientation.animate')) {
- Cu.import('resource://gre/modules/OrientationChangeHandler.jsm');
- }
-
- SystemAppProxy.setIsReady();
- }
-};
-
-Services.obs.addObserver(function onFullscreenOriginChange(subject, topic, data) {
- shell.sendChromeEvent({ type: "fullscreenoriginchange",
- fullscreenorigin: data });
-}, "fullscreen-origin-change", false);
-
-Services.obs.addObserver(function onBluetoothVolumeChange(subject, topic, data) {
- shell.sendChromeEvent({
- type: "bluetooth-volumeset",
- value: data
- });
-}, 'bluetooth-volume-change', false);
-
-Services.obs.addObserver(function(subject, topic, data) {
- shell.sendCustomEvent('mozmemorypressure');
-}, 'memory-pressure', false);
-
-Services.obs.addObserver(function(subject, topic, data) {
- shell.notifyEventListenerReady();
-}, 'system-message-listener-ready', false);
-
-var permissionMap = new Map([
- ['unknown', Services.perms.UNKNOWN_ACTION],
- ['allow', Services.perms.ALLOW_ACTION],
- ['deny', Services.perms.DENY_ACTION],
- ['prompt', Services.perms.PROMPT_ACTION],
-]);
-var permissionMapRev = new Map(Array.from(permissionMap.entries()).reverse());
-
-var CustomEventManager = {
- init: function custevt_init() {
- window.addEventListener("ContentStart", (function(evt) {
- let content = shell.contentBrowser.contentWindow;
- content.addEventListener("mozContentEvent", this, false, true);
- }).bind(this), false);
- },
-
- handleEvent: function custevt_handleEvent(evt) {
- let detail = evt.detail;
- dump('XXX FIXME : Got a mozContentEvent: ' + detail.type + "\n");
-
- switch(detail.type) {
- case 'system-message-listener-ready':
- Services.obs.notifyObservers(null, 'system-message-listener-ready', null);
- break;
- case 'captive-portal-login-cancel':
- CaptivePortalLoginHelper.handleEvent(detail);
- break;
- case 'inputmethod-update-layouts':
- case 'inputregistry-add':
- case 'inputregistry-remove':
- KeyboardHelper.handleEvent(detail);
- break;
- case 'copypaste-do-command':
- Services.obs.notifyObservers({ wrappedJSObject: shell.contentBrowser },
- 'ask-children-to-execute-copypaste-command', detail.cmd);
- break;
- case 'add-permission':
- Services.perms.add(Services.io.newURI(detail.uri, null, null),
- detail.permissionType, permissionMap.get(detail.permission));
- break;
- case 'remove-permission':
- Services.perms.remove(Services.io.newURI(detail.uri, null, null),
- detail.permissionType);
- break;
- case 'test-permission':
- let result = Services.perms.testExactPermission(
- Services.io.newURI(detail.uri, null, null), detail.permissionType);
- // Not equal check here because we want to prevent default only if it's not set
- if (result !== permissionMapRev.get(detail.permission)) {
- evt.preventDefault();
- }
- break;
- case 'shutdown-application':
- let appStartup = Cc['@mozilla.org/toolkit/app-startup;1']
- .getService(Ci.nsIAppStartup);
- appStartup.quit(appStartup.eAttemptQuit);
- break;
- case 'toggle-fullscreen-native-window':
- window.fullScreen = !window.fullScreen;
- Services.prefs.setBoolPref("b2g.nativeWindowGeometry.fullscreen",
- window.fullScreen);
- break;
- case 'minimize-native-window':
- window.minimize();
- break;
- case 'clear-cache-and-reload':
- clearCacheAndReload();
- break;
- case 'clear-cache-and-restart':
- clearCache();
- restart();
- break;
- case 'restart':
- restart();
- break;
- }
- }
-}
-
-var KeyboardHelper = {
- handleEvent: function keyboard_handleEvent(detail) {
- switch (detail.type) {
- case 'inputmethod-update-layouts':
- Keyboard.setLayouts(detail.layouts);
-
- break;
- case 'inputregistry-add':
- case 'inputregistry-remove':
- Keyboard.inputRegistryGlue.returnMessage(detail);
-
- break;
- }
- }
-};
-
-// This is the backend for Gaia's screenshot feature. Gaia requests a
-// screenshot by sending a mozContentEvent with detail.type set to
-// 'take-screenshot'. Then we take a screenshot and send a
-// mozChromeEvent with detail.type set to 'take-screenshot-success'
-// and detail.file set to the an image/png blob
-window.addEventListener('ContentStart', function ss_onContentStart() {
- let content = shell.contentBrowser.contentWindow;
- content.addEventListener('mozContentEvent', function ss_onMozContentEvent(e) {
- if (e.detail.type !== 'take-screenshot')
- return;
-
- try {
- shell.sendChromeEvent({
- type: 'take-screenshot-success',
- file: Screenshot.get()
- });
- } catch (e) {
- dump('exception while creating screenshot: ' + e + '\n');
- shell.sendChromeEvent({
- type: 'take-screenshot-error',
- error: String(e)
- });
- }
- });
-});
-
-(function contentCrashTracker() {
- Services.obs.addObserver(function(aSubject, aTopic, aData) {
- let props = aSubject.QueryInterface(Ci.nsIPropertyBag2);
- if (props.hasKey("abnormal") && props.hasKey("dumpID")) {
- shell.reportCrash(false, props.getProperty("dumpID"));
- }
- },
- "ipc:content-shutdown", false);
-})();
-
-var CaptivePortalLoginHelper = {
- init: function init() {
- Services.obs.addObserver(this, 'captive-portal-login', false);
- Services.obs.addObserver(this, 'captive-portal-login-abort', false);
- Services.obs.addObserver(this, 'captive-portal-login-success', false);
- },
- handleEvent: function handleEvent(detail) {
- Services.captivePortalDetector.cancelLogin(detail.id);
- },
- observe: function observe(subject, topic, data) {
- shell.sendChromeEvent(JSON.parse(data));
- }
-}
-
-// Listen for crashes submitted through the crash reporter UI.
-window.addEventListener('ContentStart', function cr_onContentStart() {
- let content = shell.contentBrowser.contentWindow;
- content.addEventListener("mozContentEvent", function cr_onMozContentEvent(e) {
- if (e.detail.type == "submit-crash" && e.detail.crashID) {
- debugCrashReport("submitting crash at user request ", e.detail.crashID);
- shell.submitCrash(e.detail.crashID);
- } else if (e.detail.type == "delete-crash" && e.detail.crashID) {
- debugCrashReport("deleting crash at user request ", e.detail.crashID);
- shell.deleteCrash(e.detail.crashID);
- }
- });
-});
-
-window.addEventListener('ContentStart', function update_onContentStart() {
- if (!AppConstants.MOZ_UPDATER) {
- return;
- }
-
- let promptCc = Cc["@mozilla.org/updates/update-prompt;1"];
- if (!promptCc) {
- return;
- }
-
- let updatePrompt = promptCc.createInstance(Ci.nsIUpdatePrompt);
- if (!updatePrompt) {
- return;
- }
-
- updatePrompt.wrappedJSObject.handleContentStart(shell);
-});
-/* The "GPSChipOn" is to indicate that GPS engine is turned ON by the modem.
- During this GPS engine is turned ON by the modem, we make the location tracking icon visible to user.
- Once GPS engine is turned OFF, the location icon will disappear.
- If GPS engine is not turned ON by the modem or GPS location service is triggered,
- we let GPS service take over the control of showing the location tracking icon.
- The regular sequence of the geolocation-device-events is: starting-> GPSStarting-> shutdown-> GPSShutdown
-*/
-
-
-(function geolocationStatusTracker() {
- let gGeolocationActive = false;
- let GPSChipOn = false;
-
- Services.obs.addObserver(function(aSubject, aTopic, aData) {
- let oldState = gGeolocationActive;
- let promptWarning = false;
- switch (aData) {
- case "GPSStarting":
- if (!gGeolocationActive) {
- gGeolocationActive = true;
- GPSChipOn = true;
- promptWarning = true;
- }
- break;
- case "GPSShutdown":
- if (GPSChipOn) {
- gGeolocationActive = false;
- GPSChipOn = false;
- }
- break;
- case "starting":
- gGeolocationActive = true;
- GPSChipOn = false;
- break;
- case "shutdown":
- gGeolocationActive = false;
- break;
- }
-
- if (gGeolocationActive != oldState) {
- shell.sendChromeEvent({
- type: 'geolocation-status',
- active: gGeolocationActive,
- prompt: promptWarning
- });
- }
-}, "geolocation-device-events", false);
-})();
-
-(function headphonesStatusTracker() {
- Services.obs.addObserver(function(aSubject, aTopic, aData) {
- shell.sendChromeEvent({
- type: 'headphones-status-changed',
- state: aData
- });
-}, "headphones-status-changed", false);
-})();
-
-(function audioChannelChangedTracker() {
- Services.obs.addObserver(function(aSubject, aTopic, aData) {
- shell.sendChromeEvent({
- type: 'audio-channel-changed',
- channel: aData
- });
-}, "audio-channel-changed", false);
-})();
-
-(function defaultVolumeChannelChangedTracker() {
- Services.obs.addObserver(function(aSubject, aTopic, aData) {
- shell.sendChromeEvent({
- type: 'default-volume-channel-changed',
- channel: aData
- });
-}, "default-volume-channel-changed", false);
-})();
-
-(function visibleAudioChannelChangedTracker() {
- Services.obs.addObserver(function(aSubject, aTopic, aData) {
- shell.sendChromeEvent({
- type: 'visible-audio-channel-changed',
- channel: aData
- });
- shell.visibleNormalAudioActive = (aData == 'normal');
-}, "visible-audio-channel-changed", false);
-})();
-
-(function recordingStatusTracker() {
- // Recording status is tracked per process with following data structure:
- // {<processId>: {<requestURL>: {isApp: <isApp>,
- // count: <N>,
- // audioCount: <N>,
- // videoCount: <N>}}
- let gRecordingActiveProcesses = {};
-
- let recordingHandler = function(aSubject, aTopic, aData) {
- let props = aSubject.QueryInterface(Ci.nsIPropertyBag2);
- let processId = (props.hasKey('childID')) ? props.get('childID')
- : 'main';
- if (processId && !gRecordingActiveProcesses.hasOwnProperty(processId)) {
- gRecordingActiveProcesses[processId] = {};
- }
-
- let commandHandler = function (requestURL, command) {
- let currentProcess = gRecordingActiveProcesses[processId];
- let currentActive = currentProcess[requestURL];
- let wasActive = (currentActive['count'] > 0);
- let wasAudioActive = (currentActive['audioCount'] > 0);
- let wasVideoActive = (currentActive['videoCount'] > 0);
-
- switch (command.type) {
- case 'starting':
- currentActive['count']++;
- currentActive['audioCount'] += (command.isAudio) ? 1 : 0;
- currentActive['videoCount'] += (command.isVideo) ? 1 : 0;
- break;
- case 'shutdown':
- currentActive['count']--;
- currentActive['audioCount'] -= (command.isAudio) ? 1 : 0;
- currentActive['videoCount'] -= (command.isVideo) ? 1 : 0;
- break;
- case 'content-shutdown':
- currentActive['count'] = 0;
- currentActive['audioCount'] = 0;
- currentActive['videoCount'] = 0;
- break;
- }
-
- if (currentActive['count'] > 0) {
- currentProcess[requestURL] = currentActive;
- } else {
- delete currentProcess[requestURL];
- }
-
- // We need to track changes if any active state is changed.
- let isActive = (currentActive['count'] > 0);
- let isAudioActive = (currentActive['audioCount'] > 0);
- let isVideoActive = (currentActive['videoCount'] > 0);
- if ((isActive != wasActive) ||
- (isAudioActive != wasAudioActive) ||
- (isVideoActive != wasVideoActive)) {
- shell.sendChromeEvent({
- type: 'recording-status',
- active: isActive,
- requestURL: requestURL,
- isApp: currentActive['isApp'],
- isAudio: isAudioActive,
- isVideo: isVideoActive
- });
- }
- };
-
- switch (aData) {
- case 'starting':
- case 'shutdown':
- // create page record if it is not existed yet.
- let requestURL = props.get('requestURL');
- if (requestURL &&
- !gRecordingActiveProcesses[processId].hasOwnProperty(requestURL)) {
- gRecordingActiveProcesses[processId][requestURL] = {isApp: props.get('isApp'),
- count: 0,
- audioCount: 0,
- videoCount: 0};
- }
- commandHandler(requestURL, { type: aData,
- isAudio: props.get('isAudio'),
- isVideo: props.get('isVideo')});
- break;
- case 'content-shutdown':
- // iterate through all the existing active processes
- Object.keys(gRecordingActiveProcesses[processId]).forEach(function(requestURL) {
- commandHandler(requestURL, { type: aData,
- isAudio: true,
- isVideo: true});
- });
- break;
- }
-
- // clean up process record if no page record in it.
- if (Object.keys(gRecordingActiveProcesses[processId]).length == 0) {
- delete gRecordingActiveProcesses[processId];
- }
- };
- Services.obs.addObserver(recordingHandler, 'recording-device-events', false);
- Services.obs.addObserver(recordingHandler, 'recording-device-ipc-events', false);
-
- Services.obs.addObserver(function(aSubject, aTopic, aData) {
- // send additional recording events if content process is being killed
- let processId = aSubject.QueryInterface(Ci.nsIPropertyBag2).get('childID');
- if (gRecordingActiveProcesses.hasOwnProperty(processId)) {
- Services.obs.notifyObservers(aSubject, 'recording-device-ipc-events', 'content-shutdown');
- }
- }, 'ipc:content-shutdown', false);
-})();
-
-(function volumeStateTracker() {
- Services.obs.addObserver(function(aSubject, aTopic, aData) {
- shell.sendChromeEvent({
- type: 'volume-state-changed',
- active: (aData == 'Shared')
- });
-}, 'volume-state-changed', false);
-})();
-
-if (isGonk) {
- // Devices don't have all the same partition size for /cache where we
- // store the http cache.
- (function setHTTPCacheSize() {
- let path = Services.prefs.getCharPref("browser.cache.disk.parent_directory");
- let volumeService = Cc["@mozilla.org/telephony/volume-service;1"]
- .getService(Ci.nsIVolumeService);
-
- let stats = volumeService.createOrGetVolumeByPath(path).getStats();
-
- // We must set the size in KB, and keep a bit of free space.
- let size = Math.floor(stats.totalBytes / 1024) - 1024;
-
- // keep the default value if it is smaller than the physical partition size.
- let oldSize = Services.prefs.getIntPref("browser.cache.disk.capacity");
- if (size < oldSize) {
- Services.prefs.setIntPref("browser.cache.disk.capacity", size);
- }
- })();
-
- try {
- let gmpService = Cc["@mozilla.org/gecko-media-plugin-service;1"]
- .getService(Ci.mozIGeckoMediaPluginChromeService);
- gmpService.addPluginDirectory("/system/b2g/gmp-clearkey/0.1");
- } catch(e) {
- dump("Failed to add clearkey path! " + e + "\n");
- }
-}
-
-// Calling this observer will cause a shutdown an a profile reset.
-// Use eg. : Services.obs.notifyObservers(null, 'b2g-reset-profile', null);
-Services.obs.addObserver(function resetProfile(subject, topic, data) {
- Services.obs.removeObserver(resetProfile, topic);
-
- // Listening for 'profile-before-change-telemetry' which is late in the
- // shutdown sequence, but still has xpcom access.
- Services.obs.addObserver(function clearProfile(subject, topic, data) {
- Services.obs.removeObserver(clearProfile, topic);
- if (isGonk) {
- let json = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
- json.initWithPath('/system/b2g/webapps/webapps.json');
- let toRemove = json.exists()
- // This is a user build, just rm -r /data/local /data/b2g/mozilla
- ? ['/data/local', '/data/b2g/mozilla']
- // This is an eng build. We clear the profile and a set of files
- // under /data/local.
- : ['/data/b2g/mozilla',
- '/data/local/permissions.sqlite',
- '/data/local/storage',
- '/data/local/OfflineCache'];
-
- toRemove.forEach(function(dir) {
- try {
- let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
- file.initWithPath(dir);
- file.remove(true);
- } catch(e) { dump(e); }
- });
- } else {
- // Desktop builds.
- let profile = Services.dirsvc.get('ProfD', Ci.nsIFile);
-
- // We don't want to remove everything from the profile, since this
- // would prevent us from starting up.
- let whitelist = ['defaults', 'extensions', 'settings.json',
- 'user.js', 'webapps'];
- let enumerator = profile.directoryEntries;
- while (enumerator.hasMoreElements()) {
- let file = enumerator.getNext().QueryInterface(Ci.nsIFile);
- if (whitelist.indexOf(file.leafName) == -1) {
- file.remove(true);
- }
- }
- }
- },
- 'profile-before-change-telemetry', false);
-
- let appStartup = Cc['@mozilla.org/toolkit/app-startup;1']
- .getService(Ci.nsIAppStartup);
- appStartup.quit(Ci.nsIAppStartup.eForceQuit);
-}, 'b2g-reset-profile', false);
-
-var showInstallScreen;
-
-if (AppConstants.MOZ_GRAPHENE) {
- const restoreWindowGeometry = () => {
- let screenX = Services.prefs.getIntPref("b2g.nativeWindowGeometry.screenX");
- let screenY = Services.prefs.getIntPref("b2g.nativeWindowGeometry.screenY");
- let width = Services.prefs.getIntPref("b2g.nativeWindowGeometry.width");
- let height = Services.prefs.getIntPref("b2g.nativeWindowGeometry.height");
-
- if (screenX == -1) {
- // Center
- screenX = (screen.width - width) / 2;
- screenY = (screen.height - height) / 2;
- }
-
- moveTo(screenX, screenY);
- resizeTo(width, height);
- }
- restoreWindowGeometry();
-
- const saveWindowGeometry = () => {
- window.removeEventListener("unload", saveWindowGeometry);
- Services.prefs.setIntPref("b2g.nativeWindowGeometry.screenX", screenX);
- Services.prefs.setIntPref("b2g.nativeWindowGeometry.screenY", screenY);
- Services.prefs.setIntPref("b2g.nativeWindowGeometry.width", outerWidth);
- Services.prefs.setIntPref("b2g.nativeWindowGeometry.height", outerHeight);
- }
- window.addEventListener("unload", saveWindowGeometry);
-
- var baseWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShellTreeItem)
- .treeOwner
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIBaseWindow);
-
- const showNativeWindow = () => baseWindow.visibility = true;
- const hideNativeWindow = () => baseWindow.visibility = false;
-
- showInstallScreen = () => {
- const grapheneStrings =
- Services.strings.createBundle('chrome://b2g-l10n/locale/graphene.properties');
- document.querySelector('#installing > .message').textContent =
- grapheneStrings.GetStringFromName('installing');
- showNativeWindow();
- }
-
- const hideInstallScreen = () => {
- document.body.classList.add('content-loaded');
- }
-
- window.addEventListener('ContentStart', () => {
- shell.contentBrowser.contentWindow.addEventListener('load', () => {
- hideInstallScreen();
- showNativeWindow();
- });
- });
-
- hideNativeWindow();
-}