Merge b2g-inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 22 Jul 2014 18:21:51 -0700
changeset 195564 82df3654cd801fc3e443f24256202cbb3f387656
parent 195540 5683746bac22b4b198e6d049742d04b870dfb480 (current diff)
parent 195563 e18b9e6331268829065a90d3d2424cea32c292af (diff)
child 195567 c4eb159e9eb77d5ca1c227849ba0031c4607a4ab
child 195624 1d62bb97f8868516f5e24223cda353c06bc2a709
push id27186
push userkwierso@gmail.com
push dateWed, 23 Jul 2014 01:22:14 +0000
treeherdermozilla-central@82df3654cd80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone34.0a1
first release with
nightly linux32
82df3654cd80 / 34.0a1 / 20140723030202 / files
nightly linux64
82df3654cd80 / 34.0a1 / 20140723030202 / files
nightly mac
82df3654cd80 / 34.0a1 / 20140723030202 / files
nightly win32
82df3654cd80 / 34.0a1 / 20140723030202 / files
nightly win64
82df3654cd80 / 34.0a1 / 20140723030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge b2g-inbound to m-c a=merge
--- a/b2g/chrome/content/devtools.js
+++ b/b2g/chrome/content/devtools.js
@@ -53,17 +53,17 @@ let developerHUD = {
     this._watchers.unshift(watcher);
   },
 
   init: function dwp_init() {
     if (this._client)
       return;
 
     if (!DebuggerServer.initialized) {
-      RemoteDebugger.start();
+      RemoteDebugger.initServer();
     }
 
     // We instantiate a local debugger connection so that watchers can use our
     // DebuggerClient to send requests to tab actors (e.g. the consoleActor).
     // Note the special usage of the private _serverConnection, which we need
     // to call connectToChild and set up child process actors on a frame we
     // intend to track. These actors will use the connection to communicate with
     // our DebuggerServer in the parent process.
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -346,17 +346,17 @@ let AdbController = {
       if (this.DEBUG) {
         this.debug("updateState: Waiting for all vars to be initialized");
       }
       return;
     }
 
     // Check if we have a remote debugging session going on. If so, we won't
     // disable adb even if the screen is locked.
-    let isDebugging = RemoteDebugger.isDebugging;
+    let isDebugging = USBRemoteDebugger.isDebugging;
     if (this.DEBUG) {
       this.debug("isDebugging=" + isDebugging);
     }
 
     // If USB Mass Storage, USB tethering, or a debug session is active,
     // then we don't want to disable adb in an automatic fashion (i.e.
     // when the screen locks or due to timeout).
     let sysUsbConfig = libcutils.property_get("sys.usb.config");
@@ -427,55 +427,88 @@ let AdbController = {
 };
 
 SettingsListener.observe("lockscreen.locked", false,
                          AdbController.setLockscreenState.bind(AdbController));
 SettingsListener.observe("lockscreen.enabled", false,
                          AdbController.setLockscreenEnabled.bind(AdbController));
 #endif
 
-// Keep the old setting to not break people that won't have updated
-// gaia and gecko.
-SettingsListener.observe('devtools.debugger.remote-enabled', false, function(value) {
-  Services.prefs.setBoolPref('devtools.debugger.remote-enabled', value);
-  // This preference is consulted during startup
-  Services.prefs.savePrefFile(null);
-  try {
-    value ? RemoteDebugger.start() : RemoteDebugger.stop();
-  } catch(e) {
-    dump("Error while initializing devtools: " + e + "\n" + e.stack + "\n");
-  }
+(function() {
+  // Track these separately here so we can determine the correct value for the
+  // pref "devtools.debugger.remote-enabled", which is true when either mode of
+  // using DevTools is enabled.
+  let devtoolsUSB = false;
+  let devtoolsWiFi = false;
+
+  // Keep the old setting to not break people that won't have updated
+  // gaia and gecko.
+  SettingsListener.observe('devtools.debugger.remote-enabled', false,
+                           function(value) {
+    devtoolsUSB = value;
+    Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+                               devtoolsUSB || devtoolsWiFi);
+    // This preference is consulted during startup
+    Services.prefs.savePrefFile(null);
+    try {
+      value ? USBRemoteDebugger.start() : USBRemoteDebugger.stop();
+    } catch(e) {
+      dump("Error while initializing USB devtools: "
+           + e + "\n" + e.stack + "\n");
+    }
 
 #ifdef MOZ_WIDGET_GONK
-  AdbController.setRemoteDebuggerState(value);
+    AdbController.setRemoteDebuggerState(value);
 #endif
-});
+  });
 
-SettingsListener.observe('debugger.remote-mode', false, function(value) {
-  if (['disabled', 'adb-only', 'adb-devtools'].indexOf(value) == -1) {
-    dump('Illegal value for debugger.remote-mode: ' + value + '\n');
-    return;
-  }
+  SettingsListener.observe('debugger.remote-mode', false, function(value) {
+    if (['disabled', 'adb-only', 'adb-devtools'].indexOf(value) == -1) {
+      dump('Illegal value for debugger.remote-mode: ' + value + '\n');
+      return;
+    }
 
-  Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
-                             value == 'adb-devtools');
-  // This preference is consulted during startup
-  Services.prefs.savePrefFile(null);
+    devtoolsUSB = value == 'adb-devtools';
+    Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+                               devtoolsUSB || devtoolsWiFi);
+    // This preference is consulted during startup
+    Services.prefs.savePrefFile(null);
 
-  try {
-    (value == 'adb-devtools') ? RemoteDebugger.start()
-                              : RemoteDebugger.stop();
-  } catch(e) {
-    dump("Error while initializing devtools: " + e + "\n" + e.stack + "\n");
-  }
+    try {
+      (value == 'adb-devtools') ? USBRemoteDebugger.start()
+                                : USBRemoteDebugger.stop();
+    } catch(e) {
+      dump("Error while initializing USB devtools: "
+           + e + "\n" + e.stack + "\n");
+    }
 
 #ifdef MOZ_WIDGET_GONK
-  AdbController.setRemoteDebuggerState(value != 'disabled');
+    AdbController.setRemoteDebuggerState(value != 'disabled');
 #endif
-});
+  });
+
+  SettingsListener.observe('devtools.remote.wifi.enabled', false,
+                           function(value) {
+    devtoolsWiFi = value;
+    Services.prefs.setBoolPref('devtools.debugger.remote-enabled',
+                               devtoolsUSB || devtoolsWiFi);
+    // Allow remote debugging on non-local interfaces when WiFi debug is enabled
+    // TODO: Bug 1034411: Lock down to WiFi interface, instead of all interfaces
+    Services.prefs.setBoolPref('devtools.debugger.force-local', !value);
+    // This preference is consulted during startup
+    Services.prefs.savePrefFile(null);
+
+    try {
+      value ? WiFiRemoteDebugger.start() : WiFiRemoteDebugger.stop();
+    } catch(e) {
+      dump("Error while initializing WiFi devtools: "
+           + e + "\n" + e.stack + "\n");
+    }
+  });
+})();
 
 // =================== Device Storage ====================
 SettingsListener.observe('device.storage.writable.name', 'sdcard', function(value) {
   if (Services.prefs.getPrefType('device.storage.writable.name') != Ci.nsIPrefBranch.PREF_STRING) {
     // We clear the pref because it used to be erroneously written as a bool
     // and we need to clear it before we can change it to have the correct type.
     Services.prefs.clearUserPref('device.storage.writable.name');
   }
@@ -652,16 +685,19 @@ let settingsToObserve = {
     prefName: 'layers.offmainthreadcomposition.log-animations',
     defaultValue: false
   },
   'debug.paint-flashing.enabled': {
     prefName: 'nglayout.debug.paint_flashing',
     defaultValue: false
   },
   'devtools.eventlooplag.threshold': 100,
+  'devtools.remote.wifi.visible': {
+    resetToPref: true
+  },
   'dom.mozApps.use_reviewer_certs': false,
   'layers.draw-borders': false,
   'layers.draw-tile-borders': false,
   'layers.dump': false,
   'layers.enable-tiles': true,
   'layers.simple-tiles': false,
   'privacy.donottrackheader.enabled': false,
   'ril.radio.disabled': false,
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -51,16 +51,26 @@ XPCOMUtils.defineLazyServiceGetter(Servi
                                    '@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, 'devtools', function() {
+  const { devtools } =
+    Cu.import('resource://gre/modules/devtools/Loader.jsm', {});
+  return devtools;
+});
+
+XPCOMUtils.defineLazyGetter(this, 'discovery', function() {
+  return devtools.require('devtools/toolkit/discovery/discovery');
+});
+
 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");
@@ -804,17 +814,16 @@ let IndexedDBPromptHelper = {
                        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"
     });
 
@@ -825,118 +834,158 @@ let RemoteDebugger = {
     return this._promptAnswer;
   },
 
   handleEvent: function debugger_handleEvent(detail) {
     this._promptAnswer = detail.value;
     this._promptDone = true;
   },
 
+  initServer: function() {
+    if (DebuggerServer.initialized) {
+      return;
+    }
+
+    // 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 { 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 USBRemoteDebugger = {
+
   get isDebugging() {
-    if (!this._running) {
+    if (!this._listener) {
       return false;
     }
 
     return DebuggerServer._connections &&
            Object.keys(DebuggerServer._connections).length > 0;
   },
 
-  // Start the debugger server.
-  start: function debugger_start() {
-    if (this._running) {
+  start: function() {
+    if (this._listener) {
       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);
+    RemoteDebugger.initServer();
 
-      /**
-       * 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;
-      };
+    let portOrPath =
+      Services.prefs.getCharPref("devtools.debugger.unix-domain-socket") ||
+      "/data/local/debugger-socket";
 
-#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);
+      debug("Starting USB debugger on " + portOrPath);
+      this._listener = DebuggerServer.openListener(portOrPath);
       // 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');
+      debug('Unable to start USB debugger server: ' + e);
     }
   },
 
-  stop: function debugger_stop() {
-    if (!this._running) {
-      return;
-    }
-
-    if (!DebuggerServer.initialized) {
-      // Can this really happen if we are running?
-      this._running = false;
+  stop: function() {
+    if (!this._listener) {
       return;
     }
 
     try {
-      DebuggerServer.closeAllListeners();
+      this._listener.close();
+      this._listener = null;
     } catch (e) {
-      dump('Unable to stop debugger server: ' + e + '\n');
+      debug('Unable to stop USB debugger server: ' + e);
+    }
+  }
+
+};
+
+let WiFiRemoteDebugger = {
+
+  start: function() {
+    if (this._listener) {
+      return;
     }
-    this._running = false;
+
+    RemoteDebugger.initServer();
+
+    try {
+      debug("Starting WiFi debugger");
+      this._listener = DebuggerServer.openListener(-1);
+      let port = this._listener.port;
+      debug("Started WiFi debugger on " + port);
+      discovery.addService("devtools", { port: port });
+    } catch (e) {
+      debug('Unable to start WiFi debugger server: ' + e);
+    }
+  },
+
+  stop: function() {
+    if (!this._listener) {
+      return;
+    }
+
+    try {
+      discovery.removeService("devtools");
+      this._listener.close();
+      this._listener = null;
+    } catch (e) {
+      debug('Unable to stop WiFi debugger server: ' + e);
+    }
   }
-}
+
+};
 
 let KeyboardHelper = {
   handleEvent: function keyboard_handleEvent(detail) {
     Keyboard.setLayouts(detail.layouts);
   }
 };
 
 // This is the backend for Gaia's screenshot feature.  Gaia requests a
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="cd50cb6ddeb430ddaf43e4fcf5774b9d262a8cf3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="78cbbaeb041e5dd25a32c521b3d4ff08735ac139"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3aa6abd313f965a84aa86c6b213dc154e4875139">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="cd50cb6ddeb430ddaf43e4fcf5774b9d262a8cf3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="78cbbaeb041e5dd25a32c521b3d4ff08735ac139"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="999e945b85c578c503ad445c2285940f16aacdae">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="cd50cb6ddeb430ddaf43e4fcf5774b9d262a8cf3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="78cbbaeb041e5dd25a32c521b3d4ff08735ac139"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="cd50cb6ddeb430ddaf43e4fcf5774b9d262a8cf3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="78cbbaeb041e5dd25a32c521b3d4ff08735ac139"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="227354333a185180b85471f2cc6abfb029e44718"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3aa6abd313f965a84aa86c6b213dc154e4875139">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="cd50cb6ddeb430ddaf43e4fcf5774b9d262a8cf3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="78cbbaeb041e5dd25a32c521b3d4ff08735ac139"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "18f160082057a1c10754458f9e4944220099ab67", 
+    "revision": "eff18c4265f0d8e49e5a2f2d7c9fdb01c87bd42e", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="cd50cb6ddeb430ddaf43e4fcf5774b9d262a8cf3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="78cbbaeb041e5dd25a32c521b3d4ff08735ac139"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="cd50cb6ddeb430ddaf43e4fcf5774b9d262a8cf3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="78cbbaeb041e5dd25a32c521b3d4ff08735ac139"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3aa6abd313f965a84aa86c6b213dc154e4875139">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="cd50cb6ddeb430ddaf43e4fcf5774b9d262a8cf3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="78cbbaeb041e5dd25a32c521b3d4ff08735ac139"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="0d616942c300d9fb142483210f1dda9096c9a9fc">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="cd50cb6ddeb430ddaf43e4fcf5774b9d262a8cf3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="78cbbaeb041e5dd25a32c521b3d4ff08735ac139"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7f792d756385bb894fba7645da59c67fe2c804bf"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="31215645c296ba7a62bcb3176f69e0014ab9be07"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -879,17 +879,17 @@ WebappsApplicationMgmt.prototype = {
             origin: msg.origin
           };
           let event = new this._window.MozApplicationEvent("applicationuninstall",
                            { application : createApplicationObject(this._window, detail) });
           this._onuninstall.handleEvent(event);
         }
         break;
       case "Webapps:Uninstall:Return:OK":
-        Services.DOMRequest.fireSuccess(req, msg.origin);
+        Services.DOMRequest.fireSuccess(req, msg.manifestURL);
         break;
       case "Webapps:Uninstall:Return:KO":
         Services.DOMRequest.fireError(req, "NOT_INSTALLED");
         break;
     }
     if (aMessage.name !== "Webapps:Uninstall:Broadcast:Return:OK") {
       this.removeRequest(msg.requestID);
     }
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -2075,24 +2075,21 @@ this.DOMApplicationRegistry = {
     }
 
     let checkManifest = (function() {
       if (!app.manifest) {
         sendError("MANIFEST_PARSE_ERROR");
         return false;
       }
 
-      // Disallow multiple hosted apps installations from the same origin for now.
-      // We will remove this code after multiple apps per origin are supported (bug 778277).
-      // This will also disallow reinstalls from the same origin for now.
+      // Disallow reinstalls from the same manifest url for now.
       for (let id in this.webapps) {
-        if (this.webapps[id].origin == app.origin &&
-            !this.webapps[id].packageHash &&
+        if (this.webapps[id].manifestURL == app.manifestURL &&
             this._isLaunchable(this.webapps[id])) {
-          sendError("MULTIPLE_APPS_PER_ORIGIN_FORBIDDEN");
+          sendError("REINSTALL_FORBIDDEN");
           return false;
         }
       }
 
       if (!AppsUtils.checkManifest(app.manifest, app)) {
         sendError("INVALID_MANIFEST");
         return false;
       }
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -14,16 +14,17 @@ support-files =
   signed_app_template.webapp
   signed/*
   test_packaged_app_common.js
   marketplace/*
   pkg_install_iframe.html
 
 [test_app_update.html]
 [test_bug_795164.html]
+[test_install_multiple_apps_origin.html]
 [test_install_receipts.html]
 [test_marketplace_pkg_install.html]
 skip-if = buildapp == "b2g" || toolkit == "android" # see bug 989806
 [test_packaged_app_install.html]
 [test_packaged_app_update.html]
 [test_receipt_operations.html]
 [test_signed_pkg_install.html]
 [test_uninstall_errors.html]
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/test_install_multiple_apps_origin.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id={778277}
+-->
+<head>
+  <title>Test for Bug {778277}</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={778277}">Mozilla Bug {778277}</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+
+var gManifestURL = "http://test/tests/dom/apps/tests/file_app.sjs?apptype=hosted&getmanifest=true";
+var gGenerator = runTest();
+
+function go() {
+  SpecialPowers.pushPermissions(
+    [{ "type": "webapps-manage", "allow": 1, "context": document }],
+    function() { gGenerator.next() });
+}
+
+function continueTest() {
+  try {
+    gGenerator.next();
+  } catch (e if e instanceof StopIteration) {
+    finish();
+  }
+}
+
+function finish() {
+  SimpleTest.finish();
+}
+
+function cbError(aEvent) {
+  ok(false, "Error callback invoked " +
+            aEvent.target.error.name + " " + aEvent.target.error.message);
+  finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+/**
+  * Install 2 apps from the same origin and uninstall them.
+  */
+function runTest() {
+  SpecialPowers.setAllAppsLaunchable(true);
+
+  var manifestURL1 = gManifestURL + "&app=1";
+  var manifestURL2 = gManifestURL + "&app=2";
+
+  SpecialPowers.autoConfirmAppInstall(continueTest);
+  yield undefined;
+
+  request = navigator.mozApps.mgmt.getAll();
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+  var initialAppsCount = request.result.length;
+  info("Starting with " + initialAppsCount + " apps installed.");
+
+  var request = navigator.mozApps.install(manifestURL1, { });
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+
+  var app1 = request.result;
+  ok(app1, "App 1 is non-null");
+  is(app1.manifestURL, manifestURL1, "App 1 manifest url is correct.");
+
+  request = navigator.mozApps.install(manifestURL2, { });
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+
+  var app2 = request.result;
+  ok(app2, "App 2 is non-null");
+  is(app2.manifestURL, manifestURL2, "App 2 manifest url is correct.");
+
+  request = navigator.mozApps.mgmt.uninstall(app1);
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+  is(request.result, manifestURL1, "App 1 uninstalled.");
+
+  request = navigator.mozApps.mgmt.uninstall(app2);
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+  is(request.result, manifestURL2, "App 2 uninstalled.");
+
+  request = navigator.mozApps.mgmt.getAll();
+  request.onerror = cbError;
+  request.onsuccess = continueTest;
+  yield undefined;
+  is(request.result.length, initialAppsCount, "All apps are uninstalled.");
+}
+
+addLoadEvent(go);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -92,16 +92,18 @@ struct CurrentCommand {
 
 typedef Tuple3<NetdCommand*, CommandChain*, CommandCallback> QueueData;
 
 #define GET_CURRENT_NETD_COMMAND   (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].a)
 #define GET_CURRENT_CHAIN          (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].b)
 #define GET_CURRENT_CALLBACK       (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].c)
 #define GET_CURRENT_COMMAND        (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].a->mData)
 
+#define CNT_OF_ARRAY(a) (sizeof(a) / sizeof(a[0]))
+
 static NetworkUtils* gNetworkUtils;
 static nsTArray<QueueData> gCommandQueue;
 static CurrentCommand gCurrentCommand;
 static bool gPending = false;
 static nsTArray<nsCString> gReason;
 static NetworkParams *gWifiTetheringParms = 0;
 
 
@@ -1082,64 +1084,74 @@ NetworkUtils::~NetworkUtils()
 {
 }
 
 #define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get()
 #define GET_FIELD(prop) aOptions.prop
 
 void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
 {
-  bool ret = true;
+  typedef bool (NetworkUtils::*CommandHandler)(NetworkParams&);
+
+  const static struct {
+    const char* mCommandName;
+    CommandHandler mCommandHandler;
+  } COMMAND_HANDLER_TABLE[] = {
+
+    // For command 'testCommand', BUILD_ENTRY(testCommand) will generate
+    // {"testCommand", NetworkUtils::testCommand}
+    #define BUILD_ENTRY(c) {#c, &NetworkUtils::c}
 
-  if (aOptions.mCmd.EqualsLiteral("removeNetworkRoute")) {
-    removeNetworkRoute(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("setDNS")) {
-    setDNS(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("setDefaultRouteAndDNS")) {
-    setDefaultRouteAndDNS(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("removeDefaultRoute")) {
-    removeDefaultRoute(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("addHostRoute")) {
-    addHostRoute(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("removeHostRoute")) {
-    removeHostRoute(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("removeHostRoutes")) {
-    removeHostRoutes(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("addSecondaryRoute")) {
-    addSecondaryRoute(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("removeSecondaryRoute")) {
-    removeSecondaryRoute(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("getNetworkInterfaceStats")) {
-    getNetworkInterfaceStats(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("setNetworkInterfaceAlarm")) {
-    setNetworkInterfaceAlarm(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("enableNetworkInterfaceAlarm")) {
-    enableNetworkInterfaceAlarm(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("disableNetworkInterfaceAlarm")) {
-    disableNetworkInterfaceAlarm(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("setWifiOperationMode")) {
-    setWifiOperationMode(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("setDhcpServer")) {
-    setDhcpServer(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("setWifiTethering")) {
-    setWifiTethering(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("setUSBTethering")) {
-    setUSBTethering(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("enableUsbRndis")) {
-    enableUsbRndis(aOptions);
-  } else if (aOptions.mCmd.EqualsLiteral("updateUpStream")) {
-    updateUpStream(aOptions);
-  } else {
-    WARN("unknon message");
+    BUILD_ENTRY(removeNetworkRoute),
+    BUILD_ENTRY(setDNS),
+    BUILD_ENTRY(setDefaultRouteAndDNS),
+    BUILD_ENTRY(removeDefaultRoute),
+    BUILD_ENTRY(addHostRoute),
+    BUILD_ENTRY(removeHostRoute),
+    BUILD_ENTRY(removeHostRoutes),
+    BUILD_ENTRY(addSecondaryRoute),
+    BUILD_ENTRY(removeSecondaryRoute),
+    BUILD_ENTRY(getNetworkInterfaceStats),
+    BUILD_ENTRY(setNetworkInterfaceAlarm),
+    BUILD_ENTRY(enableNetworkInterfaceAlarm),
+    BUILD_ENTRY(disableNetworkInterfaceAlarm),
+    BUILD_ENTRY(setWifiOperationMode),
+    BUILD_ENTRY(setDhcpServer),
+    BUILD_ENTRY(setWifiTethering),
+    BUILD_ENTRY(setUSBTethering),
+    BUILD_ENTRY(enableUsbRndis),
+    BUILD_ENTRY(updateUpStream),
+
+    #undef BUILD_ENTRY
+  };
+
+  // Loop until we find the command name which matches aOptions.mCmd.
+  CommandHandler handler = nullptr;
+  for (size_t i = 0; i < CNT_OF_ARRAY(COMMAND_HANDLER_TABLE); i++) {
+    if (aOptions.mCmd.EqualsASCII(COMMAND_HANDLER_TABLE[i].mCommandName)) {
+      handler = COMMAND_HANDLER_TABLE[i].mCommandHandler;
+      break;
+    }
+  }
+
+  if (!handler) {
+    // Command not found in COMMAND_HANDLER_TABLE.
+    WARN("unknown message: %s", NS_ConvertUTF16toUTF8(aOptions.mCmd).get());
     return;
   }
 
+  // Command matches! Dispatch to the handler.
+  (this->*handler)(aOptions);
+
   if (!aOptions.mIsAsync) {
+    // The requested command is synchronous, which implies the actual result
+    // from netd is not important to the client. So, just notify the
+    // registered callback.
     NetworkResultOptions result;
-    result.mRet = ret;
+    result.mRet = true;
     postMessage(aOptions, result);
   }
 }
 
 /**
  * Handle received data from netd.
  */
 void NetworkUtils::onNetdMessage(NetdCommand* aCommand)
--- a/dom/telephony/gonk/TelephonyService.js
+++ b/dom/telephony/gonk/TelephonyService.js
@@ -725,20 +725,21 @@ TelephonyService.prototype = {
     aCall.state = nsITelephonyService.CALL_STATE_DISCONNECTED;
     let duration = ("started" in aCall && typeof aCall.started == "number") ?
       new Date().getTime() - aCall.started : 0;
     let data = {
       number: aCall.number,
       serviceId: aClientId,
       emergency: aCall.isEmergency,
       duration: duration,
-      direction: aCall.isOutgoing ? "outgoing" : "incoming"
+      direction: aCall.isOutgoing ? "outgoing" : "incoming",
+      hangUpLocal: aCall.hangUpLocal
     };
 
-    if(this._cdmaCallWaitingNumber != null) {
+    if (this._cdmaCallWaitingNumber != null) {
       data.secondNumber = this._cdmaCallWaitingNumber;
       this._cdmaCallWaitingNumber = null;
     }
 
     gSystemMessenger.broadcastMessage("telephony-call-ended", data);
 
     let manualConfStateChange = false;
     let childId = this._currentCalls[aClientId][aCall.callIndex].childId;
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -649,16 +649,20 @@ pref("devtools.defaultColorUnit", "hex")
 
 // Used for devtools debugging
 pref("devtools.dump.emit", false);
 
 // Disable device discovery logging
 pref("devtools.discovery.log", false);
 // Disable scanning for DevTools devices via WiFi
 pref("devtools.remote.wifi.scan", false);
+// Hide UI options for controlling device visibility over WiFi
+// N.B.: This does not set whether the device can be discovered via WiFi, only
+// whether the UI control to make such a choice is shown to the user
+pref("devtools.remote.wifi.visible", false);
 
 // view source
 pref("view_source.syntax_highlight", true);
 pref("view_source.wrap_long_lines", false);
 pref("view_source.editor.external", false);
 pref("view_source.editor.path", "");
 // allows to add further arguments to the editor; use the %LINE% placeholder
 // for jumping to a specific line (e.g. "/line:%LINE%" or "--goto %LINE%")