Merge latest green inbound changeset and mozilla-central; a=merge
authorEd Morley <emorley@mozilla.com>
Fri, 01 Aug 2014 16:11:12 +0100
changeset 197267 44e5072476b7d9fd269a5836fc910ca407e00a96
parent 197266 afeb3d37ce5dd425e195e723736821d8ede990d9 (current diff)
parent 197201 8970589d6505f636c1d9d59ec7c8f771526b629a (diff)
child 197281 47a75561bc43d89213edaa5652990afab707a13e
push id27236
push useremorley@mozilla.com
push dateFri, 01 Aug 2014 15:52:48 +0000
treeherdermozilla-central@44e5072476b7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge latest green inbound changeset and mozilla-central; a=merge
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -797,17 +797,18 @@ var WebappsHelper = {
     json.mm = subject;
 
     switch(topic) {
       case "webapps-launch":
         DOMApplicationRegistry.getManifestFor(json.manifestURL).then((aManifest) => {
           if (!aManifest)
             return;
 
-          let manifest = new ManifestHelper(aManifest, json.origin);
+          let manifest = new ManifestHelper(aManifest, json.origin,
+                                            json.manifestURL);
           let payload = {
             timestamp: json.timestamp,
             url: manifest.fullLaunchPath(json.startPoint),
             manifestURL: json.manifestURL
           };
           shell.sendCustomEvent("webapps-launch", payload);
         });
         break;
--- a/b2g/components/AlertsHelper.jsm
+++ b/b2g/components/AlertsHelper.jsm
@@ -162,30 +162,30 @@ let AlertsHelper = {
   registerListener: function(alertId, cookie, alertListener) {
     this._listeners[alertId] = { observer: alertListener, cookie: cookie };
   },
 
   registerAppListener: function(uid, listener) {
     this._listeners[uid] = listener;
 
     appsService.getManifestFor(listener.manifestURL).then((manifest) => {
-      let helper = new ManifestHelper(manifest, listener.manifestURL);
+      let app = appsService.getAppByManifestURL(listener.manifestURL);
+      let helper = new ManifestHelper(manifest, app.origin, app.manifestURL);
       let getNotificationURLFor = function(messages) {
         if (!messages) {
           return null;
         }
 
         for (let i = 0; i < messages.length; i++) {
           let message = messages[i];
           if (message === kNotificationSystemMessageName) {
             return helper.fullLaunchPath();
           } else if (typeof message === "object" &&
                      kNotificationSystemMessageName in message) {
-            return helper.resolveFromOrigin(
-              message[kNotificationSystemMessageName]);
+            return helper.resolveURL(message[kNotificationSystemMessageName]);
           }
         }
 
         // No message found...
         return null;
       }
 
       listener.target = getNotificationURLFor(manifest.messages);
@@ -215,17 +215,18 @@ let AlertsHelper = {
     if (!manifestURL || !manifestURL.length) {
       send(null, null);
       return;
     }
 
     // If we have a manifest URL, get the icon and title from the manifest
     // to prevent spoofing.
     appsService.getManifestFor(manifestURL).then((manifest) => {
-      let helper = new ManifestHelper(manifest, manifestURL);
+      let app = appsService.getAppByManifestURL(manifestURL);
+      let helper = new ManifestHelper(manifest, app.origin, manifestURL);
       send(helper.name, helper.iconURLForSize(kNotificationIconSize));
     });
   },
 
   showAlertNotification: function(aMessage) {
     let data = aMessage.data;
     let currentListener = this._listeners[data.name];
     if (currentListener && currentListener.observer) {
--- 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="04ea7e1a4034a50d4a7a4f5b95a04a2ed8313908"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9689218473b6fc4dd927ad6aa7b06c05f0843824"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <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="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <!-- 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="04ea7e1a4034a50d4a7a4f5b95a04a2ed8313908"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9689218473b6fc4dd927ad6aa7b06c05f0843824"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <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="04ea7e1a4034a50d4a7a4f5b95a04a2ed8313908"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9689218473b6fc4dd927ad6aa7b06c05f0843824"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <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="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
--- 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="04ea7e1a4034a50d4a7a4f5b95a04a2ed8313908"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9689218473b6fc4dd927ad6aa7b06c05f0843824"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <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="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <!-- 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="04ea7e1a4034a50d4a7a4f5b95a04a2ed8313908"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9689218473b6fc4dd927ad6aa7b06c05f0843824"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <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": "9f3b6b772b7b8b45634f9911c873f59a8036e9a9", 
+    "revision": "3842156466e71f41748f86777a1b8fbd379441c0", 
     "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="04ea7e1a4034a50d4a7a4f5b95a04a2ed8313908"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9689218473b6fc4dd927ad6aa7b06c05f0843824"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <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="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <!-- 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="04ea7e1a4034a50d4a7a4f5b95a04a2ed8313908"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9689218473b6fc4dd927ad6aa7b06c05f0843824"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <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="04ea7e1a4034a50d4a7a4f5b95a04a2ed8313908"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="9689218473b6fc4dd927ad6aa7b06c05f0843824"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <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="04ea7e1a4034a50d4a7a4f5b95a04a2ed8313908"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="9689218473b6fc4dd927ad6aa7b06c05f0843824"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d61daef8fca7d6f335f659a8967bad423770e634"/>
   <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="71f5a35e3bc1801847413cff1f14fc3b5cd991ca"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/modules/WebappManager.jsm
+++ b/browser/modules/WebappManager.jsm
@@ -61,17 +61,19 @@ this.WebappManager = {
     if (aMessage.name == "Webapps:UpdateState") {
       if (data.error) {
         this.installations[manifestURL].reject(data.error);
       } else if (data.app.installState == "installed") {
         this.installations[manifestURL].resolve();
       }
     } else if (aMessage.name == "Webapps:Install:Return:OK" &&
                !data.isPackage) {
-      let manifest = new ManifestHelper(data.app.manifest, data.app.origin);
+      let manifest = new ManifestHelper(data.app.manifest,
+                                        data.app.origin,
+                                        data.app.manifestURL);
       if (!manifest.appcache_path) {
         this.installations[manifestURL].resolve();
       }
     } else if (aMessage.name == "Webapps:Install:Return:KO") {
       this.installations[manifestURL].reject(data.error);
     }
   },
 
@@ -167,17 +169,18 @@ this.WebappManager = {
               throw ex;
             }
           })
         );
       }
     };
 
     let requestingURI = chromeWin.makeURI(aData.from);
-    let manifest = new ManifestHelper(jsonManifest, aData.app.origin);
+    let app = aData.app;
+    let manifest = new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
 
     let host;
     try {
       host = requestingURI.host;
     } catch(e) {
       host = requestingURI.spec;
     }
 
--- a/dom/activities/src/ActivitiesService.jsm
+++ b/dom/activities/src/ActivitiesService.jsm
@@ -289,17 +289,20 @@ let Activities = {
         let ids = aResults.options.map((aItem) => {
           return { id: reg._appIdForManifestURL(aItem.manifest) }
         });
 
         reg._readManifests(ids).then((aManifests) => {
           let results = [];
           aManifests.forEach((aManifest, i) => {
             let manifestURL = aResults.options[i].manifest;
-            let helper = new ManifestHelper(aManifest.manifest, manifestURL);
+            // Not passing the origin is fine here since we only need
+            // helper.name which doesn't rely on url resolution.
+            let helper =
+              new ManifestHelper(aManifest.manifest, manifestURL, manifestURL);
             results.push({
               manifestURL: manifestURL,
               iconURL: aResults.options[i].icon,
               appName: helper.name
             });
           });
 
           // Now fire success with the array of choices.
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -19,17 +19,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils",
   "resource://gre/modules/WebappOSUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 
 // Shared code for AppsServiceChild.jsm, Webapps.jsm and Webapps.js
 
-this.EXPORTED_SYMBOLS = ["AppsUtils", "ManifestHelper", "isAbsoluteURI", "mozIApplication"];
+this.EXPORTED_SYMBOLS =
+  ["AppsUtils", "ManifestHelper", "isAbsoluteURI", "mozIApplication"];
 
 function debug(s) {
   //dump("-*- AppsUtils.jsm: " + s + "\n");
 }
 
 this.isAbsoluteURI = function(aURI) {
   let foo = Services.io.newURI("http://foo", null, null);
   let bar = Services.io.newURI("http://bar", null, null);
@@ -368,17 +369,18 @@ this.AppsUtils = {
    * For now, only ensure app can't rename itself.
    */
   ensureSameAppName: function ensureSameAppName(aOldManifest, aNewManifest, aApp) {
     // Ensure that app name can't be updated
     aNewManifest.name = aApp.name;
 
     // Nor through localized names
     if ('locales' in aNewManifest) {
-      let defaultName = new ManifestHelper(aOldManifest, aApp.origin).name;
+      let defaultName =
+        new ManifestHelper(aOldManifest, aApp.origin, aApp.manifestURL).name;
       for (let locale in aNewManifest.locales) {
         let entry = aNewManifest.locales[locale];
         if (!entry.name) {
           continue;
         }
         // In case previous manifest didn't had a name,
         // we use the default app name
         let localizedName = defaultName;
@@ -588,21 +590,35 @@ this.AppsUtils = {
     // Convert the binary hash data to a hex string.
     return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
   }
 }
 
 /**
  * Helper object to access manifest information with locale support
  */
-this.ManifestHelper = function(aManifest, aOrigin) {
-  this._origin = Services.io.newURI(aOrigin, null, null);
+this.ManifestHelper = function(aManifest, aOrigin, aManifestURL) {
+  // If the app is packaged, we resolve uris against the origin.
+  // If it's not, against the manifest url.
+
+  if (!aOrigin || !aManifestURL) {
+    throw Error("ManifestHelper needs both origin and manifestURL");
+  }
+
+  this._baseURI = Services.io.newURI(
+    aOrigin.startsWith("app://") ? aOrigin : aManifestURL, null, null);
+
+  // We keep the manifest url in all cases since we need it to
+  // resolve the package path for packaged apps.
+  this._manifestURL = Services.io.newURI(aManifestURL, null, null);
+
   this._manifest = aManifest;
-  let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry)
-                                                          .QueryInterface(Ci.nsIToolkitChromeRegistry);
+  let chrome = Cc["@mozilla.org/chrome/chrome-registry;1"]
+                 .getService(Ci.nsIXULChromeRegistry)
+                 .QueryInterface(Ci.nsIToolkitChromeRegistry);
   let locale = chrome.getSelectedLocale("global").toLowerCase();
   this._localeRoot = this._manifest;
 
   if (this._manifest.locales && this._manifest.locales[locale]) {
     this._localeRoot = this._manifest.locales[locale];
   }
   else if (this._manifest.locales) {
     // try with the language part of the locale ("en" for en-GB) only
@@ -681,71 +697,71 @@ ManifestHelper.prototype = {
     let iconSizes = Object.keys(icons);
     if (iconSizes.length == 0) {
       return null;
     }
 
     iconSizes.sort((a, b) => a - b);
     let biggestIconSize = iconSizes.pop();
     let biggestIcon = icons[biggestIconSize];
-    let biggestIconURL = this._origin.resolve(biggestIcon);
+    let biggestIconURL = this._baseURI.resolve(biggestIcon);
 
     return biggestIconURL;
   },
 
   iconURLForSize: function(aSize) {
     let icons = this._localeProp("icons");
     if (!icons)
       return null;
     let dist = 100000;
     let icon = null;
     for (let size in icons) {
       let iSize = parseInt(size);
       if (Math.abs(iSize - aSize) < dist) {
-        icon = this._origin.resolve(icons[size]);
+        icon = this._baseURI.resolve(icons[size]);
         dist = Math.abs(iSize - aSize);
       }
     }
     return icon;
   },
 
   fullLaunchPath: function(aStartPoint) {
     // If no start point is specified, we use the root launch path.
     // In all error cases, we just return null.
     if ((aStartPoint || "") === "") {
-      return this._origin.resolve(this._localeProp("launch_path") || "");
+      return this._baseURI.resolve(this._localeProp("launch_path") || "/");
     }
 
     // Search for the l10n entry_points property.
     let entryPoints = this._localeProp("entry_points");
     if (!entryPoints) {
       return null;
     }
 
     if (entryPoints[aStartPoint]) {
-      return this._origin.resolve(entryPoints[aStartPoint].launch_path || "");
+      return this._baseURI.resolve(entryPoints[aStartPoint].launch_path || "/");
     }
 
     return null;
   },
 
-  resolveFromOrigin: function(aURI) {
+  resolveURL: function(aURI) {
     // This should be enforced higher up, but check it here just in case.
     if (isAbsoluteURI(aURI)) {
-      throw new Error("Webapps.jsm: non-relative URI passed to resolveFromOrigin");
+      throw new Error("Webapps.jsm: non-relative URI passed to resolve");
     }
-    return this._origin.resolve(aURI);
+    return this._baseURI.resolve(aURI);
   },
 
   fullAppcachePath: function() {
     let appcachePath = this._localeProp("appcache_path");
-    return this._origin.resolve(appcachePath ? appcachePath : "");
+    return this._baseURI.resolve(appcachePath ? appcachePath : "/");
   },
 
   fullPackagePath: function() {
     let packagePath = this._localeProp("package_path");
-    return this._origin.resolve(packagePath ? packagePath : "");
+    return this._manifestURL.resolve(packagePath ? packagePath : "/");
   },
 
   get role() {
     return this._manifest.role || "";
   }
 }
--- a/dom/apps/src/PermissionsInstaller.jsm
+++ b/dom/apps/src/PermissionsInstaller.jsm
@@ -54,17 +54,18 @@ this.PermissionsInstaller = {
    *        Indicates the app was just re-installed
    * @param function aOnError
    *        A function called if an error occurs
    * @returns void
    **/
   installPermissions: function installPermissions(aApp, aIsReinstall, aOnError,
                                                   aIsSystemUpdate) {
     try {
-      let newManifest = new ManifestHelper(aApp.manifest, aApp.origin);
+      let newManifest =
+        new ManifestHelper(aApp.manifest, aApp.origin, aApp.manifestURL);
       if (!newManifest.permissions && !aIsReinstall) {
         return;
       }
 
       if (aIsReinstall) {
         // Compare the original permissions against the new permissions
         // Remove any deprecated Permissions
 
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -384,17 +384,18 @@ this.DOMApplicationRegistry = {
         });
       });
     }
   },
 
   updateOfflineCacheForApp: function(aId) {
     let app = this.webapps[aId];
     this._readManifests([{ id: aId }]).then((aResult) => {
-      let manifest = new ManifestHelper(aResult[0].manifest, app.origin);
+      let manifest =
+        new ManifestHelper(aResult[0].manifest, app.origin, app.manifestURL);
       OfflineCacheInstaller.installCache({
         cachePath: app.cachePath,
         appId: aId,
         origin: Services.io.newURI(app.origin, null, null),
         localId: app.localId,
         appcache_path: manifest.fullAppcachePath()
       });
     });
@@ -670,47 +671,47 @@ this.DOMApplicationRegistry = {
       root = aManifest.entry_points[aEntryPoint];
     }
 
     if (!root.messages || !Array.isArray(root.messages) ||
         root.messages.length == 0) {
       return;
     }
 
-    let manifest = new ManifestHelper(aManifest, aApp.origin);
+    let manifest = new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
     let launchPathURI = Services.io.newURI(manifest.fullLaunchPath(aEntryPoint), null, null);
     let manifestURI = Services.io.newURI(aApp.manifestURL, null, null);
     root.messages.forEach(function registerPages(aMessage) {
       let handlerPageURI = launchPathURI;
       let messageName;
       if (typeof(aMessage) === "object" && Object.keys(aMessage).length === 1) {
         messageName = Object.keys(aMessage)[0];
         let handlerPath = aMessage[messageName];
         // Resolve the handler path from origin. If |handler_path| is absent,
         // simply skip.
         let fullHandlerPath;
         try {
           if (handlerPath && handlerPath.trim()) {
-            fullHandlerPath = manifest.resolveFromOrigin(handlerPath);
+            fullHandlerPath = manifest.resolveURL(handlerPath);
           } else {
             throw new Error("Empty or blank handler path.");
           }
         } catch(e) {
           debug("system message handler path (" + handlerPath + ") is " +
                 "invalid, skipping. Error is: " + e);
           return;
         }
         handlerPageURI = Services.io.newURI(fullHandlerPath, null, null);
       } else {
         messageName = aMessage;
       }
 
       if (SystemMessagePermissionsChecker
             .isSystemMessagePermittedToRegister(messageName,
-                                                aApp.origin,
+                                                aApp.manifestURL,
                                                 aManifest)) {
         msgmgr.registerPage(messageName, handlerPageURI, manifestURI);
       }
     });
   },
 
   // |aEntryPoint| is either the entry_point name or the null in which case we
   // use the root of the manifest.
@@ -728,44 +729,44 @@ this.DOMApplicationRegistry = {
       return;
     }
 
     if ((typeof connections) !== "object") {
       debug("|connections| is not an object. Skipping: " + connections);
       return;
     }
 
-    let manifest = new ManifestHelper(aManifest, aApp.origin);
+    let manifest = new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
     let launchPathURI = Services.io.newURI(manifest.fullLaunchPath(aEntryPoint),
                                            null, null);
     let manifestURI = Services.io.newURI(aApp.manifestURL, null, null);
 
     for (let keyword in connections) {
       let connection = connections[keyword];
 
       // Resolve the handler path from origin. If |handler_path| is absent,
       // use |launch_path| as default.
       let fullHandlerPath;
       let handlerPath = connection.handler_path;
       if (handlerPath) {
         try {
-          fullHandlerPath = manifest.resolveFromOrigin(handlerPath);
+          fullHandlerPath = manifest.resolveURL(handlerPath);
         } catch(e) {
           debug("Connection's handler path is invalid. Skipping: keyword: " +
                 keyword + " handler_path: " + handlerPath);
           continue;
         }
       }
       let handlerPageURI = fullHandlerPath
                            ? Services.io.newURI(fullHandlerPath, null, null)
                            : launchPathURI;
 
       if (SystemMessagePermissionsChecker
             .isSystemMessagePermittedToRegister("connection",
-                                                aApp.origin,
+                                                aApp.manifestURL,
                                                 aManifest)) {
         msgmgr.registerPage("connection", handlerPageURI, manifestURI);
       }
 
       interAppCommService.
         registerConnection(keyword,
                            handlerPageURI,
                            manifestURI,
@@ -807,26 +808,26 @@ this.DOMApplicationRegistry = {
     if (aEntryPoint && aManifest.entry_points[aEntryPoint]) {
       root = aManifest.entry_points[aEntryPoint];
     }
 
     if (!root.activities) {
       return activitiesToRegister;
     }
 
-    let manifest = new ManifestHelper(aManifest, aApp.origin);
+    let manifest = new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
     for (let activity in root.activities) {
       let description = root.activities[activity];
       let href = description.href;
       if (!href) {
         href = manifest.launch_path;
       }
 
       try {
-        href = manifest.resolveFromOrigin(href);
+        href = manifest.resolveURL(href);
       } catch (e) {
         debug("Activity href (" + href + ") is invalid, skipping. " +
               "Error is: " + e);
         continue;
       }
 
       // Make a copy of the description object since we don't want to modify
       // the manifest itself, but need to register with a resolved URI.
@@ -846,17 +847,17 @@ this.DOMApplicationRegistry = {
                                     "description": newDesc });
       }
 
       let launchPathURI = Services.io.newURI(href, null, null);
       let manifestURI = Services.io.newURI(aApp.manifestURL, null, null);
 
       if (SystemMessagePermissionsChecker
             .isSystemMessagePermittedToRegister("activity",
-                                                aApp.origin,
+                                                aApp.manifestURL,
                                                 aManifest)) {
         msgmgr.registerPage("activity", launchPathURI, manifestURI);
       }
     }
     return activitiesToRegister;
   },
 
   // |aAppsToRegister| contains an array of apps to be registered, where
@@ -956,17 +957,18 @@ this.DOMApplicationRegistry = {
         let manifest = aResult.manifest;
         if (!manifest) {
           // If we can't load the manifest, we probably have a corrupted
           // registry. We delete the app since we can't do anything with it.
           delete this.webapps[aResult.id];
           return;
         }
 
-        let localeManifest = new ManifestHelper(manifest, app.origin);
+        let localeManifest =
+          new ManifestHelper(manifest, app.origin, app.manifestURL);
 
         app.name = manifest.name;
         app.csp = manifest.csp || "";
         app.role = localeManifest.role;
         if (app.appStatus >= Ci.nsIPrincipal.APP_STATUS_PRIVILEGED) {
           app.redirects = this.sanitizeRedirects(manifest.redirects);
         }
         this._registerSystemMessages(manifest, app);
@@ -1458,17 +1460,18 @@ this.DOMApplicationRegistry = {
                                  true);
 
     if (!file.exists()) {
       // This is a hosted app, let's check if it has an appcache
       // and download it.
       let results = yield this._readManifests([{ id: id }]);
 
       let jsonManifest = results[0].manifest;
-      let manifest = new ManifestHelper(jsonManifest, app.origin);
+      let manifest =
+        new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
 
       if (manifest.appcache_path) {
         debug("appcache found");
         this.startOfflineCacheDownload(manifest, app, null, isUpdate);
       } else {
         // Hosted app with no appcache, nothing to do, but we fire a
         // downloaded event.
         debug("No appcache found, sending 'downloaded' for " + aManifestURL);
@@ -1492,17 +1495,17 @@ this.DOMApplicationRegistry = {
 
     let json = yield AppsUtils.loadJSONAsync(file.path);
     if (!json) {
       debug("startDownload: No update manifest found at " + file.path + " " +
             aManifestURL);
       throw new Error("MISSING_UPDATE_MANIFEST");
     }
 
-    let manifest = new ManifestHelper(json, app.manifestURL);
+    let manifest = new ManifestHelper(json, app.origin, app.manifestURL);
     let newApp = {
       manifestURL: aManifestURL,
       origin: app.origin,
       installOrigin: app.installOrigin,
       downloadSize: app.downloadSize
     };
 
     let newManifest, newId;
@@ -1803,17 +1806,18 @@ this.DOMApplicationRegistry = {
                   requestID: aData.requestID
                 });
               });
             } else {
               sendError("NOT_UPDATABLE");
             }
           }
         };
-        let helper = new ManifestHelper(manifest, aData.manifestURL);
+        let helper =
+          new ManifestHelper(manifest, aData.origin, aData.manifestURL);
         debug("onlyCheckAppCache - launch updateSvc.checkForUpdate for " +
               helper.fullAppcachePath());
         updateSvc.checkForUpdate(Services.io.newURI(helper.fullAppcachePath(), null, null),
                                  app.localId, false, updateObserver);
       });
       return;
     }
 
@@ -1985,17 +1989,18 @@ this.DOMApplicationRegistry = {
   updatePackagedApp: Task.async(function*(aData, aId, aApp, aNewManifest) {
     debug("updatePackagedApp");
 
     // Store the new update manifest.
     let dir = this._getAppDir(aId).path;
     let manFile = OS.Path.join(dir, "staged-update.webapp");
     yield this._writeFile(manFile, JSON.stringify(aNewManifest));
 
-    let manifest = new ManifestHelper(aNewManifest, aApp.manifestURL);
+    let manifest =
+      new ManifestHelper(aNewManifest, aApp.origin, aApp.manifestURL);
     // A package is available: set downloadAvailable to fire the matching
     // event.
     aApp.downloadAvailable = true;
     aApp.downloadSize = manifest.size;
     aApp.updateManifest = aNewManifest;
     yield this._saveApps();
 
     this.broadcastMessage("Webapps:UpdateState", {
@@ -2031,17 +2036,18 @@ this.DOMApplicationRegistry = {
 
       this.notifyUpdateHandlers(AppsUtils.cloneAppObject(aApp), aNewManifest);
 
       // Store the new manifest.
       let dir = this._getAppDir(aId).path;
       let manFile = OS.Path.join(dir, "manifest.webapp");
       yield this._writeFile(manFile, JSON.stringify(aNewManifest));
 
-      manifest = new ManifestHelper(aNewManifest, aApp.origin);
+      manifest =
+        new ManifestHelper(aNewManifest, aApp.origin, aApp.manifestURL);
 
       if (supportUseCurrentProfile()) {
         // Update the permissions for this app.
         PermissionsInstaller.installPermissions({
           manifest: aApp.manifest,
           origin: aApp.origin,
           manifestURL: aData.manifestURL
         }, true);
@@ -2050,17 +2056,18 @@ this.DOMApplicationRegistry = {
       this.updateDataStore(this.webapps[aId].localId, aApp.origin,
                            aApp.manifestURL, aApp.manifest);
 
       aApp.name = aNewManifest.name;
       aApp.csp = manifest.csp || "";
       aApp.role = manifest.role || "";
       aApp.updateTime = Date.now();
     } else {
-      manifest = new ManifestHelper(aOldManifest, aApp.origin);
+      manifest =
+        new ManifestHelper(aOldManifest, aApp.origin, aApp.manifestURL);
     }
 
     // Update the registry.
     this.webapps[aId] = aApp;
     yield this._saveApps();
 
     if (!manifest.appcache_path) {
       this.broadcastMessage("Webapps:UpdateState", {
@@ -2559,17 +2566,18 @@ this.DOMApplicationRegistry = {
     }
 
     let app = this._setupApp(aData, id);
 
     let jsonManifest = aData.isPackage ? app.updateManifest : app.manifest;
     yield this._writeManifestFile(id, aData.isPackage, jsonManifest);
 
     debug("app.origin: " + app.origin);
-    let manifest = new ManifestHelper(jsonManifest, app.origin);
+    let manifest =
+      new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
 
     let appObject = this._cloneApp(aData, app, manifest, jsonManifest, id, localId);
 
     this.webapps[id] = appObject;
 
     // For package apps, the permissions are not in the mini-manifest, so
     // don't update the permissions yet.
     if (!aData.isPackage) {
@@ -2613,17 +2621,17 @@ this.DOMApplicationRegistry = {
       if (aData.app.localInstallPath) {
         dontNeedNetwork = true;
         jsonManifest.package_path = "file://" + aData.app.localInstallPath;
       }   
 #endif
 
       // origin for install apps is meaningless here, since it's app:// and this
       // can't be used to resolve package paths.
-      manifest = new ManifestHelper(jsonManifest, app.manifestURL);
+      manifest = new ManifestHelper(jsonManifest, app.origin, app.manifestURL);
 
       this.queuedPackageDownload[app.manifestURL] = {
         manifest: manifest,
         app: appObject,
         callback: aInstallSuccessCallback
       };
     }
 
--- a/dom/apps/tests/chrome.ini
+++ b/dom/apps/tests/chrome.ini
@@ -2,13 +2,14 @@
 support-files =
   asmjs/*
   file_bug_945152.html
   file_bug_945152.sjs
 
 [test_apps_service.xul]
 [test_bug_945152.html]
 run-if = os == 'linux'
+[test_manifest_helper.xul]
 [test_operator_app_install.js]
 [test_operator_app_install.xul]
 # bug 928262
  skip-if = os == "win"
 [test_packaged_app_asmjs.html]
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/test_manifest_helper.xul
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1042881
+-->
+<window title="Mozilla Bug 1042881"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1042881"
+     target="_blank">Mozilla Bug 1042881</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 1042881 **/
+
+  Components.utils.import("resource://gre/modules/AppsUtils.jsm");
+
+  // Test that we get the right launch path from manifests similar to
+  // twitter's one.
+  var manifest = {
+    "version":"1.0",
+    "name":"Twitter",
+    "installs_allowed_from":["*"],
+    "developer":{
+      "name":"Twitter",
+      "url":"http://twitter.com"
+    },
+    "icons":{
+      "30":"/images/larrybird-30.png",
+      "60":"/images/larrybird-60.png",
+      "128":"/images/larrybird-128.png"
+    }
+  }
+
+  var helper = new ManifestHelper(manifest, "https://mobile.twitter.com",
+                                  "https://mobile.twitter.com/cache/twitter.webapp");
+
+  SimpleTest.is(helper.fullLaunchPath(), "https://mobile.twitter.com/");
+  SimpleTest.is(helper.iconURLForSize(60),
+                "https://mobile.twitter.com/images/larrybird-60.png");
+
+  ]]>
+  </script>
+</window>
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -1378,84 +1378,83 @@ BluetoothServiceBluedroid::UpdateSdpReco
   BluetoothProfileManagerBase* aManager)
 {
   return true;
 }
 
 class CreateBondResultHandler MOZ_FINAL : public BluetoothResultHandler
 {
 public:
-  CreateBondResultHandler(size_t aRunnableIndex)
-  : mRunnableIndex(aRunnableIndex)
+  CreateBondResultHandler(BluetoothReplyRunnable* aRunnable)
+  : mRunnable(aRunnable)
   { }
 
   void OnError(int aStatus) MOZ_OVERRIDE
   {
-    BluetoothReplyRunnable* runnable = sBondingRunnableArray[mRunnableIndex];
-    sBondingRunnableArray[mRunnableIndex] = nullptr;
-    ReplyStatusError(runnable, aStatus, NS_LITERAL_STRING("CreatedPairedDevice"));
+    sBondingRunnableArray.RemoveElement(mRunnable);
+    ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("CreatedPairedDevice"));
   }
 
 private:
-  PRUint32 mRunnableIndex;
+  BluetoothReplyRunnable* mRunnable;
 };
 
 nsresult
 BluetoothServiceBluedroid::CreatePairedDeviceInternal(
   const nsAString& aDeviceAddress, int aTimeout,
   BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
   bt_bdaddr_t remoteAddress;
   StringToBdAddressType(aDeviceAddress, &remoteAddress);
 
-  PRUint32 i = sBondingRunnableArray.Length();
   sBondingRunnableArray.AppendElement(aRunnable);
 
-  sBtInterface->CreateBond(&remoteAddress, new CreateBondResultHandler(i));
+  sBtInterface->CreateBond(&remoteAddress,
+                           new CreateBondResultHandler(aRunnable));
 
   return NS_OK;
 }
 
 class RemoveBondResultHandler MOZ_FINAL : public BluetoothResultHandler
 {
 public:
-  RemoveBondResultHandler(size_t aRunnableIndex)
-  : mRunnableIndex(aRunnableIndex)
+  RemoveBondResultHandler(BluetoothReplyRunnable* aRunnable)
+  : mRunnable(aRunnable)
   { }
 
   void OnError(int aStatus) MOZ_OVERRIDE
   {
-    BluetoothReplyRunnable* runnable = sUnbondingRunnableArray[mRunnableIndex];
-    sUnbondingRunnableArray[mRunnableIndex] = nullptr;
-    ReplyStatusError(runnable, aStatus, NS_LITERAL_STRING("RemoveDevice"));
+    sUnbondingRunnableArray.RemoveElement(mRunnable);
+    ReplyStatusError(mRunnable, aStatus, NS_LITERAL_STRING("RemoveDevice"));
   }
 
 private:
-  PRUint32 mRunnableIndex;
+  BluetoothReplyRunnable* mRunnable;
 };
 
 nsresult
 BluetoothServiceBluedroid::RemoveDeviceInternal(
   const nsAString& aDeviceAddress, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
   bt_bdaddr_t remoteAddress;
   StringToBdAddressType(aDeviceAddress, &remoteAddress);
 
   PRUint32 i = sUnbondingRunnableArray.Length();
   sUnbondingRunnableArray.AppendElement(aRunnable);
 
-  sBtInterface->RemoveBond(&remoteAddress, new RemoveBondResultHandler(i));
+  sBtInterface->RemoveBond(&remoteAddress,
+                           new RemoveBondResultHandler(aRunnable));
 
   return NS_OK;
 }
 
 class PinReplyResultHandler MOZ_FINAL : public BluetoothResultHandler
 {
 public:
   PinReplyResultHandler(BluetoothReplyRunnable* aRunnable)
--- a/dom/messages/SystemMessagePermissionsChecker.jsm
+++ b/dom/messages/SystemMessagePermissionsChecker.jsm
@@ -165,28 +165,30 @@ this.SystemMessagePermissionsChecker = {
     return object
   },
 
   /**
    * Check if the system message is permitted to be registered for the given
    * app at start-up based on the permissions claimed in the app's manifest.
    * @param string aSysMsgName
    *        The system messsage name.
-   * @param string aOrigin
-   *        The app's origin.
+   * @param string aManifestURL
+   *        The app's manifest URL.
    * @param object aManifest
    *        The app's manifest.
    * @returns bool
    *        Is permitted or not.
    **/
   isSystemMessagePermittedToRegister:
-    function isSystemMessagePermittedToRegister(aSysMsgName, aOrigin, aManifest) {
+    function isSystemMessagePermittedToRegister(aSysMsgName,
+                                                aManifestURL,
+                                                aManifest) {
     debug("isSystemMessagePermittedToRegister(): " +
           "aSysMsgName: " + aSysMsgName + ", " +
-          "aOrigin: " + aOrigin + ", " +
+          "aManifestURL: " + aManifestURL + ", " +
           "aManifest: " + JSON.stringify(aManifest));
 
     let permNames = this.getSystemMessagePermissions(aSysMsgName);
     if (permNames === null) {
       return false;
     }
 
     // Check to see if the 'webapp' is app/privileged/certified.
@@ -202,30 +204,32 @@ this.SystemMessagePermissionsChecker = {
       appStatus = "app";
       break;
     default:
       throw new Error("SystemMessagePermissionsChecker.jsm: " +
                       "Cannot decide the app's status. Install cancelled.");
       break;
     }
 
-    let newManifest = new ManifestHelper(aManifest, aOrigin);
+    // It's ok here to not pass the origin to ManifestHelper since we only
+    // need the permission property and that doesn't depend on uri resolution.
+    let newManifest = new ManifestHelper(aManifest, aManifestURL, aManifestURL);
 
     for (let permName in permNames) {
       // The app doesn't claim valid permissions for this sytem message.
       if (!newManifest.permissions || !newManifest.permissions[permName]) {
         debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
-              "Please add the permission for app: '" + aOrigin + "'.");
+              "Please add the permission for app: '" + aManifestURL + "'.");
         return false;
       }
       let permValue = PermissionsTable[permName][appStatus];
       if (permValue != Ci.nsIPermissionManager.PROMPT_ACTION &&
           permValue != Ci.nsIPermissionManager.ALLOW_ACTION) {
         debug("'" + aSysMsgName + "' isn't permitted by '" + permName + "'. " +
-              "Please add the permission for app: '" + aOrigin + "'.");
+              "Please add the permission for app: '" + aManifestURL + "'.");
         return false;
       }
 
       // Compare the expanded permission names between the ones in
       // app's manifest and the ones needed for system message.
       let expandedPermNames =
         expandPermissions(permName,
                           newManifest.permissions[permName].access);
--- a/dom/network/src/NetworkStatsService.jsm
+++ b/dom/network/src/NetworkStatsService.jsm
@@ -686,29 +686,29 @@ this.NetworkStatsService = {
     }
   },
 
   /*
    * Callback of request stats. Store stats in database.
    */
   networkStatsAvailable: function networkStatsAvailable(aCallback, aNetId,
                                                         aResult, aRxBytes,
-                                                        aTxBytes, aDate) {
+                                                        aTxBytes, aTimestamp) {
     if (!aResult) {
       if (aCallback) {
         aCallback(false, "Netd IPC error");
       }
       return;
     }
 
     let stats = { appId:          0,
                   serviceType:    "",
                   networkId:      this._networks[aNetId].network.id,
                   networkType:    this._networks[aNetId].network.type,
-                  date:           aDate,
+                  date:           new Date(aTimestamp),
                   rxBytes:        aTxBytes,
                   txBytes:        aRxBytes,
                   isAccumulative: true };
 
     debug("Update stats for: " + JSON.stringify(stats));
 
     this._db.saveStats(stats, function onSavedStats(aError, aResult) {
       if (aCallback) {
--- a/dom/network/tests/unit_stats/test_networkstats_service.js
+++ b/dom/network/tests/unit_stats/test_networkstats_service.js
@@ -42,28 +42,28 @@ function getNetworkId(callback) {
 
 add_test(function test_networkStatsAvailable_ok() {
   getNetworkId(function onGetId(error, result) {
     do_check_eq(error, null);
     var netId = result;
     NetworkStatsService.networkStatsAvailable(function (success, msg) {
       do_check_eq(success, true);
       run_next_test();
-    }, netId, true, 1234, 4321, new Date());
+    }, netId, true, 1234, 4321, Date.now());
   });
 });
 
 add_test(function test_networkStatsAvailable_failure() {
   getNetworkId(function onGetId(error, result) {
     do_check_eq(error, null);
     var netId = result;
     NetworkStatsService.networkStatsAvailable(function (success, msg) {
       do_check_eq(success, false);
       run_next_test();
-    }, netId, false, 1234, 4321, new Date());
+    }, netId, false, 1234, 4321, Date.now());
   });
 });
 
 add_test(function test_update_invalidNetwork() {
   NetworkStatsService.update(-1, function (success, msg) {
     do_check_eq(success, false);
     do_check_eq(msg, "Invalid network -1");
     run_next_test();
--- a/dom/nfc/nsNfc.js
+++ b/dom/nfc/nsNfc.js
@@ -93,37 +93,50 @@ function MozNFCPeer() {
   debug("In MozNFCPeer Constructor");
   this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
                              .getService(Ci.nsINfcContentHelper);
   this.session = null;
 }
 MozNFCPeer.prototype = {
   _nfcContentHelper: null,
   _window: null,
+  _isLost: false,
 
   initialize: function(aWindow, aSessionToken) {
     this._window = aWindow;
     this.session = aSessionToken;
   },
 
   // NFCPeer interface:
   sendNDEF: function sendNDEF(records) {
+    if (this._isLost) {
+      throw new this._window.DOMError("InvalidStateError", "NFCPeer object is invalid");
+    }
+
     // Just forward sendNDEF to writeNDEF
     return this._nfcContentHelper.writeNDEF(this._window, records, this.session);
   },
 
   sendFile: function sendFile(blob) {
+    if (this._isLost) {
+      throw new this._window.DOMError("InvalidStateError", "NFCPeer object is invalid");
+    }
+
     let data = {
       "blob": blob
     };
     return this._nfcContentHelper.sendFile(this._window,
                                            Cu.cloneInto(data, this._window),
                                            this.session);
   },
 
+  invalidate: function invalidate() {
+    this._isLost = true;
+  },
+
   classID: Components.ID("{c1b2bcf0-35eb-11e3-aa6e-0800200c9a66}"),
   contractID: "@mozilla.org/nfc/NFCPeer;1",
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
                                          Ci.nsIDOMGlobalPropertyInitializer]),
 };
 
 /**
  * Navigator NFC object
@@ -131,20 +144,24 @@ MozNFCPeer.prototype = {
 function mozNfc() {
   debug("In mozNfc Constructor");
   try {
     this._nfcContentHelper = Cc["@mozilla.org/nfc/content-helper;1"]
                                .getService(Ci.nsINfcContentHelper);
   } catch(e) {
     debug("No NFC support.")
   }
+
+  this._nfcContentHelper.registerPeerEventListener(this);
 }
 mozNfc.prototype = {
   _nfcContentHelper: null,
   _window: null,
+  nfcObject: null,
+
   _wrap: function _wrap(obj) {
     return Cu.cloneInto(obj, this._window);
   },
 
   init: function init(aWindow) {
     debug("mozNfc init called");
     this._window = aWindow;
   },
@@ -187,23 +204,29 @@ mozNfc.prototype = {
     if (this._nfcContentHelper.setSessionToken(sessionToken)) {
       return this._window.MozNFCTag._create(this._window, obj);
     }
     throw new Error("Unable to create NFCTag object, Reason:  Bad SessionToken " +
                      sessionToken);
   },
 
   getNFCPeer: function getNFCPeer(sessionToken) {
-    let obj = new MozNFCPeer();
-    obj.initialize(this._window, sessionToken);
-    if (this._nfcContentHelper.setSessionToken(sessionToken)) {
-      return this._window.MozNFCPeer._create(this._window, obj);
+    if (!sessionToken || !this._nfcContentHelper.setSessionToken(sessionToken)) {
+      throw new Error("Unable to create NFCPeer object, Reason:  Bad SessionToken " +
+                      sessionToken);
     }
-    throw new Error("Unable to create NFCPeer object, Reason:  Bad SessionToken " +
-                     sessionToken);
+
+    if (!this.nfcObject) {
+      let obj = new MozNFCPeer();
+      obj.initialize(this._window, sessionToken);
+      this.nfcObject = obj;
+      this.nfcObject.contentObject = this._window.MozNFCPeer._create(this._window, obj);
+    }
+
+    return this.nfcObject.contentObject;
   },
 
   // get/set onpeerready
   get onpeerready() {
     return this.__DOM_IMPL__.getEventHandler("onpeerready");
   },
 
   set onpeerready(handler) {
@@ -216,42 +239,71 @@ mozNfc.prototype = {
   },
 
   set onpeerlost(handler) {
     this.__DOM_IMPL__.setEventHandler("onpeerlost", handler);
   },
 
   eventListenerWasAdded: function(evt) {
     let eventType = this.getEventType(evt);
-    if (eventType == -1)
+    if (eventType != NFC_PEER_EVENT_READY) {
       return;
-    this.registerTarget(eventType);
+    }
+
+    let appId = this._window.document.nodePrincipal.appId;
+    this._nfcContentHelper.registerTargetForPeerReady(this._window, appId);
   },
 
   eventListenerWasRemoved: function(evt) {
     let eventType = this.getEventType(evt);
-    if (eventType == -1)
+    if (eventType != NFC_PEER_EVENT_READY) {
       return;
-    this.unregisterTarget(eventType);
+    }
+
+    let appId = this._window.document.nodePrincipal.appId;
+    this._nfcContentHelper.unregisterTargetForPeerReady(this._window, appId);
   },
 
-  registerTarget: function registerTarget(event) {
-    let self = this;
-    let appId = this._window.document.nodePrincipal.appId;
-    this._nfcContentHelper.registerTargetForPeerEvent(this._window, appId,
-      event, function(evt, sessionToken) {
-        self.session = sessionToken;
-        self.firePeerEvent(evt, sessionToken);
-    });
+  notifyPeerReady: function notifyPeerReady(sessionToken) {
+    if (this.hasDeadWrapper()) {
+      dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
+      return;
+    }
+
+    this.session = sessionToken;
+
+    debug("fire onpeerready sessionToken : " + sessionToken);
+    let detail = {
+      "detail":sessionToken
+    };
+    let event = new this._window.CustomEvent("peerready", this._wrap(detail));
+    this.__DOM_IMPL__.dispatchEvent(event);
   },
 
-  unregisterTarget: function unregisterTarget(event) {
-    let appId = this._window.document.nodePrincipal.appId;
-    this._nfcContentHelper.unregisterTargetForPeerEvent(this._window,
-                                                        appId, event);
+  notifyPeerLost: function notifyPeerLost(sessionToken) {
+    if (this.hasDeadWrapper()) {
+      dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
+      return;
+    }
+
+    if (sessionToken != this.session) {
+      dump("Unpaired session for notifyPeerLost." + sessionToken);
+      return;
+    }
+
+    if (this.nfcObject && (this.nfcObject.session == sessionToken)) {
+      this.nfcObject.invalidate();
+      this.nfcObject = null;
+    }
+
+    this.session = null;
+
+    debug("fire onpeerlost");
+    let event = new this._window.Event("peerlost");
+    this.__DOM_IMPL__.dispatchEvent(event);
   },
 
   getEventType: function getEventType(evt) {
     let eventType = -1;
     switch (evt) {
       case 'peerready':
         eventType = NFC_PEER_EVENT_READY;
         break;
@@ -263,29 +315,16 @@ mozNfc.prototype = {
     }
     return eventType;
   },
 
   hasDeadWrapper: function hasDeadWrapper() {
     return Cu.isDeadWrapper(this._window) || Cu.isDeadWrapper(this.__DOM_IMPL__);
   },
 
-  firePeerEvent: function firePeerEvent(evt, sessionToken) {
-    if (this.hasDeadWrapper()) {
-      dump("this._window or this.__DOM_IMPL__ is a dead wrapper.");
-      return;
-    }
-
-    let peerEvent = (NFC_PEER_EVENT_READY === evt) ? "peerready" : "peerlost";
-    let detail = {
-      "detail":sessionToken
-    };
-    let event = new this._window.CustomEvent(peerEvent, this._wrap(detail));
-    this.__DOM_IMPL__.dispatchEvent(event);
-  },
-
   classID: Components.ID("{6ff2b290-2573-11e3-8224-0800200c9a66}"),
   contractID: "@mozilla.org/navigatorNfc;1",
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
-                                         Ci.nsIDOMGlobalPropertyInitializer]),
+                                         Ci.nsIDOMGlobalPropertyInitializer,
+                                         Ci.nsINfcPeerEventListener]),
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozNFCTag, MozNFCPeer, mozNfc]);
--- a/dom/nfc/tests/marionette/test_nfc_peer.js
+++ b/dom/nfc/tests/marionette/test_nfc_peer.js
@@ -5,26 +5,30 @@ MARIONETTE_TIMEOUT = 30000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let MANIFEST_URL = "app://system.gaiamobile.org/manifest.webapp";
 let INCORRECT_MANIFEST_URL = "app://xyz.gaiamobile.org/manifest.webapp";
 
 function peerReadyCb(evt) {
   log("peerReadyCb called");
   let peer = nfc.getNFCPeer(evt.detail);
+  let peer1 = nfc.getNFCPeer(evt.detail);
+  ok(peer == peer1, "Should get the same NFCPeer object.");
   ok(peer instanceof MozNFCPeer, "Should get a NFCPeer object.");
 
+  NCI.deactivate();
+}
+
+function peerLostCb(evt) {
+  log("peerLostCb called");
+  ok(evt.detail === undefined, "evt.detail should be undefined");
+  ok(true);
+
   // reset callback.
   nfc.onpeerready = null;
-  NCI.deactivate();
-}
-
-function peerLostCb() {
-  log("peerLostCb called");
-  ok(true);
   nfc.onpeerlost = null;
   toggleNFC(false).then(runNextTest);
 }
 
 function handleTechnologyDiscoveredRE0(msg) {
   log("Received \'nfc-manager-tech-discovered\'");
   is(msg.type, "techDiscovered", "check for correct message type");
   is(msg.techList[0], "P2P", "check for correct tech type");
@@ -88,17 +92,73 @@ function testCheckNfcPeerObjForInvalidTo
     ok(false, "Should not get a NFCPeer object.");
   } catch (ex) {
     ok(true, "Exception expected");
   }
 
   toggleNFC(false).then(runNextTest);
 }
 
+function testPeerLostShouldNotBeCalled() {
+  nfc.onpeerlost = function () {
+    ok(false, "onpeerlost shouldn't be called");
+  };
+
+  toggleNFC(true)
+    .then(() => NCI.activateRE(emulator.P2P_RE_INDEX_0))
+    .then(NCI.deactivate)
+    .then(() => toggleNFC(false));
+
+  nfc.onpeerlost = null;
+  runNextTest();
+}
+
+function testPeerShouldThrow() {
+  let peer;
+  let tnf = NDEF.TNF_WELL_KNOWN;
+  let type = new Uint8Array(NfcUtils.fromUTF8("U"));
+  let id = new Uint8Array(NfcUtils.fromUTF8(""));
+  let payload = new Uint8Array(NfcUtils.fromUTF8(url));
+  let ndef = [new MozNDEFRecord(tnf, type, id, payload)];
+
+  nfc.onpeerready = function (evt) {
+    peer = nfc.getNFCPeer(evt.detail);
+  };
+
+  let request = nfc.checkP2PRegistration(MANIFEST_URL);
+  request.onsuccess = function (evt) {
+    is(request.result, true, "check for P2P registration result");
+    nfc.notifyUserAcceptedP2P(MANIFEST_URL);
+  }
+
+  toggleNFC(true)
+    .then(() => NCI.activateRE(emulator.P2P_RE_INDEX_0))
+    .then(NCI.deactivate);
+
+  try {
+    peer.sendNDEF(ndef);
+    ok(false, "sendNDEF should throw error");
+  } catch (e) {
+    ok(true, "Exception expected");
+  }
+
+  try {
+    peer.sendFile(new Blob());
+    ok(false, "sendfile should throw error");
+  } catch (e) {
+    ok(true, "Exception expected");
+  }
+
+  nfc.onpeerready = null;
+  toggleNFC(false).then(runNextTest);
+}
+
 let tests = [
   testPeerReady,
   testCheckP2PRegFailure,
-  testCheckNfcPeerObjForInvalidToken
+  testCheckNfcPeerObjForInvalidToken,
+  testPeerLostShouldNotBeCalled,
+  testPeerShouldThrow
 ];
 
 SpecialPowers.pushPermissions(
   [{"type": "nfc-manager", "allow": true, context: document},
    {"type": "nfc-write", "allow": true, context: document}], runTests);
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -2,16 +2,17 @@
  * 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/. */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
+const Cu = Components.utils;
 
 const POSITION_UNAVAILABLE = Ci.nsIDOMGeoPositionError.POSITION_UNAVAILABLE;
 const SETTINGS_DEBUG_ENABLED = "geolocation.debugging.enabled";
 const SETTINGS_CHANGED_TOPIC = "mozsettings-changed";
 const SETTINGS_WIFI_ENABLED = "wifi.enabled";
 
 let gLoggingEnabled = false;
 
--- a/dom/system/gonk/NetworkService.js
+++ b/dom/system/gonk/NetworkService.js
@@ -119,51 +119,45 @@ NetworkService.prototype = {
     }
   },
 
   // nsINetworkService
 
   getNetworkInterfaceStats: function(networkName, callback) {
     if(DEBUG) debug("getNetworkInterfaceStats for " + networkName);
 
-    if (this.shutdown) {
-      return;
-    }
-
     let file = new FileUtils.File("/proc/net/dev");
     if (!file) {
-      callback.networkStatsAvailable(false, -1, -1, new Date());
+      callback.networkStatsAvailable(false, 0, 0, Date.now());
       return;
     }
 
     NetUtil.asyncFetch(file, function(inputStream, status) {
-      let result = {
-        success: true,  // netd always return success even interface doesn't exist.
-        rxBytes: 0,
-        txBytes: 0
-      };
-      result.date = new Date();
+      let rxBytes = 0,
+          txBytes = 0,
+          now = Date.now();
 
       if (Components.isSuccessCode(status)) {
         // Find record for corresponding interface.
         let statExpr = /(\S+): +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +(\d+) +\d+ +\d+ +\d+ +\d+ +\d+ +\d+ +\d+/;
-        let data = NetUtil.readInputStreamToString(inputStream,
-                    inputStream.available()).split("\n");
+        let data =
+          NetUtil.readInputStreamToString(inputStream, inputStream.available())
+                 .split("\n");
         for (let i = 2; i < data.length; i++) {
           let parseResult = statExpr.exec(data[i]);
           if (parseResult && parseResult[1] === networkName) {
-            result.rxBytes = parseInt(parseResult[2], 10);
-            result.txBytes = parseInt(parseResult[3], 10);
+            rxBytes = parseInt(parseResult[2], 10);
+            txBytes = parseInt(parseResult[3], 10);
             break;
           }
         }
       }
 
-      callback.networkStatsAvailable(result.success, result.rxBytes,
-                                     result.txBytes, result.date);
+      // netd always return success even interface doesn't exist.
+      callback.networkStatsAvailable(true, rxBytes, txBytes, now);
     });
   },
 
   setNetworkInterfaceAlarm: function(networkName, threshold, callback) {
     if (!networkName) {
       callback.networkUsageAlarmResult(-1);
       return;
     }
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -208,22 +208,16 @@ CommandFunc NetworkUtils::sStartDhcpServ
   NetworkUtils::setDhcpServerSuccess
 };
 
 CommandFunc NetworkUtils::sStopDhcpServerChain[] = {
   NetworkUtils::stopTethering,
   NetworkUtils::setDhcpServerSuccess
 };
 
-CommandFunc NetworkUtils::sNetworkInterfaceStatsChain[] = {
-  NetworkUtils::getRxBytes,
-  NetworkUtils::getTxBytes,
-  NetworkUtils::networkInterfaceStatsSuccess
-};
-
 CommandFunc NetworkUtils::sNetworkInterfaceEnableAlarmChain[] = {
   NetworkUtils::enableAlarm,
   NetworkUtils::setQuota,
   NetworkUtils::setAlarm,
   NetworkUtils::networkInterfaceAlarmSuccess
 };
 
 CommandFunc NetworkUtils::sNetworkInterfaceDisableAlarmChain[] = {
@@ -614,39 +608,16 @@ void NetworkUtils::clearWifiTetherParms(
                                         CommandCallback aCallback,
                                         NetworkResultOptions& aResult)
 {
   delete gWifiTetheringParms;
   gWifiTetheringParms = 0;
   next(aChain, false, aResult);
 }
 
-void NetworkUtils::getRxBytes(CommandChain* aChain,
-                              CommandCallback aCallback,
-                              NetworkResultOptions& aResult)
-{
-  char command[MAX_COMMAND_SIZE];
-  snprintf(command, MAX_COMMAND_SIZE - 1, "interface readrxcounter %s", GET_CHAR(mIfname));
-
-  doCommand(command, aChain, aCallback);
-}
-
-void NetworkUtils::getTxBytes(CommandChain* aChain,
-                              CommandCallback aCallback,
-                              NetworkResultOptions& aResult)
-{
-  NetworkParams& options = aChain->getParams();
-  options.mRxBytes = atof(NS_ConvertUTF16toUTF8(aResult.mResultReason).get());
-
-  char command[MAX_COMMAND_SIZE];
-  snprintf(command, MAX_COMMAND_SIZE - 1, "interface readtxcounter %s", GET_CHAR(mIfname));
-
-  doCommand(command, aChain, aCallback);
-}
-
 void NetworkUtils::enableAlarm(CommandChain* aChain,
                                CommandCallback aCallback,
                                NetworkResultOptions& aResult)
 {
   const char* command= "bandwidth enable";
   doCommand(command, aChain, aCallback);
 }
 
@@ -989,30 +960,16 @@ void NetworkUtils::usbTetheringFail(Netw
 void NetworkUtils::usbTetheringSuccess(CommandChain* aChain,
                                        CommandCallback aCallback,
                                        NetworkResultOptions& aResult)
 {
   ASSIGN_FIELD(mEnable)
   postMessage(aChain->getParams(), aResult);
 }
 
-void NetworkUtils::networkInterfaceStatsFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
-{
-  postMessage(aOptions, aResult);
-}
-
-void NetworkUtils::networkInterfaceStatsSuccess(CommandChain* aChain,
-                                                CommandCallback aCallback,
-                                                NetworkResultOptions& aResult)
-{
-  ASSIGN_FIELD(mRxBytes)
-  ASSIGN_FIELD_VALUE(mTxBytes, atof(NS_ConvertUTF16toUTF8(aResult.mResultReason).get()))
-  postMessage(aChain->getParams(), aResult);
-}
-
 void NetworkUtils::networkInterfaceAlarmFail(NetworkParams& aOptions, NetworkResultOptions& aResult)
 {
   postMessage(aOptions, aResult);
 }
 
 void NetworkUtils::networkInterfaceAlarmSuccess(CommandChain* aChain,
                                                 CommandCallback aCallback,
                                                 NetworkResultOptions& aResult)
@@ -1104,17 +1061,16 @@ void NetworkUtils::ExecuteCommand(Networ
     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),
@@ -1512,26 +1468,16 @@ bool NetworkUtils::removeSecondaryRoute(
            GET_CHAR(mIp),
            GET_CHAR(mPrefix),
            GET_CHAR(mGateway));
 
   doCommand(command, nullptr, nullptr);
   return true;
 }
 
-bool NetworkUtils::getNetworkInterfaceStats(NetworkParams& aOptions)
-{
-  DEBUG("getNetworkInterfaceStats: %s", GET_CHAR(mIfname));
-  aOptions.mRxBytes = -1;
-  aOptions.mTxBytes = -1;
-
-  RUN_CHAIN(aOptions, sNetworkInterfaceStatsChain, networkInterfaceStatsFail);
-  return  true;
-}
-
 bool NetworkUtils::setNetworkInterfaceAlarm(NetworkParams& aOptions)
 {
   DEBUG("setNetworkInterfaceAlarms: %s", GET_CHAR(mIfname));
   RUN_CHAIN(aOptions, sNetworkInterfaceSetAlarmChain, networkInterfaceAlarmFail);
   return true;
 }
 
 bool NetworkUtils::enableNetworkInterfaceAlarm(NetworkParams& aOptions)
--- a/dom/system/gonk/NetworkUtils.h
+++ b/dom/system/gonk/NetworkUtils.h
@@ -55,19 +55,16 @@ public:
     mInterfaceList = aOther.mInterfaceList;
     mWifiStartIp = aOther.mWifiStartIp;
     mWifiEndIp = aOther.mWifiEndIp;
     mUsbStartIp = aOther.mUsbStartIp;
     mUsbEndIp = aOther.mUsbEndIp;
     mDns1 = aOther.mDns1;
     mDns2 = aOther.mDns2;
     mDnses = aOther.mDnses;
-    mRxBytes = aOther.mRxBytes;
-    mTxBytes = aOther.mTxBytes;
-    mDate = aOther.mDate;
     mStartIp = aOther.mStartIp;
     mEndIp = aOther.mEndIp;
     mServerIp = aOther.mServerIp;
     mMaskLength = aOther.mMaskLength;
     mPreInternalIfname = aOther.mPreInternalIfname;
     mPreExternalIfname = aOther.mPreExternalIfname;
     mCurInternalIfname = aOther.mCurInternalIfname;
     mCurExternalIfname = aOther.mCurExternalIfname;
@@ -131,19 +128,16 @@ public:
     COPY_SEQUENCE_FIELD(mInterfaceList, nsString)
     COPY_OPT_STRING_FIELD(mWifiStartIp, EmptyString())
     COPY_OPT_STRING_FIELD(mWifiEndIp, EmptyString())
     COPY_OPT_STRING_FIELD(mUsbStartIp, EmptyString())
     COPY_OPT_STRING_FIELD(mUsbEndIp, EmptyString())
     COPY_OPT_STRING_FIELD(mDns1, EmptyString())
     COPY_OPT_STRING_FIELD(mDns2, EmptyString())
     COPY_SEQUENCE_FIELD(mDnses, nsString)
-    COPY_OPT_FIELD(mRxBytes, -1)
-    COPY_OPT_FIELD(mTxBytes, -1)
-    COPY_OPT_STRING_FIELD(mDate, EmptyString())
     COPY_OPT_STRING_FIELD(mStartIp, EmptyString())
     COPY_OPT_STRING_FIELD(mEndIp, EmptyString())
     COPY_OPT_STRING_FIELD(mServerIp, EmptyString())
     COPY_OPT_STRING_FIELD(mMaskLength, EmptyString())
     COPY_OPT_STRING_FIELD(mPreInternalIfname, EmptyString())
     COPY_OPT_STRING_FIELD(mPreExternalIfname, EmptyString())
     COPY_OPT_STRING_FIELD(mCurInternalIfname, EmptyString())
     COPY_OPT_STRING_FIELD(mCurExternalIfname, EmptyString())
@@ -181,19 +175,16 @@ public:
   nsTArray<nsString> mInterfaceList;
   nsString mWifiStartIp;
   nsString mWifiEndIp;
   nsString mUsbStartIp;
   nsString mUsbEndIp;
   nsString mDns1;
   nsString mDns2;
   nsTArray<nsString> mDnses;
-  float mRxBytes;
-  float mTxBytes;
-  nsString mDate;
   nsString mStartIp;
   nsString mEndIp;
   nsString mServerIp;
   nsString mMaskLength;
   nsString mPreInternalIfname;
   nsString mPreExternalIfname;
   nsString mCurInternalIfname;
   nsString mCurExternalIfname;
@@ -266,17 +257,16 @@ private:
   bool setDefaultRouteAndDNS(NetworkParams& aOptions);
   bool addHostRoute(NetworkParams& aOptions);
   bool removeDefaultRoute(NetworkParams& aOptions);
   bool removeHostRoute(NetworkParams& aOptions);
   bool removeHostRoutes(NetworkParams& aOptions);
   bool removeNetworkRoute(NetworkParams& aOptions);
   bool addSecondaryRoute(NetworkParams& aOptions);
   bool removeSecondaryRoute(NetworkParams& aOptions);
-  bool getNetworkInterfaceStats(NetworkParams& aOptions);
   bool setNetworkInterfaceAlarm(NetworkParams& aOptions);
   bool enableNetworkInterfaceAlarm(NetworkParams& aOptions);
   bool disableNetworkInterfaceAlarm(NetworkParams& aOptions);
   bool setWifiOperationMode(NetworkParams& aOptions);
   bool setDhcpServer(NetworkParams& aOptions);
   bool setWifiTethering(NetworkParams& aOptions);
   bool setUSBTethering(NetworkParams& aOptions);
   bool enableUsbRndis(NetworkParams& aOptions);
@@ -292,17 +282,16 @@ private:
   static CommandFunc sWifiRetryChain[];
   static CommandFunc sWifiOperationModeChain[];
   static CommandFunc sUSBEnableChain[];
   static CommandFunc sUSBDisableChain[];
   static CommandFunc sUSBFailChain[];
   static CommandFunc sUpdateUpStreamChain[];
   static CommandFunc sStartDhcpServerChain[];
   static CommandFunc sStopDhcpServerChain[];
-  static CommandFunc sNetworkInterfaceStatsChain[];
   static CommandFunc sNetworkInterfaceEnableAlarmChain[];
   static CommandFunc sNetworkInterfaceDisableAlarmChain[];
   static CommandFunc sNetworkInterfaceSetAlarmChain[];
   static CommandFunc sSetDnsChain[];
 
   /**
    * Individual netd command stored in command chain.
    */
@@ -312,18 +301,16 @@ private:
   static void startAccessPointDriver(PARAMS);
   static void stopAccessPointDriver(PARAMS);
   static void setAccessPoint(PARAMS);
   static void cleanUpStream(PARAMS);
   static void createUpStream(PARAMS);
   static void startSoftAP(PARAMS);
   static void stopSoftAP(PARAMS);
   static void clearWifiTetherParms(PARAMS);
-  static void getRxBytes(PARAMS);
-  static void getTxBytes(PARAMS);
   static void enableAlarm(PARAMS);
   static void disableAlarm(PARAMS);
   static void setQuota(PARAMS);
   static void removeQuota(PARAMS);
   static void setAlarm(PARAMS);
   static void setInterfaceUp(PARAMS);
   static void tetherInterface(PARAMS);
   static void preTetherInterfaceList(PARAMS);
@@ -335,34 +322,32 @@ private:
   static void untetherInterface(PARAMS);
   static void setDnsForwarders(PARAMS);
   static void enableNat(PARAMS);
   static void disableNat(PARAMS);
   static void setDefaultInterface(PARAMS);
   static void setInterfaceDns(PARAMS);
   static void wifiTetheringSuccess(PARAMS);
   static void usbTetheringSuccess(PARAMS);
-  static void networkInterfaceStatsSuccess(PARAMS);
   static void networkInterfaceAlarmSuccess(PARAMS);
   static void updateUpStreamSuccess(PARAMS);
   static void setDhcpServerSuccess(PARAMS);
   static void wifiOperationModeSuccess(PARAMS);
 #undef PARAMS
 
   /**
    * Error callback function executed when a command is fail.
    */
 #define PARAMS NetworkParams& aOptions, \
                mozilla::dom::NetworkResultOptions& aResult
   static void wifiTetheringFail(PARAMS);
   static void wifiOperationModeFail(PARAMS);
   static void usbTetheringFail(PARAMS);
   static void updateUpStreamFail(PARAMS);
   static void setDhcpServerFail(PARAMS);
-  static void networkInterfaceStatsFail(PARAMS);
   static void networkInterfaceAlarmFail(PARAMS);
   static void setDnsFail(PARAMS);
 #undef PARAMS
 
   /**
    * Command chain processing functions.
    */
   static void next(CommandChain* aChain, bool aError,
--- a/dom/system/gonk/NetworkWorker.cpp
+++ b/dom/system/gonk/NetworkWorker.cpp
@@ -40,19 +40,16 @@ public:
     COPY_FIELD(mId)
     COPY_FIELD(mRet)
     COPY_FIELD(mBroadcast)
     COPY_FIELD(mTopic)
     COPY_FIELD(mReason)
     COPY_FIELD(mResultCode)
     COPY_FIELD(mResultReason)
     COPY_FIELD(mError)
-    COPY_FIELD(mRxBytes)
-    COPY_FIELD(mTxBytes)
-    COPY_FIELD(mDate)
     COPY_FIELD(mEnable)
     COPY_FIELD(mResult)
     COPY_FIELD(mSuccess)
     COPY_FIELD(mCurExternalIfname)
     COPY_FIELD(mCurInternalIfname)
 #undef COPY_FIELD
   }
 
--- a/dom/system/gonk/Nfc.js
+++ b/dom/system/gonk/Nfc.js
@@ -57,18 +57,18 @@ const NFC_IPC_READ_PERM_MSG_NAMES = [
   "NFC:Connect",
   "NFC:Close",
 ];
 
 const NFC_IPC_WRITE_PERM_MSG_NAMES = [
   "NFC:WriteNDEF",
   "NFC:MakeReadOnlyNDEF",
   "NFC:SendFile",
-  "NFC:RegisterPeerTarget",
-  "NFC:UnregisterPeerTarget"
+  "NFC:RegisterPeerReadyTarget",
+  "NFC:UnregisterPeerReadyTarget"
 ];
 
 const NFC_IPC_MANAGER_PERM_MSG_NAMES = [
   "NFC:CheckP2PRegistration",
   "NFC:NotifyUserAcceptedP2P",
   "NFC:NotifySendFileStatus",
   "NFC:StartPoll",
   "NFC:StopPoll",
@@ -219,109 +219,75 @@ XPCOMUtils.defineLazyGetter(this, "gMess
         return;
       }
 
       for (let target of targets) {
         target.sendAsyncMessage(message, options);
       }
     },
 
-    registerPeerTarget: function registerPeerTarget(msg) {
+    registerPeerReadyTarget: function registerPeerReadyTarget(msg) {
       let appInfo = msg.json;
-      // Sanity check on PeerEvent
-      if (!this.isValidPeerEvent(appInfo.event)) {
-        return;
-      }
       let targets = this.peerTargetsMap;
       let targetInfo = targets[appInfo.appId];
       // If the application Id is already registered
       if (targetInfo) {
-        // If the event is not registered
-        if (targetInfo.event !== appInfo.event) {
-          // Update the event field ONLY
-          targetInfo.event |= appInfo.event;
-        }
-        // Otherwise event is already registered, return!
         return;
       }
+
       // Target not registered yet! Add to the target map
-
-      // Registered targetInfo target consists of 2 fields (values)
-      // target : Target to notify the right content for peer notifications
-      // event  : NFC_PEER_EVENT_READY (0x01) Or NFC_PEER_EVENT_LOST (0x02)
       let newTargetInfo = { target : msg.target,
-                            event  : appInfo.event };
+                            isPeerReadyCalled: false };
       targets[appInfo.appId] = newTargetInfo;
     },
 
-    unregisterPeerTarget: function unregisterPeerTarget(msg) {
+    unregisterPeerReadyTarget: function unregisterPeerReadyTarget(msg) {
       let appInfo = msg.json;
-      // Sanity check on PeerEvent
-      if (!this.isValidPeerEvent(appInfo.event)) {
-        return;
-      }
       let targets = this.peerTargetsMap;
       let targetInfo = targets[appInfo.appId];
       if (targetInfo) {
-        // Application Id registered and the event exactly matches.
-        if (targetInfo.event === appInfo.event) {
-          // Remove the target from the list of registered targets
-          delete targets[appInfo.appId]
-        }
-        else {
-          // Otherwise, update the event field ONLY, by removing the event flag
-          targetInfo.event &= ~appInfo.event;
-        }
+        // Remove the target from the list of registered targets
+        delete targets[appInfo.appId]
       }
     },
 
     removePeerTarget: function removePeerTarget(target) {
       let targets = this.peerTargetsMap;
       Object.keys(targets).forEach((appId) => {
         let targetInfo = targets[appId];
         if (targetInfo && targetInfo.target === target) {
           // Remove the target from the list of registered targets
           delete targets[appId];
         }
       });
     },
 
-    isRegisteredP2PTarget: function isRegisteredP2PTarget(appId, event) {
+    isPeerReadyTarget: function isPeerReadyTarget(appId) {
       let targetInfo = this.peerTargetsMap[appId];
-      // Check if it is a registered target for the 'event'
-      return ((targetInfo != null) && ((targetInfo.event & event) !== 0));
+      return (targetInfo != null);
+    },
+
+    isPeerReadyCalled: function isPeerReadyCalled(appId) {
+      let targetInfo = this.peerTargetsMap[appId];
+      return targetInfo.IsPeerReadyCalled;
     },
 
     notifyPeerEvent: function notifyPeerEvent(appId, event) {
       let targetInfo = this.peerTargetsMap[appId];
-      // Check if the application id is a registeredP2PTarget
-      if (this.isRegisteredP2PTarget(appId, event)) {
-        targetInfo.target.sendAsyncMessage("NFC:PeerEvent", {
-          event: event,
-          sessionToken: this.nfc.sessionTokenMap[this.nfc._currentSessionId]
-        });
-        return;
-      }
-      debug("Application ID : " + appId + " is not a registered target" +
-                             "for the event " + event + " notification");
-    },
-
-    isValidPeerEvent: function isValidPeerEvent(event) {
-      // Valid values : 0x01, 0x02 Or 0x03
-      return ((event === NFC.NFC_PEER_EVENT_READY) ||
-              (event === NFC.NFC_PEER_EVENT_LOST)  ||
-              (event === (NFC.NFC_PEER_EVENT_READY | NFC.NFC_PEER_EVENT_LOST)));
+      targetInfo.target.sendAsyncMessage("NFC:PeerEvent", {
+        event: event,
+        sessionToken: this.nfc.sessionTokenMap[this.nfc._currentSessionId]
+      });
     },
 
     checkP2PRegistration: function checkP2PRegistration(msg) {
       // Check if the session and application id yeild a valid registered
       // target.  It should have registered for NFC_PEER_EVENT_READY
       let isValid = !!this.nfc.sessionTokenMap[this.nfc._currentSessionId] &&
-                    this.isRegisteredP2PTarget(msg.json.appId,
-                                               NFC.NFC_PEER_EVENT_READY);
+                    this.isPeerReadyTarget(msg.json.appId);
       // Remember the current AppId if registered.
       this.currentPeerAppId = (isValid) ? msg.json.appId : null;
 
       let respMsg = { requestId: msg.json.requestId };
       if(!isValid) {
         respMsg.errorMsg = this.nfc.getErrorMessage(NFC.NFC_GECKO_ERROR_P2P_REG_INVALID);
       }
       // Notify the content process immediately of the status
@@ -373,27 +339,34 @@ XPCOMUtils.defineLazyGetter(this, "gMess
           if (msg.json.sessionToken !== this.nfc.sessionTokenMap[this.nfc._currentSessionId]) {
             debug("Received invalid Session Token: " + msg.json.sessionToken + " - Do not register this target");
             return NFC.NFC_ERROR_BAD_SESSION_ID;
           }
           this._registerMessageTarget(this.nfc.sessionTokenMap[this.nfc._currentSessionId], msg.target);
           debug("Registering target for this SessionToken : " +
                 this.nfc.sessionTokenMap[this.nfc._currentSessionId]);
           return NFC.NFC_SUCCESS;
-        case "NFC:RegisterPeerTarget":
-          this.registerPeerTarget(msg);
+        case "NFC:RegisterPeerReadyTarget":
+          this.registerPeerReadyTarget(msg);
           return null;
-        case "NFC:UnregisterPeerTarget":
-          this.unregisterPeerTarget(msg);
+        case "NFC:UnregisterPeerReadyTarget":
+          this.unregisterPeerReadyTarget(msg);
           return null;
         case "NFC:CheckP2PRegistration":
           this.checkP2PRegistration(msg);
           return null;
         case "NFC:NotifyUserAcceptedP2P":
           // Notify the 'NFC_PEER_EVENT_READY' since user has acknowledged
+          if (!this.isPeerReadyTarget(msg.json.appId)) {
+            debug("Application ID : " + appId + " is not a registered PeerReadytarget");
+            return null;
+          }
+
+          let targetInfo = this.peerTargetsMap[msg.json.appId];
+          targetInfo.IsPeerReadyCalled = true;
           this.notifyPeerEvent(msg.json.appId, NFC.NFC_PEER_EVENT_READY);
           return null;
         case "NFC:NotifySendFileStatus":
           // Upon receiving the status of sendFile operation, send the response
           // to appropriate content process.
           this.sendNfcResponseMessage(msg.name + "Response", msg.json);
           return null;
         default:
@@ -525,18 +498,25 @@ Nfc.prototype = {
         gMessageManager._unregisterMessageTarget(this.sessionTokenMap[this._currentSessionId], null);
 
         // Update the upper layers with a session token (alias)
         message.sessionToken = this.sessionTokenMap[this._currentSessionId];
         // Do not expose the actual session to the content
         delete message.sessionId;
 
         gSystemMessenger.broadcastMessage("nfc-manager-tech-lost", message);
-        // Notify 'PeerLost' to appropriate registered target, if any
-        gMessageManager.notifyPeerEvent(gMessageManager.currentPeerAppId, NFC.NFC_PEER_EVENT_LOST);
+
+        let appId = gMessageManager.currentPeerAppId;
+
+        // For peerlost, the message is delievered to the target which
+        // registered onpeerready and onpeerready has been called before.
+        if (gMessageManager.isPeerReadyTarget(appId) && gMessageManager.isPeerReadyCalled(appId)) {
+          gMessageManager.notifyPeerEvent(appId, NFC.NFC_PEER_EVENT_LOST);
+        }
+
         delete this.sessionTokenMap[this._currentSessionId];
         this._currentSessionId = null;
         this.currentPeerAppId = null;
         break;
      case "ConfigResponse":
         let target = this.targetsByRequestId[message.requestId];
         if (!target) {
           debug("No target for requestId: " + message.requestId);
--- a/dom/system/gonk/NfcContentHelper.js
+++ b/dom/system/gonk/NfcContentHelper.js
@@ -75,37 +75,33 @@ GetDetailsNDEFResponse.prototype = {
                       maxSupportedLength: 'r'}
 };
 
 function NfcContentHelper() {
   this.initDOMRequestHelper(/* aWindow */ null, NFC_IPC_MSG_NAMES);
   Services.obs.addObserver(this, "xpcom-shutdown", false);
 
   this._requestMap = [];
-
-  // Maintains an array of PeerEvent related callbacks, mainly
-  // one for 'peerReady' and another for 'peerLost'.
-  this.peerEventsCallbackMap = {};
 }
 
 NfcContentHelper.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsINfcContentHelper,
                                          Ci.nsISupportsWeakReference,
                                          Ci.nsIObserver]),
   classID:   NFCCONTENTHELPER_CID,
   classInfo: XPCOMUtils.generateCI({
     classID:          NFCCONTENTHELPER_CID,
     classDescription: "NfcContentHelper",
     interfaces:       [Ci.nsINfcContentHelper]
   }),
 
   _requestMap: null,
-  peerEventsCallbackMap: null,
+  peerEventListener: null,
 
   encodeNDEFRecords: function encodeNDEFRecords(records) {
     let encodedRecords = [];
     for (let i = 0; i < records.length; i++) {
       let record = records[i];
       encodedRecords.push({
         tnf: record.tnf,
         type: record.type,
@@ -256,44 +252,36 @@ NfcContentHelper.prototype = {
     }
 
     cpmm.sendAsyncMessage("NFC:NotifySendFileStatus", {
       status: status,
       requestId: requestId
     });
   },
 
-  registerTargetForPeerEvent: function registerTargetForPeerEvent(window,
-                                                  appId, event, callback) {
+  registerPeerEventListener: function registerPeerEventListener(listener) {
+    this.peerEventListener = listener;
+  },
+
+  registerTargetForPeerReady: function registerTargetForPeerReady(window, appId) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
-    this.peerEventsCallbackMap[event] = callback;
-    cpmm.sendAsyncMessage("NFC:RegisterPeerTarget", {
-      appId: appId,
-      event: event
-    });
+
+    cpmm.sendAsyncMessage("NFC:RegisterPeerReadyTarget", { appId: appId });
   },
 
-  unregisterTargetForPeerEvent: function unregisterTargetForPeerEvent(window,
-                                                                appId, event) {
+  unregisterTargetForPeerReady: function unregisterTargetForPeerReady(window, appId) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
-    let callback = this.peerEventsCallbackMap[event];
-    if (callback != null) {
-      delete this.peerEventsCallbackMap[event];
-    }
 
-    cpmm.sendAsyncMessage("NFC:UnregisterPeerTarget", {
-      appId: appId,
-      event: event
-    });
+    cpmm.sendAsyncMessage("NFC:UnregisterPeerReadyTarget", { appId: appId });
   },
 
   checkP2PRegistration: function checkP2PRegistration(window, appId) {
     if (window == null) {
       throw Components.Exception("Can't get window object",
                                   Cr.NS_ERROR_UNEXPECTED);
     }
     let request = Services.DOMRequest.createRequest(window);
@@ -422,22 +410,23 @@ NfcContentHelper.prototype = {
       case "NFC:ConfigResponse":
         if (result.errorMsg) {
           this.fireRequestError(atob(result.requestId), result.errorMsg);
         } else {
           this.fireRequestSuccess(atob(result.requestId), result);
         }
         break;
       case "NFC:PeerEvent":
-        let callback = this.peerEventsCallbackMap[result.event];
-        if (callback) {
-          callback.peerNotification(result.event, result.sessionToken);
-        } else {
-          debug("PeerEvent: No valid callback registered for the event " +
-                result.event);
+        switch (result.event) {
+          case NFC.NFC_PEER_EVENT_READY:
+            this.peerEventListener.notifyPeerReady(result.sessionToken);
+            break;
+          case NFC.NFC_PEER_EVENT_LOST:
+            this.peerEventListener.notifyPeerLost(result.sessionToken);
+            break;
         }
         break;
     }
   },
 
   handleReadNDEFResponse: function handleReadNDEFResponse(result) {
     let requester = this._requestMap[result.requestId];
     if (!requester) {
--- a/dom/system/gonk/nsINetworkService.idl
+++ b/dom/system/gonk/nsINetworkService.idl
@@ -14,23 +14,23 @@ interface nsIWifiTetheringCallback : nsI
    *
    * @param error
    *        An error message if the operation wasn't successful,
    *        or `null` if it was.
    */
   void wifiTetheringEnabledChange(in jsval error);
 };
 
-[scriptable, function, uuid(e079aa2a-ec0a-4bbd-b1a4-d81a9faae464)]
+[scriptable, function, uuid(9c128e68-5e4b-4626-bb88-84ec54cce5d8)]
 interface nsINetworkStatsCallback : nsISupports
 {
   void networkStatsAvailable(in boolean success,
                              in unsigned long rxBytes,
                              in unsigned long txBytes,
-                             in jsval date);
+                             in unsigned long long timestamp);
 };
 
 [scriptable, function, uuid(0706bfa2-ac2d-11e2-9a8d-7b6d988d4767)]
 interface nsINetworkUsageAlarmCallback : nsISupports
 {
   void networkUsageAlarmResult(in jsval error);
 };
 
--- a/dom/system/gonk/nsINfcContentHelper.idl
+++ b/dom/system/gonk/nsINfcContentHelper.idl
@@ -2,34 +2,37 @@
  * 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/. */
 
 #include "nsISupports.idl"
 #include "nsIDOMDOMRequest.idl"
 
 interface nsIVariant;
 
-[scriptable, function, uuid(26673d1a-4af4-470a-ba96-f1f54b1f2052)]
-interface nsINfcPeerCallback : nsISupports
+[scriptable, uuid(57fc2998-1058-4fd5-8dd9-0e303218d5fd)]
+interface nsINfcPeerEventListener : nsISupports
 {
   /**
-   * Callback function used to notify NFC peer events.
-   *
-   * @param event
-   *        An event indicating 'PeerReady' or 'PeerLost'
-   *        One of NFC_EVENT_PEER_XXXX
+   * Callback function used to notify peerready.
    *
    * @param sessionToken
    *        SessionToken received from Chrome process
    */
-   void peerNotification(in unsigned long event,
-                         in DOMString sessionToken);
+   void notifyPeerReady(in DOMString sessionToken);
+
+  /**
+   * Callback function used to notify peerlost.
+   *
+   * @param sessionToken
+   *        SessionToken received from Chrome process
+   */
+   void notifyPeerLost(in DOMString sessionToken);
 };
 
-[scriptable, uuid(26e8123f-ba00-4708-ac77-d1902457168c)]
+[scriptable, uuid(5bea28d3-67ee-4916-8c8e-4f35e3666e61)]
 interface nsINfcContentHelper : nsISupports
 {
   const long NFC_EVENT_PEER_READY = 0x01;
   const long NFC_EVENT_PEER_LOST  = 0x02;
 
   boolean setSessionToken(in DOMString sessionToken);
 
   nsIDOMDOMRequest getDetailsNDEF(in nsIDOMWindow window, in DOMString sessionToken);
@@ -56,50 +59,47 @@ interface nsINfcContentHelper : nsISuppo
   *
   * Returns DOMRequest, if initiation of send file operation is successful
   * then 'onsuccess' is called else 'onerror'
   */
   nsIDOMDOMRequest sendFile(in nsIDOMWindow window,
                             in jsval blob,
                             in DOMString sessionToken);
 
+  /**
+   * Register the peer event listener.
+   *
+   * @param listener  An instance of the nsINfcPeerEventListener.
+   */
+  void registerPeerEventListener(in nsINfcPeerEventListener listener);
+
  /**
   * Register the given application id with Chrome process
   *
   * @param window
   *        Current window
   *
   * @param appId
   *        Application ID to be registered
-  *
-  * @param event
-  *       Event to be registered. Either NFC_EVENT_PEER_READY or NFC_EVENT_PEER_LOST
-  *
-  * @param callback
-  *       Callback that is used to notify upper layers whenever PeerEvents happen.
   */
-  void registerTargetForPeerEvent(in nsIDOMWindow window,
-                                  in unsigned long appId,
-                                  in octet event,
-                                  in nsINfcPeerCallback callback);
+  void registerTargetForPeerReady(in nsIDOMWindow window,
+                                  in unsigned long appId);
+
  /**
   * Unregister the given application id with Chrome process
   *
   * @param window
   *        Current window
   *
   * @param appId
   *        Application ID to be registered
-  *
-  * @param event
-  *       Event to be unregistered. Either NFC_EVENT_PEER_READY or NFC_EVENT_PEER_LOST
   */
-  void unregisterTargetForPeerEvent(in nsIDOMWindow window,
-                                    in unsigned long appId,
-                                    in octet event);
+  void unregisterTargetForPeerReady(in nsIDOMWindow window,
+                                    in unsigned long appId);
+
  /**
   * Checks if the given application's id is a registered peer target (with the Chrome process)
   *
   * @param window
   *        Current window
   *
   * @param appId
   *        Application ID to be updated with Chrome process
--- a/dom/webidl/MozNFCPeer.webidl
+++ b/dom/webidl/MozNFCPeer.webidl
@@ -5,17 +5,19 @@
  * Part of this IDL file is from:
  * http://w3c.github.io/nfc/proposals/common/nfc.html#idl-def-NFCPeer
  *
  * Copyright © 2013 Deutsche Telekom, Inc.
  */
 
 [JSImplementation="@mozilla.org/nfc/NFCPeer;1"]
 interface MozNFCPeer {
+  [Throws]
   DOMRequest sendNDEF(sequence<MozNDEFRecord> records);
+  [Throws]
   DOMRequest sendFile(Blob blob);
 };
 
 // Mozilla Only
 partial interface MozNFCPeer {
   [ChromeOnly]
   attribute DOMString session;
 };
--- a/dom/webidl/NetworkOptions.webidl
+++ b/dom/webidl/NetworkOptions.webidl
@@ -37,19 +37,16 @@ dictionary NetworkCommandOptions
   DOMString link;                     // for "setWifiTethering", "setDhcpServer".
   sequence<DOMString> interfaceList;  // for "setWifiTethering".
   DOMString wifiStartIp;              // for "setWifiTethering".
   DOMString wifiEndIp;                // for "setWifiTethering".
   DOMString usbStartIp;               // for "setWifiTethering".
   DOMString usbEndIp;                 // for "setWifiTethering".
   DOMString dns1;                     // for "setWifiTethering".
   DOMString dns2;                     // for "setWifiTethering".
-  float rxBytes;                      // for "getNetworkInterfaceStats".
-  float txBytes;                      // for "getNetworkInterfaceStats".
-  DOMString date;                     // for "getNetworkInterfaceStats".
   long threshold;                     // for "setNetworkInterfaceAlarm",
                                       //     "enableNetworkInterfaceAlarm".
   DOMString startIp;                  // for "setDhcpServer".
   DOMString endIp;                    // for "setDhcpServer".
   DOMString serverIp;                 // for "setDhcpServer".
   DOMString maskLength;               // for "setDhcpServer".
   DOMString preInternalIfname;        // for "updateUpStream".
   DOMString preExternalIfname;        // for "updateUpStream".
@@ -67,18 +64,15 @@ dictionary NetworkResultOptions
   boolean broadcast = false;          // for netd broadcast message.
   DOMString topic = "";               // for netd broadcast message.
   DOMString reason = "";              // for netd broadcast message.
 
   long resultCode = 0;                // for all commands.
   DOMString resultReason = "";        // for all commands.
   boolean error = false;              // for all commands.
 
-  float rxBytes = -1;                 // for "getNetworkInterfaceStats".
-  float txBytes = -1;                 // for "getNetworkInterfaceStats".
-  DOMString date = "";                // for "getNetworkInterfaceStats".
   boolean enable = false;             // for "setWifiTethering", "setUSBTethering"
                                       //     "enableUsbRndis".
   boolean result = false;             // for "enableUsbRndis".
   boolean success = false;            // for "setDhcpServer".
   DOMString curExternalIfname = "";   // for "updateUpStream".
   DOMString curInternalIfname = "";   // for "updateUpStream".
 };
--- a/mobile/android/chrome/content/WebappRT.js
+++ b/mobile/android/chrome/content/WebappRT.js
@@ -78,17 +78,17 @@ let WebappRT = {
   },
 
   getManifestFor: function (aUrl, aCallback) {
     let request = navigator.mozApps.mgmt.getAll();
     request.onsuccess = function() {
       let apps = request.result;
       for (let i = 0; i < apps.length; i++) {
         let app = apps[i];
-        let manifest = new ManifestHelper(app.manifest, app.origin);
+        let manifest = new ManifestHelper(app.manifest, app.origin, app.manifestURL);
 
         // if this is a path to the manifest, or the launch path, then we have a hit.
         if (app.manifestURL == aUrl || manifest.fullLaunchPath() == aUrl) {
           aCallback(manifest, app);
           return;
         }
       }
 
--- a/mobile/android/chrome/content/aboutApps.js
+++ b/mobile/android/chrome/content/aboutApps.js
@@ -100,17 +100,17 @@ function updateList() {
       addApplication(request.result[i]);
     if (request.result.length)
       document.getElementById("main-container").classList.remove("hidden");
   }
 }
 
 function addApplication(aApp) {
   let list = document.getElementById("appgrid");
-  let manifest = new ManifestHelper(aApp.manifest, aApp.origin);
+  let manifest = new ManifestHelper(aApp.manifest, aApp.origin, aApp.manifestURL);
 
   let container = document.createElement("div");
   container.className = "app list-item";
   container.setAttribute("contextmenu", "appmenu");
   container.setAttribute("id", "app-" + aApp.manifestURL);
   container.setAttribute("title", manifest.name);
 
   let img = document.createElement("img");
--- a/mobile/android/modules/WebappManager.jsm
+++ b/mobile/android/modules/WebappManager.jsm
@@ -174,32 +174,33 @@ this.WebappManager = {
   askInstall: function(aData) {
     let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
     file.initWithPath(aData.profilePath);
 
     this._deleteAppcachePath(aData.app.manifest);
 
     DOMApplicationRegistry.registryReady.then(() => {
       DOMApplicationRegistry.confirmInstall(aData, file, (function(aApp, aManifest) {
-        this._postInstall(aData.profilePath, aManifest, aData.app.origin, aData.app.apkPackageName);
+        this._postInstall(aData.profilePath, aManifest, aData.app.origin,
+                          aData.app.apkPackageName, aData.app.manifestURL);
       }).bind(this));
     });
   },
 
-  _postInstall: function(aProfilePath, aNewManifest, aOrigin, aApkPackageName) {
+  _postInstall: function(aProfilePath, aNewManifest, aOrigin, aApkPackageName, aManifestURL) {
     // aOrigin may now point to the app: url that hosts this app.
     sendMessageToJava({
       type: "Webapps:Postinstall",
       apkPackageName: aApkPackageName,
       origin: aOrigin,
     });
 
     let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
     file.initWithPath(aProfilePath);
-    let localeManifest = new ManifestHelper(aNewManifest, aOrigin);
+    let localeManifest = new ManifestHelper(aNewManifest, aOrigin, aManifestUrl);
     this.writeDefaultPrefs(file, localeManifest);
   },
 
   launch: function({ apkPackageName }) {
     debug("launch: " + apkPackageName);
 
     sendMessageToJava({
       type: "Webapps:Launch",
@@ -328,17 +329,17 @@ this.WebappManager = {
     if (aData.type == "hosted") {
       this._deleteAppcachePath(aData.manifest);
       let oldManifest = yield DOMApplicationRegistry.getManifestFor(aData.manifestURL);
       yield DOMApplicationRegistry.updateHostedApp(aData, aOldApp.id, aOldApp, oldManifest, aData.manifest);
     } else {
       yield this._autoUpdatePackagedApp(aData, aOldApp);
     }
 
-    this._postInstall(aData.profilePath, aData.manifest, aOldApp.origin, aOldApp.apkPackageName);
+    this._postInstall(aData.profilePath, aData.manifest, aOldApp.origin, aOldApp.apkPackageName, aOldApp.manifestURL);
   }).bind(this)); },
 
   _autoUpdatePackagedApp: Task.async(function*(aData, aOldApp) {
     debug("_autoUpdatePackagedApp: " + aData.manifestURL);
 
     if (aData.updateManifest && aData.zipFilePath) {
       aData.updateManifest.package_path = aData.zipFilePath;
     }
--- a/mozglue/build/Nuwa.cpp
+++ b/mozglue/build/Nuwa.cpp
@@ -135,31 +135,17 @@ struct thread_info : public mozilla::Lin
 
   // The thread specific function to recreate the new thread. It's executed
   // after the thread is recreated.
   void (*recrFunc)(void *arg);
   void *recrArg;
 
   TLSInfoList tlsInfo;
 
-  /**
-   * We must ensure that the recreated thread has entered pthread_cond_wait() or
-   * similar functions before proceeding to recreate the next one. Otherwise, if
-   * the next thread depends on the same mutex, it may be used in an incorrect
-   * state.  To do this, the main thread must unconditionally acquire the mutex.
-   * The mutex is unconditionally released when the recreated thread enters
-   * pthread_cond_wait().  The recreated thread may have locked the mutex itself
-   * (if the pthread_mutex_trylock succeeded) or another thread may have already
-   * held the lock.  If the recreated thread did lock the mutex we must balance
-   * that with another unlock on the main thread, which is signaled by
-   * condMutexNeedsBalancing.
-   */
-  pthread_mutex_t *condMutex;
-  bool condMutexNeedsBalancing;
-
+  pthread_mutex_t *reacquireMutex;
   void *stk;
 
   pid_t origNativeThreadID;
   pid_t recreatedNativeThreadID;
   char nativeThreadName[NATIVE_THREAD_NAME_LENGTH];
 };
 
 typedef struct thread_info thread_info_t;
@@ -510,18 +496,17 @@ static thread_info_t *
 thread_info_new(void) {
   /* link tinfo to sAllThreads */
   thread_info_t *tinfo = new thread_info_t();
   tinfo->flags = 0;
   tinfo->recrFunc = nullptr;
   tinfo->recrArg = nullptr;
   tinfo->recreatedThreadID = 0;
   tinfo->recreatedNativeThreadID = 0;
-  tinfo->condMutex = nullptr;
-  tinfo->condMutexNeedsBalancing = false;
+  tinfo->reacquireMutex = nullptr;
   tinfo->stk = MozTaggedAnonymousMmap(nullptr,
                                       NUWA_STACK_SIZE + getPageSize(),
                                       PROT_READ | PROT_WRITE,
                                       MAP_PRIVATE | MAP_ANONYMOUS,
                                       /* fd */ -1,
                                       /* offset */ 0,
                                       "nuwa-thread-stack");
 
@@ -1026,26 +1011,23 @@ extern "C" MFBT_API int
   THREAD_FREEZE_POINT1_VIP();
   if (freezePoint2) {
     RECREATE_CONTINUE();
     RECREATE_PASS_VIP();
     RECREATE_GATE_VIP();
     return rv;
   }
   if (recreated && mtx) {
-    if (!freezePoint1) {
-      tinfo->condMutex = mtx;
+    if (!freezePoint1 && pthread_mutex_trylock(mtx)) {
       // The thread was frozen in pthread_cond_wait() after releasing mtx in the
       // Nuwa process. In recreating this thread, We failed to reacquire mtx
       // with the pthread_mutex_trylock() call, that is, mtx was acquired by
       // another thread. Because of this, we need the main thread's help to
       // reacquire mtx so that it will be in a valid state.
-      if (!pthread_mutex_trylock(mtx)) {
-        tinfo->condMutexNeedsBalancing = true;
-      }
+      tinfo->reacquireMutex = mtx;
     }
     RECREATE_CONTINUE();
     RECREATE_PASS_VIP();
   }
   rv = REAL(pthread_cond_wait)(cond, mtx);
   if (recreated && mtx) {
     // We still need to be gated as not to acquire another mutex associated with
     // another VIP thread and interfere with it.
@@ -1065,21 +1047,18 @@ extern "C" MFBT_API int
   THREAD_FREEZE_POINT1_VIP();
   if (freezePoint2) {
     RECREATE_CONTINUE();
     RECREATE_PASS_VIP();
     RECREATE_GATE_VIP();
     return rv;
   }
   if (recreated && mtx) {
-    if (!freezePoint1) {
-      tinfo->condMutex = mtx;
-      if (!pthread_mutex_trylock(mtx)) {
-        tinfo->condMutexNeedsBalancing = true;
-      }
+    if (!freezePoint1 && pthread_mutex_trylock(mtx)) {
+      tinfo->reacquireMutex = mtx;
     }
     RECREATE_CONTINUE();
     RECREATE_PASS_VIP();
   }
   rv = REAL(pthread_cond_timedwait)(cond, mtx, abstime);
   if (recreated && mtx) {
     RECREATE_GATE_VIP();
   }
@@ -1103,21 +1082,18 @@ extern "C" MFBT_API int
   THREAD_FREEZE_POINT1_VIP();
   if (freezePoint2) {
     RECREATE_CONTINUE();
     RECREATE_PASS_VIP();
     RECREATE_GATE_VIP();
     return rv;
   }
   if (recreated && mtx) {
-    if (!freezePoint1) {
-      tinfo->condMutex = mtx;
-      if (!pthread_mutex_trylock(mtx)) {
-        tinfo->condMutexNeedsBalancing = true;
-      }
+    if (!freezePoint1 && pthread_mutex_trylock(mtx)) {
+      tinfo->reacquireMutex = mtx;
     }
     RECREATE_CONTINUE();
     RECREATE_PASS_VIP();
   }
   rv = REAL(__pthread_cond_timedwait)(cond, mtx, abstime, clock);
   if (recreated && mtx) {
     RECREATE_GATE_VIP();
   }
@@ -1422,22 +1398,18 @@ RecreateThreads() {
   pthread_mutex_unlock(&sThreadCountLock);
 
   RECREATE_START();
   while (tinfo != nullptr) {
     if (tinfo->flags & TINFO_FLAG_NUWA_SUPPORT) {
       RECREATE_BEFORE(tinfo);
       thread_recreate(tinfo);
       RECREATE_WAIT();
-      if (tinfo->condMutex) {
-        // Synchronize with the recreated thread in pthread_cond_wait().
-        REAL(pthread_mutex_lock)(tinfo->condMutex);
-        if (tinfo->condMutexNeedsBalancing) {
-          pthread_mutex_unlock(tinfo->condMutex);
-        }
+      if (tinfo->reacquireMutex) {
+        REAL(pthread_mutex_lock)(tinfo->reacquireMutex);
       }
     } else if(!(tinfo->flags & TINFO_FLAG_NUWA_SKIP)) {
       // An unmarked thread is found other than the main thread.
 
       // All threads should be marked as one of SUPPORT or SKIP, or
       // abort the process to make sure all threads in the Nuwa
       // process are Nuwa-aware.
       abort();
--- a/toolkit/devtools/server/actors/webapps.js
+++ b/toolkit/devtools/server/actors/webapps.js
@@ -278,17 +278,18 @@ WebappsActor.prototype = {
         Services.obs.notifyObservers(null, "webapps-installed",
           JSON.stringify({ manifestURL: aApp.manifestURL }));
 
         delete aApp.manifest;
         aDeferred.resolve({ appId: aId, path: aDir.path });
 
         // We can't have appcache for packaged apps.
         if (!aApp.origin.startsWith("app://")) {
-          reg.startOfflineCacheDownload(new ManifestHelper(manifest, aApp.origin));
+          reg.startOfflineCacheDownload(
+            new ManifestHelper(manifest, aApp.origin, aApp.manifestURL));
         }
       });
       // Cleanup by removing the temporary directory.
       if (aDir.exists())
         aDir.remove(true);
     });
   },
 
@@ -742,17 +743,17 @@ WebappsActor.prototype = {
     if (!app) {
       return { error: "wrongParameter",
                message: "No application for " + manifestURL };
     }
 
     let deferred = promise.defer();
 
     this._findManifestByURL(manifestURL).then(jsonManifest => {
-      let manifest = new ManifestHelper(jsonManifest, app.origin);
+      let manifest = new ManifestHelper(jsonManifest, app.origin, manifestURL);
       let iconURL = manifest.iconURLForSize(aRequest.size || 128);
       if (!iconURL) {
         deferred.resolve({
           error: "noIcon",
           message: "This app has no icon"
         });
         return;
       }
--- a/toolkit/webapps/NativeApp.jsm
+++ b/toolkit/webapps/NativeApp.jsm
@@ -52,17 +52,18 @@ const TMP_DIR = OS.Constants.Path.tmpDir
  *
  */
 function CommonNativeApp(aApp, aManifest, aCategories, aRegistryDir) {
   // Set the name property of the app object, otherwise
   // WebappOSUtils::getUniqueName won't work.
   aApp.name = aManifest.name;
   this.uniqueName = WebappOSUtils.getUniqueName(aApp);
 
-  let localeManifest = new ManifestHelper(aManifest, aApp.origin);
+  let localeManifest =
+    new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
 
   this.appLocalizedName = localeManifest.name;
   this.appNameAsFilename = stripStringForFilename(aApp.name);
 
   if (aApp.updateManifest) {
     this.isPackaged = true;
   }
 
@@ -94,17 +95,17 @@ CommonNativeApp.prototype = {
   /**
    * This function reads and parses the data from the app
    * manifest and stores it in the NativeApp object.
    *
    * @param aManifest {Object} the manifest data provided by the web app
    *
    */
   _setData: function(aApp, aManifest) {
-    let manifest = new ManifestHelper(aManifest, aApp.origin);
+    let manifest = new ManifestHelper(aManifest, aApp.origin, aApp.manifestURL);
     let origin = Services.io.newURI(aApp.origin, null, null);
 
     this.iconURI = Services.io.newURI(manifest.biggestIconURL || DEFAULT_ICON_URL,
                                       null, null);
 
     if (manifest.developer) {
       if (manifest.developer.name) {
         let devName = manifest.developer.name.substr(0, 128);
--- a/webapprt/WebappManager.jsm
+++ b/webapprt/WebappManager.jsm
@@ -42,17 +42,18 @@ this.WebappManager = {
     let nativeApp = new NativeApp(aApp, aManifest,
                                   WebappRT.config.app.categories,
                                   WebappRT.config.registryDir);
     nativeApp.prepareUpdate(aApp, aManifest, aZipPath);
   },
 
   doInstall: function(data, window) {
     let jsonManifest = data.isPackage ? data.app.updateManifest : data.app.manifest;
-    let manifest = new ManifestHelper(jsonManifest, data.app.origin);
+    let manifest =
+      new ManifestHelper(jsonManifest, data.app.origin, data.app.manifestURL);
     let name = manifest.name;
     let bundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties");
 
     let choice = Services.prompt.confirmEx(
       window,
       bundle.formatStringFromName("webapps.install.title", [name], 1),
       bundle.formatStringFromName("webapps.install.description", [name], 1),
       // Set both buttons to strings with the cancel button being default
--- a/webapprt/WebappRT.jsm
+++ b/webapprt/WebappRT.jsm
@@ -43,17 +43,18 @@ this.WebappRT = {
   },
 
   get launchURI() {
     return this.localeManifest.fullLaunchPath();
   },
 
   get localeManifest() {
     return new ManifestHelper(this.config.app.manifest,
-                              this.config.app.origin);
+                              this.config.app.origin,
+                              this.config.app.manifestURL);
   },
 
   get appID() {
     let manifestURL = this.config.app.manifestURL;
     if (!manifestURL) {
       return Ci.nsIScriptSecurityManager.NO_APP_ID;
     }