mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
4d6a633bba
The -*- file variable lines -*- establish per-file settings that Emacs will pick up. This patch makes the following changes to those lines (and touches nothing else): - Never set the buffer's mode. Years ago, Emacs did not have a good JavaScript mode, so it made sense to use Java or C++ mode in .js files. However, Emacs has had js-mode for years now; it's perfectly serviceable, and is available and enabled by default in all major Emacs packagings. Selecting a mode in the -*- file variable line -*- is almost always the wrong thing to do anyway. It overrides Emacs's default choice, which is (now) reasonable; and even worse, it overrides settings the user might have made in their '.emacs' file for that file extension. It's only useful when there's something specific about that particular file that makes a particular mode appropriate. - Correctly propagate settings that establish the correct indentation level for this file: c-basic-offset and js2-basic-offset should be js-indent-level. Whatever value they're given should be preserved; different parts of our tree use different indentation styles. - We don't use tabs in Mozilla JS code. Always set indent-tabs-mode: nil. Remove tab-width: settings, at least in files that don't contain tab characters. - Remove js2-mode settings that belong in the user's .emacs file, like js2-skip-preprocessor-directives.
1355 lines
46 KiB
JavaScript
1355 lines
46 KiB
JavaScript
/* -*- 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/. */
|
|
|
|
Cu.import('resource://gre/modules/ContactService.jsm');
|
|
Cu.import('resource://gre/modules/SettingsChangeNotifier.jsm');
|
|
Cu.import('resource://gre/modules/DataStoreChangeNotifier.jsm');
|
|
Cu.import('resource://gre/modules/AlarmService.jsm');
|
|
Cu.import('resource://gre/modules/ActivitiesService.jsm');
|
|
Cu.import('resource://gre/modules/PermissionPromptHelper.jsm');
|
|
Cu.import('resource://gre/modules/NotificationDB.jsm');
|
|
Cu.import('resource://gre/modules/Payment.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');
|
|
#ifdef MOZ_WIDGET_GONK
|
|
Cu.import('resource://gre/modules/NetworkStatsService.jsm');
|
|
#endif
|
|
|
|
// Identity
|
|
Cu.import('resource://gre/modules/SignInToWebsite.jsm');
|
|
SignInToWebsiteController.init();
|
|
|
|
#ifdef MOZ_SERVICES_FXACCOUNTS
|
|
Cu.import('resource://gre/modules/FxAccountsMgmtService.jsm');
|
|
#endif
|
|
|
|
Cu.import('resource://gre/modules/DownloadsAPI.jsm');
|
|
Cu.import('resource://gre/modules/MobileIdentityManager.jsm');
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
|
|
"resource://gre/modules/SystemAppProxy.jsm");
|
|
|
|
Cu.import('resource://gre/modules/Webapps.jsm');
|
|
DOMApplicationRegistry.allAppsLaunchable = true;
|
|
|
|
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.defineLazyServiceGetter(Services, 'fm',
|
|
'@mozilla.org/focus-manager;1',
|
|
'nsIFocusManager');
|
|
|
|
XPCOMUtils.defineLazyGetter(this, 'DebuggerServer', function() {
|
|
Cu.import('resource://gre/modules/devtools/dbg-server.jsm');
|
|
return DebuggerServer;
|
|
});
|
|
|
|
XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
|
|
return Cc["@mozilla.org/parentprocessmessagemanager;1"]
|
|
.getService(Ci.nsIMessageListenerManager);
|
|
});
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
XPCOMUtils.defineLazyGetter(this, "libcutils", function () {
|
|
Cu.import("resource://gre/modules/systemlibs.js");
|
|
return libcutils;
|
|
});
|
|
#endif
|
|
|
|
#ifdef MOZ_CAPTIVEDETECT
|
|
XPCOMUtils.defineLazyServiceGetter(Services, 'captivePortalDetector',
|
|
'@mozilla.org/toolkit/captive-detector;1',
|
|
'nsICaptivePortalDetector');
|
|
#endif
|
|
|
|
function getContentWindow() {
|
|
return shell.contentBrowser.contentWindow;
|
|
}
|
|
|
|
function debug(str) {
|
|
dump(' -*- Shell.js: ' + str + '\n');
|
|
}
|
|
|
|
#ifdef MOZ_CRASHREPORTER
|
|
function debugCrashReport(aStr) {
|
|
dump('Crash reporter : ' + aStr);
|
|
}
|
|
#else
|
|
function debugCrashReport(aStr) {}
|
|
#endif
|
|
|
|
var shell = {
|
|
|
|
get CrashSubmit() {
|
|
delete this.CrashSubmit;
|
|
#ifdef 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;
|
|
#endif
|
|
},
|
|
|
|
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.nsINetworkInterface);
|
|
if (network.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED
|
|
&& network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
|
|
shell.submitQueuedCrashes();
|
|
|
|
Services.obs.removeObserver(observer, topic);
|
|
}
|
|
}, "network-connection-state-changed", false);
|
|
},
|
|
|
|
get contentBrowser() {
|
|
delete this.contentBrowser;
|
|
return this.contentBrowser = document.getElementById('systemapp');
|
|
},
|
|
|
|
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;
|
|
},
|
|
|
|
start: function 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) { }
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
// 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) { }
|
|
});
|
|
#endif
|
|
} 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('style', "overflow: hidden; height: 100%; width: 100%; border: none;");
|
|
systemAppFrame.setAttribute('src', "data:text/html;charset=utf-8,%3C!DOCTYPE html>%3Cbody style='background:black;");
|
|
let container = document.getElementById('container');
|
|
#ifdef MOZ_WIDGET_COCOA
|
|
// See shell.html
|
|
let hotfix = document.getElementById('placeholder');
|
|
if (hotfix) {
|
|
container.removeChild(hotfix);
|
|
}
|
|
#endif
|
|
container.appendChild(systemAppFrame);
|
|
|
|
systemAppFrame.contentWindow
|
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIWebNavigation)
|
|
.sessionHistory = Cc["@mozilla.org/browser/shistory;1"]
|
|
.createInstance(Ci.nsISHistory);
|
|
|
|
// 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('keypress', this, true);
|
|
chromeEventHandler.addEventListener('keyup', this, true);
|
|
|
|
window.addEventListener('MozApplicationManifest', this);
|
|
window.addEventListener('mozfullscreenchange', this);
|
|
window.addEventListener('MozAfterPaint', this);
|
|
window.addEventListener('sizemodechange', this);
|
|
window.addEventListener('unload', this);
|
|
this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
|
|
|
|
CustomEventManager.init();
|
|
WebappsHelper.init();
|
|
UserAgentOverrides.init();
|
|
IndexedDBPromptHelper.init();
|
|
CaptivePortalLoginHelper.init();
|
|
|
|
this.contentBrowser.src = homeURL;
|
|
this.isHomeLoaded = false;
|
|
|
|
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);
|
|
},
|
|
|
|
stop: function shell_stop() {
|
|
window.removeEventListener('unload', this);
|
|
window.removeEventListener('keydown', this, true);
|
|
window.removeEventListener('keypress', this, true);
|
|
window.removeEventListener('keyup', this, true);
|
|
window.removeEventListener('MozApplicationManifest', this);
|
|
window.removeEventListener('mozfullscreenchange', this);
|
|
window.removeEventListener('sizemodechange', this);
|
|
this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
|
|
ppmm.removeMessageListener("content-handler", this);
|
|
|
|
UserAgentOverrides.uninit();
|
|
IndexedDBPromptHelper.uninit();
|
|
},
|
|
|
|
// If this key event actually represents a hardware button, filter it here
|
|
// and send a mozChromeEvent with detail.type set to xxx-button-press or
|
|
// xxx-button-release instead.
|
|
filterHardwareKeys: function shell_filterHardwareKeys(evt) {
|
|
var type;
|
|
switch (evt.keyCode) {
|
|
case evt.DOM_VK_HOME: // Home button
|
|
type = 'home-button';
|
|
break;
|
|
case evt.DOM_VK_SLEEP: // Sleep button
|
|
case evt.DOM_VK_END: // On desktop we don't have a sleep button
|
|
type = 'sleep-button';
|
|
break;
|
|
case evt.DOM_VK_PAGE_UP: // Volume up button
|
|
type = 'volume-up-button';
|
|
break;
|
|
case evt.DOM_VK_PAGE_DOWN: // Volume down button
|
|
type = 'volume-down-button';
|
|
break;
|
|
case evt.DOM_VK_ESCAPE: // Back button (should be disabled)
|
|
type = 'back-button';
|
|
break;
|
|
case evt.DOM_VK_CONTEXT_MENU: // Menu button
|
|
type = 'menu-button';
|
|
break;
|
|
case evt.DOM_VK_F1: // headset button
|
|
type = 'headset-button';
|
|
break;
|
|
}
|
|
|
|
let mediaKeys = {
|
|
'MediaNextTrack': 'media-next-track-button',
|
|
'MediaPreviousTrack': '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',
|
|
'FastFwd': 'media-fast-forward-button'
|
|
};
|
|
|
|
let isMediaKey = false;
|
|
if (mediaKeys[evt.key]) {
|
|
isMediaKey = true;
|
|
type = mediaKeys[evt.key];
|
|
}
|
|
|
|
if (!type) {
|
|
return;
|
|
}
|
|
|
|
// If we didn't return, then the key event represents a hardware key
|
|
// and we need to prevent it from propagating to Gaia
|
|
evt.stopImmediatePropagation();
|
|
evt.preventDefault(); // Prevent keypress events (when #501496 is fixed).
|
|
|
|
// If it is a key down or key up event, we send a chrome event to Gaia.
|
|
// If it is a keypress event we just ignore it.
|
|
switch (evt.type) {
|
|
case 'keydown':
|
|
type = type + '-press';
|
|
break;
|
|
case 'keyup':
|
|
type = type + '-release';
|
|
break;
|
|
case 'keypress':
|
|
return;
|
|
}
|
|
|
|
// Let applications receive the headset button key press/release event.
|
|
if (evt.keyCode == evt.DOM_VK_F1 && type !== this.lastHardwareButtonEventType) {
|
|
this.lastHardwareButtonEventType = type;
|
|
gSystemMessenger.broadcastMessage('headset-button', type);
|
|
return;
|
|
}
|
|
|
|
if (isMediaKey) {
|
|
this.lastHardwareButtonEventType = type;
|
|
gSystemMessenger.broadcastMessage('media-button', type);
|
|
return;
|
|
}
|
|
|
|
// On my device, the physical hardware buttons (sleep and volume)
|
|
// send multiple events (press press release release), but the
|
|
// soft home button just sends one. This hack is to manually
|
|
// "debounce" the keys. If the type of this event is the same as
|
|
// the type of the last one, then don't send it. We'll never send
|
|
// two presses or two releases in a row.
|
|
// FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=761067
|
|
if (type !== this.lastHardwareButtonEventType) {
|
|
this.lastHardwareButtonEventType = type;
|
|
this.sendChromeEvent({type: type});
|
|
}
|
|
},
|
|
|
|
lastHardwareButtonEventType: null, // property for the hack above
|
|
visibleNormalAudioActive: false,
|
|
|
|
handleEvent: function shell_handleEvent(evt) {
|
|
let content = this.contentBrowser.contentWindow;
|
|
switch (evt.type) {
|
|
case 'keydown':
|
|
case 'keyup':
|
|
case 'keypress':
|
|
this.filterHardwareKeys(evt);
|
|
break;
|
|
case 'mozfullscreenchange':
|
|
// When the screen goes fullscreen make sure to set the focus to the
|
|
// main window so noboby can prevent the ESC key to get out fullscreen
|
|
// mode
|
|
if (document.mozFullScreen)
|
|
Services.fm.focusedWindow = window;
|
|
break;
|
|
case 'sizemodechange':
|
|
if (window.windowState == window.STATE_MINIMIZED && !this.visibleNormalAudioActive) {
|
|
this.contentBrowser.setVisible(false);
|
|
} else {
|
|
this.contentBrowser.setVisible(true);
|
|
}
|
|
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 '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, window);
|
|
} catch (e) {
|
|
dump('Error while creating offline cache: ' + e + '\n');
|
|
}
|
|
break;
|
|
case 'MozAfterPaint':
|
|
window.removeEventListener('MozAfterPaint', this);
|
|
this.sendChromeEvent({
|
|
type: 'system-first-paint'
|
|
});
|
|
break;
|
|
case 'unload':
|
|
this.stop();
|
|
break;
|
|
}
|
|
},
|
|
|
|
// Send an event to a specific window, document or element.
|
|
sendEvent: function shell_sendEvent(target, type, details) {
|
|
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) {
|
|
let target = getContentWindow();
|
|
let payload = details ? Cu.cloneInto(details, target) : {};
|
|
this.sendEvent(target, type, payload);
|
|
},
|
|
|
|
sendChromeEvent: function shell_sendChromeEvent(details) {
|
|
if (!this.isHomeLoaded) {
|
|
if (!('pendingChromeEvents' in this)) {
|
|
this.pendingChromeEvents = [];
|
|
}
|
|
|
|
this.pendingChromeEvents.push(details);
|
|
return;
|
|
}
|
|
|
|
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() {
|
|
this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
|
|
this.contentBrowser.removeEventListener('mozbrowserlocationchange', this, true);
|
|
|
|
let content = this.contentBrowser.contentWindow;
|
|
|
|
this.reportCrash(true);
|
|
|
|
SystemAppProxy.registerFrame(shell.contentBrowser);
|
|
|
|
this.sendEvent(window, 'ContentStart');
|
|
|
|
Services.obs.notifyObservers(null, 'content-start', null);
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
Cu.import('resource://gre/modules/OperatorApps.jsm');
|
|
#endif
|
|
|
|
content.addEventListener('load', function shell_homeLoaded() {
|
|
content.removeEventListener('load', shell_homeLoaded);
|
|
shell.isHomeLoaded = true;
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
libcutils.property_set('sys.boot_completed', '1');
|
|
#endif
|
|
|
|
Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
|
|
|
|
SystemAppProxy.setIsReady();
|
|
if ('pendingChromeEvents' in shell) {
|
|
shell.pendingChromeEvents.forEach((shell.sendChromeEvent).bind(shell));
|
|
}
|
|
delete shell.pendingChromeEvents;
|
|
});
|
|
}
|
|
};
|
|
|
|
Services.obs.addObserver(function onFullscreenOriginChange(subject, topic, data) {
|
|
shell.sendChromeEvent({ type: "fullscreenoriginchange",
|
|
fullscreenorigin: data });
|
|
}, "fullscreen-origin-change", false);
|
|
|
|
DOMApplicationRegistry.registryStarted.then(function () {
|
|
shell.sendChromeEvent({ type: 'webapps-registry-start' });
|
|
});
|
|
DOMApplicationRegistry.registryReady.then(function () {
|
|
shell.sendChromeEvent({ type: 'webapps-registry-ready' });
|
|
});
|
|
|
|
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);
|
|
|
|
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 'webapps-install-granted':
|
|
case 'webapps-install-denied':
|
|
WebappsHelper.handleEvent(detail);
|
|
break;
|
|
case 'select-choicechange':
|
|
FormsHelper.handleEvent(detail);
|
|
break;
|
|
case 'system-message-listener-ready':
|
|
Services.obs.notifyObservers(null, 'system-message-listener-ready', null);
|
|
break;
|
|
case 'remote-debugger-prompt':
|
|
RemoteDebugger.handleEvent(detail);
|
|
break;
|
|
case 'captive-portal-login-cancel':
|
|
CaptivePortalLoginHelper.handleEvent(detail);
|
|
break;
|
|
case 'inputmethod-update-layouts':
|
|
KeyboardHelper.handleEvent(detail);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
var WebappsHelper = {
|
|
_installers: {},
|
|
_count: 0,
|
|
|
|
init: function webapps_init() {
|
|
Services.obs.addObserver(this, "webapps-launch", false);
|
|
Services.obs.addObserver(this, "webapps-ask-install", false);
|
|
Services.obs.addObserver(this, "webapps-close", false);
|
|
},
|
|
|
|
registerInstaller: function webapps_registerInstaller(data) {
|
|
let id = "installer" + this._count++;
|
|
this._installers[id] = data;
|
|
return id;
|
|
},
|
|
|
|
handleEvent: function webapps_handleEvent(detail) {
|
|
if (!detail || !detail.id)
|
|
return;
|
|
|
|
let installer = this._installers[detail.id];
|
|
delete this._installers[detail.id];
|
|
switch (detail.type) {
|
|
case "webapps-install-granted":
|
|
DOMApplicationRegistry.confirmInstall(installer);
|
|
break;
|
|
case "webapps-install-denied":
|
|
DOMApplicationRegistry.denyInstall(installer);
|
|
break;
|
|
}
|
|
},
|
|
|
|
observe: function webapps_observe(subject, topic, data) {
|
|
let json = JSON.parse(data);
|
|
json.mm = subject;
|
|
|
|
switch(topic) {
|
|
case "webapps-launch":
|
|
DOMApplicationRegistry.getManifestFor(json.manifestURL).then((aManifest) => {
|
|
if (!aManifest)
|
|
return;
|
|
|
|
let manifest = new ManifestHelper(aManifest, json.origin);
|
|
let payload = {
|
|
timestamp: json.timestamp,
|
|
url: manifest.fullLaunchPath(json.startPoint),
|
|
manifestURL: json.manifestURL
|
|
};
|
|
shell.sendCustomEvent("webapps-launch", payload);
|
|
});
|
|
break;
|
|
case "webapps-ask-install":
|
|
let id = this.registerInstaller(json);
|
|
shell.sendChromeEvent({
|
|
type: "webapps-ask-install",
|
|
id: id,
|
|
app: json.app
|
|
});
|
|
break;
|
|
case "webapps-close":
|
|
shell.sendCustomEvent("webapps-close", {
|
|
"manifestURL": json.manifestURL
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
let IndexedDBPromptHelper = {
|
|
_quotaPrompt: "indexedDB-quota-prompt",
|
|
_quotaResponse: "indexedDB-quota-response",
|
|
|
|
init:
|
|
function IndexedDBPromptHelper_init() {
|
|
Services.obs.addObserver(this, this._quotaPrompt, false);
|
|
},
|
|
|
|
uninit:
|
|
function IndexedDBPromptHelper_uninit() {
|
|
Services.obs.removeObserver(this, this._quotaPrompt);
|
|
},
|
|
|
|
observe:
|
|
function IndexedDBPromptHelper_observe(subject, topic, data) {
|
|
if (topic != this._quotaPrompt) {
|
|
throw new Error("Unexpected topic!");
|
|
}
|
|
|
|
let observer = subject.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIObserver);
|
|
let responseTopic = this._quotaResponse;
|
|
|
|
setTimeout(function() {
|
|
observer.observe(null, responseTopic,
|
|
Ci.nsIPermissionManager.DENY_ACTION);
|
|
}, 0);
|
|
}
|
|
}
|
|
|
|
let RemoteDebugger = {
|
|
_promptDone: false,
|
|
_promptAnswer: false,
|
|
_running: false,
|
|
|
|
prompt: function debugger_prompt() {
|
|
this._promptDone = false;
|
|
|
|
shell.sendChromeEvent({
|
|
"type": "remote-debugger-prompt"
|
|
});
|
|
|
|
while(!this._promptDone) {
|
|
Services.tm.currentThread.processNextEvent(true);
|
|
}
|
|
|
|
return this._promptAnswer;
|
|
},
|
|
|
|
handleEvent: function debugger_handleEvent(detail) {
|
|
this._promptAnswer = detail.value;
|
|
this._promptDone = true;
|
|
},
|
|
|
|
get isDebugging() {
|
|
if (!this._running) {
|
|
return false;
|
|
}
|
|
|
|
return DebuggerServer._connections &&
|
|
Object.keys(DebuggerServer._connections).length > 0;
|
|
},
|
|
|
|
// Start the debugger server.
|
|
start: function debugger_start() {
|
|
if (this._running) {
|
|
return;
|
|
}
|
|
|
|
if (!DebuggerServer.initialized) {
|
|
// Ask for remote connections.
|
|
DebuggerServer.init(this.prompt.bind(this));
|
|
|
|
// /!\ Be careful when adding a new actor, especially global actors.
|
|
// Any new global actor will be exposed and returned by the root actor.
|
|
|
|
// Add Firefox-specific actors, but prevent tab actors to be loaded in
|
|
// the parent process, unless we enable certified apps debugging.
|
|
let restrictPrivileges = Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps");
|
|
DebuggerServer.addBrowserActors("navigator:browser", restrictPrivileges);
|
|
|
|
/**
|
|
* Construct a root actor appropriate for use in a server running in B2G.
|
|
* The returned root actor respects the factories registered with
|
|
* DebuggerServer.addGlobalActor only if certified apps debugging is on,
|
|
* otherwise we used an explicit limited list of global actors
|
|
*
|
|
* * @param connection DebuggerServerConnection
|
|
* The conection to the client.
|
|
*/
|
|
DebuggerServer.createRootActor = function createRootActor(connection)
|
|
{
|
|
let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
|
let parameters = {
|
|
// We do not expose browser tab actors yet,
|
|
// but we still have to define tabList.getList(),
|
|
// otherwise, client won't be able to fetch global actors
|
|
// from listTabs request!
|
|
tabList: {
|
|
getList: function() {
|
|
return promise.resolve([]);
|
|
}
|
|
},
|
|
// Use an explicit global actor list to prevent exposing
|
|
// unexpected actors
|
|
globalActorFactories: restrictPrivileges ? {
|
|
webappsActor: DebuggerServer.globalActorFactories.webappsActor,
|
|
deviceActor: DebuggerServer.globalActorFactories.deviceActor,
|
|
} : DebuggerServer.globalActorFactories
|
|
};
|
|
let devtools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
|
|
let { RootActor } = devtools.require("devtools/server/actors/root");
|
|
let root = new RootActor(connection, parameters);
|
|
root.applicationType = "operating-system";
|
|
return root;
|
|
};
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
DebuggerServer.on("connectionchange", function() {
|
|
AdbController.updateState();
|
|
});
|
|
#endif
|
|
}
|
|
|
|
let path = Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
|
|
"/data/local/debugger-socket";
|
|
try {
|
|
DebuggerServer.openListener(path);
|
|
// Temporary event, until bug 942756 lands and offers a way to know
|
|
// when the server is up and running.
|
|
Services.obs.notifyObservers(null, 'debugger-server-started', null);
|
|
this._running = true;
|
|
} catch (e) {
|
|
dump('Unable to start debugger server: ' + e + '\n');
|
|
}
|
|
},
|
|
|
|
stop: function debugger_stop() {
|
|
if (!this._running) {
|
|
return;
|
|
}
|
|
|
|
if (!DebuggerServer.initialized) {
|
|
// Can this really happen if we are running?
|
|
this._running = false;
|
|
return;
|
|
}
|
|
|
|
try {
|
|
DebuggerServer.closeListener();
|
|
} catch (e) {
|
|
dump('Unable to stop debugger server: ' + e + '\n');
|
|
}
|
|
this._running = false;
|
|
}
|
|
}
|
|
|
|
let KeyboardHelper = {
|
|
handleEvent: function keyboard_handleEvent(detail) {
|
|
Keyboard.setLayouts(detail.layouts);
|
|
}
|
|
};
|
|
|
|
// 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 {
|
|
var canvas = document.createElementNS('http://www.w3.org/1999/xhtml',
|
|
'canvas');
|
|
var width = window.innerWidth;
|
|
var height = window.innerHeight;
|
|
var scale = window.devicePixelRatio;
|
|
canvas.setAttribute('width', width * scale);
|
|
canvas.setAttribute('height', height * scale);
|
|
|
|
var context = canvas.getContext('2d');
|
|
var flags =
|
|
context.DRAWWINDOW_DRAW_CARET |
|
|
context.DRAWWINDOW_DRAW_VIEW |
|
|
context.DRAWWINDOW_USE_WIDGET_LAYERS;
|
|
context.scale(scale, scale);
|
|
context.drawWindow(window, 0, 0, width, height,
|
|
'rgb(255,255,255)', flags);
|
|
|
|
shell.sendChromeEvent({
|
|
type: 'take-screenshot-success',
|
|
file: canvas.mozGetAsFile('screenshot', 'image/png')
|
|
});
|
|
} 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() {
|
|
Cu.import('resource://gre/modules/WebappsUpdater.jsm');
|
|
WebappsUpdater.handleContentStart(shell);
|
|
|
|
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);
|
|
});
|
|
|
|
(function geolocationStatusTracker() {
|
|
let gGeolocationActive = false;
|
|
|
|
Services.obs.addObserver(function(aSubject, aTopic, aData) {
|
|
let oldState = gGeolocationActive;
|
|
if (aData == "starting") {
|
|
gGeolocationActive = true;
|
|
} else if (aData == "shutdown") {
|
|
gGeolocationActive = false;
|
|
}
|
|
|
|
if (gGeolocationActive != oldState) {
|
|
shell.sendChromeEvent({
|
|
type: 'geolocation-status',
|
|
active: gGeolocationActive
|
|
});
|
|
}
|
|
}, "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);
|
|
})();
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
// 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;
|
|
Services.prefs.setIntPref("browser.cache.disk.capacity", size);
|
|
}) ()
|
|
#endif
|
|
|
|
#ifdef MOZ_WIDGET_GONK
|
|
let SensorsListener = {
|
|
sensorsListenerDevices: ['crespo'],
|
|
device: libcutils.property_get("ro.product.device"),
|
|
|
|
deviceNeedsWorkaround: function SensorsListener_deviceNeedsWorkaround() {
|
|
return (this.sensorsListenerDevices.indexOf(this.device) != -1);
|
|
},
|
|
|
|
handleEvent: function SensorsListener_handleEvent(evt) {
|
|
switch(evt.type) {
|
|
case 'devicemotion':
|
|
// Listener that does nothing, we need this to have the sensor being
|
|
// able to report correct values, as explained in bug 753245, comment 6
|
|
// and in bug 871916
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
},
|
|
|
|
observe: function SensorsListener_observe(subject, topic, data) {
|
|
// We remove the listener when the screen is off, otherwise sensor will
|
|
// continue to bother us with data and we won't be able to get the
|
|
// system into suspend state, thus draining battery.
|
|
if (data === 'on') {
|
|
window.addEventListener('devicemotion', this);
|
|
} else {
|
|
window.removeEventListener('devicemotion', this);
|
|
}
|
|
},
|
|
|
|
init: function SensorsListener_init() {
|
|
if (this.deviceNeedsWorkaround()) {
|
|
// On boot, enable the listener, screen will be on.
|
|
window.addEventListener('devicemotion', this);
|
|
|
|
// Then listen for further screen state changes
|
|
Services.obs.addObserver(this, 'screen-state-changed', false);
|
|
}
|
|
}
|
|
}
|
|
|
|
SensorsListener.init();
|
|
#endif
|
|
|
|
// 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-change2' 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);
|
|
#ifdef MOZ_WIDGET_GONK
|
|
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);
|
|
}
|
|
}
|
|
#endif
|
|
},
|
|
'profile-before-change2', false);
|
|
|
|
let appStartup = Cc['@mozilla.org/toolkit/app-startup;1']
|
|
.getService(Ci.nsIAppStartup);
|
|
appStartup.quit(Ci.nsIAppStartup.eForceQuit);
|
|
}, 'b2g-reset-profile', false);
|
|
|
|
/**
|
|
* CID of our implementation of nsIDownloadManagerUI.
|
|
*/
|
|
const kTransferCid = Components.ID("{1b4c85df-cbdd-4bb6-b04e-613caece083c}");
|
|
|
|
/**
|
|
* Contract ID of the service implementing nsITransfer.
|
|
*/
|
|
const kTransferContractId = "@mozilla.org/transfer;1";
|
|
|
|
// Override Toolkit's nsITransfer implementation with the one from the
|
|
// JavaScript API for downloads. This will eventually be removed when
|
|
// nsIDownloadManager will not be available anymore (bug 851471). The
|
|
// old code in this module will be removed in bug 899110.
|
|
Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
|
|
.registerFactory(kTransferCid, "",
|
|
kTransferContractId, null);
|