Bug 1181209 - Make changes to Gecko needed for b2gdroid to boot. r?blassey draft
authorFabrice Desré <fabrice@mozilla.com>
Thu, 30 Jul 2015 11:47:47 -0700
changeset 285597 0dca3a96f62a14cc7636862241e99ef0c10ffe0f
parent 285596 4c8b0f4504dd4070aa13dcfe8bafd28a48e974b1
child 285598 61f6eaf4ee49e3e35678e3f18e70db3962d9a406
push id4466
push usernalexander@mozilla.com
push dateMon, 17 Aug 2015 16:51:24 +0000
reviewersblassey
bugs1181209
milestone43.0a1
Bug 1181209 - Make changes to Gecko needed for b2gdroid to boot. r?blassey
b2g/chrome/content/shell.html
b2g/chrome/content/shell.js
b2g/components/B2GComponents.manifest
b2g/components/moz.build
configure.in
dom/apps/Webapps.jsm
hal/android/AndroidHal.cpp
mobile/android/app/mobile.js
mobile/android/app/moz.build
mobile/android/b2gdroid/b2gdroid.js
mobile/android/b2gdroid/components/B2GDroidSetup.js
mobile/android/b2gdroid/components/b2gdroid.manifest
mobile/android/b2gdroid/components/moz.build
mobile/android/b2gdroid/confvars.sh
mobile/android/b2gdroid/installer/package-manifest.in
mobile/android/components/DirectoryProvider.js
mobile/android/components/MobileComponents.manifest
mobile/android/components/moz.build
services/moz.build
toolkit/modules/AppConstants.jsm
toolkit/modules/FileUtils.jsm
toolkit/webapps/WebappOSUtils.jsm
--- a/b2g/chrome/content/shell.html
+++ b/b2g/chrome/content/shell.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <!-- 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/.  -->
 
-<html xmlns="http://www.w3.org/1999/xhtml "
+<html xmlns="http://www.w3.org/1999/xhtml"
       id="shell"
       windowtype="navigator:browser"
 #ifdef ANDROID
       sizemode="fullscreen"
 #endif
       >
 
 <head>
@@ -20,17 +20,17 @@
   <!-- Add raptor performance marker -->
   window.performance.mark('gecko-shell-html-load');
   </script>
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/settings.js"> </script>
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/shell.js"> </script>
 
-#ifndef MOZ_WIDGET_GONK
+#ifndef ANDROID
   <!-- various task that has to happen only on desktop -->
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/desktop.js"> </script>
   <!-- this script handles the screen argument for desktop builds -->
   <script type="application/javascript;version=1.8"
           src="chrome://b2g/content/screen.js"> </script>
   <!-- this script handles the "runapp" argument for desktop builds -->
   <script type="application/javascript;version=1.8"
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -226,16 +226,21 @@ var shell = {
   },
 
   _started: false,
   hasStarted: function shell_hasStarted() {
     return this._started;
   },
 
   bootstrap: function() {
+#ifdef MOZ_B2GDROID
+    Cc["@mozilla.org/b2g/b2gdroid-setup;1"]
+      .getService(Ci.nsIObserver).observe(window, "shell-startup", null);
+#endif
+
     window.performance.mark('gecko-shell-bootstrap');
     let startManifestURL =
       Cc['@mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap']
         .getService(Ci.nsISupports).wrappedJSObject.startManifestURL;
     if (startManifestURL) {
       Cu.import('resource://gre/modules/Bootstraper.jsm');
       Bootstraper.ensureSystemAppInstall(startManifestURL)
                  .then(this.start.bind(this))
@@ -692,17 +697,18 @@ var shell = {
       }
       delete shell.pendingChromeEvents;
     });
 
     shell.handleCmdLine();
   },
 
   handleCmdLine: function shell_handleCmdLine() {
-#ifndef MOZ_WIDGET_GONK
+  // This isn't supported on devices.
+#ifndef ANDROID
     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});
@@ -1048,27 +1054,29 @@ window.addEventListener('ContentStart', 
     }
   });
 });
 
 window.addEventListener('ContentStart', function update_onContentStart() {
   Cu.import('resource://gre/modules/WebappsUpdater.jsm');
   WebappsUpdater.handleContentStart(shell);
 
+#ifdef MOZ_UPDATER
   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);
+#endif
 });
 
 (function geolocationStatusTracker() {
   let gGeolocationActive = false;
 
   Services.obs.addObserver(function(aSubject, aTopic, aData) {
     let oldState = gGeolocationActive;
     if (aData == "starting") {
--- a/b2g/components/B2GComponents.manifest
+++ b/b2g/components/B2GComponents.manifest
@@ -11,20 +11,25 @@ contract @mozilla.org/content-permission
 
 #ifdef MOZ_UPDATER
 # UpdatePrompt.js
 component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js
 contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
 category system-update-provider MozillaProvider @mozilla.org/updates/update-prompt;1,{88b3eb21-d072-4e3b-886d-f89d8c49fe59}
 #endif
 
+# On b2gdroid we want to use the android implementation of the directory service.
+#ifndef MOZ_B2GDROID
+#ifdef MOZ_B2G
 # DirectoryProvider.js
 component {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5} DirectoryProvider.js
 contract @mozilla.org/b2g/directory-provider;1 {9181eb7c-6f87-11e1-90b1-4f59d80dd2e5}
 category xpcom-directory-providers b2g-directory-provider @mozilla.org/b2g/directory-provider;1
+#endif
+#endif
 
 # ActivitiesGlue.js
 component {3a54788b-48cc-4ab4-93d6-0d6a8ef74f8e} ActivitiesGlue.js
 contract @mozilla.org/dom/activities/ui-glue;1 {3a54788b-48cc-4ab4-93d6-0d6a8ef74f8e}
 
 # InterAppCommUIGlue.js
 component {879ee66c-e246-11e3-9910-74d02b97e723} InterAppCommUIGlue.js
 contract @mozilla.org/dom/apps/inter-app-comm-ui-glue;1 {879ee66c-e246-11e3-9910-74d02b97e723}
@@ -82,29 +87,31 @@ category update-timer WebappsUpdateTimer
 # FxAccountsUIGlue.js
 component {51875c14-91d7-4b8c-b65d-3549e101228c} FxAccountsUIGlue.js
 contract @mozilla.org/fxaccounts/fxaccounts-ui-glue;1 {51875c14-91d7-4b8c-b65d-3549e101228c}
 
 # HelperAppDialog.js
 component {710322af-e6ae-4b0c-b2c9-1474a87b077e} HelperAppDialog.js
 contract @mozilla.org/helperapplauncherdialog;1 {710322af-e6ae-4b0c-b2c9-1474a87b077e}
 
+#ifndef MOZ_B2GDROID
 #ifndef MOZ_WIDGET_GONK
 component {c83c02c0-5d43-4e3e-987f-9173b313e880} SimulatorScreen.js
 contract @mozilla.org/simulator-screen;1 {c83c02c0-5d43-4e3e-987f-9173b313e880}
 category profile-after-change SimulatorScreen @mozilla.org/simulator-screen;1
 
 component {e30b0e13-2d12-4cb0-bc4c-4e617a1bf76e} OopCommandLine.js
 contract @mozilla.org/commandlinehandler/general-startup;1?type=b2goop {e30b0e13-2d12-4cb0-bc4c-4e617a1bf76e}
 category command-line-handler m-b2goop @mozilla.org/commandlinehandler/general-startup;1?type=b2goop
 
 component {385993fe-8710-4621-9fb1-00a09d8bec37} CommandLine.js
 contract @mozilla.org/commandlinehandler/general-startup;1?type=b2gcmds {385993fe-8710-4621-9fb1-00a09d8bec37}
 category command-line-handler m-b2gcmds @mozilla.org/commandlinehandler/general-startup;1?type=b2gcmds
 #endif
+#endif
 
 # BootstrapCommandLine.js
 component {fd663ec8-cf3f-4c2b-aacb-17a6915ccb44} BootstrapCommandLine.js
 contract @mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap {fd663ec8-cf3f-4c2b-aacb-17a6915ccb44}
 category command-line-handler m-b2gbootstrap @mozilla.org/commandlinehandler/general-startup;1?type=b2gbootstrap
 
 # MobileIdentityUIGlue.js
 component {83dbe26a-81f3-4a75-9541-3d0b7ca496b5} MobileIdentityUIGlue.js
--- a/b2g/components/moz.build
+++ b/b2g/components/moz.build
@@ -26,29 +26,34 @@ EXTRA_COMPONENTS += [
     'PresentationRequestUIGlue.js',
     'ProcessGlobal.js',
     'SmsProtocolHandler.js',
     'SystemMessageGlue.js',
     'TelProtocolHandler.js',
     'WebappsUpdateTimer.js',
 ]
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk':
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'gonk' and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     EXTRA_COMPONENTS += [
       'CommandLine.js',
       'OopCommandLine.js',
       'SimulatorScreen.js'
     ]
 
 EXTRA_PP_COMPONENTS += [
     'B2GComponents.manifest',
-    'DirectoryProvider.js',
     'RecoveryService.js',
 ]
 
+if CONFIG['MOZ_B2G'] and not CONFIG['MOZ_B2GDROID']:
+    EXTRA_PP_COMPONENTS += [
+        'DirectoryProvider.js',
+        'RecoveryService.js',
+    ]
+
 if CONFIG['MOZ_UPDATER']:
     EXTRA_PP_COMPONENTS += [
         'UpdatePrompt.js',
     ]
 
 EXTRA_JS_MODULES += [
     'AboutServiceWorkers.jsm',
     'AlertsHelper.jsm',
--- a/configure.in
+++ b/configure.in
@@ -4069,16 +4069,20 @@ b2g)
   AC_DEFINE(MOZ_B2G)
   ;;
 b2g/dev)
   AC_DEFINE(MOZ_B2G)
   AC_DEFINE(MOZ_MULET)
   ;;
 esac
 
+if test -n "$MOZ_B2GDROID"; then
+    AC_DEFINE(MOZ_B2GDROID)
+fi
+
 AC_SUBST(MOZ_BUILD_APP)
 AC_SUBST(MOZ_PHOENIX)
 AC_SUBST(MOZ_XULRUNNER)
 AC_SUBST(MOZ_B2G)
 AC_SUBST(MOZ_MULET)
 AC_SUBST(MOZ_B2G_VERSION)
 AC_SUBST(MOZ_B2GDROID)
 
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -86,16 +86,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/Langpacks.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TrustedHostedAppsUtils",
                                   "resource://gre/modules/TrustedHostedAppsUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ImportExport",
                                   "resource://gre/modules/ImportExport.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
+                                  "resource://gre/modules/AppConstants.jsm");
+
 #ifdef MOZ_WIDGET_GONK
 XPCOMUtils.defineLazyGetter(this, "libcutils", function() {
   Cu.import("resource://gre/modules/systemlibs.js");
   return libcutils;
 });
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
@@ -782,19 +785,19 @@ this.DOMApplicationRegistry = {
               appMigrator.observe(null, "webapps-before-update-merge", null);
           } catch(e) {
             debug("Exception running app migration: ");
             debug(e.name + " " + e.message);
             debug("Skipping app migration.");
           }
         }
 
-#ifdef MOZ_B2G
-        yield this.installSystemApps();
-#endif
+        if (AppConstants.MOZ_B2GDROID || AppConstants.MOZ_B2G) {
+          yield this.installSystemApps();
+        }
 
         // At first run, install preloaded apps and set up their permissions.
         for (let id in this.webapps) {
           let isPreinstalled = this.installPreinstalledApp(id);
           this.removeIfHttpsDuplicate(id);
           if (!this.webapps[id]) {
             continue;
           }
--- a/hal/android/AndroidHal.cpp
+++ b/hal/android/AndroidHal.cpp
@@ -134,26 +134,31 @@ GetCurrentScreenConfiguration(ScreenConf
 
   *aScreenConfiguration =
     hal::ScreenConfiguration(rect, orientation, colorDepth, pixelDepth);
 }
 
 bool
 LockScreenOrientation(const ScreenOrientation& aOrientation)
 {
-  switch (aOrientation) {
+  // Force the default orientation to be portrait-primary.
+  ScreenOrientation orientation =
+    aOrientation == eScreenOrientation_Default ? eScreenOrientation_PortraitPrimary
+                                               : aOrientation;
+
+  switch (orientation) {
     // The Android backend only supports these orientations.
     case eScreenOrientation_PortraitPrimary:
     case eScreenOrientation_PortraitSecondary:
     case eScreenOrientation_PortraitPrimary | eScreenOrientation_PortraitSecondary:
     case eScreenOrientation_LandscapePrimary:
     case eScreenOrientation_LandscapeSecondary:
     case eScreenOrientation_LandscapePrimary | eScreenOrientation_LandscapeSecondary:
     case eScreenOrientation_Default:
-      mozilla::widget::GeckoAppShell::LockScreenOrientation(aOrientation);
+      mozilla::widget::GeckoAppShell::LockScreenOrientation(orientation);
       return true;
     default:
       return false;
   }
 }
 
 void
 UnlockScreenOrientation()
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -926,8 +926,13 @@ pref("consoleservice.logcat", false);
 pref("consoleservice.logcat", true);
 #endif
 
 // Enable Cardboard VR on mobile, assuming VR at all is enabled
 pref("dom.vr.cardboard.enabled", true);
 
 // TODO: Disabled until bug 1190301 is fixed.
 pref("browser.tabs.showAudioPlayingIcon", false);
+
+// Include b2gdroid preferences.
+#ifdef MOZ_B2GDROID
+#include ../b2gdroid/b2gdroid.js
+#endif
--- a/mobile/android/app/moz.build
+++ b/mobile/android/app/moz.build
@@ -9,14 +9,14 @@ for var in ('APP_NAME', 'APP_VERSION'):
 
 for var in ('MOZ_UPDATER', 'MOZ_APP_UA_NAME', 'ANDROID_PACKAGE_NAME'):
     DEFINES[var] = CONFIG[var]
 
 if CONFIG['MOZ_PKG_SPECIAL']:
     DEFINES['MOZ_PKG_SPECIAL'] = CONFIG['MOZ_PKG_SPECIAL']
 
 JS_PREFERENCE_FILES += [
-    'mobile.js',
+     'mobile.js',
 ]
 
 DIST_FILES += [
   'ua-update.json.in',
 ]
new file mode 100644
--- /dev/null
+++ b/mobile/android/b2gdroid/b2gdroid.js
@@ -0,0 +1,90 @@
+// B2GDroid preferences.
+
+pref("toolkit.defaultChromeURI", "chrome://b2g/content/shell.html");
+
+// Setup webapps to use a single profile.
+pref("dom.webapps.useCurrentProfile", true);
+
+// Enable system messages.
+pref("dom.sysmsg.enabled", true);
+
+// Enable the mozSettings api.
+pref("dom.mozSettings.enabled", true);
+
+// Enable dataStore.
+pref("dom.datastore.enabled", true);
+// When an entry is changed, use two timers to fire system messages in a more
+// moderate pattern.
+pref("dom.datastore.sysMsgOnChangeShortTimeoutSec", 10);
+pref("dom.datastore.sysMsgOnChangeLongTimeoutSec", 60);
+
+// DOM Inter-App Communication API.
+pref("dom.inter-app-communication-api.enabled", true);
+
+// Downloads API
+pref("dom.mozDownloads.enabled", true);
+pref("dom.downloads.max_retention_days", 7);
+
+// Startup prefs.
+// TODO: don't hardcode that.
+pref("b2g.system_startup_url", "app://system.gaiamobile.org/index.html");
+pref("b2g.system_manifest_url", "app://system.gaiamobile.org/manifest.webapp");
+
+pref("dom.mozBrowserFramesEnabled", true);
+
+// Default Content Security Policy to apply to certified apps.
+// If you change this CSP, make sure to update the fast path in nsCSPService.cpp
+pref("security.apps.certified.CSP.default", "default-src * data: blob:; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline' app://theme.gaiamobile.org");
+// Default Content Security Policy to apply to trusted apps.
+pref("security.apps.trusted.CSP.default", "default-src * data: blob:; object-src 'none'; frame-src 'none'");
+
+pref("b2g.orientation.animate", true);
+
+// The origin used for the shared themes uri space.
+pref("b2g.theme.origin", "app://theme.gaiamobile.org");
+pref("dom.mozApps.themable", true);
+pref("dom.mozApps.selected_theme", "default_theme.gaiamobile.org");
+
+// InputMethod so we can do soft keyboards.
+pref("dom.mozInputMethod.enabled", true);
+
+// sms api.
+pref("dom.sms.enabled", true);
+
+// Enable native identity (persona/browserid)
+pref("dom.identity.enabled", true);
+
+// Enable sync and mozId with Firefox Accounts.
+pref("services.sync.fxaccounts.enabled", true);
+pref("identity.fxaccounts.enabled", true);
+
+// Mobile Identity API.
+pref("services.mobileid.server.uri", "https://msisdn.services.mozilla.com");
+
+// Enable Web Speech synthesis API
+pref("media.webspeech.synth.enabled", true);
+
+// Enable the permissions api.
+pref("dom.mozPermissionSettings.enabled", true);
+
+// TCPSocket
+pref("dom.mozTCPSocket.enabled", true);
+
+// OOP apps.
+pref("dom.ipc.tabs.disabled", true);
+pref("dom.ipc.processCount", 100000);
+
+// Enable pre-launching content processes for improved startup time
+// (hiding latency).
+pref("dom.ipc.processPrelaunch.enabled", false);
+// Wait this long before pre-launching a new subprocess.
+pref("dom.ipc.processPrelaunch.delayMs", 5000);
+
+pref("dom.ipc.reuse_parent_app", false);
+
+// When a process receives a system message, we hold a CPU wake lock on its
+// behalf for this many seconds, or until it handles the system message,
+// whichever comes first.
+pref("dom.ipc.systemMessageCPULockTimeoutSec", 30);
+
+pref("dom.wakelock.enabled", true);
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/b2gdroid/components/B2GDroidSetup.js
@@ -0,0 +1,244 @@
+/* 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/. */
+
+"use strict";
+
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+let Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+                                  "resource://gre/modules/FileUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "AppsUtils",
+                                  "resource://gre/modules/AppsUtils.jsm");
+
+function debug() {
+  dump("-*- B2GDroidSetup " + Array.slice(arguments) + "\n");
+}
+
+function B2GDroidSetup() { }
+
+B2GDroidSetup.prototype = {
+  classID:         Components.ID('{8bc88ef2-3aab-4e94-a40c-e2c80added2c}'),
+  QueryInterface:  XPCOMUtils.generateQI([Ci.nsIObserver,
+                                          Ci.nsISupportsWeakReference]),
+
+  getApk: function() {
+    let registry = Cc["@mozilla.org/chrome/chrome-registry;1"]
+                     .getService(Ci.nsIChromeRegistry);
+    let url = registry.convertChromeURL(
+      Services.io.newURI("chrome://b2g/content/shell.html", null, null)).spec;
+    // url is something like jar:jar:file:///data/app/org.mozilla.fennec_fabrice-1.apk!/assets/omni.ja!/chrome/chrome/content/shell.html
+    // and we just need the apk file path.
+    let path = url.substring(url.indexOf("///") + 2, url.indexOf(".apk") + 4);
+    debug("apk path: " + path);
+    let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+    file.initWithPath(path);
+    return file;
+  },
+
+  installWebapps: function(aDir) {
+    debug("Extracting webapps");
+    let apk = this.getApk();
+    let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
+                      .createInstance(Ci.nsIZipReader);
+    zipReader.open(apk);
+
+    // Get the target file for a zip entry, normalizing it to remove the
+    // assets/gaia part.
+    function getTargetFile(aDir, entry) {
+      let target = aDir.clone();
+      let part = 0;
+      entry.split("/").forEach(aPart => {
+        if (part > 1) {
+          target.append(aPart);
+        }
+        part++;
+      });
+      return target;
+    }
+
+    try {
+      // create directories first
+      let entries = zipReader.findEntries("assets/gaia/webapps/*");
+      while (entries.hasMore()) {
+        let entryName = entries.getNext();
+        let entry = zipReader.getEntry(entryName);
+        let target = getTargetFile(aDir, entryName);
+        if (!target.exists() && entry.isDirectory) {
+          try {
+            debug("Creating " + entryName);
+            target.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+          }
+          catch (e) {
+            debug("extractFiles: failed to create directory " + target.path + " : " + e);
+          }
+        }
+      }
+
+      entries = zipReader.findEntries("assets/gaia/webapps/*");
+      while (entries.hasMore()) {
+        let entryName = entries.getNext();
+        let target = getTargetFile(aDir, entryName);
+        if (target.exists())
+          continue;
+
+        debug("Extracting " + entryName + " to " + target.path);
+        zipReader.extract(entryName, target);
+        try {
+          target.permissions |= FileUtils.PERMS_FILE;
+        }
+        catch (e) {
+          debug("Failed to set permissions " + aPermissions.toString(8) + " on " + target.path);
+        }
+      }
+    }
+    finally {
+      zipReader.close();
+    }
+
+    debug("Webapps extracted");
+  },
+
+  installSettings: function(aDir) {
+    debug("Installing default settings");
+    let apk = this.getApk();
+    let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
+                      .createInstance(Ci.nsIZipReader);
+    zipReader.open(apk);
+
+    try {
+      let dest = aDir.clone();
+      dest.append("settings.json")
+      zipReader.extract("assets/gaia/settings.json", dest);
+      debug("Default settings installed to " + dest.path);
+    }
+    catch(e) {
+      dump("Error extracting settings.json : " + e);
+    }
+    finally {
+      zipReader.close();
+    }
+  },
+
+  shellStartup: function(aWindow) {
+    Services.androidBridge.browserApp = {
+      selectedTab: function() {
+        debug("browserApp::selectedTab");
+        return null;
+      },
+
+      getBrowserTab: function(aTabId) {
+        debug("browserApp::getBrowserTab " + aTabId);
+        return null;
+      },
+
+      getPreferences: function(aRequestId, aPrefNames, aCount) {
+        debug("browserApp::getPreferences " + uneval(aPrefNames));
+        let prefs = [];
+
+        for (let prefName of aPrefNames) {
+          let pref = {
+            name: prefName,
+            type: "",
+            value: null
+          };
+
+          try {
+            switch (Services.prefs.getPrefType(prefName)) {
+              case Ci.nsIPrefBranch.PREF_BOOL:
+                pref.type = "bool";
+                pref.value = Services.prefs.getBoolPref(prefName);
+                break;
+              case Ci.nsIPrefBranch.PREF_INT:
+                pref.type = "int";
+                pref.value = Services.prefs.getIntPref(prefName);
+                break;
+              case Ci.nsIPrefBranch.PREF_STRING:
+              default:
+                pref.type = "string";
+                try {
+                  // Try in case it's a localized string (will throw an exception if not)
+                  pref.value = Services.prefs.getComplexValue(prefName, Ci.nsIPrefLocalizedString).data;
+                } catch (e) {
+                  pref.value = Services.prefs.getCharPref(prefName);
+                }
+                break;
+            }
+          } catch (e) {
+            debug("Error reading pref [" + prefName + "]: " + e);
+            // preference does not exist; do not send it
+            continue;
+          }
+
+          prefs.push(pref);
+        }
+
+        Messaging.sendRequest({
+          type: "Preferences:Data",
+          requestId: aRequestId, // opaque request identifier, can be any string/int/whatever
+          preferences: prefs
+        });
+      },
+
+      observePreferences: function(requestId, prefNames, count) {
+        debug("browserApp::observePreferences " + prefNames);
+      },
+
+      removePreferenceObservers: function(aRequestId) {
+        debug("browserApp::removePreferenceObservers");
+      },
+
+      getUITelemetryObserver: function() {
+        debug("browserApp:getUITelemetryObserver");
+        return null;
+      }
+    };
+
+    Cu.import("resource://gre/modules/Messaging.jsm");
+    Messaging.sendRequest({ type: "Launcher:Ready" });
+    Messaging.sendRequest({ type: "Gecko:Ready" });
+    debug("Sent Gecko:Ready");
+    aWindow.top.QueryInterface(Ci.nsIInterfaceRequestor)
+           .getInterface(Ci.nsIDOMWindowUtils).isFirstPaint = true;
+    Services.androidBridge.contentDocumentChanged();
+  },
+
+  observe: function (aSubject, aTopic, aData) {
+    debug("observer notification: " + aTopic);
+    if (aTopic === "shell-startup") {
+      this.shellStartup(aSubject);
+      return;
+    }
+
+    if (aTopic !== "profile-after-change") {
+      return;
+    }
+
+    // At first run of after updates, unpack gaia.
+    let branch = Services.prefs.getBranch("b2gdroid");
+    if (!AppsUtils.isFirstRun(branch)) {
+      debug("No need to unpack gaia again.");
+      return;
+    }
+
+    let profile = Services.dirsvc.get("DefRt", Ci.nsIFile);
+    debug("profile directory is " + profile.path);
+
+    let webapps = profile.clone();
+    webapps.append("webapps");
+    webapps.append("webapps.json");
+    this.installWebapps(profile);
+
+    let settings = profile.clone();
+    settings.append("settings.json");
+    this.installSettings(profile);
+  }
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([B2GDroidSetup]);
new file mode 100644
--- /dev/null
+++ b/mobile/android/b2gdroid/components/b2gdroid.manifest
@@ -0,0 +1,4 @@
+# B2GDroidSetup.js
+component {8bc88ef2-3aab-4e94-a40c-e2c80added2c} B2GDroidSetup.js
+contract @mozilla.org/b2g/b2gdroid-setup;1 {8bc88ef2-3aab-4e94-a40c-e2c80added2c}
+category profile-after-change B2GDroidSetup @mozilla.org/b2g/b2gdroid-setup;1
new file mode 100644
--- /dev/null
+++ b/mobile/android/b2gdroid/components/moz.build
@@ -0,0 +1,8 @@
+# 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/.
+
+EXTRA_COMPONENTS += [
+    'b2gdroid.manifest',
+    'B2GDroidSetup.js',
+]
--- a/mobile/android/b2gdroid/confvars.sh
+++ b/mobile/android/b2gdroid/confvars.sh
@@ -1,20 +1,26 @@
 # 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/.
 
 MOZ_APP_BASENAME=B2GDroid
 MOZ_APP_VENDOR=Mozilla
 
 MOZ_B2GDROID=1
+MOZ_B2G=1
 
 MOZ_APP_VERSION=42.0a1
 MOZ_APP_UA_NAME=Firefox
 
+MOZ_UA_OS_AGNOSTIC=1
+
+MOZ_B2G_VERSION=2.5.0.0-prerelease
+MOZ_B2G_OS_NAME=Boot2Gecko
+
 MOZ_BRANDING_DIRECTORY=mobile/android/b2gdroid/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/android/b2gdroid/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 # We support Android SDK version 9 and up by default.
 # See the --enable-android-min-sdk and --enable-android-max-sdk arguments in configure.in.
 MOZ_ANDROID_MIN_SDK_VERSION=9
 
@@ -92,17 +98,17 @@ MOZ_WEBGL_CONFORMANT=1
 # Enable the share handler.
 MOZ_ANDROID_SHARE_OVERLAY=1
 
 # Enable the Mozilla Location Service stumbler.
 # TODO: remove once bug 1180104 is fixed.
 MOZ_ANDROID_MLS_STUMBLER=1
 
 # Enable adding to the system downloads list in pre-release builds.
-MOZ_ANDROID_DOWNLOADS_INTEGRATION=1
+# MOZ_ANDROID_DOWNLOADS_INTEGRATION=1
 
 # Enable Tab Queue
 if test "$NIGHTLY_BUILD"; then
   MOZ_ANDROID_TAB_QUEUE=1
 fi
 
 # Use the low-memory GC tuning.
 export JS_GC_SMALL_CHUNK_SIZE=1
@@ -115,8 +121,11 @@ fi
 # Enable checking that add-ons are signed by the trusted root
 MOZ_ADDON_SIGNING=1
 if test "$MOZ_OFFICIAL_BRANDING"; then
   if test "$MOZ_UPDATE_CHANNEL" = "beta" -o \
           "$MOZ_UPDATE_CHANNEL" = "release"; then
     MOZ_REQUIRE_SIGNING=1
   fi
 fi
+
+MOZ_JSDOWNLOADS=1
+MOZ_TIME_MANAGER=1
--- a/mobile/android/b2gdroid/installer/package-manifest.in
+++ b/mobile/android/b2gdroid/installer/package-manifest.in
@@ -37,16 +37,17 @@
 #ifdef MOZ_DMD
 @BINPATH@/@DLL_PREFIX@dmd@DLL_SUFFIX@
 #endif
 #ifndef MOZ_FOLD_LIBS
 @BINPATH@/@DLL_PREFIX@plc4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@plds4@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@nspr4@DLL_SUFFIX@
 #endif
+@BINPATH@/@DLL_PREFIX@lgpllibs@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@omxplugin@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@omxplugingb@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@omxplugingb235@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@omxpluginhc@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@omxpluginkk@DLL_SUFFIX@
 @BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@
 
 @BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
@@ -126,16 +127,17 @@
 @BINPATH@/components/commandlines.xpt
 @BINPATH@/components/composer.xpt
 @BINPATH@/components/content_events.xpt
 @BINPATH@/components/content_geckomediaplugins.xpt
 @BINPATH@/components/content_html.xpt
 @BINPATH@/components/content_webrtc.xpt
 @BINPATH@/components/content_xslt.xpt
 @BINPATH@/components/cookie.xpt
+@BINPATH@/components/devtools_security.xpt
 @BINPATH@/components/directory.xpt
 @BINPATH@/components/docshell.xpt
 @BINPATH@/components/dom.xpt
 @BINPATH@/components/dom_activities.xpt
 @BINPATH@/components/dom_apps.xpt
 @BINPATH@/components/dom_base.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_core.xpt
@@ -235,19 +237,17 @@
 @BINPATH@/components/profiler.xpt
 #endif
 @BINPATH@/components/proxyObject.xpt
 @BINPATH@/components/rdf.xpt
 @BINPATH@/components/satchel.xpt
 @BINPATH@/components/saxparser.xpt
 @BINPATH@/components/sessionstore.xpt
 @BINPATH@/components/services-crypto-component.xpt
-#ifdef MOZ_CAPTIVEDETECT
 @BINPATH@/components/captivedetect.xpt
-#endif
 @BINPATH@/components/shellservice.xpt
 @BINPATH@/components/shistory.xpt
 @BINPATH@/components/spellchecker.xpt
 @BINPATH@/components/storage.xpt
 @BINPATH@/components/telemetry.xpt
 @BINPATH@/components/toolkit_asyncshutdown.xpt
 @BINPATH@/components/toolkit_filewatcher.xpt
 @BINPATH@/components/toolkit_finalizationwitness.xpt
@@ -316,16 +316,18 @@
 @BINPATH@/components/PermissionPromptService.js
 @BINPATH@/components/PermissionPromptService.manifest
 @BINPATH@/components/fuelApplication.manifest
 @BINPATH@/components/fuelApplication.js
 @BINPATH@/components/WebContentConverter.js
 @BINPATH@/components/BrowserComponents.manifest
 @BINPATH@/components/nsBrowserContentHandler.js
 @BINPATH@/components/nsBrowserGlue.js
+@BINPATH@/components/nsDNSServiceDiscovery.manifest
+@BINPATH@/components/nsDNSServiceDiscovery.js
 @BINPATH@/components/nsSetDefaultBrowser.manifest
 @BINPATH@/components/nsSetDefaultBrowser.js
 @BINPATH@/components/toolkitsearch.manifest
 @BINPATH@/components/nsSearchService.js
 @BINPATH@/components/nsSearchSuggestions.js
 @BINPATH@/components/passwordmgr.manifest
 @BINPATH@/components/nsLoginInfo.js
 @BINPATH@/components/nsLoginManager.js
@@ -432,20 +434,18 @@
 @BINPATH@/components/PeerConnection.manifest
 #endif
 
 #ifdef MOZ_SERVICES_HEALTHREPORT
 @BINPATH@/components/HealthReportComponents.manifest
 @BINPATH@/components/HealthReportService.js
 #endif
 
-#ifdef MOZ_CAPTIVEDETECT
 @BINPATH@/components/CaptivePortalDetectComponents.manifest
 @BINPATH@/components/captivedetect.js
-#endif
 
 #ifdef MOZ_WEBSPEECH
 @BINPATH@/components/dom_webspeechsynth.xpt
 #endif
 
 #ifdef MOZ_DEBUG
 @BINPATH@/components/TestInterfaceJS.js
 @BINPATH@/components/TestInterfaceJS.manifest
@@ -470,16 +470,17 @@
 @BINPATH@/modules/*
 
 #ifdef MOZ_SAFE_BROWSING
 ; Safe Browsing
 @BINPATH@/components/nsURLClassifier.manifest
 @BINPATH@/components/nsUrlClassifierHashCompleter.js
 @BINPATH@/components/nsUrlClassifierListManager.js
 @BINPATH@/components/nsUrlClassifierLib.js
+@BINPATH@/components/PrivateBrowsingTrackingProtectionWhitelist.js
 @BINPATH@/components/url-classifier.xpt
 #endif
 
 ; GNOME hooks
 #ifdef MOZ_ENABLE_GNOME_COMPONENT
 @BINPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@
 #endif
 
@@ -651,12 +652,64 @@ bin/libfreebl_32int64_3.so
 
 #ifdef ENABLE_MARIONETTE
 @BINPATH@/chrome/marionette@JAREXT@ 
 @BINPATH@/chrome/marionette.manifest
 @BINPATH@/components/MarionetteComponents.manifest
 @BINPATH@/components/marionettecomponent.js
 #endif
 
-@BINPATH@/components/WebappsUpdateTimer.js
 @BINPATH@/components/DataStore.manifest
 @BINPATH@/components/DataStoreImpl.js
 @BINPATH@/components/dom_datastore.xpt
+
+# b2g and b2gdroid components
+@BINPATH@/components/b2gdroid.manifest
+@BINPATH@/components/B2GDroidSetup.js
+
+@BINPATH@/components/dom_alarm.xpt
+@BINPATH@/components/AlarmsManager.js
+@BINPATH@/components/AlarmsManager.manifest
+
+@BINPATH@/components/InterAppComm.manifest
+@BINPATH@/components/InterAppCommService.js
+@BINPATH@/components/InterAppConnection.js
+@BINPATH@/components/InterAppMessagePort.js
+
+@BINPATH@/components/nsDOMIdentity.js
+@BINPATH@/components/nsIDService.js
+@BINPATH@/components/Identity.manifest
+
+@BINPATH@/components/SystemMessageInternal.js
+@BINPATH@/components/SystemMessageManager.js
+@BINPATH@/components/SystemMessageCache.js
+@BINPATH@/components/SystemMessageManager.manifest
+
+@BINPATH@/components/B2GComponents.manifest
+@BINPATH@/components/AlertsService.js
+@BINPATH@/components/ContentPermissionPrompt.js
+@BINPATH@/components/ActivitiesGlue.js
+@BINPATH@/components/InterAppCommUIGlue.js
+@BINPATH@/components/SystemMessageGlue.js
+@BINPATH@/components/ProcessGlobal.js
+@BINPATH@/components/OMAContentHandler.js
+@BINPATH@/components/PaymentGlue.js
+@BINPATH@/components/TelProtocolHandler.js
+@BINPATH@/components/SmsProtocolHandler.js
+@BINPATH@/components/MailtoProtocolHandler.js
+@BINPATH@/components/RecoveryService.js
+@BINPATH@/components/B2GAboutRedirector.js
+@BINPATH@/components/FilePicker.js
+@BINPATH@/components/WebappsUpdateTimer.js
+@BINPATH@/components/FxAccountsUIGlue.js
+@BINPATH@/components/HelperAppDialog.js
+@BINPATH@/components/MobileIdentityUIGlue.js
+@BINPATH@/components/B2GAppMigrator.js
+@BINPATH@/components/B2GPresentationDevicePrompt.js
+@BINPATH@/components/BootstrapCommandLine.js
+#ifdef MOZ_UPDATER
+@BINPATH@/components/UpdatePrompt.js
+#endif
+@BINPATH@/components/DownloadsAPI.js
+@BINPATH@/components/DownloadsAPI.manifest
+; InputMethod API
+@BINPATH@/components/MozKeyboard.js
+@BINPATH@/components/InputMethod.manifest
--- a/mobile/android/components/DirectoryProvider.js
+++ b/mobile/android/components/DirectoryProvider.js
@@ -72,16 +72,19 @@ DirectoryProvider.prototype = {
       }
       return new FileUtils.File(env.get("DOWNLOADS_DIRECTORY"));
     } else if (prop == DOWNLOAD_DIR) {
       // Downloads.getSystemDownloadsDirectory is asynchronous, but getFile is
       // synchronous, so just return what the getSystemDownloadsDirectory
       // implementation would have returned.
       let env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
       return new FileUtils.File(env.get("DOWNLOADS_DIRECTORY"));
+    } else if (AppConstants.MOZ_B2GDROID && prop === "coreAppsDir") {
+      let dirsvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
+      return dirsvc.get("DefRt", Ci.nsIFile);
     }
 
     // We are retuning null to show failure instead for throwing an error. The
     // interface is called quite a bit and throwing an error is noisy. Returning
     // null works with the way the interface is called [see bug 529077]
     return null;
   },
 
--- a/mobile/android/components/MobileComponents.manifest
+++ b/mobile/android/components/MobileComponents.manifest
@@ -24,28 +24,35 @@ contract @mozilla.org/network/protocol/a
 contract @mozilla.org/network/protocol/about;1?what=logins {322ba47e-7047-4f71-aebf-cb7d69325cd9}
 #endif
 
 # DirectoryProvider.js
 component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js
 contract @mozilla.org/browser/directory-provider;1 {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b}
 category xpcom-directory-providers browser-directory-provider @mozilla.org/browser/directory-provider;1
 
+# stylesheets
+category agent-style-sheets browser-content-stylesheet chrome://browser/skin/content.css
+
+#ifndef MOZ_B2GDROID
 # SessionStore.js
 component {8c1f07d6-cba3-4226-a315-8bd43d67d032} SessionStore.js
 contract @mozilla.org/browser/sessionstore;1 {8c1f07d6-cba3-4226-a315-8bd43d67d032}
 category app-startup SessionStore service,@mozilla.org/browser/sessionstore;1
 
-# stylesheets
-category agent-style-sheets browser-content-stylesheet chrome://browser/skin/content.css
-
 # ContentPermissionPrompt.js
 component {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5} ContentPermissionPrompt.js
 contract @mozilla.org/content-permission/prompt;1 {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}
 
+# WebappsUpdateTimer.js
+component {8f7002cb-e959-4f0a-a2e8-563232564385} WebappsUpdateTimer.js
+contract @mozilla.org/webapps-update-timer;1 {8f7002cb-e959-4f0a-a2e8-563232564385}
+category update-timer WebappsUpdateTimer @mozilla.org/webapps-update-timer;1,getService,webapp-background-update-timer,browser.webapps.updateInterval,86400
+#endif
+
 # XPIDialogService.js
 component {c1242012-27d8-477e-a0f1-0b098ffc329b} XPIDialogService.js
 contract @mozilla.org/addons/web-install-prompt;1 {c1242012-27d8-477e-a0f1-0b098ffc329b}
 
 # HelperAppDialog.js
 component {e9d277a0-268a-4ec2-bb8c-10fdf3e44611} HelperAppDialog.js
 contract @mozilla.org/helperapplauncherdialog;1 {e9d277a0-268a-4ec2-bb8c-10fdf3e44611}
 
@@ -104,20 +111,15 @@ contract @mozilla.org/tab-source-service
 #endif
 
 # Snippets.js
 component {a78d7e59-b558-4321-a3d6-dffe2f1e76dd} Snippets.js
 contract @mozilla.org/snippets;1 {a78d7e59-b558-4321-a3d6-dffe2f1e76dd}
 category browser-delayed-startup-finished Snippets @mozilla.org/snippets;1
 category update-timer Snippets @mozilla.org/snippets;1,getService,snippets-update-timer,browser.snippets.updateInterval,86400
 
-# WebappsUpdateTimer.js
-component {8f7002cb-e959-4f0a-a2e8-563232564385} WebappsUpdateTimer.js
-contract @mozilla.org/webapps-update-timer;1 {8f7002cb-e959-4f0a-a2e8-563232564385}
-category update-timer WebappsUpdateTimer @mozilla.org/webapps-update-timer;1,getService,webapp-background-update-timer,browser.webapps.updateInterval,86400
-
 # ColorPicker.js
 component {430b987f-bb9f-46a3-99a5-241749220b29} ColorPicker.js
 contract @mozilla.org/colorpicker;1 {430b987f-bb9f-46a3-99a5-241749220b29}
 
 # ActivitiesGlue.js
 component {e4deb5f6-d5e3-4fce-bc53-901dd9951c48} ActivitiesGlue.js
 contract @mozilla.org/dom/activities/ui-glue;1 {e4deb5f6-d5e3-4fce-bc53-901dd9951c48}
--- a/mobile/android/components/moz.build
+++ b/mobile/android/components/moz.build
@@ -13,31 +13,35 @@ XPIDL_MODULE = 'MobileComponents'
 EXTRA_COMPONENTS += [
     'AboutRedirector.js',
     'ActivitiesGlue.js',
     'AddonUpdateService.js',
     'BlocklistPrompt.js',
     'BrowserCLH.js',
     'ColorPicker.js',
     'ContentDispatchChooser.js',
-    'ContentPermissionPrompt.js',
     'DirectoryProvider.js',
     'FilePicker.js',
     'HelperAppDialog.js',
     'LoginManagerPrompter.js',
     'NSSDialogService.js',
     'PromptService.js',
-    'SessionStore.js',
     'SiteSpecificUserAgent.js',
     'Snippets.js',
     'TabSource.js',
-    'WebappsUpdateTimer.js',
     'XPIDialogService.js',
 ]
 
+if not CONFIG['MOZ_B2GDROID']:
+    EXTRA_COMPONENTS += [
+        'ContentPermissionPrompt.js',
+        'SessionStore',
+        'WebappsUpdateTimer.js',
+    ]
+
 if CONFIG['MOZ_PAY']:
     EXTRA_COMPONENTS += [
         'PaymentProviderStrategy.js',
         'PaymentsUI.js'
     ]
 
 # Keep it this way if at all possible.  If you need preprocessing,
 # consider adding fields to AppConstants.jsm.
--- a/services/moz.build
+++ b/services/moz.build
@@ -20,15 +20,15 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'andr
         DIRS += ['datareporting']
 
 if CONFIG['MOZ_SERVICES_METRICS']:
     DIRS += ['metrics']
 
 if CONFIG['MOZ_SERVICES_SYNC']:
     DIRS += ['sync']
 
-if CONFIG['MOZ_B2G']:
+if CONFIG['MOZ_B2G'] or CONFIG['MOZ_B2GDROID']:
     DIRS += ['mobileid']
 
 if CONFIG['MOZ_SERVICES_CLOUDSYNC']:
     DIRS += ['cloudsync']
 
 SPHINX_TREES['services'] = 'docs'
--- a/toolkit/modules/AppConstants.jsm
+++ b/toolkit/modules/AppConstants.jsm
@@ -159,16 +159,30 @@ this.AppConstants = Object.freeze({
 
   DEBUG:
 #ifdef DEBUG
   true,
 #else
   false,
 #endif
 
+  MOZ_B2G:
+#ifdef MOZ_B2G
+  true,
+#else
+  false,
+#endif
+
+  MOZ_B2GDROID:
+#ifdef MOZ_B2GDROID
+  true,
+#else
+  false,
+#endif
+
   DLL_PREFIX: "@DLL_PREFIX@",
   DLL_SUFFIX: "@DLL_SUFFIX@",
 
   MOZ_APP_NAME: "@MOZ_APP_NAME@",
   MOZ_APP_VERSION: "@MOZ_APP_VERSION@",
   MOZ_APP_VERSION_DISPLAY: "@MOZ_APP_VERSION_DISPLAY@",
   MOZ_BUILD_APP: "@MOZ_BUILD_APP@",
   MOZ_UPDATE_CHANNEL: "@MOZ_UPDATE_CHANNEL@",
--- a/toolkit/modules/FileUtils.jsm
+++ b/toolkit/modules/FileUtils.jsm
@@ -55,17 +55,24 @@ this.FileUtils = {
    * @param   shouldCreate
    *          true if the directory hierarchy specified in |pathArray|
    *          should be created if it does not exist, false otherwise.
    * @param   followLinks (optional)
    *          true if links should be followed, false otherwise.
    * @return  nsIFile object for the location specified.
    */
   getDir: function FileUtils_getDir(key, pathArray, shouldCreate, followLinks) {
-    var dir = gDirService.get(key, Ci.nsIFile);
+    var dir;
+    try {
+      dir = gDirService.get(key, Ci.nsIFile);
+    } catch(e) {
+      dump("FileUtils.getDir failed for " + key);
+      throw e;
+    }
+
     for (var i = 0; i < pathArray.length; ++i) {
       dir.append(pathArray[i]);
     }
 
     if (shouldCreate) {
       try {
         dir.create(Ci.nsIFile.DIRECTORY_TYPE, this.PERMS_DIRECTORY);
       } catch (ex if ex.result == Cr.NS_ERROR_FILE_ALREADY_EXISTS) {
--- a/toolkit/webapps/WebappOSUtils.jsm
+++ b/toolkit/webapps/WebappOSUtils.jsm
@@ -166,22 +166,18 @@ this.WebappOSUtils = {
       }
     }
 
     return exeFile;
 #endif
   },
 
   getInstallPath: function(aApp) {
-#ifdef MOZ_B2G
-    // All b2g builds
-    return aApp.basePath + "/" + aApp.id;
-
-#elifdef MOZ_FENNEC
-   // All fennec
+#if defined(MOZ_B2G) || defined(MOZ_B2GDROID) || defined(MOZ_FENNEC)
+    // All b2g & fennec builds.
     return aApp.basePath + "/" + aApp.id;
 
 #elifdef MOZ_PHOENIX
    // Firefox
 
 #ifdef XP_WIN
     let execFile = this.getLaunchTarget(aApp);
     if (!execFile) {
@@ -399,17 +395,17 @@ this.WebappOSUtils = {
   },
 
   /**
    * Checks if the given app is locally installed.
    */
   isLaunchable: function(aApp) {
 #ifdef MOZ_WIDGET_ANDROID
     return true;
-#endif
+#else
 
     let uniqueName = this.getUniqueName(aApp);
 
 #ifdef XP_WIN
     if (!this.getLaunchTarget(aApp)) {
       return false;
     }
 
@@ -461,16 +457,17 @@ this.WebappOSUtils = {
       installDir.append("." + origin.scheme + ";" + origin.host +
                         (origin.port != -1 ? ";" + origin.port : ""));
 
       return isOldInstallPathValid(aApp, installDir.path);
     }
 
     return true;
 #endif
+#endif
   },
 
   /**
    * Sanitize the filename (accepts only a-z, 0-9, - and _)
    */
   sanitizeStringForFilename: function(aPossiblyBadFilenameString) {
     return aPossiblyBadFilenameString.replace(/[^a-z0-9_\-]/gi, "");
   }