michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: let Cc = Components.classes; michael@0: let Ci = Components.interfaces; michael@0: let Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/FileUtils.jsm"); michael@0: Cu.import("resource://gre/modules/NetUtil.jsm"); michael@0: Cu.import("resource://gre/modules/PermissionsInstaller.jsm"); michael@0: Cu.import("resource://gre/modules/PermissionPromptHelper.jsm"); michael@0: Cu.import("resource://gre/modules/ContactService.jsm"); michael@0: #ifdef MOZ_ANDROID_SYNTHAPKS michael@0: Cu.import("resource://gre/modules/AppsUtils.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm"); michael@0: #endif michael@0: michael@0: function pref(name, value) { michael@0: return { michael@0: name: name, michael@0: value: value michael@0: } michael@0: } michael@0: michael@0: let WebappRT = { michael@0: DEFAULT_PREFS_FILENAME: "default-prefs.js", michael@0: michael@0: prefs: [ michael@0: // Disable all add-on locations other than the profile (which can't be disabled this way) michael@0: pref("extensions.enabledScopes", 1), michael@0: // Auto-disable any add-ons that are "dropped in" to the profile michael@0: pref("extensions.autoDisableScopes", 1), michael@0: // Disable add-on installation via the web-exposed APIs michael@0: pref("xpinstall.enabled", false), michael@0: // Set a future policy version to avoid the telemetry prompt. michael@0: pref("toolkit.telemetry.prompted", 999), michael@0: pref("toolkit.telemetry.notifiedOptOut", 999), michael@0: pref("media.useAudioChannelService", true), michael@0: pref("dom.mozTCPSocket.enabled", true), michael@0: // Don't check for updates in webapp processes to avoid duplicate notifications. michael@0: pref("browser.webapps.checkForUpdates", 0), michael@0: ], michael@0: michael@0: init: function(aStatus, aUrl, aCallback) { michael@0: this.deck = document.getElementById("browsers"); michael@0: this.deck.addEventListener("click", this, false, true); michael@0: michael@0: // on first run, update any prefs michael@0: if (aStatus == "new") { michael@0: this.getDefaultPrefs().forEach(this.addPref); michael@0: michael@0: // update the blocklist url to use a different app id michael@0: let blocklist = Services.prefs.getCharPref("extensions.blocklist.url"); michael@0: blocklist = blocklist.replace(/%APP_ID%/g, "webapprt-mobile@mozilla.org"); michael@0: Services.prefs.setCharPref("extensions.blocklist.url", blocklist); michael@0: } michael@0: michael@0: // On firstrun, set permissions to their default values. michael@0: // When the webapp runtime is updated, update the permissions. michael@0: if (aStatus == "new" || aStatus == "upgrade") { michael@0: this.getManifestFor(aUrl, function (aManifest, aApp) { michael@0: if (aManifest) { michael@0: PermissionsInstaller.installPermissions(aApp, true); michael@0: } michael@0: }); michael@0: } michael@0: michael@0: #ifdef MOZ_ANDROID_SYNTHAPKS michael@0: // If the app is in debug mode, configure and enable the remote debugger. michael@0: sendMessageToJava({ type: "NativeApp:IsDebuggable" }, (response) => { michael@0: if (response.isDebuggable) { michael@0: this._enableRemoteDebugger(aUrl); michael@0: } michael@0: }); michael@0: #endif michael@0: michael@0: this.findManifestUrlFor(aUrl, aCallback); michael@0: }, michael@0: michael@0: getManifestFor: function (aUrl, aCallback) { michael@0: DOMApplicationRegistry.registryReady.then(() => { michael@0: let request = navigator.mozApps.mgmt.getAll(); michael@0: request.onsuccess = function() { michael@0: let apps = request.result; michael@0: for (let i = 0; i < apps.length; i++) { michael@0: let app = apps[i]; michael@0: let manifest = new ManifestHelper(app.manifest, app.origin); michael@0: michael@0: // if this is a path to the manifest, or the launch path, then we have a hit. michael@0: if (app.manifestURL == aUrl || manifest.fullLaunchPath() == aUrl) { michael@0: aCallback(manifest, app); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // Otherwise, once we loop through all of them, we have a miss. michael@0: aCallback(undefined); michael@0: }; michael@0: michael@0: request.onerror = function() { michael@0: // Treat an error like a miss. We can't find the manifest. michael@0: aCallback(undefined); michael@0: }; michael@0: }); michael@0: }, michael@0: michael@0: findManifestUrlFor: function(aUrl, aCallback) { michael@0: this.getManifestFor(aUrl, function(aManifest, aApp) { michael@0: if (!aManifest) { michael@0: // we can't find the manifest, so open it like a web page michael@0: aCallback(aUrl); michael@0: return; michael@0: } michael@0: michael@0: BrowserApp.manifest = aManifest; michael@0: BrowserApp.manifestUrl = aApp.manifestURL; michael@0: michael@0: aCallback(aManifest.fullLaunchPath()); michael@0: }); michael@0: }, michael@0: michael@0: getDefaultPrefs: function() { michael@0: // read default prefs from the disk michael@0: try { michael@0: let defaultPrefs = []; michael@0: try { michael@0: defaultPrefs = this.readDefaultPrefs(FileUtils.getFile("ProfD", [this.DEFAULT_PREFS_FILENAME])); michael@0: } catch(ex) { michael@0: // this can throw if the defaultprefs.js file doesn't exist michael@0: } michael@0: for (let i = 0; i < defaultPrefs.length; i++) { michael@0: this.prefs.push(defaultPrefs[i]); michael@0: } michael@0: } catch(ex) { michael@0: console.log("Error reading defaultPrefs file: " + ex); michael@0: } michael@0: return this.prefs; michael@0: }, michael@0: michael@0: readDefaultPrefs: function webapps_readDefaultPrefs(aFile) { michael@0: let fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream); michael@0: fstream.init(aFile, -1, 0, 0); michael@0: let prefsString = NetUtil.readInputStreamToString(fstream, fstream.available(), {}); michael@0: return JSON.parse(prefsString); michael@0: }, michael@0: michael@0: addPref: function(aPref) { michael@0: switch (typeof aPref.value) { michael@0: case "string": michael@0: Services.prefs.setCharPref(aPref.name, aPref.value); michael@0: break; michael@0: case "boolean": michael@0: Services.prefs.setBoolPref(aPref.name, aPref.value); michael@0: break; michael@0: case "number": michael@0: Services.prefs.setIntPref(aPref.name, aPref.value); michael@0: break; michael@0: } michael@0: }, michael@0: michael@0: #ifdef MOZ_ANDROID_SYNTHAPKS michael@0: _enableRemoteDebugger: function(aUrl) { michael@0: // Skip the connection prompt in favor of notifying the user below. michael@0: Services.prefs.setBoolPref("devtools.debugger.prompt-connection", false); michael@0: michael@0: // Automagically find a free port and configure the debugger to use it. michael@0: let serv = Cc['@mozilla.org/network/server-socket;1'].createInstance(Ci.nsIServerSocket); michael@0: serv.init(-1, true, -1); michael@0: let port = serv.port; michael@0: serv.close(); michael@0: Services.prefs.setIntPref("devtools.debugger.remote-port", port); michael@0: michael@0: Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true); michael@0: michael@0: // Notify the user that we enabled the debugger and which port it's using michael@0: // so they can use the DevTools Connect… dialog to connect the client to it. michael@0: DOMApplicationRegistry.registryReady.then(() => { michael@0: let name; michael@0: let app = DOMApplicationRegistry.getAppByManifestURL(aUrl); michael@0: if (app) { michael@0: name = app.name; michael@0: } else { michael@0: name = Strings.browser.GetStringFromName("remoteNotificationGenericName"); michael@0: } michael@0: michael@0: Notifications.create({ michael@0: title: Strings.browser.formatStringFromName("remoteNotificationTitle", [name], 1), michael@0: message: Strings.browser.formatStringFromName("remoteNotificationMessage", [port], 1), michael@0: icon: "drawable://warning_doorhanger", michael@0: }); michael@0: }); michael@0: }, michael@0: #endif michael@0: michael@0: handleEvent: function(event) { michael@0: let target = event.target; michael@0: michael@0: // walk up the tree to find the nearest link tag michael@0: while (target && !(target instanceof HTMLAnchorElement)) { michael@0: target = target.parentNode; michael@0: } michael@0: michael@0: if (!target || target.getAttribute("target") != "_blank") { michael@0: return; michael@0: } michael@0: michael@0: let uri = Services.io.newURI(target.href, target.ownerDocument.characterSet, null); michael@0: michael@0: // Direct the URL to the browser. michael@0: Cc["@mozilla.org/uriloader/external-protocol-service;1"]. michael@0: getService(Ci.nsIExternalProtocolService). michael@0: getProtocolHandlerInfo(uri.scheme). michael@0: launchWithURI(uri); michael@0: michael@0: // Prevent the runtime from loading the URL. We do this after directing it michael@0: // to the browser to give the runtime a shot at handling the URL if we fail michael@0: // to direct it to the browser for some reason. michael@0: event.preventDefault(); michael@0: } michael@0: }