Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 let Cc = Components.classes;
5 let Ci = Components.interfaces;
6 let Cu = Components.utils;
8 Cu.import("resource://gre/modules/Services.jsm");
9 Cu.import("resource://gre/modules/FileUtils.jsm");
10 Cu.import("resource://gre/modules/NetUtil.jsm");
11 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
12 Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
13 Cu.import("resource://gre/modules/ContactService.jsm");
14 #ifdef MOZ_ANDROID_SYNTHAPKS
15 Cu.import("resource://gre/modules/AppsUtils.jsm");
17 XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm");
18 #endif
20 function pref(name, value) {
21 return {
22 name: name,
23 value: value
24 }
25 }
27 let WebappRT = {
28 DEFAULT_PREFS_FILENAME: "default-prefs.js",
30 prefs: [
31 // Disable all add-on locations other than the profile (which can't be disabled this way)
32 pref("extensions.enabledScopes", 1),
33 // Auto-disable any add-ons that are "dropped in" to the profile
34 pref("extensions.autoDisableScopes", 1),
35 // Disable add-on installation via the web-exposed APIs
36 pref("xpinstall.enabled", false),
37 // Set a future policy version to avoid the telemetry prompt.
38 pref("toolkit.telemetry.prompted", 999),
39 pref("toolkit.telemetry.notifiedOptOut", 999),
40 pref("media.useAudioChannelService", true),
41 pref("dom.mozTCPSocket.enabled", true),
42 // Don't check for updates in webapp processes to avoid duplicate notifications.
43 pref("browser.webapps.checkForUpdates", 0),
44 ],
46 init: function(aStatus, aUrl, aCallback) {
47 this.deck = document.getElementById("browsers");
48 this.deck.addEventListener("click", this, false, true);
50 // on first run, update any prefs
51 if (aStatus == "new") {
52 this.getDefaultPrefs().forEach(this.addPref);
54 // update the blocklist url to use a different app id
55 let blocklist = Services.prefs.getCharPref("extensions.blocklist.url");
56 blocklist = blocklist.replace(/%APP_ID%/g, "webapprt-mobile@mozilla.org");
57 Services.prefs.setCharPref("extensions.blocklist.url", blocklist);
58 }
60 // On firstrun, set permissions to their default values.
61 // When the webapp runtime is updated, update the permissions.
62 if (aStatus == "new" || aStatus == "upgrade") {
63 this.getManifestFor(aUrl, function (aManifest, aApp) {
64 if (aManifest) {
65 PermissionsInstaller.installPermissions(aApp, true);
66 }
67 });
68 }
70 #ifdef MOZ_ANDROID_SYNTHAPKS
71 // If the app is in debug mode, configure and enable the remote debugger.
72 sendMessageToJava({ type: "NativeApp:IsDebuggable" }, (response) => {
73 if (response.isDebuggable) {
74 this._enableRemoteDebugger(aUrl);
75 }
76 });
77 #endif
79 this.findManifestUrlFor(aUrl, aCallback);
80 },
82 getManifestFor: function (aUrl, aCallback) {
83 DOMApplicationRegistry.registryReady.then(() => {
84 let request = navigator.mozApps.mgmt.getAll();
85 request.onsuccess = function() {
86 let apps = request.result;
87 for (let i = 0; i < apps.length; i++) {
88 let app = apps[i];
89 let manifest = new ManifestHelper(app.manifest, app.origin);
91 // if this is a path to the manifest, or the launch path, then we have a hit.
92 if (app.manifestURL == aUrl || manifest.fullLaunchPath() == aUrl) {
93 aCallback(manifest, app);
94 return;
95 }
96 }
98 // Otherwise, once we loop through all of them, we have a miss.
99 aCallback(undefined);
100 };
102 request.onerror = function() {
103 // Treat an error like a miss. We can't find the manifest.
104 aCallback(undefined);
105 };
106 });
107 },
109 findManifestUrlFor: function(aUrl, aCallback) {
110 this.getManifestFor(aUrl, function(aManifest, aApp) {
111 if (!aManifest) {
112 // we can't find the manifest, so open it like a web page
113 aCallback(aUrl);
114 return;
115 }
117 BrowserApp.manifest = aManifest;
118 BrowserApp.manifestUrl = aApp.manifestURL;
120 aCallback(aManifest.fullLaunchPath());
121 });
122 },
124 getDefaultPrefs: function() {
125 // read default prefs from the disk
126 try {
127 let defaultPrefs = [];
128 try {
129 defaultPrefs = this.readDefaultPrefs(FileUtils.getFile("ProfD", [this.DEFAULT_PREFS_FILENAME]));
130 } catch(ex) {
131 // this can throw if the defaultprefs.js file doesn't exist
132 }
133 for (let i = 0; i < defaultPrefs.length; i++) {
134 this.prefs.push(defaultPrefs[i]);
135 }
136 } catch(ex) {
137 console.log("Error reading defaultPrefs file: " + ex);
138 }
139 return this.prefs;
140 },
142 readDefaultPrefs: function webapps_readDefaultPrefs(aFile) {
143 let fstream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream);
144 fstream.init(aFile, -1, 0, 0);
145 let prefsString = NetUtil.readInputStreamToString(fstream, fstream.available(), {});
146 return JSON.parse(prefsString);
147 },
149 addPref: function(aPref) {
150 switch (typeof aPref.value) {
151 case "string":
152 Services.prefs.setCharPref(aPref.name, aPref.value);
153 break;
154 case "boolean":
155 Services.prefs.setBoolPref(aPref.name, aPref.value);
156 break;
157 case "number":
158 Services.prefs.setIntPref(aPref.name, aPref.value);
159 break;
160 }
161 },
163 #ifdef MOZ_ANDROID_SYNTHAPKS
164 _enableRemoteDebugger: function(aUrl) {
165 // Skip the connection prompt in favor of notifying the user below.
166 Services.prefs.setBoolPref("devtools.debugger.prompt-connection", false);
168 // Automagically find a free port and configure the debugger to use it.
169 let serv = Cc['@mozilla.org/network/server-socket;1'].createInstance(Ci.nsIServerSocket);
170 serv.init(-1, true, -1);
171 let port = serv.port;
172 serv.close();
173 Services.prefs.setIntPref("devtools.debugger.remote-port", port);
175 Services.prefs.setBoolPref("devtools.debugger.remote-enabled", true);
177 // Notify the user that we enabled the debugger and which port it's using
178 // so they can use the DevTools Connect… dialog to connect the client to it.
179 DOMApplicationRegistry.registryReady.then(() => {
180 let name;
181 let app = DOMApplicationRegistry.getAppByManifestURL(aUrl);
182 if (app) {
183 name = app.name;
184 } else {
185 name = Strings.browser.GetStringFromName("remoteNotificationGenericName");
186 }
188 Notifications.create({
189 title: Strings.browser.formatStringFromName("remoteNotificationTitle", [name], 1),
190 message: Strings.browser.formatStringFromName("remoteNotificationMessage", [port], 1),
191 icon: "drawable://warning_doorhanger",
192 });
193 });
194 },
195 #endif
197 handleEvent: function(event) {
198 let target = event.target;
200 // walk up the tree to find the nearest link tag
201 while (target && !(target instanceof HTMLAnchorElement)) {
202 target = target.parentNode;
203 }
205 if (!target || target.getAttribute("target") != "_blank") {
206 return;
207 }
209 let uri = Services.io.newURI(target.href, target.ownerDocument.characterSet, null);
211 // Direct the URL to the browser.
212 Cc["@mozilla.org/uriloader/external-protocol-service;1"].
213 getService(Ci.nsIExternalProtocolService).
214 getProtocolHandlerInfo(uri.scheme).
215 launchWithURI(uri);
217 // Prevent the runtime from loading the URL. We do this after directing it
218 // to the browser to give the runtime a shot at handling the URL if we fail
219 // to direct it to the browser for some reason.
220 event.preventDefault();
221 }
222 }