Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 17 Oct 2013 13:53:51 +0200
changeset 165012 754c8ebb278eeaf1f472c3c2800406661a4e0c47
parent 165011 f7cb9517f8cecc9700b6377e7bbfca5899177fc8 (current diff)
parent 164922 855da6d8a327c277e9653911b3244c3229134363 (diff)
child 165013 08784e2a346ea968174ef6b357151eff815a08d5
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.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 mozilla-central to b2g-inbound
build/mobile/robocop/robotium-solo-4.2.jar
dom/tests/unit/test_geolocation_reset_accuracy.js
dom/tests/unit/test_geolocation_reset_accuracy_wrap.js
testing/specialpowers/chrome.manifest
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Bug 872934 - Clobber needed for webidl updates for style sheet change events. Again and again.
\ No newline at end of file
+Bug 895047 - Clobber needed for touching js/src/js-confdefs.h.in
--- a/Makefile.in
+++ b/Makefile.in
@@ -2,18 +2,19 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 ifndef .PYMAKE
 ifeq (,$(MAKE_VERSION))
 $(error GNU Make is required)
 endif
-ifeq (,$(filter-out 3.78 3.79,$(MAKE_VERSION)))
-$(error GNU Make 3.80 or higher is required)
+make_min_ver := 3.81
+ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
+$(error GNU Make $(make_min_ver) or higher is required)
 endif
 endif
 
 export TOPLEVEL_BUILD := 1
 
 default::
 
 ifdef COMPILE_ENVIRONMENT
--- a/accessible/src/windows/sdn/sdnAccessible.cpp
+++ b/accessible/src/windows/sdn/sdnAccessible.cpp
@@ -461,22 +461,21 @@ sdnAccessible::get_innerHTML(BSTR __RPC_
 
   if (!aInnerHTML)
     return E_INVALIDARG;
   *aInnerHTML = nullptr;
 
   if (IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
-  nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mNode);
-  if (!htmlElement)
+  if (!mNode->IsElement())
     return S_FALSE;
 
   nsAutoString innerHTML;
-  htmlElement->GetInnerHTML(innerHTML);
+  mNode->AsElement()->GetInnerHTML(innerHTML);
   if (innerHTML.IsEmpty())
     return S_FALSE;
 
   *aInnerHTML = ::SysAllocStringLen(innerHTML.get(), innerHTML.Length());
   if (!*aInnerHTML)
     return E_OUTOFMEMORY;
 
   return S_OK;
--- a/b2g/gaia/Makefile.in
+++ b/b2g/gaia/Makefile.in
@@ -4,29 +4,34 @@
 
 GAIA_PATH := gaia/profile
 
 ifeq ($(OS_ARCH),WINNT)
 DEFINES += \
   -DB2G_NAME=L\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \
   -DGAIA_PATH=L\"$(subst /,\\\\,$(GAIA_PATH))\" \
   $(NULL)
-GAIA_MAKE=make
 else # Non-windows machines use the same wrapper program
 CSRCS = run-b2g.c
 DEFINES += \
   -DB2G_NAME=\"$(MOZ_APP_NAME)-bin$(BIN_SUFFIX)\" \
   -DGAIA_PATH=\"$(GAIA_PATH)\" \
   $(NULL)
+endif
+
+ifdef .PYMAKE
+# For use of GNU make in pymake builds.
+GAIA_MAKE=$(GMAKE)
+else
 GAIA_MAKE=$(MAKE)
 endif
 
 # This is needed to avoid making run-b2g depend on mozglue
 WRAP_LDFLAGS :=
 
 GENERATED_DIRS += $(DIST)/bin/$(GAIA_PATH)
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
-	$(GAIA_MAKE) -j1 -C $(GAIADIR) clean
-	$(GAIA_MAKE) -j1 -C $(GAIADIR) profile
+	+$(GAIA_MAKE) -j1 -C $(GAIADIR) clean
+	+$(GAIA_MAKE) -j1 -C $(GAIADIR) profile
 	(cd $(GAIADIR)/profile && tar $(TAR_CREATE_FLAGS) - .) | (cd $(abspath $(DIST))/bin/$(GAIA_PATH) && tar -xf -)
--- a/browser/app/profile/extensions/Makefile.in
+++ b/browser/app/profile/extensions/Makefile.in
@@ -1,14 +1,14 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-DISTROEXT = $(call core_abspath,$(FINAL_TARGET))/distribution/extensions
+DISTROEXT = $(abspath $(FINAL_TARGET))/distribution/extensions
 
 include $(topsrcdir)/config/config.mk
 
 ifneq (,$(filter beta,$(MOZ_UPDATE_CHANNEL)))
 EXTENSIONS = \
   $(NULL)
 
 all_xpis = $(foreach dir,$(EXTENSIONS),$(DISTROEXT)/$(dir).xpi)
@@ -29,12 +29,12 @@ endef
 
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 $(all_xpis): $(DISTROEXT)/%.xpi: $(call mkdir_deps,$(DISTROEXT)) libs-%
 	cd $* && \
 	$(ZIP) -r9XD $@ * -x \*.in -x \*.mkdir.done
-	cd $(call core_abspath,$(srcdir)/$*) && \
+	cd $(abspath $(srcdir)/$*) && \
 	$(ZIP) -r9XD $@ * -x \*.in -x \*.mkdir.done
 
 .PHONY: $(all_xpis:.xpi=)
--- a/browser/base/Makefile.in
+++ b/browser/base/Makefile.in
@@ -1,16 +1,16 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include $(topsrcdir)/config/config.mk
 
-abs_srcdir = $(call core_abspath,$(srcdir))
+abs_srcdir = $(abspath $(srcdir))
 
 CHROME_DEPS += $(abs_srcdir)/content/overrides/app-license.html
 
 include $(topsrcdir)/config/rules.mk
 
 PRE_RELEASE_SUFFIX := ""
 
 DEFINES += \
--- a/browser/base/content/test/general/browser_popupNotification.js
+++ b/browser/base/content/test/general/browser_popupNotification.js
@@ -150,16 +150,64 @@ function basicNotification() {
     }
   };
   this.addOptions = function(options) {
     for (let [name, value] in Iterator(options))
       self.options[name] = value;
   }
 }
 
+function errorNotification() {
+  var self = this;
+  this.browser = gBrowser.selectedBrowser;
+  this.id = "test-notification-" + gTestIndex;
+  this.message = "This is popup notification " + this.id + " from test " + gTestIndex;
+  this.anchorID = null;
+  this.mainAction = {
+    label: "Main Action",
+    accessKey: "M",
+    callback: function () {
+      self.mainActionClicked = true;
+      throw new Error("Oops!");
+    }
+  };
+  this.secondaryActions = [
+    {
+      label: "Secondary Action",
+      accessKey: "S",
+      callback: function () {
+        self.secondaryActionClicked = true;
+        throw new Error("Oops!");
+      }
+    }
+  ];
+  this.options = {
+    eventCallback: function (eventName) {
+      switch (eventName) {
+        case "dismissed":
+          self.dismissalCallbackTriggered = true;
+          break;
+        case "showing":
+          self.showingCallbackTriggered = true;
+          break;
+        case "shown":
+          self.shownCallbackTriggered = true;
+          break;
+        case "removed":
+          self.removedCallbackTriggered = true;
+          break;
+      }
+    }
+  };
+  this.addOptions = function(options) {
+    for (let [name, value] in Iterator(options))
+      self.options[name] = value;
+  }
+}
+
 var wrongBrowserNotificationObject = new basicNotification();
 var wrongBrowserNotification;
 
 var tests = [
   { // Test #0
     run: function () {
       this.notifyObj = new basicNotification();
       showNotification(this.notifyObj);
@@ -826,17 +874,79 @@ var tests = [
         showNotification(notifyObj);
         executeSoon(function () {
           content.document.getElementById("iframe")
                           .setAttribute("src", "http://example.org/");
         });
       });
     }
   },
-  { // Test #29 -  Existing popup notification shouldn't disappear when adding a dismissed notification
+  { // Test #29 - Popup Notifications should catch exceptions from callbacks
+    run: function () {
+      let callbackCount = 0;
+      this.testNotif1 = new basicNotification();
+      this.testNotif1.message += " 1";
+      showNotification(this.testNotif1);
+      this.testNotif1.options.eventCallback = function (eventName) {
+        info("notifyObj1.options.eventCallback: " + eventName);
+        if (eventName == "dismissed") {
+          throw new Error("Oops 1!");
+          if (++callbackCount == 2) {
+            executeSoon(goNext);
+          }
+        }
+      };
+
+      this.testNotif2 = new basicNotification();
+      this.testNotif2.message += " 2";
+      this.testNotif2.id += "-2";
+      this.testNotif2.options.eventCallback = function (eventName) {
+        info("notifyObj2.options.eventCallback: " + eventName);
+        if (eventName == "dismissed") {
+          throw new Error("Oops 2!");
+          if (++callbackCount == 2) {
+            executeSoon(goNext);
+          }
+        }
+      };
+      showNotification(this.testNotif2);
+    },
+    onShown: function (popup) {
+      is(popup.childNodes.length, 2, "two notifications are shown");
+      dismissNotification(popup);
+    },
+    onHidden: function () {}
+  },
+  { // Test #30 - Popup Notifications main actions should catch exceptions from callbacks
+    run: function () {
+      this.testNotif = new errorNotification();
+      showNotification(this.testNotif);
+    },
+    onShown: function (popup) {
+      checkPopup(popup, this.testNotif);
+      triggerMainCommand(popup);
+    },
+    onHidden: function (popup) {
+      ok(this.testNotif.mainActionClicked, "main action has been triggered");
+    }
+  },
+  { // Test #31 - Popup Notifications secondary actions should catch exceptions from callbacks
+    run: function () {
+      this.testNotif = new errorNotification();
+      showNotification(this.testNotif);
+    },
+    onShown: function (popup) {
+      checkPopup(popup, this.testNotif);
+      triggerSecondaryCommand(popup, 0);
+    },
+    onHidden: function (popup) {
+      ok(this.testNotif.secondaryActionClicked, "secondary action has been triggered");
+    }
+  },
+  { // Test #32 -  Existing popup notification shouldn't disappear when adding a dismissed notification
     run: function () {
       this.notifyObj1 = new basicNotification();
       this.notifyObj1.id += "_1";
       this.notifyObj1.anchorID = "default-notification-icon";
       this.notification1 = showNotification(this.notifyObj1);
     },
     onShown: function (popup) {
       // Now show a dismissed notification, and check that it doesn't clobber
@@ -857,17 +967,17 @@ var tests = [
 
       dismissNotification(popup);
     },
     onHidden: function(popup) {
       this.notification1.remove();
       this.notification2.remove();
     }
   },
-  { // Test #30 - Showing should be able to modify the popup data
+  { // Test #33 - Showing should be able to modify the popup data
     run: function() {
       this.notifyObj = new basicNotification();
       var normalCallback = this.notifyObj.options.eventCallback;
       this.notifyObj.options.eventCallback = function (eventName) {
         if (eventName == "showing") {
           this.mainAction.label = "Alternate Label";
         }
         normalCallback.call(this, eventName);
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -477,17 +477,17 @@
                 fieldname : textBox.getAttribute("autocompletesearchparam"),
                 value : aData },
               { handleError : function(aError) {
                   Components.utils.reportError("Saving search to form history failed: " + aError.message);
               }});
           }
           
           // null parameter below specifies HTML response for search
-          var submission = this.currentEngine.getSubmission(aData);
+          var submission = this.currentEngine.getSubmission(aData, null, "searchbar");
           BrowserSearch.recordSearchInHealthReport(this.currentEngine.name, "searchbar");
           openUILinkIn(submission.uri.spec, aWhere, null, submission.postData);
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="command"><![CDATA[
@@ -526,24 +526,24 @@
       <![CDATA[
         // Speculatively connect to the current engine's search URI (and
         // suggest URI, if different) to reduce request latency
 
         const SUGGEST_TYPE = "application/x-suggestions+json";
         var engine = this.currentEngine;
         var connector =
             Services.io.QueryInterface(Components.interfaces.nsISpeculativeConnect);
-        var searchURI = engine.getSubmission("dummy").uri;
+        var searchURI = engine.getSubmission("dummy", null, "searchbar").uri;
         let callbacks = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                               .getInterface(Components.interfaces.nsIWebNavigation)
                               .QueryInterface(Components.interfaces.nsILoadContext);
         connector.speculativeConnect(searchURI, callbacks);
 
         if (engine.supportsResponseType(SUGGEST_TYPE)) {
-          var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE).uri;
+          var suggestURI = engine.getSubmission("dummy", SUGGEST_TYPE, "searchbar").uri;
           if (suggestURI.prePath != searchURI.prePath)
             connector.speculativeConnect(suggestURI, callbacks);
         }
       ]]></handler>
     </handlers>
   </binding>
 
   <binding id="searchbar-textbox"
--- a/browser/components/search/test/Makefile.in
+++ b/browser/components/search/test/Makefile.in
@@ -1,12 +1,13 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 ifdef ENABLE_TESTS
 pp_mochitest_browser_files := \
   browser_google.js \
+  browser_google_behavior.js \
   $(NULL)
 pp_mochitest_browser_files_PATH := $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 pp_mochitest_browser_files_FLAGS := -DMOZ_DISTRIBUTION_ID=$(MOZ_DISTRIBUTION_ID)
 PP_TARGETS += pp_mochitest_browser_files
 endif # ENABLE_TESTS
--- a/browser/components/search/test/browser_google.js
+++ b/browser/components/search/test/browser_google.js
@@ -75,16 +75,20 @@ function test() {
 
   // Test search URLs (including purposes).
   url = engine.getSubmission("foo").uri.spec;
   is(url, base, "Check search URL for 'foo'");
   url = engine.getSubmission("foo", null, "contextmenu").uri.spec;
   is(url, base + "&channel=rcs", "Check context menu search URL for 'foo'");
   url = engine.getSubmission("foo", null, "keyword").uri.spec;
   is(url, base + "&channel=fflb", "Check keyword search URL for 'foo'");
+  url = engine.getSubmission("foo", null, "searchbar").uri.spec;
+  is(url, base + "&channel=sb", "Check search bar search URL for 'foo'");
+  url = engine.getSubmission("foo", null, "homepage").uri.spec;
+  is(url, base + "&channel=np&source=hp", "Check homepage search URL for 'foo'");
 
   // Check search suggestion URL.
   url = engine.getSubmission("foo", "application/x-suggestions+json").uri.spec;
   is(url, "https://www.google.com/complete/search?client=firefox&q=foo", "Check search suggestion URL for 'foo'");
 
   // Check all other engine properties.
   const EXPECTED_ENGINE = {
     name: "Google",
@@ -144,16 +148,21 @@ function test() {
             },
             {
               "name": "channel",
               "value": "fflb",
               "purpose": "keyword",
             },
             {
               "name": "channel",
+              "value": "sb",
+              "purpose": "searchbar",
+            },
+            {
+              "name": "channel",
               "value": "np",
               "purpose": "homepage",
             },
             {
               "name": "source",
               "value": "hp",
               "purpose": "homepage",
             },
new file mode 100644
--- /dev/null
+++ b/browser/components/search/test/browser_google_behavior.js
@@ -0,0 +1,187 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Test Google search plugin URLs
+ */
+
+"use strict";
+
+const BROWSER_SEARCH_PREF      = "browser.search.";
+
+const MOZ_PARAM_LOCALE         = /\{moz:locale\}/g;
+const MOZ_PARAM_DIST_ID        = /\{moz:distributionID\}/g;
+const MOZ_PARAM_OFFICIAL       = /\{moz:official\}/g;
+
+// Custom search parameters
+#ifdef MOZ_OFFICIAL_BRANDING
+const MOZ_OFFICIAL = "official";
+#else
+const MOZ_OFFICIAL = "unofficial";
+#endif
+
+#if MOZ_UPDATE_CHANNEL == beta
+const GOOGLE_CLIENT = "firefox-beta";
+#elif MOZ_UPDATE_CHANNEL == aurora
+const GOOGLE_CLIENT = "firefox-aurora";
+#elif MOZ_UPDATE_CHANNEL == nightly
+const GOOGLE_CLIENT = "firefox-nightly";
+#else
+const GOOGLE_CLIENT = "firefox-a";
+#endif
+
+#expand const MOZ_DISTRIBUTION_ID = __MOZ_DISTRIBUTION_ID__;
+
+function getLocale() {
+  const localePref = "general.useragent.locale";
+  return getLocalizedPref(localePref, Services.prefs.getCharPref(localePref));
+}
+
+function getLocalizedPref(aPrefName, aDefault) {
+  try {
+    return Services.prefs.getComplexValue(aPrefName, Ci.nsIPrefLocalizedString).data;
+  } catch (ex) {
+    return aDefault;
+  }
+
+  return aDefault;
+}
+
+function test() {
+  let engine = Services.search.getEngineByName("Google");
+  ok(engine, "Google is installed");
+
+  is(Services.search.defaultEngine, engine, "Check that Google is the default search engine");
+
+  let distributionID;
+  try {
+    distributionID = Services.prefs.getCharPref(BROWSER_SEARCH_PREF + "distributionID");
+  } catch (ex) {
+    distributionID = MOZ_DISTRIBUTION_ID;
+  }
+
+  let base = "https://www.google.com/search?q=foo&ie=utf-8&oe=utf-8&aq=t&rls={moz:distributionID}:{moz:locale}:{moz:official}&client=" + GOOGLE_CLIENT;
+  base = base.replace(MOZ_PARAM_LOCALE, getLocale());
+  base = base.replace(MOZ_PARAM_DIST_ID, distributionID);
+  base = base.replace(MOZ_PARAM_OFFICIAL, MOZ_OFFICIAL);
+
+  let url;
+
+  // Test search URLs (including purposes).
+  url = engine.getSubmission("foo").uri.spec;
+  is(url, base, "Check search URL for 'foo'");
+
+  waitForExplicitFinish();
+
+  var gCurrTest;
+  var gTests = [
+    {
+      name: "context menu search",
+      searchURL: base + "&channel=rcs",
+      run: function () {
+        // Simulate a contextmenu search
+        // FIXME: This is a bit "low-level"...
+        BrowserSearch.loadSearch("foo", false, "contextmenu");
+      }
+    },
+    {
+      name: "keyword search",
+      searchURL: base + "&channel=fflb",
+      run: function () {
+        gURLBar.value = "? foo";
+        gURLBar.focus();
+        EventUtils.synthesizeKey("VK_RETURN", {});
+      }
+    },
+    {
+      name: "search bar search",
+      searchURL: base + "&channel=sb",
+      run: function () {
+        let sb = BrowserSearch.searchBar;
+        sb.focus();
+        sb.value = "foo";
+        registerCleanupFunction(function () {
+          sb.value = "";
+        });
+        EventUtils.synthesizeKey("VK_RETURN", {});
+      }
+    },
+    {
+      name: "home page search",
+      searchURL: base + "&channel=np&source=hp",
+      run: function () {
+        // load about:home, but remove the listener first so it doesn't
+        // get in the way
+        gBrowser.removeProgressListener(listener);
+        gBrowser.loadURI("about:home");
+        info("Waiting for about:home load");
+        tab.linkedBrowser.addEventListener("load", function load(event) {
+          if (event.originalTarget != tab.linkedBrowser.contentDocument ||
+              event.target.location.href == "about:blank") {
+            info("skipping spurious load event");
+            return;
+          }
+          tab.linkedBrowser.removeEventListener("load", load, true);
+
+          // Observe page setup
+          let doc = gBrowser.contentDocument;
+          let mutationObserver = new MutationObserver(function (mutations) {
+            for (let mutation of mutations) {
+              if (mutation.attributeName == "searchEngineName") {
+                // Re-add the listener, and perform a search
+                gBrowser.addProgressListener(listener);
+                doc.getElementById("searchText").value = "foo";
+                doc.getElementById("searchSubmit").click();
+              }
+            }
+          });
+          mutationObserver.observe(doc.documentElement, { attributes: true });
+        }, true);
+      }
+    }
+  ];
+
+  function nextTest() {
+    if (gTests.length) {
+      gCurrTest = gTests.shift();
+      info("Running : " + gCurrTest.name);
+      executeSoon(gCurrTest.run);
+    } else {
+      finish();
+    }
+  }
+
+  let tab = gBrowser.selectedTab = gBrowser.addTab();
+
+  let listener = {
+    onStateChange: function onStateChange(webProgress, req, flags, status) {
+      info("onStateChange");
+      // Only care about top-level document starts
+      let docStart = Ci.nsIWebProgressListener.STATE_IS_DOCUMENT |
+                     Ci.nsIWebProgressListener.STATE_START;
+      if (!(flags & docStart) || !webProgress.isTopLevel)
+        return;
+
+      info("received document start");
+
+      ok(req instanceof Ci.nsIChannel, "req is a channel");
+      is(req.originalURI.spec, gCurrTest.searchURL, "search URL was loaded");
+      info("Actual URI: " + req.URI.spec);
+
+      req.cancel(Components.results.NS_ERROR_FAILURE);
+
+      executeSoon(nextTest);
+    }
+  }
+
+  registerCleanupFunction(function () {
+    gBrowser.removeProgressListener(listener);
+    gBrowser.removeTab(tab);
+  });
+
+  tab.linkedBrowser.addEventListener("load", function load() {
+    tab.linkedBrowser.removeEventListener("load", load, true);
+    gBrowser.addProgressListener(listener);
+    nextTest();
+  }, true);
+}
--- a/browser/locales/en-US/searchplugins/google.xml
+++ b/browser/locales/en-US/searchplugins/google.xml
@@ -20,13 +20,14 @@
   <MozParam name="client" condition="defaultEngine" trueValue="firefox-aurora" falseValue="firefox"/>
 #elif MOZ_UPDATE_CHANNEL == nightly
   <MozParam name="client" condition="defaultEngine" trueValue="firefox-nightly" falseValue="firefox"/>
 #else
   <MozParam name="client" condition="defaultEngine" trueValue="firefox-a" falseValue="firefox"/>
 #endif
   <MozParam name="channel" condition="purpose" purpose="contextmenu" value="rcs"/>
   <MozParam name="channel" condition="purpose" purpose="keyword" value="fflb"/>
+  <MozParam name="channel" condition="purpose" purpose="searchbar" value="sb"/>
   <MozParam name="channel" condition="purpose" purpose="homepage" value="np"/>
   <MozParam name="source" condition="purpose" purpose="homepage" value="hp"/>
 </Url>
 <SearchForm>https://www.google.com/</SearchForm>
 </SearchPlugin>
--- a/browser/metro/base/content/Site.js
+++ b/browser/metro/base/content/Site.js
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 'use strict';
 
 /**
  * dumb model class to provide default values for sites.
  * link parameter/model object expected to have a .url property, and optionally .title
  */
 function Site(aLink) {
-  if(!aLink.url) {
+  if (!aLink.url) {
     throw Cr.NS_ERROR_INVALID_ARG;
   }
   this._link = aLink;
 }
 
 Site.prototype = {
   icon: '',
   get url() {
@@ -59,16 +59,16 @@ Site.prototype = {
     for (let key in attrs) {
       if (undefined === attrs[key]) {
         aNode.removeAttribute(key);
       } else {
         aNode.setAttribute(key, attrs[key]);
       }
     }
     // is binding already applied?
-    if (aNode.refresh) {
+    if ('refresh' in aNode) {
       // just update it
       aNode.refresh();
     } else {
       // these attribute values will get picked up later when the binding is applied
     }
   }
 };
--- a/browser/metro/base/content/bindings/grid.xml
+++ b/browser/metro/base/content/bindings/grid.xml
@@ -22,17 +22,17 @@
       <property name="_grid" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'grid');"/>
 
       <property name="isBound" readonly="true" onget="return !!this._grid"/>
       <property name="isArranging" readonly="true" onget="return !!this._scheduledArrangeItemsTimerId"/>
 
       <field name="controller">null</field>
 
       <!-- collection of child items excluding empty tiles -->
-      <property name="items" readonly="true" onget="return this.querySelectorAll('richgriditem');"/>
+      <property name="items" readonly="true" onget="return this.querySelectorAll('richgriditem[value]');"/>
       <property name="itemCount" readonly="true" onget="return this.items.length;"/>
 
       <!-- nsIDOMXULMultiSelectControlElement (not fully implemented) -->
 
       <method name="clearSelection">
         <body>
           <![CDATA[
             // 'selection' and 'selected' are confusingly overloaded here
@@ -91,17 +91,17 @@
         </body>
       </method>
 
       <method name="handleItemClick">
         <parameter name="aItem"/>
         <parameter name="aEvent"/>
         <body>
           <![CDATA[
-            if(!this.isBound)
+            if (!this.isBound)
               return;
 
             if ("single" == this.getAttribute("seltype")) {
               // we'll republish this as a selectionchange event on the grid
               aEvent.stopPropagation();
               this.selectItem(aItem);
             }
 
@@ -111,17 +111,17 @@
         </body>
       </method>
 
       <method name="handleItemContextMenu">
         <parameter name="aItem"/>
         <parameter name="aEvent"/>
         <body>
           <![CDATA[
-            if(!this.isBound || this.suppressOnSelect)
+            if (!this.isBound || this.suppressOnSelect)
               return;
             // we'll republish this as a selectionchange event on the grid
             aEvent.stopPropagation();
             this.toggleItemSelection(aItem);
           ]]>
         </body>
       </method>
 
@@ -170,17 +170,17 @@
           ]]>
         </setter>
       </property>
 
       <!-- partial implementation of multiple selection interface -->
       <property name="selectedItems">
         <getter>
           <![CDATA[
-            return this.querySelectorAll("richgriditem[selected]");
+            return this.querySelectorAll("richgriditem[value][selected]");
           ]]>
         </getter>
       </property>
 
       <property name="selectedIndex">
         <getter>
           <![CDATA[
             return this.getIndexOfItem(this._selectedItem);
@@ -199,58 +199,146 @@
       </property>
 
       <method name="appendItem">
         <parameter name="aLabel"/>
         <parameter name="aValue"/>
         <parameter name="aSkipArrange"/>
         <body>
           <![CDATA[
-            let addition = this._createItemElement(aLabel, aValue);
-            this.appendChild(addition);
+            let item = this.nextSlot();
+            item.setAttribute("value", aValue);
+            item.setAttribute("label", aLabel);
+
             if (!aSkipArrange)
               this.arrangeItems();
-            return addition;
+            return item;
           ]]>
         </body>
       </method>
 
+      <method name="_slotValues">
+        <body><![CDATA[
+          return Array.map(this.children, (cnode) => cnode.getAttribute("value"));
+        ]]></body>
+      </method>
+
+      <property name="minSlots" readonly="true"
+                onget="return this.getAttribute('minSlots') || 3;"/>
+
       <method name="clearAll">
         <parameter name="aSkipArrange"/>
         <body>
           <![CDATA[
-            while (this.firstChild) {
-              this.removeChild(this.firstChild);
+            const ELEMENT_NODE_TYPE = Components.interfaces.nsIDOMNode.ELEMENT_NODE;
+            let slotCount = this.minSlots;
+            let childIndex = 0;
+            let child = this.firstChild;
+            while (child) {
+              // remove excess elements and non-element nodes
+              if (child.nodeType !== ELEMENT_NODE_TYPE || childIndex+1 > slotCount) {
+                let orphanNode = child;
+                child = orphanNode.nextSibling;
+                this.removeChild(orphanNode);
+                continue;
+              }
+              if (child.hasAttribute("value")) {
+                this._releaseSlot(child);
+              }
+              child = child.nextSibling;
+              childIndex++;
             }
+            // create our quota of item slots
+            for (let count = this.childElementCount; count < slotCount; count++) {
+              this.appendChild( this._createItemElement() );
+            }
+
             if (!aSkipArrange)
               this.arrangeItems();
           ]]>
         </body>
       </method>
 
+      <method name="_slotAt">
+        <parameter name="anIndex"/>
+        <body>
+          <![CDATA[
+            // backfill with new slots as necessary
+            let count = Math.max(1+anIndex, this.minSlots) - this.childElementCount;
+            for (; count > 0; count--) {
+              this.appendChild( this._createItemElement() );
+            }
+            return this.children[anIndex];
+          ]]>
+        </body>
+      </method>
+
+      <method name="nextSlot">
+        <body>
+          <![CDATA[
+            if (!this.itemCount) {
+              return this._slotAt(0);
+            }
+            let lastItem = this.items[this.itemCount-1];
+            let nextIndex = 1 + Array.indexOf(this.children, lastItem);
+            return this._slotAt(nextIndex);
+          ]]>
+        </body>
+      </method>
+
+      <method name="_releaseSlot">
+        <parameter name="anItem"/>
+        <body>
+          <![CDATA[
+            // Flush out data and state attributes so we can recycle this slot/element
+            let exclude = { value: 1, tiletype: 1 };
+            let attrNames = [attr.name for (attr of anItem.attributes)];
+            for (let attrName of attrNames) {
+              if (!(attrName in exclude))
+                anItem.removeAttribute(attrName);
+            }
+            // clear out inline styles
+            anItem.removeAttribute("style");
+            // finally clear the value, which should apply the richgrid-empty-item binding
+            anItem.removeAttribute("value");
+          ]]>
+        </body>
+      </method>
+
       <method name="insertItemAt">
         <parameter name="anIndex"/>
         <parameter name="aLabel"/>
         <parameter name="aValue"/>
         <parameter name="aSkipArrange"/>
         <body>
           <![CDATA[
+            anIndex = Math.min(this.itemCount, anIndex);
+            let insertedItem;
             let existing = this.getItemAtIndex(anIndex);
-            let addition = this._createItemElement(aLabel, aValue);
             if (existing) {
-              this.insertBefore(addition, existing);
-            } else {
-              this.appendChild(addition);
+              // use an empty slot if we have one, otherwise insert it
+              let childIndex = Array.indexOf(this.children, existing);
+              if (childIndex > 0 && !this.children[childIndex-1].hasAttribute("value")) {
+                insertedItem = this.children[childIndex-1];
+              } else {
+                insertedItem = this.insertBefore(this._createItemElement(),existing);
+              }
             }
+            if (!insertedItem) {
+              insertedItem = this._slotAt(anIndex);
+            }
+            insertedItem.setAttribute("value", aValue);
+            insertedItem.setAttribute("label", aLabel);
             if (!aSkipArrange)
               this.arrangeItems();
-            return addition;
+            return insertedItem;
           ]]>
         </body>
       </method>
+
       <method name="removeItemAt">
         <parameter name="anIndex"/>
         <parameter name="aSkipArrange"/>
         <body>
           <![CDATA[
             let item = this.getItemAtIndex(anIndex);
             if (!item)
               return null;
@@ -261,17 +349,23 @@
 
       <method name="removeItem">
         <parameter name="aItem"/>
         <parameter name="aSkipArrange"/>
         <body>
           <![CDATA[
             if (!aItem || Array.indexOf(this.items, aItem) < 0)
               return null;
+
             let removal = this.removeChild(aItem);
+            // replace the slot if necessary
+            if (this.childElementCount < this.minSlots) {
+              this.nextSlot();
+            }
+
             if (removal && !aSkipArrange)
                 this.arrangeItems();
 
             // note that after removal the node is unbound
             // so none of the richgriditem binding methods & properties are available
             return removal;
           ]]>
         </body>
@@ -417,16 +511,17 @@
             return true;
           ]]>
         </getter>
       </property>
 
       <field name="_scheduledArrangeItemsTimerId">null</field>
       <field name="_scheduledArrangeItemsTries">0</field>
       <field name="_maxArrangeItemsRetries">5</field>
+
       <method name="_scheduleArrangeItems">
         <parameter name="aTime"/>
         <body>
           <![CDATA[
               // cap the number of times we reschedule calling arrangeItems
               if (
                   !this._scheduledArrangeItemsTimerId &&
                   this._maxArrangeItemsRetries > this._scheduledArrangeItemsTries
@@ -448,50 +543,51 @@
             if (!this._canLayout) {
               // try again later
               this._scheduleArrangeItems();
               return;
             }
 
             let itemDims = this._itemSize;
             let containerDims = this._containerSize;
+            let slotsCount = this.childElementCount;
 
             // reset the flags
             if (this._scheduledArrangeItemsTimerId) {
               clearTimeout(this._scheduledArrangeItemsTimerId);
               delete this._scheduledArrangeItemsTimerId;
             }
             this._scheduledArrangeItemsTries = 0;
 
             // clear explicit width and columns before calculating from avail. height again
             let gridStyle = this._grid.style;
             gridStyle.removeProperty("min-width");
             gridStyle.removeProperty("-moz-column-count");
 
             if (this.hasAttribute("vertical")) {
               this._columnCount = Math.floor(containerDims.width / itemDims.width) || 1;
-              this._rowCount = Math.floor(this.itemCount / this._columnCount);
+              this._rowCount = Math.floor(slotsCount / this._columnCount);
             } else {
-              // We favor overflowing horizontally, not vertically (rows then colums)
-              // rows attribute = max rows
-              let maxRowCount = Math.min(this.getAttribute("rows") || Infinity, Math.floor(containerDims.height / itemDims.height));
-              this._rowCount = Math.min(this.itemCount, maxRowCount);
+              // rows attribute is fixed number of rows
+              let maxRows = Math.floor(containerDims.height / itemDims.height);
+              this._rowCount = this.getAttribute("rows") ?
+                                  // fit indicated rows when possible
+                                  Math.min(maxRows, this.getAttribute("rows")) :
+                                  // at least 1 row
+                                  Math.min(maxRows, slotsCount) || 1;
 
-              // columns attribute = min cols
-              this._columnCount = this.itemCount ?
-                    Math.max(
-                        // at least 1 column when there are items
-                        this.getAttribute("columns") || 1,
-                        Math.ceil(this.itemCount / this._rowCount)
-                    ) : this.getAttribute("columns") || 0;
+              // columns attribute is min number of cols
+              this._columnCount = Math.ceil(slotsCount / this._rowCount) || 1;
+              if (this.getAttribute("columns")) {
+                this._columnCount = Math.max(this._columnCount, this.getAttribute("columns"));
+              }
             }
 
             // width is typically auto, cap max columns by truncating items collection
             // or, setting max-width style property with overflow hidden
-            // '0' is an invalid value, just leave the property unset when 0 columns
             if (this._columnCount) {
               gridStyle.MozColumnCount = this._columnCount;
             }
             this._fireEvent("arranged");
           ]]>
         </body>
       </method>
       <method name="arrangeItemsNow">
@@ -516,16 +612,22 @@
                   onset="this.setAttribute('suppressonselect', val);"/>
       <property name="crossSlideBoundary"
           onget="return this.hasAttribute('crossslideboundary')? this.getAttribute('crossslideboundary') : Infinity;"/>
 
     <!-- Internal methods -->
       <field name="_xslideHandler"/>
       <constructor>
         <![CDATA[
+          // create our quota of item slots
+          for (let count = this.childElementCount, slotCount = this.minSlots;
+              count < slotCount; count++) {
+            this.appendChild( this._createItemElement() );
+          }
+
           if (this.controller && this.controller.gridBoundCallback != undefined)
             this.controller.gridBoundCallback();
 
           // set up cross-slide gesture handling for multiple-selection grids
           if ("undefined" !== typeof CrossSlide && "multiple" == this.getAttribute("seltype")) {
             this._xslideHandler = new CrossSlide.Handler(this, {
                   REARRANGESTART: this.crossSlideBoundary
             });
@@ -600,16 +702,17 @@
               }
             } else {
               throw new Error("Failed to find stylesheet to parse out richgriditem dimensions\n");
             }
             return typeSizes;
           ]]>
         </body>
       </method>
+
       <method name="_isIndexInBounds">
         <parameter name="anIndex"/>
         <body>
           <![CDATA[
             return anIndex >= 0 && anIndex < this.itemCount;
           ]]>
         </body>
       </method>
@@ -621,17 +724,17 @@
           <![CDATA[
             let item = this.ownerDocument.createElement("richgriditem");
             if (aValue) {
               item.setAttribute("value", aValue);
             }
             if (aLabel) {
               item.setAttribute("label", aLabel);
             }
-            if(this.hasAttribute("tiletype")) {
+            if (this.hasAttribute("tiletype")) {
               item.setAttribute("tiletype", this.getAttribute("tiletype"));
             }
             return item;
           ]]>
         </body>
       </method>
 
       <method name="_fireEvent">
@@ -699,16 +802,17 @@
               bendNode.style.transform = "perspective("+perspective+") rotateY(" + angle + "deg)";
               bendNode.style.transformOrigin = "right center";
             }
           }
           // mark when bend effect is applied
           aItem.setAttribute("bending", true);
         ]]></body>
       </method>
+
       <method name="unbendItem">
         <parameter name="aItem"/>
         <body><![CDATA[
           // clear the 'bend' transform on the contentBox element of the item
           let bendNode = 'richgriditem' == aItem.nodeName && aItem._contentBox;
           if (bendNode && aItem.hasAttribute("bending")) {
             bendNode.style.removeProperty('transform');
             bendNode.style.removeProperty('transformOrigin');
@@ -744,17 +848,17 @@
       <handler event="MozCrossSliding">
         <![CDATA[
           // MozCrossSliding is swipe gesture across a tile
           // The tile should follow the drag to reinforce the gesture
           // (with inertia/speedbump behavior)
           let state = event.crossSlidingState;
           let thresholds = this._xslideHandler.thresholds;
           let transformValue;
-          switch(state) {
+          switch (state) {
             case "cancelled":
               this.unbendItem(event.target);
               event.target.removeAttribute('crosssliding');
               // hopefully nothing else is transform-ing the tile
               event.target.style.removeProperty('transform');
               break;
             case "dragging":
             case "selecting":
@@ -839,17 +943,17 @@
       <property name="pinned"
                 onget="return this.hasAttribute('pinned')"
                 onset="if (val) { this.setAttribute('pinned', val) } else this.removeAttribute('pinned');"/>
 
       <method name="refresh">
         <body>
           <![CDATA[
             // Prevent an exception in case binding is not done yet.
-            if(!this.isBound)
+            if (!this.isBound)
               return;
 
             // Seed the binding properties from bound-node attribute values
             // Usage: node.refresh()
             //        - reinitializes all binding properties from their associated attributes
 
             this.iconSrc = this.getAttribute('iconURI');
             this.color = this.getAttribute("customColor");
@@ -909,30 +1013,30 @@
             this.removeAttribute("customImage");
             this._top.style.removeProperty("background-image");
           }
         ]]></setter>
       </property>
 
       <method name="refreshBackgroundImage">
         <body><![CDATA[
-          if(!this.isBound)
+          if (!this.isBound)
             return;
           if (this.backgroundImage) {
             this._top.style.removeProperty("background-image");
             this._top.style.setProperty("background-image", this.backgroundImage);
           }
         ]]></body>
       </method>
 
       <field name="_contextActions">null</field>
       <property name="contextActions">
         <getter>
           <![CDATA[
-            if(!this._contextActions) {
+            if (!this._contextActions) {
               this._contextActions = new Set();
               let actionSet = this._contextActions;
               let actions = this.getAttribute("data-contextactions");
               if (actions) {
                 actions.split(/[,\s]+/).forEach(function(verb){
                   actionSet.add(verb);
                 });
               }
@@ -954,17 +1058,23 @@
         ]]>
       </handler>
 
       <handler event="contextmenu">
         <![CDATA[
           // fires for right-click, long-click and (keyboard) contextmenu input
           // toggle the selected state of tiles in a grid
           let gridParent = this.control;
-          if(!this.isBound || !gridParent)
+          if (!this.isBound || !gridParent)
             return;
           gridParent.handleItemContextMenu(this, event);
         ]]>
       </handler>
     </handlers>
   </binding>
 
+  <binding id="richgrid-empty-item">
+    <content>
+      <html:div anonid="anon-tile" class="tile-content"></html:div>
+    </content>
+  </binding>
+
 </bindings>
--- a/browser/metro/base/content/browser.css
+++ b/browser/metro/base/content/browser.css
@@ -114,16 +114,19 @@ setting[type="menulist"] {
   -moz-binding: url("chrome://browser/content/bindings/urlbar.xml#urlbar-autocomplete");
 }
 
 richgrid {
   -moz-binding: url("chrome://browser/content/bindings/grid.xml#richgrid");
 }
 
 richgriditem {
+  -moz-binding: url("chrome://browser/content/bindings/grid.xml#richgrid-empty-item");
+}
+richgriditem[value] {
   -moz-binding: url("chrome://browser/content/bindings/grid.xml#richgrid-item");
 }
 
 placeitem {
   -moz-binding: url("chrome://browser/content/bindings/bindings.xml#place-item");
   background-color: transparent;
 }
 
--- a/browser/metro/base/content/startui/BookmarksView.js
+++ b/browser/metro/base/content/startui/BookmarksView.js
@@ -68,21 +68,21 @@ BookmarksView.prototype = Util.extend(Ob
   },
 
   handleItemClick: function bv_handleItemClick(aItem) {
     let url = aItem.getAttribute("value");
     StartUI.goToURI(url);
   },
 
   _getItemForBookmarkId: function bv__getItemForBookmark(aBookmarkId) {
-    return this._set.querySelector("richgriditem[bookmarkId='" + aBookmarkId + "']");
+    return this._set.querySelector("richgriditem[anonid='" + aBookmarkId + "']");
   },
 
   _getBookmarkIdForItem: function bv__getBookmarkForItem(aItem) {
-    return +aItem.getAttribute("bookmarkId");
+    return +aItem.getAttribute("anonid");
   },
 
   _updateItemWithAttrs: function dv__updateItemWithAttrs(anItem, aAttrs) {
     for (let name in aAttrs)
       anItem.setAttribute(name, aAttrs[name]);
   },
 
   getBookmarks: function bv_getBookmarks(aRefresh) {
@@ -137,37 +137,39 @@ BookmarksView.prototype = Util.extend(Ob
 
     // Remove extra items in case a refresh added more than the limit.
     // This can happen when undoing a delete.
     if (aRefresh) {
       while (this._set.itemCount > limit)
         this._set.removeItemAt(this._set.itemCount - 1, true);
     }
     this._set.arrangeItems();
+    this._set.removeAttribute("fade");
     this._inBatch = false;
     rootNode.containerOpen = false;
   },
 
   inCurrentView: function bv_inCurrentView(aParentId, aItemId) {
     if (this._root && aParentId != this._root)
       return false;
 
     return !!this._getItemForBookmarkId(aItemId);
   },
 
   clearBookmarks: function bv_clearBookmarks() {
-    this._set.clearAll();
+    if ('clearAll' in this._set)
+      this._set.clearAll();
   },
 
   addBookmark: function bv_addBookmark(aBookmarkId, aPos) {
     let index = this._bookmarkService.getItemIndex(aBookmarkId);
     let uri = this._bookmarkService.getBookmarkURI(aBookmarkId);
     let title = this._bookmarkService.getItemTitle(aBookmarkId) || uri.spec;
     let item = this._set.insertItemAt(aPos || index, title, uri.spec, this._inBatch);
-    item.setAttribute("bookmarkId", aBookmarkId);
+    item.setAttribute("anonid", aBookmarkId);
     this._setContextActions(item);
     this._updateFavicon(item, uri);
   },
 
   _setContextActions: function bv__setContextActions(aItem) {
     let itemId = this._getBookmarkIdForItem(aItem);
     aItem.setAttribute("data-contextactions", "delete," + (this._pinHelper.isPinned(itemId) ? "unpin" : "pin"));
     if (aItem.refresh) aItem.refresh();
@@ -193,16 +195,17 @@ BookmarksView.prototype = Util.extend(Ob
       this.removeBookmark(aBookmarkId);
       this.addBookmark(aBookmarkId);
       return;
     }
 
     let uri = this._bookmarkService.getBookmarkURI(aBookmarkId);
     let title = this._bookmarkService.getItemTitle(aBookmarkId) || uri.spec;
 
+    item.setAttribute("anonid", aBookmarkId);
     item.setAttribute("value", uri.spec);
     item.setAttribute("label", title);
 
     this._updateFavicon(item, uri);
   },
 
   removeBookmark: function bv_removeBookmark(aBookmarkId) {
     let item = this._getItemForBookmarkId(aBookmarkId);
--- a/browser/metro/base/content/startui/HistoryView.js
+++ b/browser/metro/base/content/startui/HistoryView.js
@@ -90,16 +90,17 @@ HistoryView.prototype = Util.extend(Obje
     // This can happen when undoing a delete.
     if (aRefresh) {
       while (this._set.itemCount > limit)
         this._set.removeItemAt(this._set.itemCount - 1);
     }
 
     rootNode.containerOpen = false;
     this._set.arrangeItems();
+    this._set.removeAttribute("fade");
     if (this._inBatch > 0)
       this._inBatch--;
   },
 
   addItemToSet: function addItemToSet(aURI, aTitle, aIcon, aPos) {
     let item = this._set.insertItemAt(aPos || 0, aTitle, aURI, this._inBatch);
     this._setContextActions(item);
     this._updateFavicon(item, aURI);
@@ -125,16 +126,19 @@ HistoryView.prototype = Util.extend(Obje
     if (!this._inBatch)
       this._set.arrangeItems();
   },
 
   doActionOnSelectedTiles: function bv_doActionOnSelectedTiles(aActionName, aEvent) {
     let tileGroup = this._set;
     let selectedTiles = tileGroup.selectedItems;
 
+    // just arrange the grid once at the end of any action handling
+    this._inBatch = true;
+
     switch (aActionName){
       case "delete":
         Array.forEach(selectedTiles, function(aNode) {
           if (!this._toRemove) {
             this._toRemove = [];
           }
 
           let uri = aNode.getAttribute("value");
@@ -177,19 +181,21 @@ HistoryView.prototype = Util.extend(Obje
         Array.forEach(selectedTiles, function(aNode) {
           let uri = aNode.getAttribute("value");
 
           this._pinHelper.setPinned(uri);
         }, this);
         break;
 
       default:
+        this._inBatch = false;
         return;
     }
 
+    this._inBatch = false;
     // Send refresh event so all view are in sync.
     this._sendNeedsRefresh();
   },
 
   handleEvent: function bv_handleEvent(aEvent) {
     switch (aEvent.type){
       case "MozAppbarDismissing":
         // If undo wasn't pressed, time to do definitive actions.
@@ -249,27 +255,28 @@ HistoryView.prototype = Util.extend(Obje
     }
   },
 
   onDeleteURI: function(aURI) {
     this.removeHistory(aURI.spec);
   },
 
   onClearHistory: function() {
-    this._set.clearAll();
+    if ('clearAll' in this._set)
+      this._set.clearAll();
   },
 
   onPageChanged: function(aURI, aWhat, aValue) {
     if (aWhat ==  Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
       let changedItems = this._set.getItemsByUrl(aURI.spec);
       for (let item of changedItems) {
         let currIcon = item.getAttribute("iconURI");
         if (currIcon != aValue) {
           item.setAttribute("iconURI", aValue);
-          if("refresh" in item)
+          if ("refresh" in item)
             item.refresh();
         }
       }
     }
   },
 
   onDeleteVisits: function (aURI, aVisitTime, aGUID, aReason, aTransitionType) {
     if ((aReason ==  Ci.nsINavHistoryObserver.REASON_DELETED) && !this._inBatch) {
--- a/browser/metro/base/content/startui/RemoteTabsView.js
+++ b/browser/metro/base/content/startui/RemoteTabsView.js
@@ -88,16 +88,17 @@ RemoteTabsView.prototype = Util.extend(O
 
         let item = this._set.appendItem((title || url), url);
         item.setAttribute("iconURI", Weave.Utils.getIcon(icon));
 
       }, this);
     }
     this.setUIAccessVisible(show);
     this._set.arrangeItems();
+    this._set.removeAttribute("fade");
   },
 
   destruct: function destruct() {
     Weave.Svc.Obs.remove("weave:engine:sync:finish", this);
     Weave.Svc.Obs.remove("weave:service:logout:start-over", this);
     View.prototype.destruct.call(this);
   },
 
--- a/browser/metro/base/content/startui/Start.xul
+++ b/browser/metro/base/content/startui/Start.xul
@@ -43,39 +43,61 @@
   <hbox id="start-container" class="meta-section-container"
         observes="bcast_windowState">
   <!-- the start-container element has a max-height set in StartUI.js -->
       <vbox id="start-topsites" class="meta-section" expanded="true">
         <label class="meta-section-title wide-title" value="&topSitesHeader.label;"/>
         <html:div class="meta-section-title narrow-title" onclick="StartUI.onNarrowTitleClick('start-topsites')">
           &narrowTopSitesHeader.label;
         </html:div>
-        <richgrid id="start-topsites-grid" observes="bcast_windowState" set-name="topSites" rows="3" columns="3" tiletype="thumbnail" seltype="multiple" flex="1"/>
+        <richgrid id="start-topsites-grid" observes="bcast_windowState" set-name="topSites" rows="3" columns="3" tiletype="thumbnail" seltype="multiple" minSlots="8" fade="true" flex="1">
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+        </richgrid>
       </vbox>
 
       <vbox id="start-bookmarks" class="meta-section">
         <label class="meta-section-title wide-title" value="&bookmarksHeader.label;"/>
         <html:div class="meta-section-title narrow-title" onclick="StartUI.onNarrowTitleClick('start-bookmarks')">
           &narrowBookmarksHeader.label;
         </html:div>
-        <richgrid id="start-bookmarks-grid" observes="bcast_windowState" set-name="bookmarks" seltype="multiple" flex="1"/>
+        <richgrid id="start-bookmarks-grid" observes="bcast_windowState" set-name="bookmarks" seltype="multiple" fade="true" flex="1" minSlots="2">
+          <richgriditem/>
+          <richgriditem/>
+        </richgrid>
       </vbox>
 
       <vbox id="start-history" class="meta-section">
         <label class="meta-section-title wide-title" value="&recentHistoryHeader.label;"/>
         <html:div class="meta-section-title narrow-title" onclick="StartUI.onNarrowTitleClick('start-history')">
           &narrowRecentHistoryHeader.label;
         </html:div>
-        <richgrid id="start-history-grid" observes="bcast_windowState" set-name="recentHistory" seltype="multiple" flex="1"/>
+        <richgrid id="start-history-grid" observes="bcast_windowState" set-name="recentHistory" seltype="multiple" fade="true" flex="1">
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+        </richgrid>
       </vbox>
 
 #ifdef MOZ_SERVICES_SYNC
       <vbox id="start-remotetabs" class="meta-section">
         <label class="meta-section-title wide-title" value="&remoteTabsHeader.label;"/>
         <html:div id="snappedRemoteTabsLabel" class="meta-section-title narrow-title" onclick="StartUI.onNarrowTitleClick('start-remotetabs')">
           &narrowRemoteTabsHeader.label;
         </html:div>
-        <richgrid id="start-remotetabs-grid" observes="bcast_windowState" set-name="remoteTabs" seltype="multiple" flex="1"/>
+        <richgrid id="start-remotetabs-grid" observes="bcast_windowState" set-name="remoteTabs" seltype="multiple" fade="true" flex="1">
+          <richgriditem/>
+          <richgriditem/>
+          <richgriditem/>
+        </richgrid>
+
       </vbox>
 #endif
   </hbox>
   </html:body>
 </html:html>
--- a/browser/metro/base/content/startui/TopSitesView.js
+++ b/browser/metro/base/content/startui/TopSitesView.js
@@ -153,16 +153,19 @@ TopSitesView.prototype = Util.extend(Obj
         // flush, recreate all
       this.isUpdating = true;
       // destroy and recreate all item nodes, skip calling arrangeItems
       this.populateGrid();
     }
   },
 
   updateTile: function(aTileNode, aSite, aArrangeGrid) {
+    if (!(aSite && aSite.url)) {
+      throw new Error("Invalid Site object passed to TopSitesView updateTile");
+    }
     this._updateFavicon(aTileNode, Util.makeURI(aSite.url));
 
     Task.spawn(function() {
       let filepath = PageThumbsStorage.getFilePathForURL(aSite.url);
       if (yield OS.File.exists(filepath)) {
         aSite.backgroundImage = 'url("'+PageThumbs.getThumbnailURL(aSite.url)+'")';
         if ('backgroundImage' in aTileNode) {
           aTileNode.backgroundImage = aSite.backgroundImage;
@@ -187,24 +190,21 @@ TopSitesView.prototype = Util.extend(Obj
     let sites = TopSites.getSites();
     if (this._topSitesMax) {
       sites = sites.slice(0, this._topSitesMax);
     }
     let tileset = this._set;
     tileset.clearAll(true);
 
     for (let site of sites) {
-      // call to private _createItemElement is a temp measure
-      // we'll eventually just request the next slot
-      let item = tileset._createItemElement(site.title, site.url);
-
-      this.updateTile(item, site);
-      tileset.appendChild(item);
+      let slot = tileset.nextSlot();
+      this.updateTile(slot, site);
     }
     tileset.arrangeItems();
+    tileset.removeAttribute("fade");
     this.isUpdating = false;
   },
 
   forceReloadOfThumbnail: function forceReloadOfThumbnail(url) {
     let nodes = this._set.querySelectorAll('richgriditem[value="'+url+'"]');
     for (let item of nodes) {
       if ("isBound" in item && item.isBound) {
         item.refreshBackgroundImage();
@@ -239,17 +239,17 @@ TopSitesView.prototype = Util.extend(Obj
       } else {
         item.removeAttribute("tiletype");
       }
     }
   },
 
   // nsIObservers
   observe: function (aSubject, aTopic, aState) {
-    switch(aTopic) {
+    switch (aTopic) {
       case "Metro:RefreshTopsiteThumbnail":
         this.forceReloadOfThumbnail(aState);
         break;
     }
   },
 
   // nsINavHistoryObserver
   onBeginUpdateBatch: function() {
@@ -264,17 +264,18 @@ TopSitesView.prototype = Util.extend(Obj
 
   onTitleChanged: function(aURI, aPageTitle) {
   },
 
   onDeleteURI: function(aURI) {
   },
 
   onClearHistory: function() {
-    this._set.clearAll();
+    if ('clearAll' in this._set)
+      this._set.clearAll();
   },
 
   onPageChanged: function(aURI, aWhat, aValue) {
   },
 
   onDeleteVisits: function (aURI, aVisitTime, aGUID, aReason, aTransitionType) {
   },
 
--- a/browser/metro/base/tests/mochitest/browser_history.js
+++ b/browser/metro/base/tests/mochitest/browser_history.js
@@ -62,17 +62,17 @@ gTests.push({
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     unpinButton.click();
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(!gStartView._pinHelper.isPinned(uriFromIndex(2)), "Item unpinned");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    is(gStartView._set.itemCount, gStartView._limit, "Grid repopulated");
 
     // --------- unpin multiple items
 
     let item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     let item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     let item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     scrollToEnd();
@@ -119,17 +119,17 @@ gTests.push({
 
     let promise = waitForCondition(() => !restoreButton.hidden);
     EventUtils.synthesizeMouse(deleteButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
-    ok(HistoryTestHelper._nodes[uriFromIndex(2)], "Item not deleted yet");
+    ok(HistoryTestHelper._nodes[uriFromIndex(2)], "Item not actually deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
     ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     EventUtils.synthesizeMouse(restoreButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
@@ -145,34 +145,41 @@ gTests.push({
     sendContextMenuClickToElement(window, item, 10, 10);
     yield promise;
 
     yield waitForCondition(() => !deleteButton.hidden);
 
     ok(!deleteButton.hidden, "Delete button is visible.");
 
     let promise = waitForCondition(() => !restoreButton.hidden);
+    let populateGridSpy = spyOnMethod(gStartView, "populateGrid");
+
     EventUtils.synthesizeMouse(deleteButton, 10, 10, {}, window);
     yield promise;
 
+    is(populateGridSpy.callCount, 1, "populateGrid was called in response to the deleting a tile");
+
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(HistoryTestHelper._nodes[uriFromIndex(2)], "Item not deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     Elements.contextappbar.dismiss();
     yield promise;
 
+    is(populateGridSpy.callCount, 1, "populateGrid not called when a removed item is actually deleted");
+    populateGridSpy.restore();
+
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(!HistoryTestHelper._nodes[uriFromIndex(2)], "Item RIP");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    is(gStartView._set.itemCount, gStartView._limit, "Grid repopulated");
 
     // --------- delete multiple items and restore
 
     let item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     let item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     let item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     let initialLocation1 = gStartView._set.getIndexOfItem(item1);
--- a/browser/metro/base/tests/mochitest/browser_tilegrid.xul
+++ b/browser/metro/base/tests/mochitest/browser_tilegrid.xul
@@ -12,26 +12,29 @@
 <!DOCTYPE window []>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <vbox id="alayout">
       <richgrid id="grid_layout" seltype="single" flex="1">
       </richgrid>
   </vbox>
+  <vbox>
+      <richgrid id="slots_grid" seltype="single" minSlots="6" flex="1"/>
+  </vbox>
   <vbox style="height:600px">
     <hbox>
       <richgrid id="clearGrid" seltype="single" flex="1" rows="2">
         <richgriditem value="about:blank" id="clearGrid_item1" label="First item"/>
         <richgriditem value="about:blank" id="clearGrid_item2" label="2nd item"/>
         <richgriditem value="about:blank" id="clearGrid_item1" label="First item"/>
       </richgrid>
     </hbox>
     <hbox>
-      <richgrid id="emptyGrid" seltype="single" flex="1" rows="2">
+      <richgrid id="emptyGrid" seltype="single" flex="1" rows="2" minSlots="6">
       </richgrid>
     </hbox>
     <hbox>
       <richgrid id="grid1" seltype="single" flex="1">
         <richgriditem value="about:blank" id="grid1_item1" label="First item"/>
         <richgriditem value="about:blank" id="grid1_item2" label="2nd item"/>
       </richgrid>
     </hbox>
--- a/browser/metro/base/tests/mochitest/browser_tiles.js
+++ b/browser/metro/base/tests/mochitest/browser_tiles.js
@@ -4,27 +4,35 @@ function test() {
   waitForExplicitFinish();
   Task.spawn(function(){
     info(chromeRoot + "browser_tilegrid.xul");
     yield addTab(chromeRoot + "browser_tilegrid.xul");
     doc = Browser.selectedTab.browser.contentWindow.document;
   }).then(runTests);
 }
 
+function _checkIfBoundByRichGrid_Item(expected, node, idx) {
+  let binding = node.ownerDocument.defaultView.getComputedStyle(node).MozBinding;
+  let result = ('url("chrome://browser/content/bindings/grid.xml#richgrid-item")' == binding);
+  return (result == expected);
+}
+let isBoundByRichGrid_Item = _checkIfBoundByRichGrid_Item.bind(this, true);
+let isNotBoundByRichGrid_Item = _checkIfBoundByRichGrid_Item.bind(this, false);
+
 gTests.push({
   desc: "richgrid binding is applied",
   run: function() {
     ok(doc, "doc got defined");
 
     let grid = doc.querySelector("#grid1");
     ok(grid, "#grid1 is found");
     is(typeof grid.clearSelection, "function", "#grid1 has the binding applied");
-
     is(grid.items.length, 2, "#grid1 has a 2 items");
     is(grid.items[0].control, grid, "#grid1 item's control points back at #grid1'");
+    ok(Array.every(grid.items, isBoundByRichGrid_Item), "All items are bound by richgrid-item");
   }
 });
 
 gTests.push({
   desc: "item clicks are handled",
   run: function() {
     let grid = doc.querySelector("#grid1");
     is(typeof grid.handleItemClick, "function", "grid.handleItemClick is a function");
@@ -120,29 +128,38 @@ gTests.push({
     is(arrangeSpy.callCount, 1, "arrangeItems is called once when we clearAll");
     arrangeSpy.restore();
   }
 });
 
 gTests.push({
   desc: "empty grid",
   run: function() {
+    // XXX grids have minSlots and may not be ever truly empty
+
     let grid = doc.getElementById("emptyGrid");
     grid.arrangeItems();
     yield waitForCondition(() => !grid.isArranging);
 
-    // grid has rows=2 but 0 items
+    // grid has 2 rows, 6 slots, 0 items
     ok(grid.isBound, "binding was applied");
     is(grid.itemCount, 0, "empty grid has 0 items");
-    is(grid.rowCount, 0, "empty grid has 0 rows");
-    is(grid.columnCount, 0, "empty grid has 0 cols");
+    // minSlots attr. creates unpopulated slots
+    is(grid.rowCount, grid.getAttribute("rows"), "empty grid with rows-attribute has that number of rows");
+    is(grid.columnCount, 3, "empty grid has expected number of columns");
 
-    let columnsNode = grid._grid;
-    let cStyle = doc.defaultView.getComputedStyle(columnsNode);
-    is(cStyle.getPropertyValue("-moz-column-count"), "auto", "empty grid has -moz-column-count: auto");
+    // remove rows attribute and allow space for the grid to find its own height
+    // for its number of slots
+    grid.removeAttribute("rows");
+    grid.parentNode.style.height = 20+(grid.tileHeight*grid.minSlots)+"px";
+
+    grid.arrangeItems();
+    yield waitForCondition(() => !grid.isArranging);
+    is(grid.rowCount, grid.minSlots, "empty grid has this.minSlots rows");
+    is(grid.columnCount, 1, "empty grid has 1 column");
   }
 });
 
 gTests.push({
   desc: "appendItem",
   run: function() {
      // implements an appendItem with signature title, uri, returns item element
      // appendItem triggers arrangeItems
@@ -206,26 +223,35 @@ gTests.push({
      // implements an insertItemAt method, with index, title, uri.spec signature
      // insertItemAt triggers arrangeItems
     let grid = doc.querySelector("#grid3");
 
     is(grid.itemCount, 2, "2 items initially");
     is(typeof grid.insertItemAt, "function", "insertItemAt is a function on the grid");
 
     let arrangeStub = stubMethod(grid, "arrangeItems");
-    let insertedItem = grid.insertItemAt(1, "inserted item", "http://example.com/inserted");
+    let insertedAt0 = grid.insertItemAt(0, "inserted item 0", "http://example.com/inserted0");
+    let insertedAt00 = grid.insertItemAt(0, "inserted item 00", "http://example.com/inserted00");
+
+    ok(insertedAt0 && insertedAt00, "insertItemAt gives back an item");
+
+    is(insertedAt0.getAttribute("label"), "inserted item 0", "insertItemAt creates item with the correct label");
+    is(insertedAt0.getAttribute("value"), "http://example.com/inserted0", "insertItemAt creates item with the correct url value");
 
-    ok(insertedItem, "insertItemAt gives back an item");
-    is(grid.items[1], insertedItem, "item is inserted at the correct index");
-    is(insertedItem.getAttribute("label"), "inserted item", "insertItemAt creates item with the correct label");
-    is(insertedItem.getAttribute("value"), "http://example.com/inserted", "insertItemAt creates item with the correct url value");
-    is(grid.items[2].getAttribute("id"), "grid3_item2", "following item ends up at the correct index");
-    is(grid.itemCount, 3, "itemCount is incremented when we insertItemAt");
+    is(grid.items[0], insertedAt00, "item is inserted at the correct index");
+    is(grid.children[0], insertedAt00, "first item occupies the first slot");
+    is(grid.items[1], insertedAt0, "item is inserted at the correct index");
+    is(grid.children[1], insertedAt0, "next item occupies the next slot");
 
-    is(arrangeStub.callCount, 1, "arrangeItems is called when we insertItemAt");
+    is(grid.items[2].getAttribute("label"), "First item", "Old first item is now at index 2");
+    is(grid.items[3].getAttribute("label"), "2nd item", "Old 2nd item is now at index 3");
+
+    is(grid.itemCount, 4, "itemCount is incremented when we insertItemAt");
+
+    is(arrangeStub.callCount, 2, "arrangeItems is called when we insertItemAt");
     arrangeStub.restore();
   }
 });
 
 gTests.push({
   desc: "getIndexOfItem",
   run: function() {
      // implements a getIndexOfItem method, with item (element) signature
@@ -412,8 +438,177 @@ gTests.push({
 
     is(handlerStub.callCount, 1, "selectionchange event handler was called when we selected an item");
     is(handlerStub.calledWith[0].type, "selectionchange", "handler got a selectionchange event");
     is(handlerStub.calledWith[0].target, grid, "select event had the originating grid as the target");
     handlerStub.restore();
     doc.defaultView.removeEventListener("selectionchange", handler, false);
   }
 });
+
+function gridSlotsSetup() {
+    let grid = this.grid = doc.createElement("richgrid");
+    grid.setAttribute("minSlots", 6);
+    doc.documentElement.appendChild(grid);
+    is(grid.ownerDocument, doc, "created grid in the expected document");
+}
+function gridSlotsTearDown() {
+    this.grid && this.grid.parentNode.removeChild(this.grid);
+}
+
+gTests.push({
+  desc: "richgrid slots init",
+  setUp: gridSlotsSetup,
+  run: function() {
+    let grid = this.grid;
+    // grid is initially populated with empty slots matching the minSlots attribute
+    is(grid.children.length, 6, "minSlots slots are created");
+    is(grid.itemCount, 0, "slots do not count towards itemCount");
+    ok(Array.every(grid.children, (node) => node.nodeName == 'richgriditem'), "slots have nodeName richgriditem");
+    ok(Array.every(grid.children, isNotBoundByRichGrid_Item), "slots aren't bound by the richgrid-item binding");
+  },
+  tearDown: gridSlotsTearDown
+});
+
+gTests.push({
+  desc: "richgrid using slots for items",
+  setUp: gridSlotsSetup, // creates grid with minSlots = num. slots = 6
+  run: function() {
+    let grid = this.grid;
+    let numSlots = grid.getAttribute("minSlots");
+    is(grid.children.length, numSlots);
+    // adding items occupies those slots
+    for (let idx of [0,1,2,3,4,5,6]) {
+      let slot = grid.children[idx];
+      let item = grid.appendItem("item "+idx, "about:mozilla");
+      if (idx < numSlots) {
+        is(grid.children.length, numSlots);
+        is(slot, item, "The same node is reused when an item is assigned to a slot");
+      } else {
+        is(typeof slot, 'undefined');
+        ok(item);
+        is(grid.children.length, grid.itemCount);
+      }
+    }
+  },
+  tearDown: gridSlotsTearDown
+});
+
+gTests.push({
+  desc: "richgrid assign and release slots",
+  setUp: function(){
+    info("assign and release slots setUp");
+    this.grid = doc.getElementById("slots_grid");
+    this.grid.scrollIntoView();
+    let rect = this.grid.getBoundingClientRect();
+    info("slots grid at top: " + rect.top + ", window.pageYOffset: " + doc.defaultView.pageYOffset);
+  },
+  run: function() {
+    let grid = this.grid;
+    // start with 5 of 6 slots occupied
+    for (let idx of [0,1,2,3,4]) {
+      let item = grid.appendItem("item "+idx, "about:mozilla");
+      item.setAttribute("id", "test_item_"+idx);
+    }
+    is(grid.itemCount, 5);
+    is(grid.children.length, 6); // see setup, where we init with 6 slots
+    let firstItem = grid.items[0];
+
+    ok(firstItem.ownerDocument, "item has ownerDocument");
+    is(doc, firstItem.ownerDocument, "item's ownerDocument is the document we expect");
+
+    is(firstItem, grid.children[0], "Item and assigned slot are one and the same");
+    is(firstItem.control, grid, "Item is bound and its .control points back at the grid");
+
+    // before releasing, the grid should be nofified of clicks on that slot
+    let testWindow = grid.ownerDocument.defaultView;
+
+    let rect = firstItem.getBoundingClientRect();
+    {
+      let handleStub = stubMethod(grid, 'handleItemClick');
+      // send click to item and wait for next tick;
+      sendElementTap(testWindow, firstItem);
+      yield waitForMs(0);
+
+      is(handleStub.callCount, 1, "handleItemClick was called when we clicked an item");
+      handleStub.restore();
+    }
+    // _releaseSlot is semi-private, we don't expect consumers of the binding to call it
+    // but want to be sure it does what we expect
+    grid._releaseSlot(firstItem);
+
+    is(grid.itemCount, 4, "Releasing a slot gives us one less item");
+    is(firstItem, grid.children[0],"Released slot is still the same node we started with");
+
+    // after releasing, the grid should NOT be nofified of clicks
+    {
+      let handleStub = stubMethod(grid, 'handleItemClick');
+      // send click to item and wait for next tick;
+      sendElementTap(testWindow, firstItem);
+      yield waitForMs(0);
+
+      is(handleStub.callCount, 0, "handleItemClick was NOT called when we clicked a released slot");
+      handleStub.restore();
+    }
+
+    ok(!firstItem.mozMatchesSelector("richgriditem[value]"), "Released slot doesn't match binding selector");
+    ok(isNotBoundByRichGrid_Item(firstItem), "Released slot is no longer bound");
+
+    waitForCondition(() => isNotBoundByRichGrid_Item(firstItem));
+    ok(true, "Slot eventually gets unbound");
+    is(firstItem, grid.children[0], "Released slot is still at expected index in children collection");
+
+    let firstSlot = grid.children[0];
+    firstItem = grid.insertItemAt(0, "New item 0", "about:blank");
+    ok(firstItem == grid.items[0], "insertItemAt 0 creates item at expected index");
+    ok(firstItem == firstSlot, "insertItemAt occupies the released slot with the new item");
+    is(grid.itemCount, 5);
+    is(grid.children.length, 6);
+    is(firstItem.control, grid,"Item is bound and its .control points back at the grid");
+
+    let nextSlotIndex = grid.itemCount;
+    let lastItem = grid.insertItemAt(9, "New item 9", "about:blank");
+    // Check we don't create sparse collection of items
+    is(lastItem, grid.children[nextSlotIndex], "Item is appended at the next index when an out of bounds index is provided");
+    is(grid.children.length, 6);
+    is(grid.itemCount, 6);
+
+    grid.appendItem("one more", "about:blank");
+    is(grid.children.length, 7);
+    is(grid.itemCount, 7);
+
+    // clearAll results in slots being emptied
+    grid.clearAll();
+    is(grid.children.length, 6, "Extra slots are trimmed when we clearAll");
+    ok(!Array.some(grid.children, (node) => node.hasAttribute("value")), "All slots have no value attribute after clearAll")
+  },
+  tearDown: gridSlotsTearDown
+});
+
+gTests.push({
+  desc: "richgrid slot management",
+  setUp: gridSlotsSetup,
+  run: function() {
+    let grid = this.grid;
+    // populate grid with some items
+    let numSlots = grid.getAttribute("minSlots");
+    for (let idx of [0,1,2,3,4,5]) {
+      let item = grid.appendItem("item "+idx, "about:mozilla");
+    }
+
+    is(grid.itemCount, 6, "Grid setup with 6 items");
+    is(grid.children.length, 6, "Full grid has the expected number of slots");
+
+    // removing an item creates a replacement slot *on the end of the stack*
+    let item = grid.removeItemAt(0);
+    is(item.getAttribute("label"), "item 0", "removeItemAt gives back the populated node");
+    is(grid.children.length, 6);
+    is(grid.itemCount, 5);
+    is(grid.items[0].getAttribute("label"), "item 1", "removeItemAt removes the node so the nextSibling takes its place");
+    ok(grid.children[5] && !grid.children[5].hasAttribute("value"), "empty slot is added at the end of the existing children");
+
+    let item1 = grid.removeItem(grid.items[0]);
+    is(grid.children.length, 6);
+    is(grid.itemCount, 4);
+    is(grid.items[0].getAttribute("label"), "item 2", "removeItem removes the node so the nextSibling takes its place");
+  },
+  tearDown: gridSlotsTearDown
+});
--- a/browser/metro/theme/tiles.css
+++ b/browser/metro/theme/tiles.css
@@ -270,16 +270,36 @@ richgriditem[pinned]:-moz-locale-dir(rtl
 richgriditem[customColor] {
   color: #f1f1f1;
 }
 
 richgriditem[bending] > .tile-content {
   transform-origin: center center;
 }
 
+/* Empty/unused tiles */
+richgriditem:not([value]) {
+  visibility: hidden;
+}
+richgriditem[tiletype="thumbnail"]:not([value]) {
+  visibility: visible;
+}
+richgriditem:not([value]) > .tile-content {
+  padding: 10px 14px;
+}
+richgriditem[tiletype="thumbnail"]:not([value]) > .tile-content {
+  box-shadow: 0px 0px 0px 1px rgba(0, 0, 0, 0.05);
+  background-image: url("chrome://browser/skin/images/firefox-watermark.png");
+  background-origin: content-box;
+  background-repeat: no-repeat;
+  background-color: rgba(255,255,255, 0.2);
+  background-position: center center;
+  background-size: @grid_row_height@;
+}
+
 /* Snapped-view variation
    We use the compact, single-column grid treatment for <=320px */
 
 @media (max-width: 330px) {
 
   richgrid > .richgrid-grid {
     -moz-column-width: auto!important; /* let it flow */
     -moz-column-count: auto!important; /* let it flow */
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -164,17 +164,17 @@ let AboutHome = {
         } catch(ex) {
           Cu.reportError(ex);
           break;
         }
 #ifdef MOZ_SERVICES_HEALTHREPORT
         window.BrowserSearch.recordSearchInHealthReport(data.engineName, "abouthome");
 #endif
         // Trigger a search through nsISearchEngine.getSubmission()
-        let submission = Services.search.currentEngine.getSubmission(data.searchTerms);
+        let submission = Services.search.currentEngine.getSubmission(data.searchTerms, null, "homepage");
         window.loadURI(submission.uri.spec, null, submission.postData);
         break;
     }
   },
 
   // Send all the chrome-privileged data needed by about:home. This
   // gets re-sent when the search engine changes.
   sendAboutHomeData: function(target) {
--- a/build/automation-build.mk
+++ b/build/automation-build.mk
@@ -3,17 +3,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include $(MOZILLA_DIR)/build/binary-location.mk
 
 browser_path := \"$(browser_path)\"
 
 _PROFILE_DIR = $(TARGET_DEPTH)/_profile/pgo
 
-ABSOLUTE_TOPSRCDIR = $(call core_abspath,$(MOZILLA_DIR))
+ABSOLUTE_TOPSRCDIR = $(abspath $(MOZILLA_DIR))
 _CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs
 
 AUTOMATION_PPARGS = 	\
 			-DBROWSER_PATH=$(browser_path) \
 			-DXPC_BIN_PATH=\"$(LIBXUL_DIST)/bin\" \
 			-DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \
 			-DPROFILE_DIR=\"$(_PROFILE_DIR)\" \
 			-DCERTS_SRC_DIR=\"$(_CERTS_SRC_DIR)\" \
--- a/build/cl.py
+++ b/build/cl.py
@@ -71,18 +71,19 @@ def InvokeClWithDependencyGeneration(cmd
     def on_line(line):
         # cl -showIncludes prefixes every header with "Note: including file:"
         # and an indentation corresponding to the depth (which we don't need)
         if line.startswith(CL_INCLUDES_PREFIX):
             dep = line[len(CL_INCLUDES_PREFIX):].strip()
             # We can't handle pathes with spaces properly in mddepend.pl, but
             # we can assume that anything in a path with spaces is a system
             # header and throw it away.
+            dep = normcase(dep)
             if ' ' not in dep:
-                rule.add_dependencies([normcase(dep)])
+                rule.add_dependencies([dep])
         else:
             # Make sure we preserve the relevant output from cl. mozprocess
             # swallows the newline delimiter, so we need to re-add it.
             sys.stdout.write(line)
             sys.stdout.write('\n')
 
     # We need to ignore children because MSVC can fire up a background process
     # during compilation. This process is cleaned up on its own. If we kill it,
--- a/build/macosx/universal/flight.mk
+++ b/build/macosx/universal/flight.mk
@@ -14,24 +14,22 @@ DIST_ARCH_2 = $(OBJDIR_ARCH_2)/dist
 DIST_UNI = $(DIST_ARCH_1)/universal
 OBJDIR = $(OBJDIR_ARCH_1)
 endif
 
 topsrcdir = $(TOPSRCDIR)
 DEPTH = $(OBJDIR)
 include $(OBJDIR)/config/autoconf.mk
 
-core_abspath = $(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1))
-
 DIST = $(OBJDIR)/dist
 
 postflight_all:
 	mkdir -p $(DIST_UNI)/$(MOZ_PKG_APPNAME)
 	rm -f $(DIST_ARCH_2)/universal
-	ln -s $(call core_abspath,$(DIST_UNI)) $(DIST_ARCH_2)/universal
+	ln -s $(abspath $(DIST_UNI)) $(DIST_ARCH_2)/universal
 # Stage a package for buildsymbols to be happy. Doing so in OBJDIR_ARCH_1
 # actually does a universal staging with both OBJDIR_ARCH_1 and OBJDIR_ARCH_2.
 	$(MAKE) -C $(OBJDIR_ARCH_1)/$(MOZ_BUILD_APP)/installer \
 	   PKG_SKIP_STRIP=1 stage-package
 ifdef ENABLE_TESTS
 # Now, repeat the process for the test package.
 	$(MAKE) -C $(OBJDIR_ARCH_1) UNIVERSAL_BINARY= CHROME_JAR= package-tests
 	$(MAKE) -C $(OBJDIR_ARCH_2) UNIVERSAL_BINARY= CHROME_JAR= package-tests
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -4,21 +4,17 @@
 
 mobile-tests := mobile/android/base/tests
 TESTPATH     := $(topsrcdir)/$(mobile-tests)
 dir-tests    := $(DEPTH)/$(mobile-tests)
 
 ANDROID_APK_NAME := robocop-debug
 
 ANDROID_EXTRA_JARS += \
-  $(srcdir)/robotium-solo-4.2.jar \
-  $(NULL)
-
-ANDROID_RESFILES = \
-  res/values/strings.xml \
+  $(srcdir)/robotium-solo-4.3.jar \
   $(NULL)
 
 ANDROID_ASSETS_DIR := $(TESTPATH)/assets
 
 _JAVA_HARNESS := \
   Actions.java \
   Assert.java \
   Driver.java \
--- a/build/mobile/robocop/README
+++ b/build/mobile/robocop/README
@@ -1,12 +1,12 @@
 Robocop is a Mozilla project which uses Robotium to test Firefox on Android devices.
 
-Robotium is an open source tool licensed under the Apache 2.0 license and the original 
+Robotium is an open source tool licensed under the Apache 2.0 license and the original
 source can be found here:
 http://code.google.com/p/robotium/
 
-We are including robotium-solo-4.2.jar as a binary and are not modifying it in any way 
-from the original download found at: 
+We are including robotium-solo-4.3.jar as a binary and are not modifying it in any way
+from the original download found at:
 http://code.google.com/p/robotium/
 
 Firefox for Android developers should read the documentation in
 mobile/android/base/tests/README.rst.
--- a/build/mobile/robocop/moz.build
+++ b/build/mobile/robocop/moz.build
@@ -1,8 +1,11 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'robocop'
 
+ANDROID_RESFILES = [
+    'res/values/strings.xml',
+]
deleted file mode 100644
index 349f7516a0bf0d63a845577032429047073aeb72..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2bee9cb5f95ea5c4309640cf19fdced9fb2fa706
GIT binary patch
literal 106466
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%0Ax9;nLJLT
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
--- a/build/mobile/sutagent/android/Makefile.in
+++ b/build/mobile/sutagent/android/Makefile.in
@@ -15,26 +15,16 @@ JAVAFILES = \
   Power.java \
   RedirOutputThread.java \
   RunCmdThread.java \
   RunDataThread.java \
   SUTAgentAndroid.java \
   WifiConfiguration.java \
   $(NULL)
 
-ANDROID_RESFILES = \
-  res/drawable/icon.png \
-  res/drawable/ateamlogo.png \
-  res/drawable/ic_stat_first.png \
-  res/drawable/ic_stat_neterror.png \
-  res/drawable/ic_stat_warning.png \
-  res/layout/main.xml \
-  res/values/strings.xml \
-  $(NULL)
-
 ANDROID_EXTRA_JARS = \
   $(srcdir)/network-libs/commons-net-2.0.jar \
   $(srcdir)/network-libs/jmdns.jar \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 tools:: $(ANDROID_APK_NAME).apk
--- a/build/mobile/sutagent/android/fencp/Makefile.in
+++ b/build/mobile/sutagent/android/fencp/Makefile.in
@@ -6,19 +6,11 @@ ANDROID_APK_NAME := FenCP
 
 JAVAFILES = \
   DirCursor.java \
   FenCP.java \
   FenCPFP.java \
   FileCursor.java \
   $(NULL)
 
-ANDROID_RESFILES = \
-  res/drawable-hdpi/icon.png \
-  res/drawable-ldpi/icon.png \
-  res/drawable-mdpi/icon.png \
-  res/layout/main.xml \
-  res/values/strings.xml \
-  $(NULL)
-
 include $(topsrcdir)/config/rules.mk
 
 tools:: $(ANDROID_APK_NAME).apk
--- a/build/mobile/sutagent/android/fencp/moz.build
+++ b/build/mobile/sutagent/android/fencp/moz.build
@@ -1,8 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'FenCP'
 
+ANDROID_RESFILES = [
+    'res/drawable-hdpi/icon.png',
+    'res/drawable-ldpi/icon.png',
+    'res/drawable-mdpi/icon.png',
+    'res/layout/main.xml',
+    'res/values/strings.xml',
+]
--- a/build/mobile/sutagent/android/ffxcp/Makefile.in
+++ b/build/mobile/sutagent/android/ffxcp/Makefile.in
@@ -6,19 +6,11 @@ ANDROID_APK_NAME := FfxCP
 
 JAVAFILES = \
   DirCursor.java \
   ffxcp.java \
   FfxCPFP.java \
   FileCursor.java \
   $(NULL)
 
-ANDROID_RESFILES = \
-  res/drawable-hdpi/icon.png \
-  res/drawable-ldpi/icon.png \
-  res/drawable-mdpi/icon.png \
-  res/layout/main.xml \
-  res/values/strings.xml \
-  $(NULL)
-
 include $(topsrcdir)/config/rules.mk
 
 tools:: $(ANDROID_APK_NAME).apk
--- a/build/mobile/sutagent/android/ffxcp/moz.build
+++ b/build/mobile/sutagent/android/ffxcp/moz.build
@@ -1,8 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'FfxCP'
 
+ANDROID_RESFILES = [
+    'res/drawable-hdpi/icon.png',
+    'res/drawable-ldpi/icon.png',
+    'res/drawable-mdpi/icon.png',
+    'res/layout/main.xml',
+    'res/values/strings.xml',
+]
--- a/build/mobile/sutagent/android/moz.build
+++ b/build/mobile/sutagent/android/moz.build
@@ -1,8 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'sutAgentAndroid'
 
+ANDROID_RESFILES = [
+    'res/drawable/ateamlogo.png',
+    'res/drawable/ic_stat_first.png',
+    'res/drawable/ic_stat_neterror.png',
+    'res/drawable/ic_stat_warning.png',
+    'res/drawable/icon.png',
+    'res/layout/main.xml',
+    'res/values/strings.xml',
+]
--- a/build/mobile/sutagent/android/watcher/Makefile.in
+++ b/build/mobile/sutagent/android/watcher/Makefile.in
@@ -7,22 +7,11 @@ ANDROID_APK_NAME := Watcher
 JAVAFILES = \
   IWatcherService.java \
   RedirOutputThread.java \
   WatcherMain.java \
   WatcherReceiver.java \
   WatcherService.java \
   $(NULL)
 
-ANDROID_RESFILES = \
-  res/drawable-hdpi/icon.png \
-  res/drawable-hdpi/ateamlogo.png \
-  res/drawable-ldpi/icon.png \
-  res/drawable-ldpi/ateamlogo.png \
-  res/drawable-mdpi/icon.png \
-  res/drawable-mdpi/ateamlogo.png \
-  res/layout/main.xml \
-  res/values/strings.xml \
-  $(NULL)
-
 include $(topsrcdir)/config/rules.mk
 
 tools:: $(ANDROID_APK_NAME).apk
--- a/build/mobile/sutagent/android/watcher/RedirOutputThread.java
+++ b/build/mobile/sutagent/android/watcher/RedirOutputThread.java
@@ -96,17 +96,16 @@ public class RedirOutputThread extends T
                             strOutput += sRep;
                         }
                     }
 
                 bStillRunning = (IsProcRunning(pProc) || (sutOut.available() > 0) || (sutErr.available() > 0));
                 }
             catch (IOException e)
                 {
-//                Toast.makeText(SUTAgentAndroid.me.getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
                 e.printStackTrace();
                 }
             }
 
         pProc.destroy();
         buffer = null;
         System.gc();
         }
--- a/build/mobile/sutagent/android/watcher/WatcherService.java
+++ b/build/mobile/sutagent/android/watcher/WatcherService.java
@@ -38,23 +38,21 @@ import android.content.pm.PackageManager
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.BatteryManager;
 import android.os.Debug;
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.util.Log;
-import android.view.Gravity;
-import android.widget.Toast;
 import android.os.Environment;
 
 public class WatcherService extends Service
 {
-    private final String prgVersion = "Watcher Version 1.16";
+    private final String prgVersion = "Watcher Version 1.17";
     private final String LOGTAG = "Watcher";
 
     String sErrorPrefix = "##Installer Error## ";
     String currentDir = "/";
     String sPingTarget = "";
     long lDelay = 60000;
     long lPeriod = 300000;
     int nMaxStrikes = 0; // maximum number of tries before we consider network unreachable (0 means don't check)
@@ -130,26 +128,26 @@ public class WatcherService extends Serv
         this.bStartSUTAgent = Boolean.parseBoolean(sHold.trim());
 
         sHold = GetIniData("watcher", "stayon", sIniFile,"0");
         int nStayOn = Integer.parseInt(sHold.trim());
         
         try {
             if (nStayOn != 0) {
                 if (!Settings.System.putInt(getContentResolver(), Settings.System.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB)) {
-                    doToast("Screen couldn't be set to Always On [stay on while plugged in]");
+                    Log.e(LOGTAG, "Screen couldn't be set to Always On [stay on while plugged in]");
                 }
             }
         } catch (Exception e) {
             e.printStackTrace();
             String sExcept = e.getMessage();
-            doToast("Screen couldn't be set to Always On [exception " + sExcept + "]");
+            Log.e(LOGTAG, "Screen couldn't be set to Always On [exception " + sExcept + "]");
         }
 
-        doToast("WatcherService created");
+        Log.i(LOGTAG, "WatcherService created");
         }
 
     public String GetIniData(String sSection, String sKey, String sFile, String sDefault)
         {
         String sRet = sDefault;
         String sComp = "";
         String sLine = "";
         boolean bFound = false;
@@ -223,30 +221,30 @@ public class WatcherService extends Serv
                 Log.i(LOGTAG, "WatcherService updating " + sPkgName + " using file " + sPkgFile);
 
                 UpdateApplication worker = new UpdateApplication(sPkgName, sPkgFile, sOutFile, nReboot);
                 }
             else if (sCmd.equalsIgnoreCase("start"))
                 {
                 if (!this.bStartedTimer) 
                     {
-                    doToast("WatcherService started");
+                    Log.i(LOGTAG, "WatcherService started");
                     myTimer = new Timer();
                     Date startSchedule = new Date(System.currentTimeMillis() + lDelay);
                     myTimer.schedule(new MyTime(), startSchedule, lPeriod);
                     this.bStartedTimer = true;
                     }
                 }
             else
                 {
-                doToast("WatcherService unknown command");
+                Log.w(LOGTAG, "WatcherService unknown command");
                 }
             }
         else
-            doToast("WatcherService created");
+            Log.w(LOGTAG, "WatcherService intent had null command");
         }
 
     public void writeVersion() {
         PrintWriter pw = null;
         String appPath = getApplicationContext().getFilesDir().getAbsolutePath();
         String versionPath = appPath + "/version.txt";
         Log.i(LOGTAG, "writing version string to: " + versionPath);
         try {
@@ -275,17 +273,17 @@ public class WatcherService extends Serv
         writeVersion();
         handleCommand(intent);
         return START_STICKY;
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
-        doToast("WatcherService destroyed");
+        Log.i(LOGTAG, "WatcherService destroyed");
         if (pwl != null)
             pwl.release();
         stopForegroundCompat(R.string.foreground_service_started);
     }
 
     @Override
     public void onLowMemory() {
         Log.e(LOGTAG, "onLowMemory");
@@ -442,24 +440,16 @@ public class WatcherService extends Serv
                 e.printStackTrace();
             } catch (InvocationTargetException e) {
                 Log.e(LOGTAG, "Unable to invoke setForeground", e);
                 e.printStackTrace();
             }
         }
     }
 
-    public void doToast(String sMsg)
-        {
-        Log.i(LOGTAG, sMsg);
-        Toast toast = Toast.makeText(this, sMsg, Toast.LENGTH_LONG);
-        toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 100);
-        toast.show();
-        }
-
     public void CheckMem() 
         {
         System.gc();
         long lFreeMemory = Runtime.getRuntime().freeMemory();
         long lTotMemory = Runtime.getRuntime().totalMemory();
         long lMaxMemory = Runtime.getRuntime().maxMemory();
 
         Log.i(LOGTAG, "Free: " + lFreeMemory + "Total: " + lTotMemory + "Max: " + lMaxMemory);
--- a/build/mobile/sutagent/android/watcher/moz.build
+++ b/build/mobile/sutagent/android/watcher/moz.build
@@ -1,8 +1,18 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 MODULE = 'Watcher'
 
+ANDROID_RESFILES = [
+    'res/drawable-hdpi/ateamlogo.png',
+    'res/drawable-hdpi/icon.png',
+    'res/drawable-ldpi/ateamlogo.png',
+    'res/drawable-ldpi/icon.png',
+    'res/drawable-mdpi/ateamlogo.png',
+    'res/drawable-mdpi/icon.png',
+    'res/layout/main.xml',
+    'res/values/strings.xml',
+]
--- a/build/unix/elfhack/Makefile.in
+++ b/build/unix/elfhack/Makefile.in
@@ -11,16 +11,18 @@ NO_PROFILE_GUIDED_OPTIMIZE = 1
 VPATH += $(topsrcdir)/build
 
 OS_CXXFLAGS := $(filter-out -fno-exceptions,$(OS_CXXFLAGS)) -fexceptions
 
 WRAP_LDFLAGS=
 
 include $(topsrcdir)/config/rules.mk
 
+DEFINES += -DELFHACK_BUILD
+
 test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): %$(DLL_SUFFIX): %.$(OBJ_SUFFIX) elfhack $(filter inject/%,$(CSRCS:.c=.$(OBJ_SUFFIX)))
 	$(MKSHLIB) $(LDFLAGS) $< -nostartfiles
 	@echo ===
 	@echo === If you get failures below, please file a bug describing the error
 	@echo === and your environment \(compiler and linker versions\), and use
 	@echo === --disable-elf-hack until this is fixed.
 	@echo ===
 	# Fail if the library doesn't have $(DT_TYPE) .dynamic info
--- a/build/unix/elfhack/inject/Makefile.in
+++ b/build/unix/elfhack/inject/Makefile.in
@@ -25,10 +25,12 @@ include $(topsrcdir)/config/rules.mk
 
 export:: $(CSRCS:.c=.$(OBJ_SUFFIX))
 
 $(CSRCS): %.c: ../inject.c
 	cp $< $@
 
 GARBAGE += $(CSRCS)
 
+DEFINES += -DELFHACK_BUILD
+
 CFLAGS := -O2 -fno-stack-protector $(filter -m% -I%,$(CFLAGS))
 $(CPU)-noinit.$(OBJ_SUFFIX): DEFINES += -DNOINIT
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -123,17 +123,17 @@ install::
 	$(SYSINSTALL) $(IFLAGS1) $(DEPTH)/mozilla-config.h $(DESTDIR)$(includedir)
 
 GARBAGE += \
   $(FINAL_LINK_COMPS) $(FINAL_LINK_LIBS) $(FINAL_LINK_COMP_NAMES) buildid $(srcdir)/*.pyc *.pyc
 
 ifndef CROSS_COMPILE
 ifdef USE_ELF_DYNSTR_GC
 elf-dynstr-gc: elf-dynstr-gc.c $(GLOBAL_DEPS) $(call mkdir_deps,$(MDDEPDIR))
-	$(CC) $(COMPILE_CFLAGS) $(GLIB_CFLAGS) -o $@ $< $(LDFLAGS) $(GLIB_LIBS)
+	$(CC) $(COMPILE_CFLAGS) $(GLIB_CFLAGS) -DELFDYNSTRGC_BUILD -o $@ $< $(LDFLAGS) $(GLIB_LIBS)
 endif
 endif
 
 FORCE:
 
 check-preqs = \
   check-jar-mn \
   check-makefiles \
--- a/config/android-common.mk
+++ b/config/android-common.mk
@@ -8,17 +8,17 @@ ifndef ANDROID_SDK
   $(error ANDROID_SDK must be defined before including android-common.mk)
 endif
 
 ifndef JAVA_CLASSPATH
   $(error JAVA_CLASSPATH must be defined before including android-common.mk)
 endif
 
 # DEBUG_JARSIGNER always debug signs.
-DEBUG_JARSIGNER=$(PYTHON) $(call core_abspath,$(topsrcdir)/mobile/android/debug_sign_tool.py) \
+DEBUG_JARSIGNER=$(PYTHON) $(abspath $(topsrcdir)/mobile/android/debug_sign_tool.py) \
   --keytool=$(KEYTOOL) \
   --jarsigner=$(JARSIGNER) \
   $(NULL)
 
 # For Android, this defaults to $(ANDROID_SDK)/android.jar
 ifndef JAVA_BOOTCLASSPATH
   JAVA_BOOTCLASSPATH = $(ANDROID_SDK)/android.jar:$(ANDROID_COMPAT_LIB)
 endif
--- a/config/config.mk
+++ b/config/config.mk
@@ -627,17 +627,17 @@ GARBAGE		+= $(DEPENDENCIES) core $(wildc
 
 ifeq ($(OS_ARCH),Darwin)
 ifndef NSDISTMODE
 NSDISTMODE=absolute_symlink
 endif
 PWD := $(CURDIR)
 endif
 
-NSINSTALL_PY := $(PYTHON) $(call core_abspath,$(topsrcdir)/config/nsinstall.py)
+NSINSTALL_PY := $(PYTHON) $(abspath $(topsrcdir)/config/nsinstall.py)
 # For Pymake, wherever we use nsinstall.py we're also going to try to make it
 # a native command where possible. Since native commands can't be used outside
 # of single-line commands, we continue to provide INSTALL for general use.
 # Single-line commands should be switched over to install_cmd.
 NSINSTALL_NATIVECMD := %nsinstall nsinstall
 
 ifdef NSINSTALL_BIN
 NSINSTALL = $(NSINSTALL_BIN)
@@ -686,17 +686,17 @@ sysinstall_cmd = install_cmd
 AB_CD = $(MOZ_UI_LOCALE)
 
 ifndef L10NBASEDIR
   L10NBASEDIR = $(error L10NBASEDIR not defined by configure)
 else
   IS_LANGUAGE_REPACK = 1
 endif
 
-EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(call core_realpath,$(L10NBASEDIR))/$(AB_CD)/$(subst /locales,,$(1)))
+EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/$(subst /locales,,$(1)))
 
 ifdef relativesrcdir
 LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
 endif
 
 ifdef relativesrcdir
 MAKE_JARS_FLAGS += --relativesrcdir=$(relativesrcdir)
 ifneq (en-US,$(AB_CD))
@@ -737,17 +737,17 @@ endif # ! OS2
 
 # Make sure any compiled classes work with at least JVM 1.4
 JAVAC_FLAGS += -source 1.4
 
 ifdef MOZ_DEBUG
 JAVAC_FLAGS += -g
 endif
 
-CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
+CREATE_PRECOMPLETE_CMD = $(PYTHON) $(abspath $(topsrcdir)/config/createprecomplete.py)
 
 # MDDEPDIR is the subdirectory where dependency files are stored
 MDDEPDIR := .deps
 
 EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/expandlibs_exec.py $(if $@,--depend $(MDDEPDIR)/$(dir $@)/$(@F).pp --target $@)
 EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/expandlibs_gen.py $(if $@,--depend $(MDDEPDIR)/$(dir $@)/$(@F).pp)
 EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR)
 EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC)
--- a/config/makefiles/functions.mk
+++ b/config/makefiles/functions.mk
@@ -11,12 +11,12 @@
 #
 
 # Define an include-at-most-once flag
 ifdef INCLUDED_FUNCTIONS_MK
 $(error Do not include functions.mk twice!)
 endif
 INCLUDED_FUNCTIONS_MK = 1
 
-core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1)))
-core_realpath = $(if $(realpath $(1)),$(realpath $(1)),$(call core_abspath,$(1)))
+core_abspath = $(error core_abspath is unsupported, use $$(abspath) instead)
+core_realpath = $(error core_realpath is unsupported)
 
-core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1)))))))
+core_winabspath = $(error core_winabspath is unsupported)
--- a/config/makefiles/java-build.mk
+++ b/config/makefiles/java-build.mk
@@ -3,16 +3,17 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 ifndef INCLUDED_JAVA_BUILD_MK #{
 
 ifdef ANDROID_RESFILES #{
+ifndef IGNORE_ANDROID_RESFILES #{
 res-dep := .deps-copy-java-res
 
 GENERATED_DIRS += res
 GARBAGE        += $(res-dep)
 
 export:: $(res-dep)
 
 res-dep-preqs := \
@@ -20,16 +21,17 @@ res-dep-preqs := \
   $(call mkdir_deps,res) \
   $(if $(IS_LANGUAGE_REPACK),FORCE) \
   $(NULL)
 
 # nop-build: only copy res/ files when needed
 $(res-dep): $(res-dep-preqs)
 	$(call copy_dir,$(srcdir)/res,$(CURDIR)/res)
 	@$(TOUCH) $@
+endif #} IGNORE_ANDROID_RESFILES
 endif #} ANDROID_RESFILES
 
 
 ifdef JAVAFILES #{
 GENERATED_DIRS += classes
 
 export:: classes
 classes: $(call mkdir_deps,classes)
--- a/config/nspr/Makefile.in
+++ b/config/nspr/Makefile.in
@@ -8,17 +8,17 @@ include $(topsrcdir)/config/rules.mk
 ifdef LIBXUL_SDK
 $(error config/nspr/Makefile.in is not compatible with --enable-libxul-sdk=)
 endif
 ifdef MOZ_NATIVE_NSPR
 $(error config/nspr/Makefile.in is not compatible with MOZ_NATIVE_NSPR)
 endif
 
 # Copy NSPR to the SDK
-ABS_DIST = $(call core_abspath,$(DIST))
+ABS_DIST = $(abspath $(DIST))
 
 ifdef MOZ_FOLD_LIBS
 # Trick the nspr build system into not building shared libraries.
 # bug #851869.
 EXTRA_MAKE_FLAGS := SHARED_LIBRARY= IMPORT_LIBRARY= SHARED_LIB_PDB=
 
 # Work around libVersionPoint conflict between all three libraries.
 # See bug #838566.
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -16,16 +16,18 @@ ifdef INCLUDED_RULES_MK
 endif
 INCLUDED_RULES_MK = 1
 
 # Integrate with mozbuild-generated make files. We first verify that no
 # variables provided by the automatically generated .mk files are
 # present. If they are, this is a violation of the separation of
 # responsibility between Makefile.in and mozbuild files.
 _MOZBUILD_EXTERNAL_VARIABLES := \
+  ANDROID_GENERATED_RESFILES \
+  ANDROID_RESFILES \
   CMMSRCS \
   CPP_UNIT_TESTS \
   DIRS \
   EXTRA_PP_COMPONENTS \
   EXTRA_PP_JS_MODULES \
   GTEST_CMMSRCS \
   GTEST_CPPSRCS \
   GTEST_CSRCS \
new file mode 100644
--- /dev/null
+++ b/content/base/crashtests/851353-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+    <meta charset="UTF-8">
+    <script>
+        function start() {
+            var doc = document.getElementsByTagName("iframe")[0].contentDocument;
+            var vid = doc.getElementsByTagName("video")[0];
+
+            function runnable() {
+                // The doc.write forces us to recreate doc's body.
+                doc.write("Hello, world");
+                doc.body.appendChild(vid);
+                document.documentElement.removeAttribute("class");
+            }
+
+            doc.open();
+            setTimeout(runnable, 0);
+        }
+    </script>
+</head>
+<body onload='start()'>
+    <iframe src="data:text/html,<meta charset=UTF-8><body><video src=http://localhost:8080/ controls=true loop=true autoplay=true autobuffer=false></video>"></iframe>
+</body>
+</html>
--- a/content/base/crashtests/crashtests.list
+++ b/content/base/crashtests/crashtests.list
@@ -128,10 +128,11 @@ load 824719.html
 load 827190.html
 load 828054.html
 load 829428.html
 load 836890.html
 load 841205.html
 load 844404.html
 load 847127.html
 load 849601.html
+skip-if(Android) load 851353-1.html
 load 863950.html
 load 864448.html
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -1168,17 +1168,19 @@ Element::UnbindFromTree(bool aDeep, bool
                                       "RemovedFullScreenElement");
       // Fully exit full-screen.
       nsIDocument::ExitFullscreen(OwnerDoc(), /* async */ false);
     }
     if (HasPointerLock()) {
       nsIDocument::UnlockPointer();
     }
     if (GetParent()) {
-      NS_RELEASE(mParent);
+      nsINode* p = mParent;
+      mParent = nullptr;
+      NS_RELEASE(p);
     } else {
       mParent = nullptr;
     }
     SetParentIsContent(false);
   }
   ClearInDocument();
 
   // Begin keeping track of our subtree root.
--- a/content/base/test/mochitest.ini
+++ b/content/base/test/mochitest.ini
@@ -501,16 +501,17 @@ support-files =
 [test_bug809003.html]
 [test_bug810494.html]
 [test_bug811701.html]
 [test_bug811701.xhtml]
 [test_bug813919.html]
 [test_bug814576.html]
 [test_bug819051.html]
 [test_bug820909.html]
+[test_bug840098.html]
 [test_bug868999.html]
 [test_bug869000.html]
 [test_bug869002.html]
 [test_bug869006.html]
 [test_bug876282.html]
 [test_bug890580.html]
 [test_bug894874.html]
 [test_bug895239.html]
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug840098.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=840098
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 840098</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=840098">Mozilla Bug 840098</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  <div id="foo"></div>
+</div>
+<marquee id="m">Hello</marquee>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 840098 **/
+var anonymousNode = document.getElementById("m").outerDiv;
+try {
+  document.implementation.createDocument("", "", null).adoptNode(anonymousNode);
+  ok(false, "shouldn't be able to adopt the root of an anonymous subtree");
+} catch (e) {
+  is(e.name, "NotSupportedError", "threw the correct type of error");
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/canvas/crashtests/916128-1.html
@@ -0,0 +1,13 @@
+<script>
+o0 = document.createElement('canvas');
+(document.body || document.documentElement).appendChild(o0);
+o1 = o0.getContext('2d');
+o2 = document.createElement('img');
+//o2.src = "image2.png";
+o3 = o1.createImageData(0.7409945472006207, 0.8815588599260801);
+o1.save();
+o1.mozCurrentTransform = [0.18777365986904448, 4, 4, -2048, 3, 32];
+o0.width = 0.52;
+o1.putImageData(o3, -32, -0.16596290333335356);
+o0.toBlob(function() {}, "video/mp4", 16);
+</script>
--- a/content/canvas/crashtests/crashtests.list
+++ b/content/canvas/crashtests/crashtests.list
@@ -12,8 +12,9 @@ load 746813-1.html
 # this test crashes in a bunch places still
 #load 745818-large-source.html
 load 743499-negative-size.html
 skip-if(Android) load 767337-1.html
 skip-if(Android||B2G) load 780392-1.html # bug 833371 for B2G
 skip-if(Android||B2G) load 789933-1.html # bug 833371
 load 794463-1.html
 load 802926-1.html
+load 916128-1.html
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -9,18 +9,18 @@
 #include "nsISupports.h"
 #include "nsIInputStream.h"
 #include "nsIDocShell.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "GraphicsFilter.h"
 #include "mozilla/RefPtr.h"
 
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
-{ 0x8b8da863, 0xd151, 0x4014, \
-  { 0x8b, 0xdc, 0x62, 0xb5, 0x0d, 0xc0, 0x2b, 0x62 } }
+{ 0x9a6a5bdf, 0x1261, 0x4057, \
+  { 0x85, 0xcc, 0xaf, 0x97, 0x6c, 0x36, 0x99, 0xa9 } }
 
 class gfxContext;
 class gfxASurface;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
@@ -66,16 +66,19 @@ public:
 
   NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) = 0;
 
   // Render the canvas at the origin of the given gfxContext
   NS_IMETHOD Render(gfxContext *ctx,
                     GraphicsFilter aFilter,
                     uint32_t aFlags = RenderFlagPremultAlpha) = 0;
 
+  // Creates an image buffer. Returns null on failure.
+  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) = 0;
+
   // Gives you a stream containing the image represented by this context.
   // The format is given in aMimeTime, for example "image/png".
   //
   // If the image format does not support transparency or aIncludeTransparency
   // is false, alpha will be discarded and the result will be the image
   // composited on black.
   NS_IMETHOD GetInputStream(const char *aMimeType,
                             const PRUnichar *aEncoderOptions,
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -36,17 +36,17 @@
 #include "nsGfxCIID.h"
 #include "nsIDocShell.h"
 #include "nsIDOMWindow.h"
 #include "nsPIDOMWindow.h"
 #include "nsDisplayList.h"
 
 #include "nsTArray.h"
 
-#include "imgIEncoder.h"
+#include "ImageEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxASurface.h"
 #include "gfxImageSurface.h"
 #include "gfxPlatform.h"
 #include "gfxFont.h"
 #include "gfxBlur.h"
 #include "gfxUtils.h"
@@ -923,26 +923,28 @@ CanvasRenderingContext2D::GetHeight() co
 }
 #endif
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::SetDimensions(int32_t width, int32_t height)
 {
   ClearTarget();
 
-  // Zero sized surfaces cause issues, so just go with 1x1.
-  if (height == 0 || width == 0) {
+  // Zero sized surfaces can cause problems.
+  mZero = false;
+  if (height == 0) {
+    height = 1;
     mZero = true;
-    mWidth = 1;
-    mHeight = 1;
-  } else {
-    mZero = false;
-    mWidth = width;
-    mHeight = height;
   }
+  if (width == 0) {
+    width = 1;
+    mZero = true;
+  }
+  mWidth = width;
+  mHeight = height;
 
   return NS_OK;
 }
 
 void
 CanvasRenderingContext2D::ClearTarget()
 {
   Reset();
@@ -1045,81 +1047,81 @@ CanvasRenderingContext2D::Render(gfxCont
       MOZ_ASSERT(gis, "If non-premult alpha, must be able to get image surface!");
 
       gfxUtils::UnpremultiplyImageSurface(gis);
   }
 
   return rv;
 }
 
+void
+CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer,
+                                         int32_t* aFormat)
+{
+  *aImageBuffer = nullptr;
+  *aFormat = 0;
+
+  nsRefPtr<gfxASurface> surface;
+  nsresult rv = GetThebesSurface(getter_AddRefs(surface));
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  static const fallible_t fallible = fallible_t();
+  uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
+  if (!imageBuffer) {
+    return;
+  }
+
+  nsRefPtr<gfxImageSurface> imgsurf =
+    new gfxImageSurface(imageBuffer,
+                        gfxIntSize(mWidth, mHeight),
+                        mWidth * 4,
+                        gfxImageFormatARGB32);
+
+  if (!imgsurf || imgsurf->CairoStatus()) {
+    delete[] imageBuffer;
+    return;
+  }
+
+  nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
+  if (!ctx || ctx->HasError()) {
+    delete[] imageBuffer;
+    return;
+  }
+
+  ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
+  ctx->SetSource(surface, gfxPoint(0, 0));
+  ctx->Paint();
+
+  *aImageBuffer = imageBuffer;
+  *aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
+}
+
 NS_IMETHODIMP
 CanvasRenderingContext2D::GetInputStream(const char *aMimeType,
                                          const PRUnichar *aEncoderOptions,
                                          nsIInputStream **aStream)
 {
-  EnsureTarget();
-  if (!IsTargetValid()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxASurface> surface;
-
-  if (NS_FAILED(GetThebesSurface(getter_AddRefs(surface)))) {
+  uint8_t* imageBuffer = nullptr;
+  int32_t format = 0;
+  GetImageBuffer(&imageBuffer, &format);
+  if (!imageBuffer) {
     return NS_ERROR_FAILURE;
   }
 
-  nsresult rv;
-  const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
-  static const fallible_t fallible = fallible_t();
-  nsAutoArrayPtr<char> conid(new (fallible) char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
-
-  if (!conid) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  strcpy(conid, encoderPrefix);
-  strcat(conid, aMimeType);
-
-  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
+  nsCString enccid("@mozilla.org/image/encoder;2?type=");
+  enccid += aMimeType;
+  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
   if (!encoder) {
     return NS_ERROR_FAILURE;
   }
 
-  nsAutoArrayPtr<uint8_t> imageBuffer(new (fallible) uint8_t[mWidth * mHeight * 4]);
-  if (!imageBuffer) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  nsRefPtr<gfxImageSurface> imgsurf =
-    new gfxImageSurface(imageBuffer.get(),
-                        gfxIntSize(mWidth, mHeight),
-                        mWidth * 4,
-                        gfxImageFormatARGB32);
-
-  if (!imgsurf || imgsurf->CairoStatus()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
-
-  if (!ctx || ctx->HasError()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
-  ctx->SetSource(surface, gfxPoint(0, 0));
-  ctx->Paint();
-
-  rv = encoder->InitFromData(imageBuffer.get(),
-                              mWidth * mHeight * 4, mWidth, mHeight, mWidth * 4,
-                              imgIEncoder::INPUT_FORMAT_HOSTARGB,
-                              nsDependentString(aEncoderOptions));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return CallQueryInterface(encoder, aStream);
+  return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
+                                      encoder, aEncoderOptions, aStream);
 }
 
 SurfaceFormat
 CanvasRenderingContext2D::GetSurfaceFormat() const
 {
   return mOpaque ? FORMAT_B8G8R8X8 : FORMAT_B8G8R8A8;
 }
 
@@ -3810,16 +3812,19 @@ CanvasRenderingContext2D::PutImageData_e
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 CanvasRenderingContext2D::GetThebesSurface(gfxASurface **surface)
 {
   EnsureTarget();
+  if (!IsTargetValid()) {
+    return NS_ERROR_FAILURE;
+  }
 
   nsRefPtr<gfxASurface> thebesSurface =
       gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
 
   if (!thebesSurface) {
     return NS_ERROR_FAILURE;
   }
 
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -17,16 +17,17 @@
 #include "gfxFont.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/CanvasGradient.h"
 #include "mozilla/dom/CanvasRenderingContext2DBinding.h"
 #include "mozilla/dom/CanvasPattern.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/2D.h"
 #include "gfx2DGlue.h"
+#include "imgIEncoder.h"
 
 class nsXULElement;
 
 namespace mozilla {
 namespace gfx {
 class SourceSurface;
 }
 
@@ -455,16 +456,18 @@ public:
       mDSPathBuilder->BezierTo(transform * aCP1,
                                 transform * aCP2,
                                 transform * aCP3);
     }
   }
 
   friend class CanvasRenderingContext2DUserData;
 
+  virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
+
 protected:
   nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
                              uint32_t aWidth, uint32_t aHeight,
                              JSObject** aRetval);
 
   nsresult PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
                                  unsigned char *aData, uint32_t aDataLen,
                                  bool hasDirtyRect, int32_t dirtyX, int32_t dirtyY,
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/ImageEncoder.cpp
@@ -0,0 +1,330 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxImageSurface.h"
+#include "ImageEncoder.h"
+#include "mozilla/dom/CanvasRenderingContext2D.h"
+
+namespace mozilla {
+namespace dom {
+
+class EncodingCompleteEvent : public nsRunnable
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  EncodingCompleteEvent(nsIScriptContext* aScriptContext,
+                        nsIThread* aEncoderThread,
+                        FileCallback& aCallback)
+    : mImgSize(0)
+    , mType()
+    , mImgData(nullptr)
+    , mScriptContext(aScriptContext)
+    , mEncoderThread(aEncoderThread)
+    , mCallback(&aCallback)
+    , mFailed(false)
+  {}
+  virtual ~EncodingCompleteEvent() {}
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    mozilla::ErrorResult rv;
+
+    if (!mFailed) {
+      nsRefPtr<nsDOMMemoryFile> blob =
+        new nsDOMMemoryFile(mImgData, mImgSize, mType);
+
+      if (mScriptContext) {
+        JSContext* jsContext = mScriptContext->GetNativeContext();
+        if (jsContext) {
+          JS_updateMallocCounter(jsContext, mImgSize);
+        }
+      }
+
+      mCallback->Call(blob, rv);
+    }
+
+    // These members aren't thread-safe. We're making sure that they're being
+    // released on the main thread here. Otherwise, they could be getting
+    // released by EncodingRunnable's destructor on the encoding thread
+    // (bug 916128).
+    mScriptContext = nullptr;
+    mCallback = nullptr;
+
+    mEncoderThread->Shutdown();
+    return rv.ErrorCode();
+  }
+
+  void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
+  {
+    mImgData = aImgData;
+    mImgSize = aImgSize;
+    mType = aType;
+  }
+
+  void SetFailed()
+  {
+    mFailed = true;
+  }
+
+private:
+  uint64_t mImgSize;
+  nsAutoString mType;
+  void* mImgData;
+  nsCOMPtr<nsIScriptContext> mScriptContext;
+  nsCOMPtr<nsIThread> mEncoderThread;
+  nsRefPtr<FileCallback> mCallback;
+  bool mFailed;
+};
+
+NS_IMPL_ISUPPORTS1(EncodingCompleteEvent, nsIRunnable);
+
+class EncodingRunnable : public nsRunnable
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  EncodingRunnable(const nsAString& aType,
+                   const nsAString& aOptions,
+                   uint8_t* aImageBuffer,
+                   imgIEncoder* aEncoder,
+                   EncodingCompleteEvent* aEncodingCompleteEvent,
+                   int32_t aFormat,
+                   const nsIntSize aSize,
+                   bool aUsingCustomOptions)
+    : mType(aType)
+    , mOptions(aOptions)
+    , mImageBuffer(aImageBuffer)
+    , mEncoder(aEncoder)
+    , mEncodingCompleteEvent(aEncodingCompleteEvent)
+    , mFormat(aFormat)
+    , mSize(aSize)
+    , mUsingCustomOptions(aUsingCustomOptions)
+  {}
+  virtual ~EncodingRunnable() {}
+
+  nsresult ProcessImageData(uint64_t* aImgSize, void** aImgData)
+  {
+    nsCOMPtr<nsIInputStream> stream;
+    nsresult rv = ImageEncoder::ExtractDataInternal(mType,
+                                                    mOptions,
+                                                    mImageBuffer,
+                                                    mFormat,
+                                                    mSize,
+                                                    nullptr,
+                                                    getter_AddRefs(stream),
+                                                    mEncoder);
+
+    // If there are unrecognized custom parse options, we should fall back to
+    // the default values for the encoder without any options at all.
+    if (rv == NS_ERROR_INVALID_ARG && mUsingCustomOptions) {
+      rv = ImageEncoder::ExtractDataInternal(mType,
+                                             EmptyString(),
+                                             mImageBuffer,
+                                             mFormat,
+                                             mSize,
+                                             nullptr,
+                                             getter_AddRefs(stream),
+                                             mEncoder);
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = stream->Available(aImgSize);
+    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_TRUE(*aImgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
+
+    rv = NS_ReadInputStreamToBuffer(stream, aImgData, *aImgSize);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return rv;
+  }
+
+  NS_IMETHOD Run()
+  {
+    uint64_t imgSize;
+    void* imgData = nullptr;
+
+    nsresult rv = ProcessImageData(&imgSize, &imgData);
+    if (NS_FAILED(rv)) {
+      mEncodingCompleteEvent->SetFailed();
+    } else {
+      mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
+    }
+    rv = NS_DispatchToMainThread(mEncodingCompleteEvent, NS_DISPATCH_NORMAL);
+    if (NS_FAILED(rv)) {
+      // Better to leak than to crash.
+      mEncodingCompleteEvent.forget();
+      return rv;
+    }
+
+    return rv;
+  }
+
+private:
+  nsAutoString mType;
+  nsAutoString mOptions;
+  nsAutoArrayPtr<uint8_t> mImageBuffer;
+  nsCOMPtr<imgIEncoder> mEncoder;
+  nsRefPtr<EncodingCompleteEvent> mEncodingCompleteEvent;
+  int32_t mFormat;
+  const nsIntSize mSize;
+  bool mUsingCustomOptions;
+};
+
+NS_IMPL_ISUPPORTS1(EncodingRunnable, nsIRunnable)
+
+/* static */
+nsresult
+ImageEncoder::ExtractData(nsAString& aType,
+                          const nsAString& aOptions,
+                          const nsIntSize aSize,
+                          nsICanvasRenderingContextInternal* aContext,
+                          nsIInputStream** aStream)
+{
+  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
+  if (!encoder) {
+    return NS_IMAGELIB_ERROR_NO_ENCODER;
+  }
+
+  return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, aContext,
+                             aStream, encoder);
+}
+
+/* static */
+nsresult
+ImageEncoder::ExtractDataAsync(nsAString& aType,
+                               const nsAString& aOptions,
+                               bool aUsingCustomOptions,
+                               uint8_t* aImageBuffer,
+                               int32_t aFormat,
+                               const nsIntSize aSize,
+                               nsICanvasRenderingContextInternal* aContext,
+                               nsIScriptContext* aScriptContext,
+                               FileCallback& aCallback)
+{
+  nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
+  if (!encoder) {
+    return NS_IMAGELIB_ERROR_NO_ENCODER;
+  }
+
+  nsCOMPtr<nsIThread> encoderThread;
+  nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<EncodingCompleteEvent> completeEvent =
+    new EncodingCompleteEvent(aScriptContext, encoderThread, aCallback);
+
+  nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
+                                                     aOptions,
+                                                     aImageBuffer,
+                                                     encoder,
+                                                     completeEvent,
+                                                     aFormat,
+                                                     aSize,
+                                                     aUsingCustomOptions);
+  return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
+}
+
+/*static*/ nsresult
+ImageEncoder::GetInputStream(int32_t aWidth,
+                             int32_t aHeight,
+                             uint8_t* aImageBuffer,
+                             int32_t aFormat,
+                             imgIEncoder* aEncoder,
+                             const PRUnichar* aEncoderOptions,
+                             nsIInputStream** aStream)
+{
+  nsresult rv =
+    aEncoder->InitFromData(aImageBuffer,
+                           aWidth * aHeight * 4, aWidth, aHeight, aWidth * 4,
+                           aFormat,
+                           nsDependentString(aEncoderOptions));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return CallQueryInterface(aEncoder, aStream);
+}
+
+/* static */
+nsresult
+ImageEncoder::ExtractDataInternal(const nsAString& aType,
+                                  const nsAString& aOptions,
+                                  uint8_t* aImageBuffer,
+                                  int32_t aFormat,
+                                  const nsIntSize aSize,
+                                  nsICanvasRenderingContextInternal* aContext,
+                                  nsIInputStream** aStream,
+                                  imgIEncoder* aEncoder)
+{
+  nsCOMPtr<nsIInputStream> imgStream;
+
+  // get image bytes
+  nsresult rv;
+  if (aImageBuffer) {
+    rv = ImageEncoder::GetInputStream(
+      aSize.width,
+      aSize.height,
+      aImageBuffer,
+      aFormat,
+      aEncoder,
+      nsPromiseFlatString(aOptions).get(),
+      getter_AddRefs(imgStream));
+  } else if (aContext) {
+    NS_ConvertUTF16toUTF8 encoderType(aType);
+    rv = aContext->GetInputStream(encoderType.get(),
+                                  nsPromiseFlatString(aOptions).get(),
+                                  getter_AddRefs(imgStream));
+  } else {
+    // no context, so we have to encode an empty image
+    // note that if we didn't have a current context, the spec says we're
+    // supposed to just return transparent black pixels of the canvas
+    // dimensions.
+    nsRefPtr<gfxImageSurface> emptyCanvas =
+      new gfxImageSurface(gfxIntSize(aSize.width, aSize.height),
+                          gfxImageFormatARGB32);
+    if (emptyCanvas->CairoStatus()) {
+      return NS_ERROR_INVALID_ARG;
+    }
+    rv = aEncoder->InitFromData(emptyCanvas->Data(),
+                                aSize.width * aSize.height * 4,
+                                aSize.width,
+                                aSize.height,
+                                aSize.width * 4,
+                                imgIEncoder::INPUT_FORMAT_HOSTARGB,
+                                aOptions);
+    if (NS_SUCCEEDED(rv)) {
+      imgStream = do_QueryInterface(aEncoder);
+    }
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  imgStream.forget(aStream);
+  return rv;
+}
+
+/* static */
+already_AddRefed<imgIEncoder>
+ImageEncoder::GetImageEncoder(nsAString& aType)
+{
+  // Get an image encoder for the media type.
+  nsCString encoderCID("@mozilla.org/image/encoder;2?type=");
+  NS_ConvertUTF16toUTF8 encoderType(aType);
+  encoderCID += encoderType;
+  nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(encoderCID.get());
+
+  if (!encoder && aType != NS_LITERAL_STRING("image/png")) {
+    // Unable to create an encoder instance of the specified type. Falling back
+    // to PNG.
+    aType.AssignLiteral("image/png");
+    nsCString PNGEncoderCID("@mozilla.org/image/encoder;2?type=image/png");
+    encoder = do_CreateInstance(PNGEncoderCID.get());
+  }
+
+  return encoder.forget();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/ImageEncoder.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ImageEncoder_h
+#define ImageEncoder_h
+
+#include "imgIEncoder.h"
+#include "nsDOMFile.h"
+#include "nsError.h"
+#include "mozilla/dom/HTMLCanvasElementBinding.h"
+#include "nsLayoutUtils.h"
+#include "nsNetUtil.h"
+#include "nsSize.h"
+
+class nsICanvasRenderingContextInternal;
+
+namespace mozilla {
+namespace dom {
+
+class EncodingRunnable;
+
+class ImageEncoder
+{
+public:
+  // Extracts data synchronously and gives you a stream containing the image
+  // represented by aContext. aType may change to "image/png" if we had to fall
+  // back to a PNG encoder. A return value of NS_OK implies successful data
+  // extraction. If there are any unrecognized custom parse options in
+  // aOptions, NS_ERROR_INVALID_ARG will be returned. When encountering this
+  // error it is usual to call this function again without any options at all.
+  static nsresult ExtractData(nsAString& aType,
+                              const nsAString& aOptions,
+                              const nsIntSize aSize,
+                              nsICanvasRenderingContextInternal* aContext,
+                              nsIInputStream** aStream);
+
+  // Extracts data asynchronously. aType may change to "image/png" if we had to
+  // fall back to a PNG encoder. aOptions are the options to be passed to the
+  // encoder and aUsingCustomOptions specifies whether custom parse options were
+  // used (i.e. by using -moz-parse-options). If there are any unrecognized
+  // custom parse options, we fall back to the default values for the encoder
+  // without any options at all. A return value of NS_OK only implies
+  // successful dispatching of the extraction step to the encoding thread.
+  static nsresult ExtractDataAsync(nsAString& aType,
+                                   const nsAString& aOptions,
+                                   bool aUsingCustomOptions,
+                                   uint8_t* aImageBuffer,
+                                   int32_t aFormat,
+                                   const nsIntSize aSize,
+                                   nsICanvasRenderingContextInternal* aContext,
+                                   nsIScriptContext* aScriptContext,
+                                   FileCallback& aCallback);
+
+  // Gives you a stream containing the image represented by aImageBuffer.
+  // The format is given in aFormat, for example
+  // imgIEncoder::INPUT_FORMAT_HOSTARGB.
+  static nsresult GetInputStream(int32_t aWidth,
+                                 int32_t aHeight,
+                                 uint8_t* aImageBuffer,
+                                 int32_t aFormat,
+                                 imgIEncoder* aEncoder,
+                                 const PRUnichar* aEncoderOptions,
+                                 nsIInputStream** aStream);
+
+private:
+  // When called asynchronously, aContext is null.
+  static nsresult
+  ExtractDataInternal(const nsAString& aType,
+                      const nsAString& aOptions,
+                      uint8_t* aImageBuffer,
+                      int32_t aFormat,
+                      const nsIntSize aSize,
+                      nsICanvasRenderingContextInternal* aContext,
+                      nsIInputStream** aStream,
+                      imgIEncoder* aEncoder);
+
+  // Creates and returns an encoder instance of the type specified in aType.
+  // aType may change to "image/png" if no instance of the original type could
+  // be created and we had to fall back to a PNG encoder. A return value of
+  // NULL should be interpreted as NS_IMAGELIB_ERROR_NO_ENCODER and aType is
+  // undefined in this case.
+  static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
+
+  friend class EncodingRunnable;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // ImageEncoder_h
\ No newline at end of file
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -17,10 +17,11 @@ CXXFLAGS	+= $(MOZ_CAIRO_CFLAGS) $(MOZ_PI
 INCLUDES	+= \
 		-I$(srcdir)/../../../layout/xul/base/src \
 		-I$(srcdir)/../../../layout/style \
 		-I$(srcdir)/../../../layout/generic \
 		-I$(srcdir)/../../base/src \
 		-I$(srcdir)/../../html/content/src \
 		-I$(srcdir)/../../../js/xpconnect/src \
 		-I$(srcdir)/../../../dom/base \
+		-I$(srcdir)/../../../image/src \
 		-I$(topsrcdir)/content/xul/content/src \
 		$(NULL)
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -22,17 +22,17 @@
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsError.h"
 #include "nsIGfxInfo.h"
 #include "nsIWidget.h"
 
 #include "nsIVariant.h"
 
-#include "imgIEncoder.h"
+#include "ImageEncoder.h"
 
 #include "gfxContext.h"
 #include "gfxPattern.h"
 #include "gfxUtils.h"
 
 #include "CanvasUtils.h"
 #include "nsDisplayList.h"
 
@@ -376,18 +376,20 @@ WebGLContext::SetDimensions(int32_t widt
     // Early success return cases
 
     GetCanvas()->InvalidateCanvas();
 
     if (gl && mWidth == width && mHeight == height)
         return NS_OK;
 
     // Zero-sized surfaces can cause problems.
-    if (width == 0 || height == 0) {
+    if (width == 0) {
         width = 1;
+    }
+    if (height == 0) {
         height = 1;
     }
 
     // If we already have a gl context, then we just need to resize it
     if (gl) {
         MakeContextCurrent();
 
         gl->ResizeOffscreen(gfxIntSize(width, height)); // Doesn't matter if it succeeds (soft-fail)
@@ -728,67 +730,89 @@ void WebGLContext::LoseOldestWebGLContex
     } else if (numContexts > kMaxWebGLContexts) {
         GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
                         kMaxWebGLContexts);
         MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
         const_cast<WebGLContext*>(oldestContext)->LoseContext();
     }
 }
 
+void
+WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
+{
+    *aImageBuffer = nullptr;
+    *aFormat = 0;
+
+    nsRefPtr<gfxImageSurface> imgsurf =
+        new gfxImageSurface(gfxIntSize(mWidth, mHeight),
+                            gfxImageFormatARGB32);
+
+    if (!imgsurf || imgsurf->CairoStatus()) {
+        return;
+    }
+
+    nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
+    if (!ctx || ctx->HasError()) {
+        return;
+    }
+
+    // Use Render() to make sure that appropriate y-flip gets applied
+    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
+    nsresult rv = Render(ctx, GraphicsFilter::FILTER_NEAREST, flags);
+    if (NS_FAILED(rv)) {
+        return;
+    }
+
+    int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
+    if (!mOptions.premultipliedAlpha) {
+        // We need to convert to INPUT_FORMAT_RGBA, otherwise
+        // we are automatically considered premult, and unpremult'd.
+        // Yes, it is THAT silly.
+        // Except for different lossy conversions by color,
+        // we could probably just change the label, and not change the data.
+        gfxUtils::ConvertBGRAtoRGBA(imgsurf);
+        format = imgIEncoder::INPUT_FORMAT_RGBA;
+    }
+
+    static const fallible_t fallible = fallible_t();
+    uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
+    if (!imageBuffer) {
+        return;
+    }
+    memcpy(imageBuffer, imgsurf->Data(), mWidth * mHeight * 4);
+
+    *aImageBuffer = imageBuffer;
+    *aFormat = format;
+}
+
 NS_IMETHODIMP
 WebGLContext::GetInputStream(const char* aMimeType,
                              const PRUnichar* aEncoderOptions,
                              nsIInputStream **aStream)
 {
     NS_ASSERTION(gl, "GetInputStream on invalid context?");
     if (!gl)
         return NS_ERROR_FAILURE;
 
-    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
-                                                         gfxImageFormatARGB32);
-    if (surf->CairoStatus() != 0)
+    uint8_t* imageBuffer = nullptr;
+    int32_t format = 0;
+    GetImageBuffer(&imageBuffer, &format);
+    if (!imageBuffer) {
         return NS_ERROR_FAILURE;
-
-    nsRefPtr<gfxContext> tmpcx = new gfxContext(surf);
-    // Use Render() to make sure that appropriate y-flip gets applied
-    uint32_t flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
-    nsresult rv = Render(tmpcx, GraphicsFilter::FILTER_NEAREST, flags);
-    if (NS_FAILED(rv))
-        return rv;
-
-    const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
-    nsAutoArrayPtr<char> conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
-
-    strcpy(conid, encoderPrefix);
-    strcat(conid, aMimeType);
-
-    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
-    if (!encoder)
-        return NS_ERROR_FAILURE;
-
-    int format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
-    if (!mOptions.premultipliedAlpha) {
-        // We need to convert to INPUT_FORMAT_RGBA, otherwise
-        // we are automatically considered premult, and unpremult'd.
-        // Yes, it is THAT silly.
-        // Except for different lossy conversions by color,
-        // we could probably just change the label, and not change the data.
-        gfxUtils::ConvertBGRAtoRGBA(surf);
-        format = imgIEncoder::INPUT_FORMAT_RGBA;
     }
 
-    rv = encoder->InitFromData(surf->Data(),
-                               mWidth * mHeight * 4,
-                               mWidth, mHeight,
-                               surf->Stride(),
-                               format,
-                               nsDependentString(aEncoderOptions));
-    NS_ENSURE_SUCCESS(rv, rv);
+    nsCString enccid("@mozilla.org/image/encoder;2?type=");
+    enccid += aMimeType;
+    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
+    if (!encoder) {
+        return NS_ERROR_FAILURE;
+    }
 
-    return CallQueryInterface(encoder, aStream);
+    return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
+                                        encoder, aEncoderOptions, aStream);
 }
 
 NS_IMETHODIMP
 WebGLContext::GetThebesSurface(gfxASurface **surface)
 {
     return NS_ERROR_NOT_AVAILABLE;
 }
 
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -162,16 +162,17 @@ public:
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) MOZ_OVERRIDE;
     NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, int32_t width, int32_t height) MOZ_OVERRIDE
         { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Reset() MOZ_OVERRIDE
         { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Render(gfxContext *ctx,
                       GraphicsFilter f,
                       uint32_t aFlags = RenderFlagPremultAlpha) MOZ_OVERRIDE;
+    virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream) MOZ_OVERRIDE;
     NS_IMETHOD GetThebesSurface(gfxASurface **surface) MOZ_OVERRIDE;
     mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot() MOZ_OVERRIDE;
 
     NS_IMETHOD SetIsOpaque(bool b) MOZ_OVERRIDE { return NS_OK; };
     NS_IMETHOD SetContextOptions(JSContext* aCx,
--- a/content/canvas/src/moz.build
+++ b/content/canvas/src/moz.build
@@ -17,16 +17,17 @@ EXPORTS.mozilla.dom += [
 
 CPP_SOURCES += [
     'CanvasImageCache.cpp',
     'CanvasRenderingContext2D.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',
     'DocumentRendererParent.cpp',
     'ImageData.cpp',
+    'ImageEncoder.cpp',
 ]
 
 if CONFIG['MOZ_WEBGL']:
     CPP_SOURCES += [
         'WebGLActiveInfo.cpp',
         'WebGLBuffer.cpp',
         'WebGL1Context.cpp',
         'WebGL2Context.cpp',
--- a/content/canvas/test/test_mozGetAsFile.html
+++ b/content/canvas/test/test_mozGetAsFile.html
@@ -2,49 +2,50 @@
 <title>Canvas test: mozGetAsFile</title>
 <script src="/MochiKit/MochiKit.js"></script>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <body>
 <canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
-var gCompares = 0;
-
-function compareAsync(file, canvas, type)
+function compareAsync(file, canvas, type, callback)
 {
-  ++gCompares;
-
   var reader = new FileReader();
   reader.onload = 
     function(e) {
       is(e.target.result, canvas.toDataURL(type),
  "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
-      if (--gCompares == 0) {
-        SimpleTest.finish();
-      }
+      callback(canvas);
     };
   reader.readAsDataURL(file);
 }
 
+function test1(canvas)
+{
+  var pngfile = canvas.mozGetAsFile("foo.png");
+  is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
+  compareAsync(pngfile, canvas, "image/png", test2);
+  is(pngfile.name, "foo.png", "File name should be what we passed in");
+}
+
+function test2(canvas)
+{
+  var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
+  is(jpegfile.type, "image/jpeg",
+     "When a valid type is specified that should be returned");
+  compareAsync(jpegfile, canvas, "image/jpeg", SimpleTest.finish);
+  is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
+}
+
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function () {
 
 var canvas = document.getElementById('c');
 var ctx = canvas.getContext('2d');
-
 ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
 
-var pngfile = canvas.mozGetAsFile("foo.png");
-is(pngfile.type, "image/png", "Default type for mozGetAsFile should be PNG");
-compareAsync(pngfile, canvas, "image/png");
-is(pngfile.name, "foo.png", "File name should be what we passed in");
-
-var jpegfile = canvas.mozGetAsFile("bar.jpg", "image/jpeg");
-is(jpegfile.type, "image/jpeg",
-   "When a valid type is specified that should be returned");
-compareAsync(jpegfile, canvas, "image/jpeg");
-is(jpegfile.name, "bar.jpg", "File name should be what we passed in");
+test1(canvas);
 
 });
 </script>
 <img src="image_yellow75.png" id="yellow75.png" class="resource">
 
--- a/content/canvas/test/test_toBlob.html
+++ b/content/canvas/test/test_toBlob.html
@@ -1,42 +1,48 @@
 <!DOCTYPE HTML>
-<title>Canvas test: mozGetAsFile</title>
+<title>Canvas test: toBlob</title>
 <script src="/MochiKit/MochiKit.js"></script>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" href="/tests/SimpleTest/test.css">
 <body>
 <canvas id="c" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
 
-var gCompares = 2;
-
-function BlobListener(type, canvas, file)
+function BlobListener(type, canvas, callback, file)
 {
   is(file.type, type,
      "When a valid type is specified that should be returned");
   var reader = new FileReader();
-  reader.onload = 
+  reader.onload =
     function(e) {
       is(e.target.result, canvas.toDataURL(type),
- "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
-      if (--gCompares == 0) {
-        SimpleTest.finish();
-      }
+  "<canvas>.mozGetAsFile().getAsDataURL() should equal <canvas>.toDataURL()");
+      callback(canvas);
     };
   reader.readAsDataURL(file);
 }
 
+function test1(canvas)
+{
+  canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas, test2));
+}
+
+function test2(canvas)
+{
+  canvas.toBlob(
+    BlobListener.bind(undefined, "image/jpeg", canvas, SimpleTest.finish),
+    "image/jpeg");
+}
+
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function () {
 
 var canvas = document.getElementById('c');
 var ctx = canvas.getContext('2d');
-
 ctx.drawImage(document.getElementById('yellow75.png'), 0, 0);
 
-canvas.toBlob(BlobListener.bind(undefined, "image/png", canvas));
-canvas.toBlob(BlobListener.bind(undefined, "image/jpeg", canvas), "image/jpeg");
+test1(canvas);
 
 });
 </script>
 <img src="image_yellow75.png" id="yellow75.png" class="resource">
 
--- a/content/html/content/public/HTMLCanvasElement.h
+++ b/content/html/content/public/HTMLCanvasElement.h
@@ -222,20 +222,19 @@ protected:
   nsIntSize GetWidthHeight();
 
   nsresult UpdateContext(JSContext* aCx, JS::Handle<JS::Value> options);
   nsresult ParseParams(JSContext* aCx,
                        const nsAString& aType,
                        const JS::Value& aEncoderOptions,
                        nsAString& aParams,
                        bool* usingCustomParseOptions);
-  nsresult ExtractData(const nsAString& aType,
+  nsresult ExtractData(nsAString& aType,
                        const nsAString& aOptions,
-                       nsIInputStream** aStream,
-                       bool& aFellBackToPNG);
+                       nsIInputStream** aStream);
   nsresult ToDataURLImpl(JSContext* aCx,
                          const nsAString& aMimeType,
                          const JS::Value& aEncoderOptions,
                          nsAString& aDataURL);
   nsresult MozGetAsFileImpl(const nsAString& aName,
                             const nsAString& aType,
                             nsIDOMFile** aResult);
   nsresult GetContextHelper(const nsAString& aContextId,
--- a/content/html/content/src/HTMLCanvasElement.cpp
+++ b/content/html/content/src/HTMLCanvasElement.cpp
@@ -1,33 +1,34 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/HTMLCanvasElement.h"
 
-#include "Layers.h"
-#include "imgIEncoder.h"
+#include "ImageEncoder.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "gfxImageSurface.h"
+#include "Layers.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/dom/HTMLCanvasElementBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "nsAsyncDOMEvent.h"
 #include "nsAttrValueInlines.h"
 #include "nsContentUtils.h"
 #include "nsDisplayList.h"
 #include "nsDOMFile.h"
+#include "nsDOMJSUtils.h"
 #include "nsFrameManager.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsITimer.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
 #include "nsMathUtils.h"
 #include "nsNetUtil.h"
@@ -41,39 +42,16 @@ using namespace mozilla::layers;
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
 
 namespace {
 
 typedef mozilla::dom::HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement
 HTMLImageOrCanvasOrVideoElement;
 
-class ToBlobRunnable : public nsRunnable
-{
-public:
-  ToBlobRunnable(mozilla::dom::FileCallback& aCallback,
-                 nsIDOMBlob* aBlob)
-    : mCallback(&aCallback),
-      mBlob(aBlob)
-  {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-  }
-
-  NS_IMETHOD Run()
-  {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-    mozilla::ErrorResult rv;
-    mCallback->Call(mBlob, rv);
-    return rv.ErrorCode();
-  }
-private:
-  nsRefPtr<mozilla::dom::FileCallback> mCallback;
-  nsCOMPtr<nsIDOMBlob> mBlob;
-};
-
 } // anonymous namespace
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_3(HTMLCanvasPrintState, mCanvas,
                                         mContext, mCallback)
 
@@ -364,20 +342,20 @@ HTMLCanvasElement::ToDataURL(const nsASt
 NS_IMETHODIMP
 HTMLCanvasElement::MozFetchAsStream(nsIInputStreamCallback *aCallback,
                                     const nsAString& aType)
 {
   if (!nsContentUtils::IsCallerChrome())
     return NS_ERROR_FAILURE;
 
   nsresult rv;
-  bool fellBackToPNG = false;
   nsCOMPtr<nsIInputStream> inputData;
 
-  rv = ExtractData(aType, EmptyString(), getter_AddRefs(inputData), fellBackToPNG);
+  nsAutoString type(aType);
+  rv = ExtractData(type, EmptyString(), getter_AddRefs(inputData));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIAsyncInputStream> asyncData = do_QueryInterface(inputData, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIThread> mainThread;
   rv = NS_GetMainThread(getter_AddRefs(mainThread));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -399,78 +377,25 @@ HTMLCanvasElement::GetMozPrintCallback()
 {
   if (mOriginalCanvas) {
     return mOriginalCanvas->GetMozPrintCallback();
   }
   return mPrintCallback;
 }
 
 nsresult
-HTMLCanvasElement::ExtractData(const nsAString& aType,
+HTMLCanvasElement::ExtractData(nsAString& aType,
                                const nsAString& aOptions,
-                               nsIInputStream** aStream,
-                               bool& aFellBackToPNG)
+                               nsIInputStream** aStream)
 {
-  // note that if we don't have a current context, the spec says we're
-  // supposed to just return transparent black pixels of the canvas
-  // dimensions.
-  nsRefPtr<gfxImageSurface> emptyCanvas;
-  nsIntSize size = GetWidthHeight();
-  if (!mCurrentContext) {
-    emptyCanvas = new gfxImageSurface(gfxIntSize(size.width, size.height), gfxImageFormatARGB32);
-    if (emptyCanvas->CairoStatus()) {
-      return NS_ERROR_INVALID_ARG;
-    }
-  }
-
-  nsresult rv;
-
-  // get image bytes
-  nsCOMPtr<nsIInputStream> imgStream;
-  NS_ConvertUTF16toUTF8 encoderType(aType);
-
- try_again:
-  if (mCurrentContext) {
-    rv = mCurrentContext->GetInputStream(encoderType.get(),
-                                         nsPromiseFlatString(aOptions).get(),
-                                         getter_AddRefs(imgStream));
-  } else {
-    // no context, so we have to encode the empty image we created above
-    nsCString enccid("@mozilla.org/image/encoder;2?type=");
-    enccid += encoderType;
-
-    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get(), &rv);
-    if (NS_SUCCEEDED(rv) && encoder) {
-      rv = encoder->InitFromData(emptyCanvas->Data(),
-                                 size.width * size.height * 4,
-                                 size.width,
-                                 size.height,
-                                 size.width * 4,
-                                 imgIEncoder::INPUT_FORMAT_HOSTARGB,
-                                 aOptions);
-      if (NS_SUCCEEDED(rv)) {
-        imgStream = do_QueryInterface(encoder);
-      }
-    } else {
-      rv = NS_ERROR_FAILURE;
-    }
-  }
-
-  if (NS_FAILED(rv) && !aFellBackToPNG) {
-    // Try image/png instead.
-    // XXX ERRMSG we need to report an error to developers here! (bug 329026)
-    aFellBackToPNG = true;
-    encoderType.AssignLiteral("image/png");
-    goto try_again;
-  }
-
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  imgStream.forget(aStream);
-  return NS_OK;
+  return ImageEncoder::ExtractData(aType,
+                                   aOptions,
+                                   GetSize(),
+                                   mCurrentContext,
+                                   aStream);
 }
 
 nsresult
 HTMLCanvasElement::ParseParams(JSContext* aCx,
                                const nsAString& aType,
                                const JS::Value& aEncoderOptions,
                                nsAString& aParams,
                                bool* usingCustomParseOptions)
@@ -511,18 +436,16 @@ HTMLCanvasElement::ParseParams(JSContext
 }
 
 nsresult
 HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
                                  const nsAString& aMimeType,
                                  const JS::Value& aEncoderOptions,
                                  nsAString& aDataURL)
 {
-  bool fallbackToPNG = false;
-
   nsIntSize size = GetWidthHeight();
   if (size.height == 0 || size.width == 0) {
     aDataURL = NS_LITERAL_STRING("data:,");
     return NS_OK;
   }
 
   nsAutoString type;
   nsresult rv = nsContentUtils::ASCIIToLower(aMimeType, type);
@@ -533,43 +456,37 @@ HTMLCanvasElement::ToDataURLImpl(JSConte
   nsAutoString params;
   bool usingCustomParseOptions;
   rv = ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsCOMPtr<nsIInputStream> stream;
-  rv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
+  rv = ExtractData(type, params, getter_AddRefs(stream));
 
   // If there are unrecognized custom parse options, we should fall back to
   // the default values for the encoder without any options at all.
   if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
-    fallbackToPNG = false;
-    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
+    rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // build data URL string
-  if (fallbackToPNG)
-    aDataURL = NS_LITERAL_STRING("data:image/png;base64,");
-  else
-    aDataURL = NS_LITERAL_STRING("data:") + type +
-      NS_LITERAL_STRING(";base64,");
+  aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
 
   uint64_t count;
   rv = stream->Available(&count);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(count <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
 
   return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
 }
 
-// XXXkhuey the encoding should be off the main thread, but we're lazy.
 void
 HTMLCanvasElement::ToBlob(JSContext* aCx,
                           FileCallback& aCallback,
                           const nsAString& aType,
                           const Optional<JS::Handle<JS::Value> >& aParams,
                           ErrorResult& aRv)
 {
   // do a trust check if this is a write-only canvas
@@ -592,68 +509,45 @@ HTMLCanvasElement::ToBlob(JSContext* aCx
   bool usingCustomParseOptions;
   aRv = ParseParams(aCx, type, encoderOptions, params, &usingCustomParseOptions);
   if (aRv.Failed()) {
     return;
   }
 
 #ifdef DEBUG
   if (mCurrentContext) {
+    // We disallow canvases of width or height zero, and set them to 1, so
+    // we will have a discrepancy with the sizes of the canvas and the context.
+    // That discrepancy is OK, the rest are not.
     nsIntSize elementSize = GetWidthHeight();
-    MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth());
-    MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight());
+    MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth() ||
+               (elementSize.width == 0 && mCurrentContext->GetWidth() == 1));
+    MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight() ||
+               (elementSize.height == 0 && mCurrentContext->GetHeight() == 1));
   }
 #endif
 
-  bool fallbackToPNG = false;
+  nsCOMPtr<nsIScriptContext> scriptContext =
+    GetScriptContextFromJSContext(nsContentUtils::GetCurrentJSContext());
 
-  nsCOMPtr<nsIInputStream> stream;
-  aRv = ExtractData(type, params, getter_AddRefs(stream), fallbackToPNG);
-  // If there are unrecognized custom parse options, we should fall back to
-  // the default values for the encoder without any options at all.
-  if (aRv.ErrorCode() == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
-    fallbackToPNG = false;
-    aRv = ExtractData(type, EmptyString(), getter_AddRefs(stream), fallbackToPNG);
-  }
-
-  if (aRv.Failed()) {
-    return;
-  }
-
-  if (fallbackToPNG) {
-    type.AssignLiteral("image/png");
+  uint8_t* imageBuffer = nullptr;
+  int32_t format = 0;
+  if (mCurrentContext) {
+    mCurrentContext->GetImageBuffer(&imageBuffer, &format);
   }
 
-  uint64_t imgSize;
-  aRv = stream->Available(&imgSize);
-  if (aRv.Failed()) {
-    return;
-  }
-  if (imgSize > UINT32_MAX) {
-    aRv.Throw(NS_ERROR_FILE_TOO_BIG);
-    return;
-  }
-
-  void* imgData = nullptr;
-  aRv = NS_ReadInputStreamToBuffer(stream, &imgData, imgSize);
-  if (aRv.Failed()) {
-    return;
-  }
-
-  // The DOMFile takes ownership of the buffer
-  nsRefPtr<nsDOMMemoryFile> blob =
-    new nsDOMMemoryFile(imgData, imgSize, type);
-
-  JSContext* cx = nsContentUtils::GetCurrentJSContext();
-  if (cx) {
-    JS_updateMallocCounter(cx, imgSize);
-  }
-
-  nsRefPtr<ToBlobRunnable> runnable = new ToBlobRunnable(aCallback, blob);
-  aRv = NS_DispatchToCurrentThread(runnable);
+  aRv = ImageEncoder::ExtractDataAsync(type,
+                                       params,
+                                       usingCustomParseOptions,
+                                       imageBuffer,
+                                       format,
+                                       GetSize(),
+                                       mCurrentContext,
+                                       scriptContext,
+                                       aCallback);
 }
 
 already_AddRefed<nsIDOMFile>
 HTMLCanvasElement::MozGetAsFile(const nsAString& aName,
                                 const nsAString& aType,
                                 ErrorResult& aRv)
 {
   nsCOMPtr<nsIDOMFile> file;
@@ -677,28 +571,21 @@ HTMLCanvasElement::MozGetAsFile(const ns
   return MozGetAsFileImpl(aName, aType, aResult);
 }
 
 nsresult
 HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
                                     const nsAString& aType,
                                     nsIDOMFile** aResult)
 {
-  bool fallbackToPNG = false;
-
   nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = ExtractData(aType, EmptyString(), getter_AddRefs(stream),
-                            fallbackToPNG);
+  nsAutoString type(aType);
+  nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsAutoString type(aType);
-  if (fallbackToPNG) {
-    type.AssignLiteral("image/png");
-  }
-
   uint64_t imgSize;
   rv = stream->Available(&imgSize);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
 
   void* imgData = nullptr;
   rv = NS_ReadInputStreamToBuffer(stream, &imgData, (uint32_t)imgSize);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -17,12 +17,13 @@ INCLUDES	+= \
 		-I$(srcdir)/../../../../layout/tables \
 		-I$(srcdir)/../../../../layout/xul/base/src \
 		-I$(srcdir)/../../../../layout/generic \
 		-I$(srcdir)/../../../../dom/base \
 		-I$(srcdir)/../../../../editor/libeditor/base \
 		-I$(srcdir)/../../../../editor/libeditor/text \
 		-I$(srcdir)/../../../../editor/txmgr/src \
 		-I$(srcdir)/../../../../netwerk/base/src \
+		-I$(srcdir)/../../../../content/canvas/src \
 		-I$(srcdir) \
 		-I$(topsrcdir)/xpcom/ds \
 		-I$(topsrcdir)/content/media/ \
 		$(NULL)
--- a/content/xslt/src/xslt/txExecutionState.cpp
+++ b/content/xslt/src/xslt/txExecutionState.cpp
@@ -11,41 +11,37 @@
 #include "txRtfHandler.h"
 #include "txXSLTProcessor.h"
 #include "txLog.h"
 #include "txURIUtils.h"
 #include "txXMLParser.h"
 
 const int32_t txExecutionState::kMaxRecursionDepth = 20000;
 
-nsresult txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
+void
+txLoadedDocumentsHash::init(txXPathNode* aSourceDocument)
 {
     mSourceDocument = aSourceDocument;
-    
+
     nsAutoString baseURI;
     txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
 
-    txLoadedDocumentEntry* entry = PutEntry(baseURI);
-    if (!entry) {
-        return NS_ERROR_FAILURE;
-    }
-
-    entry->mDocument = mSourceDocument;
-
-    return NS_OK;
+    PutEntry(baseURI)->mDocument = mSourceDocument;
 }
 
 txLoadedDocumentsHash::~txLoadedDocumentsHash()
 {
-    nsAutoString baseURI;
-    txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
+    if (mSourceDocument) {
+        nsAutoString baseURI;
+        txXPathNodeUtils::getBaseURI(*mSourceDocument, baseURI);
 
-    txLoadedDocumentEntry* entry = GetEntry(baseURI);
-    if (entry) {
-        delete entry->mDocument.forget();
+        txLoadedDocumentEntry* entry = GetEntry(baseURI);
+        if (entry) {
+            delete entry->mDocument.forget();
+        }
     }
 }
 
 txExecutionState::txExecutionState(txStylesheet* aStylesheet,
                                    bool aDisableLoads)
     : mOutputHandler(nullptr),
       mResultHandler(nullptr),
       mStylesheet(aStylesheet),
@@ -113,24 +109,17 @@ txExecutionState::init(const txXPathNode
         createHandlerWith(mStylesheet->getOutputFormat(), &handler);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mOutputHandler = handler;
     mResultHandler = handler;
     mOutputHandler->startDocument();
 
     // Set up loaded-documents-hash
-    nsAutoPtr<txXPathNode> document(txXPathNodeUtils::getOwnerDocument(aNode));
-    NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
-
-    rv = mLoadedDocuments.init(document);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // loaded-documents-hash owns this now
-    document.forget();
+    mLoadedDocuments.init(txXPathNodeUtils::getOwnerDocument(aNode));
 
     // Init members
     rv = mKeyHash.init();
     NS_ENSURE_SUCCESS(rv, rv);
     
     mRecycler = new txResultRecycler;
     NS_ENSURE_TRUE(mRecycler, NS_ERROR_OUT_OF_MEMORY);
     
--- a/content/xslt/src/xslt/txExecutionState.h
+++ b/content/xslt/src/xslt/txExecutionState.h
@@ -52,21 +52,22 @@ public:
     nsAutoPtr<txXPathNode> mDocument;
     nsresult mLoadResult;
 };
 
 class txLoadedDocumentsHash : public nsTHashtable<txLoadedDocumentEntry>
 {
 public:
     txLoadedDocumentsHash()
-        : nsTHashtable<txLoadedDocumentEntry>(8)
+        : nsTHashtable<txLoadedDocumentEntry>(8),
+          mSourceDocument(nullptr)
     {
     }
     ~txLoadedDocumentsHash();
-    nsresult init(txXPathNode* aSourceDocument);
+    void init(txXPathNode* aSourceDocument);
 
 private:
     friend class txExecutionState;
     txXPathNode* mSourceDocument;
 };
 
 
 class txExecutionState : public txIMatchContext
--- a/dom/bindings/Makefile.in
+++ b/dom/bindings/Makefile.in
@@ -96,34 +96,36 @@ LOCAL_INCLUDES += \
 endif
 
 ifdef MOZ_B2G_RIL
 LOCAL_INCLUDES += \
   -I$(topsrcdir)/dom/icc/src \
   $(NULL)
 endif
 
+ABS_DIST := $(abspath $(DIST))
+
 EXTRA_EXPORT_MDDEPEND_FILES := $(addsuffix .pp,$(binding_dependency_trackers))
 
 EXPORTS_GENERATED_FILES := $(exported_binding_headers) $(exported_generated_events_headers)
-EXPORTS_GENERATED_DEST := $(DIST)/include/$(binding_include_path)
+EXPORTS_GENERATED_DEST := $(ABS_DIST)/include/$(binding_include_path)
 EXPORTS_GENERATED_TARGET := export
 INSTALL_TARGETS += EXPORTS_GENERATED
 
 # Install auto-generated GlobalGen files. The rules for the install must
 # be in the same target/subtier as GlobalGen.py, otherwise the files will not
 # get installed into the appropriate location as they are generated.
 globalgen_headers_FILES := \
   GeneratedAtomList.h \
   PrototypeList.h \
   RegisterBindings.h \
   UnionConversions.h \
   UnionTypes.h \
   $(NULL)
-globalgen_headers_DEST = $(DIST)/include/mozilla/dom
+globalgen_headers_DEST = $(ABS_DIST)/include/mozilla/dom
 globalgen_headers_TARGET := export
 INSTALL_TARGETS += globalgen_headers
 
 include $(topsrcdir)/config/rules.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 ifdef GNU_CC
 CXXFLAGS += -Wno-uninitialized
--- a/dom/network/interfaces/nsIDOMNetworkStats.idl
+++ b/dom/network/interfaces/nsIDOMNetworkStats.idl
@@ -1,40 +1,39 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-interface nsIDOMMozNetworkStatsInterface;
-
 [scriptable, builtinclass, uuid(3b16fe17-5583-483a-b486-b64a3243221c)]
 interface nsIDOMMozNetworkStatsData : nsISupports
 {
   readonly attribute unsigned long   rxBytes;   // Received bytes.
   readonly attribute unsigned long   txBytes;   // Sent bytes.
   readonly attribute jsval           date;      // Date.
 };
 
-[scriptable, builtinclass, uuid(b6fc4b14-628d-4c99-bf4e-e4ed56916cbe)]
+[scriptable, builtinclass, uuid(6613ea55-b99c-44f9-91bf-d07da10b9b74)]
 interface nsIDOMMozNetworkStats : nsISupports
 {
   /**
    * Manifest URL of an application for specifying the per-app
    * stats of the specified app. If null, system stats are returned.
    */
   readonly attribute DOMString    manifestURL;
 
   /**
-   * Network the returned data belongs to.
+   * Can be 'mobile', 'wifi' or null.
+   * If null, stats for both mobile and wifi are returned.
    */
-  readonly attribute nsIDOMMozNetworkStatsInterface network;
+  readonly attribute DOMString    connectionType;
 
   /**
-   * Stats for a network.
+   * Stats for connectionType
    */
   readonly attribute jsval        data;      // array of NetworkStatsData.
                                              // one element per day.
 
   /**
    * Dates
    */
   readonly attribute jsval        start; // Date.
--- a/dom/network/interfaces/nsIDOMNetworkStatsManager.idl
+++ b/dom/network/interfaces/nsIDOMNetworkStatsManager.idl
@@ -1,70 +1,62 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMDOMRequest;
 
-/**
- * Represents a data interface for which the manager is recording statistics.
- */
-[scriptable, uuid(f540615b-d803-43ff-8200-2a9d145a5645)]
-interface nsIDOMMozNetworkStatsInterface : nsISupports
+dictionary NetworkStatsOptions
 {
-  readonly attribute long type;
-
   /**
-   * Id value is '0' for wifi or the iccid for mobile (SIM).
+   * Connection type used to filter which network stats will be returned:
+   * 'mobile', 'wifi' or null.
+   * If null, stats for both mobile and wifi are returned.
+   *
+   * Manifest URL used to retrieve network stats per app.
+   * If null, system stats (regardless of the app) are returned.
    */
-  readonly attribute DOMString id;
+  DOMString connectionType;
+  DOMString manifestURL;
+  jsval start;              // date
+  jsval end;                // date
 };
 
-[scriptable,  uuid(5fbdcae6-a2cd-47b3-929f-83ac75bd4881)]
+[scriptable,  uuid(87529a6c-aef6-11e1-a595-4f034275cfa6)]
 interface nsIDOMMozNetworkStatsManager : nsISupports
 {
   /**
-   * Constants for known interface types.
-   */
-  const long WIFI = 0;
-  const long MOBILE = 1;
-
-  /**
-   * Find samples between two dates start and end, both included.
+   * Query network statistics.
+   *
+   * If options.connectionType is not provided, return statistics for all known
+   * network interfaces.
+   *
+   * If options.manifestURL is not provided, return statistics regardless of the app.
    *
-   * If manifestURL is provided, per-app usage is retrieved,
-   * otherwise the target will be system usage.
+   * If successful, the request result will be an nsIDOMMozNetworkStats object.
    *
-   * If success, the request result will be an nsIDOMMozNetworkStats object.
+   * If network stats are not available for some dates, then rxBytes &
+   * txBytes are undefined for those dates.
    */
-  nsIDOMDOMRequest getSamples(in nsIDOMMozNetworkStatsInterface network,
-                              in jsval start,
-                              in jsval end,
-                              [optional] in DOMString manifestURL);
+  nsIDOMDOMRequest               getNetworkStats(in jsval options);
 
   /**
-   * Remove all stats related with the provided network from DB.
+   * Return available connection types.
    */
-  nsIDOMDOMRequest clearStats(in nsIDOMMozNetworkStatsInterface network);
+  readonly attribute jsval       connectionTypes; // array of DOMStrings.
 
   /**
-   * Remove all stats in the database.
+   * Clear all stats from DB.
    */
-  nsIDOMDOMRequest clearAllStats();
+  nsIDOMDOMRequest               clearAllData();
 
   /**
-   * Return currently available networks.
+   * Time in seconds between samples stored in database.
    */
-  readonly attribute jsval availableNetworks; // array of nsIDOMMozNetworkStatsInterface.
+  readonly attribute long        sampleRate;
 
   /**
-   * Minimum time in milliseconds between samples stored in the database.
+   * Maximum number of samples stored in the database per connection type.
    */
-  readonly attribute long sampleRate;
-
-  /**
-   * Time in milliseconds recorded by the API until present time. All samples
-   * older than maxStorageAge from now are deleted.
-   */
-  readonly attribute long long maxStorageAge;
+  readonly attribute long        maxStorageSamples;
 };
--- a/dom/network/interfaces/nsINetworkStatsServiceProxy.idl
+++ b/dom/network/interfaces/nsINetworkStatsServiceProxy.idl
@@ -1,37 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-interface nsINetworkInterface;
-
 [scriptable, function, uuid(5f821529-1d80-4ab5-a933-4e1b3585b6bc)]
 interface nsINetworkStatsServiceProxyCallback : nsISupports
 {
   /*
    * @param aResult callback result with boolean value
    * @param aMessage message
    */
   void notify(in boolean aResult, in jsval aMessage);
 };
 
-[scriptable, uuid(facef032-3fd9-4509-a396-83d94c1a11ae)]
+[scriptable, uuid(8fbd115d-f590-474c-96dc-e2b6803ca975)]
 interface nsINetworkStatsServiceProxy : nsISupports
 {
   /*
    * An interface used to record per-app traffic data.
    * @param aAppId app id
-   * @param aNetworkInterface network
+   * @param aConnectionType network connection type (0 for wifi, 1 for mobile)
    * @param aTimeStamp time stamp
    * @param aRxBytes received data amount
    * @param aTxBytes transmitted data amount
    * @param aCallback an optional callback
    */
   void saveAppStats(in unsigned long aAppId,
-                    in nsINetworkInterface aNetwork,
+                    in long aConnectionType,
                     in unsigned long long aTimeStamp,
                     in unsigned long long aRxBytes,
                     in unsigned long long aTxBytes,
          [optional] in nsINetworkStatsServiceProxyCallback aCallback);
 };
--- a/dom/network/src/NetworkStatsDB.jsm
+++ b/dom/network/src/NetworkStatsDB.jsm
@@ -11,43 +11,45 @@ function debug(s) { dump("-*- NetworkSta
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 
 const DB_NAME = "net_stats";
 const DB_VERSION = 2;
-const STORE_NAME = "net_stats";
+const STORE_NAME = "net_stats"; // Deprecated. Use "net_stats_v2" instead.
+const STORE_NAME_V2 = "net_stats_v2";
 
 // Constant defining the maximum values allowed per interface. If more, older
 // will be erased.
 const VALUES_MAX_LENGTH = 6 * 30;
 
 // Constant defining the rate of the samples. Daily.
 const SAMPLE_RATE = 1000 * 60 * 60 * 24;
 
-this.NetworkStatsDB = function NetworkStatsDB() {
+this.NetworkStatsDB = function NetworkStatsDB(aConnectionTypes) {
   if (DEBUG) {
     debug("Constructor");
   }
-  this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME]);
+  this._connectionTypes = aConnectionTypes;
+  this.initDBHelper(DB_NAME, DB_VERSION, [STORE_NAME_V2]);
 }
 
 NetworkStatsDB.prototype = {
   __proto__: IndexedDBHelper.prototype,
 
   dbNewTxn: function dbNewTxn(txn_type, callback, txnCb) {
     function successCb(result) {
       txnCb(null, result);
     }
     function errorCb(error) {
       txnCb(error, null);
     }
-    return this.newTxn(txn_type, STORE_NAME, callback, successCb, errorCb);
+    return this.newTxn(txn_type, STORE_NAME_V2, callback, successCb, errorCb);
   },
 
   upgradeSchema: function upgradeSchema(aTransaction, aDb, aOldVersion, aNewVersion) {
     if (DEBUG) {
       debug("upgrade schema from: " + aOldVersion + " to " + aNewVersion + " called!");
     }
     let db = aDb;
     let objectStore;
@@ -62,392 +64,371 @@ NetworkStatsDB.prototype = {
         objectStore.createIndex("timestamp", "timestamp", { unique: false });
         objectStore.createIndex("rxBytes", "rxBytes", { unique: false });
         objectStore.createIndex("txBytes", "txBytes", { unique: false });
         objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
         objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
         if (DEBUG) {
           debug("Created object stores and indexes");
         }
+
+        // There could be a time delay between the point when the network
+        // interface comes up and the point when the database is initialized.
+        // In this short interval some traffic data are generated but are not
+        // registered by the first sample. The initialization of the database
+        // should make up the missing sample.
+        let stats = [];
+        for (let connection in this._connectionTypes) {
+          let connectionType = this._connectionTypes[connection].name;
+          let timestamp = this.normalizeDate(new Date());
+          stats.push({ connectionType: connectionType,
+                       timestamp:      timestamp,
+                       rxBytes:        0,
+                       txBytes:        0,
+                       rxTotalBytes:   0,
+                       txTotalBytes:   0 });
+        }
+        this._saveStats(aTransaction, objectStore, stats);
+        if (DEBUG) {
+          debug("Database initialized");
+        }
       } else if (currVersion == 1) {
         // In order to support per-app traffic data storage, the original
         // objectStore needs to be replaced by a new objectStore with new
         // key path ("appId") and new index ("appId").
-        // Also, since now networks are identified by their
-        // [networkId, networkType] not just by their connectionType,
-        // to modify the keyPath is mandatory to delete the object store
-        // and create it again. Old data is going to be deleted because the
-        // networkId for each sample can not be set.
-        db.deleteObjectStore(STORE_NAME);
+        let newObjectStore;
+        newObjectStore = db.createObjectStore(STORE_NAME_V2, { keyPath: ["appId", "connectionType", "timestamp"] });
+        newObjectStore.createIndex("appId", "appId", { unique: false });
+        newObjectStore.createIndex("connectionType", "connectionType", { unique: false });
+        newObjectStore.createIndex("timestamp", "timestamp", { unique: false });
+        newObjectStore.createIndex("rxBytes", "rxBytes", { unique: false });
+        newObjectStore.createIndex("txBytes", "txBytes", { unique: false });
+        newObjectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
+        newObjectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
+        if (DEBUG) {
+          debug("Created new object stores and indexes");
+        }
 
-        objectStore = db.createObjectStore(STORE_NAME, { keyPath: ["appId", "network", "timestamp"] });
-        objectStore.createIndex("appId", "appId", { unique: false });
-        objectStore.createIndex("network", "network", { unique: false });
-        objectStore.createIndex("networkType", "networkType", { unique: false });
-        objectStore.createIndex("timestamp", "timestamp", { unique: false });
-        objectStore.createIndex("rxBytes", "rxBytes", { unique: false });
-        objectStore.createIndex("txBytes", "txBytes", { unique: false });
-        objectStore.createIndex("rxTotalBytes", "rxTotalBytes", { unique: false });
-        objectStore.createIndex("txTotalBytes", "txTotalBytes", { unique: false });
+        // Copy the data from the original objectStore to the new objectStore.
+        objectStore = aTransaction.objectStore(STORE_NAME);
+        objectStore.openCursor().onsuccess = function(event) {
+          let cursor = event.target.result;
+          if (!cursor) {
+            // Delete the original object store.
+            db.deleteObjectStore(STORE_NAME);
+            return;
+          }
 
-        debug("Created object stores and indexes for version 2");
+          let oldStats = cursor.value;
+          let newStats = { appId:          0,
+                           connectionType: oldStats.connectionType,
+                           timestamp:      oldStats.timestamp,
+                           rxBytes:        oldStats.rxBytes,
+                           txBytes:        oldStats.txBytes,
+                           rxTotalBytes:   oldStats.rxTotalBytes,
+                           txTotalBytes:   oldStats.txTotalBytes };
+          this._saveStats(aTransaction, newObjectStore, newStats);
+          cursor.continue();
+        }.bind(this);
       }
     }
   },
 
-  importData: function importData(aStats) {
-    let stats = { appId:        aStats.appId,
-                  network:      [aStats.networkId, aStats.networkType],
-                  timestamp:    aStats.timestamp,
-                  rxBytes:      aStats.rxBytes,
-                  txBytes:      aStats.txBytes,
-                  rxTotalBytes: aStats.rxTotalBytes,
-                  txTotalBytes: aStats.txTotalBytes };
-
-    return stats;
-  },
-
-  exportData: function exportData(aStats) {
-    let stats = { appId:        aStats.appId,
-                  networkId:    aStats.network[0],
-                  networkType:  aStats.network[1],
-                  timestamp:    aStats.timestamp,
-                  rxBytes:      aStats.rxBytes,
-                  txBytes:      aStats.txBytes,
-                  rxTotalBytes: aStats.rxTotalBytes,
-                  txTotalBytes: aStats.txTotalBytes };
-
-    return stats;
-  },
-
   normalizeDate: function normalizeDate(aDate) {
     // Convert to UTC according to timezone and
     // filter timestamp to get SAMPLE_RATE precission
     let timestamp = aDate.getTime() - aDate.getTimezoneOffset() * 60 * 1000;
     timestamp = Math.floor(timestamp / SAMPLE_RATE) * SAMPLE_RATE;
     return timestamp;
   },
 
-  saveStats: function saveStats(aStats, aResultCb) {
-    let timestamp = this.normalizeDate(aStats.date);
+  saveStats: function saveStats(stats, aResultCb) {
+    let timestamp = this.normalizeDate(stats.date);
 
-    let stats = { appId:        aStats.appId,
-                  networkId:    aStats.networkId,
-                  networkType:  aStats.networkType,
-                  timestamp:    timestamp,
-                  rxBytes:      (aStats.appId == 0) ? 0 : aStats.rxBytes,
-                  txBytes:      (aStats.appId == 0) ? 0 : aStats.txBytes,
-                  rxTotalBytes: (aStats.appId == 0) ? aStats.rxBytes : 0,
-                  txTotalBytes: (aStats.appId == 0) ? aStats.txBytes : 0 };
+    stats = { appId:          stats.appId,
+              connectionType: stats.connectionType,
+              timestamp:      timestamp,
+              rxBytes:        (stats.appId == 0) ? 0 : stats.rxBytes,
+              txBytes:        (stats.appId == 0) ? 0 : stats.txBytes,
+              rxTotalBytes:   (stats.appId == 0) ? stats.rxBytes : 0,
+              txTotalBytes:   (stats.appId == 0) ? stats.txBytes : 0 };
 
-    stats = this.importData(stats);
-
-    this.dbNewTxn("readwrite", function(aTxn, aStore) {
+    this.dbNewTxn("readwrite", function(txn, store) {
       if (DEBUG) {
         debug("Filtered time: " + new Date(timestamp));
         debug("New stats: " + JSON.stringify(stats));
       }
 
-    let request = aStore.index("network").openCursor(stats.network, "prev");
+      let request = store.index("connectionType").openCursor(stats.connectionType, "prev");
       request.onsuccess = function onsuccess(event) {
         let cursor = event.target.result;
         if (!cursor) {
           // Empty, so save first element.
-
-          // There could be a time delay between the point when the network
-          // interface comes up and the point when the database is initialized.
-          // In this short interval some traffic data are generated but are not
-          // registered by the first sample.
-          if (stats.appId == 0) {
-            stats.rxBytes = stats.rxTotalBytes;
-            stats.txBytes = stats.txTotalBytes;
-          }
-
-          this._saveStats(aTxn, aStore, stats);
+          this._saveStats(txn, store, stats);
           return;
         }
 
         if (stats.appId != cursor.value.appId) {
           cursor.continue();
           return;
         }
 
         // There are old samples
         if (DEBUG) {
           debug("Last value " + JSON.stringify(cursor.value));
         }
 
         // Remove stats previous to now - VALUE_MAX_LENGTH
-        this._removeOldStats(aTxn, aStore, stats.appId, stats.network, stats.timestamp);
+        this._removeOldStats(txn, store, stats.appId, stats.connectionType, stats.timestamp);
 
         // Process stats before save
-        this._processSamplesDiff(aTxn, aStore, cursor, stats);
+        this._processSamplesDiff(txn, store, cursor, stats);
       }.bind(this);
     }.bind(this), aResultCb);
   },
 
   /*
    * This function check that stats are saved in the database following the sample rate.
    * In this way is easier to find elements when stats are requested.
    */
-  _processSamplesDiff: function _processSamplesDiff(aTxn, aStore, aLastSampleCursor, aNewSample) {
-    let lastSample = aLastSampleCursor.value;
+  _processSamplesDiff: function _processSamplesDiff(txn, store, lastSampleCursor, newSample) {
+    let lastSample = lastSampleCursor.value;
 
     // Get difference between last and new sample.
-    let diff = (aNewSample.timestamp - lastSample.timestamp) / SAMPLE_RATE;
+    let diff = (newSample.timestamp - lastSample.timestamp) / SAMPLE_RATE;
     if (diff % 1) {
       // diff is decimal, so some error happened because samples are stored as a multiple
       // of SAMPLE_RATE
-      aTxn.abort();
+      txn.abort();
       throw new Error("Error processing samples");
     }
 
     if (DEBUG) {
-      debug("New: " + aNewSample.timestamp + " - Last: " +
-            lastSample.timestamp + " - diff: " + diff);
+      debug("New: " + newSample.timestamp + " - Last: " + lastSample.timestamp + " - diff: " + diff);
     }
 
     // If the incoming data is obtained from netd (|newSample.appId| is 0),
     // the new |txBytes|/|rxBytes| is assigend by the differnce between the new
     // |txTotalBytes|/|rxTotalBytes| and the last |txTotalBytes|/|rxTotalBytes|.
     // Else, the incoming data is per-app data (|newSample.appId| is not 0),
     // the |txBytes|/|rxBytes| is directly the new |txBytes|/|rxBytes|.
-    if (aNewSample.appId == 0) {
-      let rxDiff = aNewSample.rxTotalBytes - lastSample.rxTotalBytes;
-      let txDiff = aNewSample.txTotalBytes - lastSample.txTotalBytes;
+    if (newSample.appId == 0) {
+      let rxDiff = newSample.rxTotalBytes - lastSample.rxTotalBytes;
+      let txDiff = newSample.txTotalBytes - lastSample.txTotalBytes;
       if (rxDiff < 0 || txDiff < 0) {
-        rxDiff = aNewSample.rxTotalBytes;
-        txDiff = aNewSample.txTotalBytes;
+        rxDiff = newSample.rxTotalBytes;
+        txDiff = newSample.txTotalBytes;
       }
-      aNewSample.rxBytes = rxDiff;
-      aNewSample.txBytes = txDiff;
+      newSample.rxBytes = rxDiff;
+      newSample.txBytes = txDiff;
     }
 
     if (diff == 1) {
       // New element.
 
       // If the incoming data is per-data data, new |rxTotalBytes|/|txTotalBytes|
       // needs to be obtained by adding new |rxBytes|/|txBytes| to last
       // |rxTotalBytes|/|txTotalBytes|.
-      if (aNewSample.appId != 0) {
-        aNewSample.rxTotalBytes = aNewSample.rxBytes + lastSample.rxTotalBytes;
-        aNewSample.txTotalBytes = aNewSample.txBytes + lastSample.txTotalBytes;
+      if (newSample.appId != 0) {
+        newSample.rxTotalBytes = newSample.rxBytes + lastSample.rxTotalBytes;
+        newSample.txTotalBytes = newSample.txBytes + lastSample.txTotalBytes;
       }
-
-      this._saveStats(aTxn, aStore, aNewSample);
+      this._saveStats(txn, store, newSample);
       return;
     }
     if (diff > 1) {
       // Some samples lost. Device off during one or more samplerate periods.
       // Time or timezone changed
       // Add lost samples with 0 bytes and the actual one.
       if (diff > VALUES_MAX_LENGTH) {
         diff = VALUES_MAX_LENGTH;
       }
 
       let data = [];
       for (let i = diff - 2; i >= 0; i--) {
-        let time = aNewSample.timestamp - SAMPLE_RATE * (i + 1);
-        let sample = { appId:        aNewSample.appId,
-                       network:      aNewSample.network,
-                       timestamp:    time,
-                       rxBytes:      0,
-                       txBytes:      0,
-                       rxTotalBytes: lastSample.rxTotalBytes,
-                       txTotalBytes: lastSample.txTotalBytes };
-
+        let time = newSample.timestamp - SAMPLE_RATE * (i + 1);
+        let sample = {appId:          newSample.appId,
+                      connectionType: newSample.connectionType,
+                      timestamp:      time,
+                      rxBytes:        0,
+                      txBytes:        0,
+                      rxTotalBytes:   lastSample.rxTotalBytes,
+                      txTotalBytes:   lastSample.txTotalBytes};
         data.push(sample);
       }
 
-      data.push(aNewSample);
-      this._saveStats(aTxn, aStore, data);
+      data.push(newSample);
+      this._saveStats(txn, store, data);
       return;
     }
     if (diff == 0 || diff < 0) {
       // New element received before samplerate period.
       // It means that device has been restarted (or clock / timezone change).
       // Update element.
 
       // If diff < 0, clock or timezone changed back. Place data in the last sample.
 
-      lastSample.rxBytes += aNewSample.rxBytes;
-      lastSample.txBytes += aNewSample.txBytes;
+      lastSample.rxBytes += newSample.rxBytes;
+      lastSample.txBytes += newSample.txBytes;
 
       // If incoming data is obtained from netd, last |rxTotalBytes|/|txTotalBytes|
       // needs to get updated by replacing the new |rxTotalBytes|/|txTotalBytes|.
-      if (aNewSample.appId == 0) {
-        lastSample.rxTotalBytes = aNewSample.rxTotalBytes;
-        lastSample.txTotalBytes = aNewSample.txTotalBytes;
+      if (newSample.appId == 0) {
+        lastSample.rxTotalBytes = newSample.rxTotalBytes;
+        lastSample.txTotalBytes = newSample.txTotalBytes;
       } else {
         // Else, the incoming data is per-app data, old |rxTotalBytes|/
         // |txTotalBytes| needs to get updated by adding the new
         // |rxBytes|/|txBytes| to last |rxTotalBytes|/|txTotalBytes|.
-        lastSample.rxTotalBytes += aNewSample.rxBytes;
-        lastSample.txTotalBytes += aNewSample.txBytes;
+        lastSample.rxTotalBytes += newSample.rxBytes;
+        lastSample.txTotalBytes += newSample.txBytes;
       }
       if (DEBUG) {
         debug("Update: " + JSON.stringify(lastSample));
       }
-      let req = aLastSampleCursor.update(lastSample);
+      let req = lastSampleCursor.update(lastSample);
     }
   },
 
-  _saveStats: function _saveStats(aTxn, aStore, aNetworkStats) {
+  _saveStats: function _saveStats(txn, store, networkStats) {
     if (DEBUG) {
-      debug("_saveStats: " + JSON.stringify(aNetworkStats));
+      debug("_saveStats: " + JSON.stringify(networkStats));
     }
 
-    if (Array.isArray(aNetworkStats)) {
-      let len = aNetworkStats.length - 1;
+    if (Array.isArray(networkStats)) {
+      let len = networkStats.length - 1;
       for (let i = 0; i <= len; i++) {
-        aStore.put(aNetworkStats[i]);
+        store.put(networkStats[i]);
       }
     } else {
-      aStore.put(aNetworkStats);
+      store.put(networkStats);
     }
   },
 
-  _removeOldStats: function _removeOldStats(aTxn, aStore, aAppId, aNetwork, aDate) {
+  _removeOldStats: function _removeOldStats(txn, store, appId, connType, date) {
     // Callback function to remove old items when new ones are added.
-    let filterDate = aDate - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1);
-    let lowerFilter = [aAppId, aNetwork, 0];
-    let upperFilter = [aAppId, aNetwork, filterDate];
+    let filterDate = date - (SAMPLE_RATE * VALUES_MAX_LENGTH - 1);
+    let lowerFilter = [appId, connType, 0];
+    let upperFilter = [appId, connType, filterDate];
     let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
-    let lastSample = null;
-    let self = this;
-
-    aStore.openCursor(range).onsuccess = function(event) {
+    store.openCursor(range).onsuccess = function(event) {
       var cursor = event.target.result;
       if (cursor) {
-        lastSample = cursor.value;
         cursor.delete();
         cursor.continue();
-        return;
       }
-
-      // If all samples for a network are removed, an empty sample
-      // has to be saved to keep the totalBytes in order to compute
-      // future samples because system counters are not set to 0.
-      // Thus, if there are no samples left, the last sample removed
-      // will be saved again after setting its bytes to 0.
-      let request = aStore.index("network").openCursor(aNetwork);
-      request.onsuccess = function onsuccess(event) {
-        let cursor = event.target.result;
-        if (!cursor && lastSample != null) {
-          let timestamp = new Date();
-          timestamp = self.normalizeDate(timestamp);
-          lastSample.timestamp = timestamp;
-          lastSample.rxBytes = 0;
-          lastSample.txBytes = 0;
-          self._saveStats(aTxn, aStore, lastSample);
-        }
-      };
-    };
+    }.bind(this);
   },
 
-  clearInterfaceStats: function clearInterfaceStats(aNetwork, aResultCb) {
-    let network = [aNetwork.id, aNetwork.type];
-    let self = this;
-
-    // Clear and save an empty sample to keep sync with system counters
-    this.dbNewTxn("readwrite", function(aTxn, aStore) {
-      let sample = null;
-      let request = aStore.index("network").openCursor(network, "prev");
-      request.onsuccess = function onsuccess(event) {
-        let cursor = event.target.result;
-        if (cursor) {
-          if (!sample) {
-            sample = cursor.value;
-          }
-
-          cursor.delete();
-          cursor.continue();
-          return;
-        }
-
-        if (sample) {
-          let timestamp = new Date();
-          timestamp = self.normalizeDate(timestamp);
-          sample.timestamp = timestamp;
-          sample.appId = 0;
-          sample.rxBytes = 0;
-          sample.txBytes = 0;
-
-          self._saveStats(aTxn, aStore, sample);
-        }
-      };
+  clear: function clear(aResultCb) {
+    this.dbNewTxn("readwrite", function(txn, store) {
+      if (DEBUG) {
+        debug("Going to clear all!");
+      }
+      store.clear();
     }, aResultCb);
   },
 
-  clearStats: function clearStats(aNetworks, aResultCb) {
-    let index = 0;
-    let stats = [];
-    let self = this;
-
-    let callback = function(aError, aResult) {
-      index++;
-
-      if (!aError && index < aNetworks.length) {
-        self.clearInterfaceStats(aNetworks[index], callback);
-        return;
-      }
-
-      aResultCb(aError, aResult);
-    };
-
-    if (!aNetworks[index]) {
-      aResultCb(null, true);
-      return;
-    }
-    this.clearInterfaceStats(aNetworks[index], callback);
-  },
-
-  find: function find(aResultCb, aNetwork, aStart, aEnd, aAppId, aManifestURL) {
+  find: function find(aResultCb, aOptions) {
     let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
-    let start = this.normalizeDate(aStart);
-    let end = this.normalizeDate(aEnd);
+    let start = this.normalizeDate(aOptions.start);
+    let end = this.normalizeDate(aOptions.end);
 
     if (DEBUG) {
-      debug("Find samples for appId: " + aAppId + " network " +
-            JSON.stringify(aNetwork) + " from " + start + " until " + end);
+      debug("Find: appId: " + aOptions.appId + " connectionType:" +
+            aOptions.connectionType + " start: " + start + " end: " + end);
       debug("Start time: " + new Date(start));
       debug("End time: " + new Date(end));
     }
 
-    this.dbNewTxn("readonly", function(aTxn, aStore) {
-      let network = [aNetwork.id, aNetwork.type];
-      let lowerFilter = [aAppId, network, start];
-      let upperFilter = [aAppId, network, end];
+    this.dbNewTxn("readonly", function(txn, store) {
+      let lowerFilter = [aOptions.appId, aOptions.connectionType, start];
+      let upperFilter = [aOptions.appId, aOptions.connectionType, end];
       let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
 
       let data = [];
 
-      if (!aTxn.result) {
-        aTxn.result = {};
+      if (!txn.result) {
+        txn.result = {};
       }
 
-      let request = aStore.openCursor(range).onsuccess = function(event) {
+      let request = store.openCursor(range).onsuccess = function(event) {
         var cursor = event.target.result;
         if (cursor){
           data.push({ rxBytes: cursor.value.rxBytes,
                       txBytes: cursor.value.txBytes,
                       date: new Date(cursor.value.timestamp + offset) });
           cursor.continue();
           return;
         }
 
         // When requested samples (start / end) are not in the range of now and
         // now - VALUES_MAX_LENGTH, fill with empty samples.
         this.fillResultSamples(start + offset, end + offset, data);
 
-        aTxn.result.manifestURL = aManifestURL;
-        aTxn.result.network = aNetwork;
-        aTxn.result.start = aStart;
-        aTxn.result.end = aEnd;
-        aTxn.result.data = data;
+        txn.result.manifestURL = aOptions.manifestURL;
+        txn.result.connectionType = aOptions.connectionType;
+        txn.result.start = aOptions.start;
+        txn.result.end = aOptions.end;
+        txn.result.data = data;
+      }.bind(this);
+    }.bind(this), aResultCb);
+  },
+
+  findAll: function findAll(aResultCb, aOptions) {
+    let offset = (new Date()).getTimezoneOffset() * 60 * 1000;
+    let start = this.normalizeDate(aOptions.start);
+    let end = this.normalizeDate(aOptions.end);
+
+    if (DEBUG) {
+      debug("FindAll: appId: " + aOptions.appId +
+            " start: " + start + " end: " + end + "\n");
+    }
+
+    let self = this;
+    this.dbNewTxn("readonly", function(txn, store) {
+      let lowerFilter = start;
+      let upperFilter = end;
+      let range = IDBKeyRange.bound(lowerFilter, upperFilter, false, false);
+
+      let data = [];
+
+      if (!txn.result) {
+        txn.result = {};
+      }
+
+      let request = store.index("timestamp").openCursor(range).onsuccess = function(event) {
+        var cursor = event.target.result;
+        if (cursor) {
+          if (cursor.value.appId != aOptions.appId) {
+            cursor.continue();
+            return;
+          }
+
+          if (data.length > 0 &&
+              data[data.length - 1].date.getTime() == cursor.value.timestamp + offset) {
+            // Time is the same, so add values.
+            data[data.length - 1].rxBytes += cursor.value.rxBytes;
+            data[data.length - 1].txBytes += cursor.value.txBytes;
+          } else {
+            data.push({ rxBytes: cursor.value.rxBytes,
+                        txBytes: cursor.value.txBytes,
+                        date: new Date(cursor.value.timestamp + offset) });
+          }
+          cursor.continue();
+          return;
+        }
+
+        this.fillResultSamples(start + offset, end + offset, data);
+
+        txn.result.manifestURL = aOptions.manifestURL;
+        txn.result.connectionType = aOptions.connectionType;
+        txn.result.start = aOptions.start;
+        txn.result.end = aOptions.end;
+        txn.result.data = data;
       }.bind(this);
     }.bind(this), aResultCb);
   },
 
   /*
    * Fill data array (samples from database) with empty samples to match
    * requested start / end dates.
    */
@@ -475,15 +456,15 @@ NetworkStatsDB.prototype = {
     return SAMPLE_RATE;
   },
 
   get maxStorageSamples () {
     return VALUES_MAX_LENGTH;
   },
 
   logAllRecords: function logAllRecords(aResultCb) {
-    this.dbNewTxn("readonly", function(aTxn, aStore) {
-      aStore.mozGetAll().onsuccess = function onsuccess(event) {
-        aTxn.result = event.target.result;
+    this.dbNewTxn("readonly", function(txn, store) {
+      store.mozGetAll().onsuccess = function onsuccess(event) {
+        txn.result = event.target.result;
       };
     }, aResultCb);
   },
 };
--- a/dom/network/src/NetworkStatsManager.js
+++ b/dom/network/src/NetworkStatsManager.js
@@ -50,90 +50,60 @@ NetworkStatsData.prototype = {
                                      contractID:"@mozilla.org/networkstatsdata;1",
                                      classDescription: "NetworkStatsData",
                                      interfaces: [nsIDOMMozNetworkStatsData],
                                      flags: nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsData])
 };
 
-// NetworkStatsInterface
-const NETWORKSTATSINTERFACE_CONTRACTID = "@mozilla.org/networkstatsinterface;1";
-const NETWORKSTATSINTERFACE_CID        = Components.ID("{f540615b-d803-43ff-8200-2a9d145a5645}");
-const nsIDOMMozNetworkStatsInterface   = Components.interfaces.nsIDOMMozNetworkStatsInterface;
-
-function NetworkStatsInterface(aNetwork) {
-  if (DEBUG) {
-    debug("NetworkStatsInterface Constructor");
-  }
-  this.type = aNetwork.type;
-  this.id = aNetwork.id;
-}
-
-NetworkStatsInterface.prototype = {
-  __exposedProps__: {
-                      id: 'r',
-                      type: 'r',
-                    },
-
-  classID : NETWORKSTATSINTERFACE_CID,
-  classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSINTERFACE_CID,
-                                     contractID: NETWORKSTATSINTERFACE_CONTRACTID,
-                                     classDescription: "NetworkStatsInterface",
-                                     interfaces: [nsIDOMMozNetworkStatsInterface],
-                                     flags: nsIClassInfo.DOM_OBJECT}),
-
-  QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStatsInterface])
-}
-
 // NetworkStats
 const NETWORKSTATS_CONTRACTID = "@mozilla.org/networkstats;1";
-const NETWORKSTATS_CID        = Components.ID("{b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}");
+const NETWORKSTATS_CID        = Components.ID("{6613ea55-b99c-44f9-91bf-d07da10b9b74}");
 const nsIDOMMozNetworkStats   = Components.interfaces.nsIDOMMozNetworkStats;
 
 function NetworkStats(aWindow, aStats) {
   if (DEBUG) {
     debug("NetworkStats Constructor");
   }
   this.manifestURL = aStats.manifestURL || null;
-  this.network = new NetworkStatsInterface(aStats.network);
+  this.connectionType = aStats.connectionType || null;
   this.start = aStats.start || null;
   this.end = aStats.end || null;
 
   let samples = this.data = Cu.createArrayIn(aWindow);
   for (let i = 0; i < aStats.data.length; i++) {
     samples.push(new NetworkStatsData(aStats.data[i]));
   }
 }
 
 NetworkStats.prototype = {
   __exposedProps__: {
                       manifestURL: 'r',
-                      network: 'r',
+                      connectionType: 'r',
                       start: 'r',
                       end:  'r',
                       data:  'r',
                     },
 
   classID : NETWORKSTATS_CID,
   classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATS_CID,
                                      contractID: NETWORKSTATS_CONTRACTID,
                                      classDescription: "NetworkStats",
                                      interfaces: [nsIDOMMozNetworkStats],
                                      flags: nsIClassInfo.DOM_OBJECT}),
 
   QueryInterface : XPCOMUtils.generateQI([nsIDOMMozNetworkStats,
-                                          nsIDOMMozNetworkStatsData,
-                                          nsIDOMMozNetworkStatsInterface])
+                                          nsIDOMMozNetworkStatsData])
 }
 
 // NetworkStatsManager
 
 const NETWORKSTATSMANAGER_CONTRACTID = "@mozilla.org/networkStatsManager;1";
-const NETWORKSTATSMANAGER_CID        = Components.ID("{5fbdcae6-a2cd-47b3-929f-83ac75bd4881}");
+const NETWORKSTATSMANAGER_CID        = Components.ID("{87529a6c-aef6-11e1-a595-4f034275cfa6}");
 const nsIDOMMozNetworkStatsManager   = Components.interfaces.nsIDOMMozNetworkStatsManager;
 
 function NetworkStatsManager() {
   if (DEBUG) {
     debug("Constructor");
   }
 }
 
@@ -141,74 +111,52 @@ NetworkStatsManager.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   checkPrivileges: function checkPrivileges() {
     if (!this.hasPrivileges) {
       throw Components.Exception("Permission denied", Cr.NS_ERROR_FAILURE);
     }
   },
 
-  getSamples: function getSamples(aNetwork, aStart, aEnd, aManifestURL) {
+  getNetworkStats: function getNetworkStats(aOptions) {
     this.checkPrivileges();
 
-    if (aStart.constructor.name !== "Date" ||
-        aEnd.constructor.name !== "Date" ||
-        aStart > aEnd) {
+    if (!aOptions.start || !aOptions.end ||
+      aOptions.start > aOptions.end) {
       throw Components.results.NS_ERROR_INVALID_ARG;
     }
 
     let request = this.createRequest();
     cpmm.sendAsyncMessage("NetworkStats:Get",
-                          { network: aNetwork,
-                            start: aStart,
-                            end: aEnd,
-                            manifestURL: aManifestURL,
-                            id: this.getRequestId(request) });
+                          {data: aOptions, id: this.getRequestId(request)});
     return request;
   },
 
-  clearStats: function clearStats(aNetwork) {
+  clearAllData: function clearAllData() {
     this.checkPrivileges();
 
     let request = this.createRequest();
     cpmm.sendAsyncMessage("NetworkStats:Clear",
-                          { network: aNetwork,
-                            id: this.getRequestId(request) });
-    return request;
-  },
-
-  clearAllStats: function clearAllStats() {
-    this.checkPrivileges();
-
-    let request = this.createRequest();
-    cpmm.sendAsyncMessage("NetworkStats:ClearAll",
                           {id: this.getRequestId(request)});
     return request;
   },
 
-  get availableNetworks() {
+  get connectionTypes() {
     this.checkPrivileges();
-
-    let result = ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Networks")[0], this._window);
-    let networks = this.data = Cu.createArrayIn(this._window);
-    for (let i = 0; i < result.length; i++) {
-      networks.push(new NetworkStatsInterface(result[i]));
-    }
-
-    return networks;
+    return ObjectWrapper.wrap(cpmm.sendSyncMessage("NetworkStats:Types")[0], this._window);
   },
 
   get sampleRate() {
     this.checkPrivileges();
-    return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0];
+    return cpmm.sendSyncMessage("NetworkStats:SampleRate")[0] / 1000;
   },
 
-  get maxStorageAge() {
+  get maxStorageSamples() {
     this.checkPrivileges();
-    return cpmm.sendSyncMessage("NetworkStats:MaxStorageAge")[0];
+    return cpmm.sendSyncMessage("NetworkStats:MaxStorageSamples")[0];
   },
 
   receiveMessage: function(aMessage) {
     if (DEBUG) {
       debug("NetworkStatsmanager::receiveMessage: " + aMessage.name);
     }
     let msg = aMessage.json;
 
@@ -230,17 +178,16 @@ NetworkStatsManager.prototype = {
         let result = new NetworkStats(this._window, msg.result);
         if (DEBUG) {
           debug("result: " + JSON.stringify(result));
         }
         Services.DOMRequest.fireSuccess(req, result);
         break;
 
       case "NetworkStats:Clear:Return":
-      case "NetworkStats:ClearAll:Return":
         if (msg.error) {
           Services.DOMRequest.fireError(req, msg.error);
           return;
         }
 
         Services.DOMRequest.fireSuccess(req, true);
         break;
 
@@ -270,18 +217,17 @@ NetworkStatsManager.prototype = {
       debug("has privileges: " + this.hasPrivileges);
     }
 
     if (!this.hasPrivileges) {
       return null;
     }
 
     this.initDOMRequestHelper(aWindow, ["NetworkStats:Get:Return",
-                                        "NetworkStats:Clear:Return",
-                                        "NetworkStats:ClearAll:Return"]);
+                              "NetworkStats:Clear:Return"]);
   },
 
   // Called from DOMRequestIpcHelper
   uninit: function uninit() {
     if (DEBUG) {
       debug("uninit call");
     }
   },
@@ -294,11 +240,10 @@ NetworkStatsManager.prototype = {
   classInfo : XPCOMUtils.generateCI({classID: NETWORKSTATSMANAGER_CID,
                                      contractID: NETWORKSTATSMANAGER_CONTRACTID,
                                      classDescription: "NetworkStatsManager",
                                      interfaces: [nsIDOMMozNetworkStatsManager],
                                      flags: nsIClassInfo.DOM_OBJECT})
 }
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsData,
-                                                     NetworkStatsInterface,
                                                      NetworkStats,
                                                      NetworkStatsManager]);
--- a/dom/network/src/NetworkStatsManager.manifest
+++ b/dom/network/src/NetworkStatsManager.manifest
@@ -1,12 +1,9 @@
 component {3b16fe17-5583-483a-b486-b64a3243221c} NetworkStatsManager.js
 contract @mozilla.org/networkStatsdata;1 {3b16fe17-5583-483a-b486-b64a3243221c}
 
-component {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe} NetworkStatsManager.js
-contract @mozilla.org/networkStats;1 {b6fc4b14-628d-4c99-bf4e-e4ed56916cbe}
+component {6613ea55-b99c-44f9-91bf-d07da10b9b74} NetworkStatsManager.js
+contract @mozilla.org/networkStats;1 {6613ea55-b99c-44f9-91bf-d07da10b9b74}
 
-component {f540615b-d803-43ff-8200-2a9d145a5645} NetworkStatsManager.js
-contract @mozilla.org/networkstatsinterface;1 {f540615b-d803-43ff-8200-2a9d145a5645}
-
-component {5fbdcae6-a2cd-47b3-929f-83ac75bd4881} NetworkStatsManager.js
-contract @mozilla.org/networkStatsManager;1 {5fbdcae6-a2cd-47b3-929f-83ac75bd4881}
+component {87529a6c-aef6-11e1-a595-4f034275cfa6} NetworkStatsManager.js
+contract @mozilla.org/networkStatsManager;1 {87529a6c-aef6-11e1-a595-4f034275cfa6}
 category JavaScript-navigator-property mozNetworkStats @mozilla.org/networkStatsManager;1
--- a/dom/network/src/NetworkStatsService.jsm
+++ b/dom/network/src/NetworkStatsService.jsm
@@ -1,108 +1,80 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const DEBUG = false;
-function debug(s) {
-  if (DEBUG) {
-    dump("-*- NetworkStatsService: " + s + "\n");
-  }
-}
+function debug(s) { dump("-*- NetworkStatsService: " + s + "\n"); }
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 this.EXPORTED_SYMBOLS = ["NetworkStatsService"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
 
 const NET_NETWORKSTATSSERVICE_CONTRACTID = "@mozilla.org/network/netstatsservice;1";
 const NET_NETWORKSTATSSERVICE_CID = Components.ID("{18725604-e9ac-488a-8aa0-2471e7f6c0a4}");
 
 const TOPIC_INTERFACE_REGISTERED   = "network-interface-registered";
 const TOPIC_INTERFACE_UNREGISTERED = "network-interface-unregistered";
 const NET_TYPE_WIFI = Ci.nsINetworkInterface.NETWORK_TYPE_WIFI;
 const NET_TYPE_MOBILE = Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE;
+const NET_TYPE_UNKNOWN = Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN;
 
 // The maximum traffic amount can be saved in the |cachedAppStats|.
 const MAX_CACHED_TRAFFIC = 500 * 1000 * 1000; // 500 MB
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "networkManager",
                                    "@mozilla.org/network/manager;1",
                                    "nsINetworkManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "appsService",
                                    "@mozilla.org/AppsService;1",
                                    "nsIAppsService");
 
-XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService",
-                                   "@mozilla.org/settingsService;1",
-                                   "nsISettingsService");
-
-XPCOMUtils.defineLazyGetter(this, "gRadioInterface", function () {
-  let ril = Cc["@mozilla.org/ril;1"].getService(Ci["nsIRadioInterfaceLayer"]);
-  // TODO: Bug 923382 - B2G Multi-SIM: support multiple SIM cards for network metering.
-  return ril.getRadioInterface(0);
-});
-
 this.NetworkStatsService = {
   init: function() {
-    debug("Service started");
+    if (DEBUG) {
+      debug("Service started");
+    }
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     Services.obs.addObserver(this, TOPIC_INTERFACE_REGISTERED, false);
     Services.obs.addObserver(this, TOPIC_INTERFACE_UNREGISTERED, false);
     Services.obs.addObserver(this, "profile-after-change", false);
 
     this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
 
-    // Object to store network interfaces, each network interface is composed
-    // by a network object (network type and network Id) and a interfaceName
-    // that contains the name of the physical interface (wlan0, rmnet0, etc.).
-    // The network type can be 0 for wifi or 1 for mobile. On the other hand,
-    // the network id is '0' for wifi or the iccid for mobile (SIM).
-    // Each networkInterface is placed in the _networks object by the index of
-    // 'networkId + networkType'.
-    //
-    // _networks object allows to map available network interfaces at low level
-    // (wlan0, rmnet0, etc.) to a network. It's not mandatory to have a
-    // networkInterface per network but can't exist a networkInterface not
-    // being mapped to a network.
+    this._connectionTypes = Object.create(null);
+    this._connectionTypes[NET_TYPE_WIFI] = { name: "wifi",
+                                             network: Object.create(null) };
+    this._connectionTypes[NET_TYPE_MOBILE] = { name: "mobile",
+                                               network: Object.create(null) };
 
-    this._networks = Object.create(null);
-
-    // There is no way to know a priori if wifi connection is available,
-    // just when the wifi driver is loaded, but it is unloaded when
-    // wifi is switched off. So wifi connection is hardcoded
-    let netId = this.getNetworkId('0', NET_TYPE_WIFI);
-    this._networks[netId] = { network:       { id: '0',
-                                               type: NET_TYPE_WIFI },
-                              interfaceName: null };
 
     this.messages = ["NetworkStats:Get",
                      "NetworkStats:Clear",
-                     "NetworkStats:ClearAll",
-                     "NetworkStats:Networks",
+                     "NetworkStats:Types",
                      "NetworkStats:SampleRate",
-                     "NetworkStats:MaxStorageAge"];
+                     "NetworkStats:MaxStorageSamples"];
 
-    this.messages.forEach(function(aMsgName) {
-      ppmm.addMessageListener(aMsgName, this);
+    this.messages.forEach(function(msgName) {
+      ppmm.addMessageListener(msgName, this);
     }, this);
 
-    this._db = new NetworkStatsDB();
+    this._db = new NetworkStatsDB(this._connectionTypes);
 
     // Stats for all interfaces are updated periodically
     this.timer.initWithCallback(this, this._db.sampleRate,
                                 Ci.nsITimer.TYPE_REPEATING_PRECISE);
 
     // App stats are firstly stored in the cached.
     this.cachedAppStats = Object.create(null);
     this.cachedAppStatsDate = new Date();
@@ -111,66 +83,68 @@ this.NetworkStatsService = {
     this.isQueueRunning = false;
   },
 
   receiveMessage: function(aMessage) {
     if (!aMessage.target.assertPermission("networkstats-manage")) {
       return;
     }
 
-    debug("receiveMessage " + aMessage.name);
-
+    if (DEBUG) {
+      debug("receiveMessage " + aMessage.name);
+    }
     let mm = aMessage.target;
     let msg = aMessage.json;
 
     switch (aMessage.name) {
       case "NetworkStats:Get":
-        this.getSamples(mm, msg);
+        this.getStats(mm, msg);
         break;
       case "NetworkStats:Clear":
-        this.clearInterfaceStats(mm, msg);
-        break;
-      case "NetworkStats:ClearAll":
         this.clearDB(mm, msg);
         break;
-      case "NetworkStats:Networks":
-        return this.availableNetworks();
+      case "NetworkStats:Types":
+        // This message is sync.
+        let types = [];
+        for (let i in this._connectionTypes) {
+          types.push(this._connectionTypes[i].name);
+        }
+        return types;
       case "NetworkStats:SampleRate":
         // This message is sync.
         return this._db.sampleRate;
-      case "NetworkStats:MaxStorageAge":
+      case "NetworkStats:MaxStorageSamples":
         // This message is sync.
-        return this._db.maxStorageSamples * this._db.sampleRate;
+        return this._db.maxStorageSamples;
     }
   },
 
-  observe: function observe(aSubject, aTopic, aData) {
-    switch (aTopic) {
+  observe: function observe(subject, topic, data) {
+    switch (topic) {
       case TOPIC_INTERFACE_REGISTERED:
       case TOPIC_INTERFACE_UNREGISTERED:
-
         // If new interface is registered (notified from NetworkManager),
         // the stats are updated for the new interface without waiting to
-        // complete the updating period.
-
-        let network = aSubject.QueryInterface(Ci.nsINetworkInterface);
-        debug("Network " + network.name + " of type " + network.type + " status change");
-
-        let netId = this.convertNetworkInterface(network);
-        if (!netId) {
-          break;
+        // complete the updating period
+        let network = subject.QueryInterface(Ci.nsINetworkInterface);
+        if (DEBUG) {
+          debug("Network " + network.name + " of type " + network.type + " status change");
+        }
+        if (this._connectionTypes[network.type]) {
+          this._connectionTypes[network.type].network = network;
+          this.updateStats(network.type);
+        }
+        break;
+      case "xpcom-shutdown":
+        if (DEBUG) {
+          debug("Service shutdown");
         }
 
-        this.updateStats(netId);
-        break;
-      case "xpcom-shutdown":
-        debug("Service shutdown");
-
-        this.messages.forEach(function(aMsgName) {
-          ppmm.removeMessageListener(aMsgName, this);
+        this.messages.forEach(function(msgName) {
+          ppmm.removeMessageListener(msgName, this);
         }, this);
 
         Services.obs.removeObserver(this, "xpcom-shutdown");
         Services.obs.removeObserver(this, "profile-after-change");
         Services.obs.removeObserver(this, TOPIC_INTERFACE_REGISTERED);
         Services.obs.removeObserver(this, TOPIC_INTERFACE_UNREGISTERED);
 
         this.timer.cancel();
@@ -181,206 +155,167 @@ this.NetworkStatsService = {
         break;
     }
   },
 
   /*
    * nsITimerCallback
    * Timer triggers the update of all stats
    */
-  notify: function(aTimer) {
+  notify: function(timer) {
     this.updateAllStats();
   },
 
   /*
-   * nsINetworkStatsService
-   */
-
-  convertNetworkInterface: function(aNetwork) {
-    if (aNetwork.type != NET_TYPE_MOBILE &&
-        aNetwork.type != NET_TYPE_WIFI) {
-      return null;
-    }
-
-    let id = '0';
-    if (aNetwork.type == NET_TYPE_MOBILE) {
-      // Bug 904542 will provide the serviceId to map the iccId with the
-      // nsINetworkInterface of the NetworkManager. Now, lets assume that
-      // network is mapped with the current iccId of the single SIM.
-      id = gRadioInterface.rilContext.iccInfo.iccid;
-    }
-
-    let netId = this.getNetworkId(id, aNetwork.type);
-
-    if (!this._networks[netId]) {
-      this._networks[netId] = Object.create(null);
-      this._networks[netId].network = { id: id,
-                                        type: aNetwork.type };
-    }
-
-    this._networks[netId].interfaceName = aNetwork.name;
-    return netId;
-  },
-
-  getNetworkId: function getNetworkId(aIccId, aNetworkType) {
-    return aIccId + '' + aNetworkType;
-  },
-
-  availableNetworks: function availableNetworks() {
-    let result = [];
-    for (let netId in this._networks) {
-      result.push(this._networks[netId].network);
-    }
-
-    return result;
-  },
-
-  /*
    * Function called from manager to get stats from database.
    * In order to return updated stats, first is performed a call to
    * updateAllStats function, which will get last stats from netd
    * and update the database.
    * Then, depending on the request (stats per appId or total stats)
    * it retrieve them from database and return to the manager.
    */
-  getSamples: function getSamples(mm, msg) {
-    let self = this;
-    let network = msg.network;
-    let netId = this.getNetworkId(network.id, network.type);
+  getStats: function getStats(mm, msg) {
+    this.updateAllStats(function onStatsUpdated(aResult, aMessage) {
+
+      let data = msg.data;
+
+      let options = { appId:          0,
+                      connectionType: data.connectionType,
+                      start:          data.start,
+                      end:            data.end };
+
+      let manifestURL = data.manifestURL;
+      if (manifestURL) {
+        let appId = appsService.getAppLocalIdByManifestURL(manifestURL);
+        if (DEBUG) {
+          debug("get appId: " + appId + " from manifestURL: " + manifestURL);
+        }
+
+        if (!appId) {
+          mm.sendAsyncMessage("NetworkStats:Get:Return",
+                              { id: msg.id, error: "Invalid manifestURL", result: null });
+          return;
+        }
 
-    if (!this._networks[netId]) {
+        options.appId = appId;
+        options.manifestURL = manifestURL;
+      }
+
+      if (DEBUG) {
+        debug("getStats for options: " + JSON.stringify(options));
+      }
+
+      if (!options.connectionType || options.connectionType.length == 0) {
+        this._db.findAll(function onStatsFound(error, result) {
+          mm.sendAsyncMessage("NetworkStats:Get:Return",
+                              { id: msg.id, error: error, result: result });
+        }, options);
+        return;
+      }
+
+      for (let i in this._connectionTypes) {
+        if (this._connectionTypes[i].name == options.connectionType) {
+          this._db.find(function onStatsFound(error, result) {
+            mm.sendAsyncMessage("NetworkStats:Get:Return",
+                                { id: msg.id, error: error, result: result });
+          }, options);
+          return;
+        }
+      }
+
       mm.sendAsyncMessage("NetworkStats:Get:Return",
                           { id: msg.id, error: "Invalid connectionType", result: null });
-      return;
-    }
 
-    let appId = 0;
-    let manifestURL = msg.manifestURL;
-    if (manifestURL) {
-      appId = appsService.getAppLocalIdByManifestURL(manifestURL);
+    }.bind(this));
+  },
 
-      if (!appId) {
-        mm.sendAsyncMessage("NetworkStats:Get:Return",
-                            { id: msg.id, error: "Invalid manifestURL", result: null });
-        return;
-      }
-    }
-
-    let start = new Date(msg.start);
-    let end = new Date(msg.end);
-
-    this.updateStats(netId, function onStatsUpdated(aResult, aMessage) {
-      debug("getstats for network " + network.id + " of type " + network.type);
-      debug("appId: " + appId + " from manifestURL: " + manifestURL);
-
-      self._db.find(function onStatsFound(aError, aResult) {
-        mm.sendAsyncMessage("NetworkStats:Get:Return",
-                            { id: msg.id, error: aError, result: aResult });
-      }, network, start, end, appId, manifestURL);
-
+  clearDB: function clearDB(mm, msg) {
+    this._db.clear(function onDBCleared(error, result) {
+      mm.sendAsyncMessage("NetworkStats:Clear:Return",
+                          { id: msg.id, error: error, result: result });
     });
   },
 
-  clearInterfaceStats: function clearInterfaceStats(mm, msg) {
-    let network = msg.network;
-    let netId = this.getNetworkId(network.id, network.type);
-
-    debug("clear stats for network " + network.id + " of type " + network.type);
-
-    if (!this._networks[netId]) {
-      mm.sendAsyncMessage("NetworkStats:Clear:Return",
-                          { id: msg.id, error: "Invalid networkType", result: null });
-      return;
-    }
-
-    this._db.clearInterfaceStats(network, function onDBCleared(aError, aResult) {
-        mm.sendAsyncMessage("NetworkStats:Clear:Return",
-                            { id: msg.id, error: aError, result: aResult });
-    });
-  },
-
-  clearDB: function clearDB(mm, msg) {
-    let networks = this.availableNetworks();
-    this._db.clearStats(networks, function onDBCleared(aError, aResult) {
-      mm.sendAsyncMessage("NetworkStats:ClearAll:Return",
-                          { id: msg.id, error: aError, result: aResult });
-    });
-  },
-
-  updateAllStats: function updateAllStats(aCallback) {
+  updateAllStats: function updateAllStats(callback) {
     // Update |cachedAppStats|.
     this.updateCachedAppStats();
 
     let elements = [];
     let lastElement;
 
     // For each connectionType create an object containning the type
     // and the 'queueIndex', the 'queueIndex' is an integer representing
     // the index of a connection type in the global queue array. So, if
     // the connection type is already in the queue it is not appended again,
     // else it is pushed in 'elements' array, which later will be pushed to
     // the queue array.
-    for (let netId in this._networks) {
-      lastElement = { netId: netId,
-                      queueIndex: this.updateQueueIndex(netId)};
-
+    for (let i in this._connectionTypes) {
+      lastElement = { type: i,
+                      queueIndex: this.updateQueueIndex(i)};
       if (lastElement.queueIndex == -1) {
-        elements.push({netId: lastElement.netId, callbacks: []});
+        elements.push({type: lastElement.type, callbacks: []});
       }
     }
 
     if (elements.length > 0) {
       // If length of elements is greater than 0, callback is set to
       // the last element.
-      elements[elements.length - 1].callbacks.push(aCallback);
+      elements[elements.length - 1].callbacks.push(callback);
       this.updateQueue = this.updateQueue.concat(elements);
     } else {
       // Else, it means that all connection types are already in the queue to
       // be updated, so callback for this request is added to
       // the element in the main queue with the index of the last 'lastElement'.
       // But before is checked that element is still in the queue because it can
       // be processed while generating 'elements' array.
-      let element = this.updateQueue[lastElement.queueIndex];
-      if (aCallback &&
-         (!element || element.netId != lastElement.netId)) {
-        aCallback();
+
+      if (!this.updateQueue[lastElement.queueIndex] ||
+          this.updateQueue[lastElement.queueIndex].type != lastElement.queueIndex) {
+        if (callback) {
+          callback();
+        }
         return;
       }
 
-      this.updateQueue[lastElement.queueIndex].callbacks.push(aCallback);
+      this.updateQueue[lastElement.queueIndex].callbacks.push(callback);
     }
 
     // Call the function that process the elements of the queue.
     this.processQueue();
 
     if (DEBUG) {
       this.logAllRecords();
     }
   },
 
-  updateStats: function updateStats(aNetId, aCallback) {
-    // Check if the connection is in the main queue, push a new element
+  updateStats: function updateStats(connectionType, callback) {
+    // Check if the connection type is in the main queue, push a new element
     // if it is not being processed or add a callback if it is.
-    let index = this.updateQueueIndex(aNetId);
+    let index = this.updateQueueIndex(connectionType);
     if (index == -1) {
-      this.updateQueue.push({netId: aNetId, callbacks: [aCallback]});
+      this.updateQueue.push({type: connectionType, callbacks: [callback]});
     } else {
-      this.updateQueue[index].callbacks.push(aCallback);
+      this.updateQueue[index].callbacks.push(callback);
     }
 
     // Call the function that process the elements of the queue.
     this.processQueue();
   },
 
   /*
-   * Find if a connection is in the main queue array and return its
+   * Find if a connection type is in the main queue array and return its
    * index, if it is not in the array return -1.
    */
-  updateQueueIndex: function updateQueueIndex(aNetId) {
-    return this.updateQueue.map(function(e) { return e.netId; }).indexOf(aNetId);
+  updateQueueIndex: function updateQueueIndex(type) {
+    for (let i in this.updateQueue) {
+      if (this.updateQueue[i].type == type) {
+        return i;
+      }
+    }
+    return -1;
   },
 
   /*
    * Function responsible of process all requests in the queue.
    */
   processQueue: function processQueue(aResult, aMessage) {
     // If aResult is not undefined, the caller of the function is the result
     // of processing an element, so remove that element and call the callbacks
@@ -407,109 +342,101 @@ this.NetworkStatsService = {
 
     // Check length to determine if queue is empty and stop processing.
     if (this.updateQueue.length < 1) {
       this.isQueueRunning = false;
       return;
     }
 
     // Call the update function for the next element.
-    this.update(this.updateQueue[0].netId, this.processQueue.bind(this));
+    this.update(this.updateQueue[0].type, this.processQueue.bind(this));
   },
 
-  update: function update(aNetId, aCallback) {
+  update: function update(connectionType, callback) {
     // Check if connection type is valid.
-    if (!this._networks[aNetId]) {
-      if (aCallback) {
-        aCallback(false, "Invalid network " + aNetId);
+    if (!this._connectionTypes[connectionType]) {
+      if (callback) {
+        callback(false, "Invalid network type " + connectionType);
       }
       return;
     }
 
-    let interfaceName = this._networks[aNetId].interfaceName;
-    debug("Update stats for " + interfaceName);
+    if (DEBUG) {
+      debug("Update stats for " + this._connectionTypes[connectionType].name);
+    }
 
     // Request stats to NetworkManager, which will get stats from netd, passing
     // 'networkStatsAvailable' as a callback.
-    if (interfaceName) {
-      networkManager.getNetworkInterfaceStats(interfaceName,
-                this.networkStatsAvailable.bind(this, aCallback, aNetId));
+    let networkName = this._connectionTypes[connectionType].network.name;
+    if (networkName) {
+      networkManager.getNetworkInterfaceStats(networkName,
+                this.networkStatsAvailable.bind(this, callback, connectionType));
       return;
     }
-
-    if (aCallback) {
-      aCallback(true, "ok");
+    if (callback) {
+      callback(true, "ok");
     }
   },
 
   /*
    * Callback of request stats. Store stats in database.
    */
-  networkStatsAvailable: function networkStatsAvailable(aCallback, aNetId,
-                                                        aResult, aRxBytes,
-                                                        aTxBytes, aDate) {
-    if (!aResult) {
-      if (aCallback) {
-        aCallback(false, "Netd IPC error");
+  networkStatsAvailable: function networkStatsAvailable(callback, connType, result, rxBytes, txBytes, date) {
+    if (!result) {
+      if (callback) {
+        callback(false, "Netd IPC error");
       }
       return;
     }
 
-    let stats = { appId:       0,
-                  networkId:   this._networks[aNetId].network.id,
-                  networkType: this._networks[aNetId].network.type,
-                  date:        aDate,
-                  rxBytes:     aTxBytes,
-                  txBytes:     aRxBytes };
+    let stats = { appId:          0,
+                  connectionType: this._connectionTypes[connType].name,
+                  date:           date,
+                  rxBytes:        rxBytes,
+                  txBytes:        txBytes };
 
-    debug("Update stats for: " + JSON.stringify(stats));
-
-    this._db.saveStats(stats, function onSavedStats(aError, aResult) {
-      if (aCallback) {
-        if (aError) {
-          aCallback(false, aError);
+    if (DEBUG) {
+      debug("Update stats for " + stats.connectionType + ": rx=" + stats.rxBytes +
+            " tx=" + stats.txBytes + " timestamp=" + stats.date);
+    }
+    this._db.saveStats(stats, function onSavedStats(error, result) {
+      if (callback) {
+        if (error) {
+          callback(false, error);
           return;
         }
 
-        aCallback(true, "OK");
+        callback(true, "OK");
       }
     });
   },
 
   /*
    * Function responsible for receiving per-app stats.
    */
-  saveAppStats: function saveAppStats(aAppId, aNetwork, aTimeStamp, aRxBytes, aTxBytes, aCallback) {
-    let netId = this.convertNetworkInterface(aNetwork);
-    if (!netId) {
-      if (aCallback) {
-        aCallback.notify(false, "Invalid network type");
-      }
-      return;
+  saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp, aRxBytes, aTxBytes, aCallback) {
+    if (DEBUG) {
+      debug("saveAppStats: " + aAppId + " " + aConnectionType + " " +
+            aTimeStamp + " " + aRxBytes + " " + aTxBytes);
     }
 
-    debug("saveAppStats: " + aAppId + " " + netId + " " +
-          aTimeStamp + " " + aRxBytes + " " + aTxBytes);
-
     // Check if |aAppId| and |aConnectionType| are valid.
-    if (!aAppId || !this._networks[netId]) {
-      debug("Invalid appId or network interface");
+    if (!aAppId || aConnectionType == NET_TYPE_UNKNOWN) {
       return;
     }
 
     let stats = { appId: aAppId,
-                  networkId: this._networks[netId].network.id,
-                  networkType: this._networks[netId].network.type,
+                  connectionType: this._connectionTypes[aConnectionType].name,
                   date: new Date(aTimeStamp),
                   rxBytes: aRxBytes,
                   txBytes: aTxBytes };
 
     // Generate an unique key from |appId| and |connectionType|,
     // which is used to retrieve data in |cachedAppStats|.
-    let key = stats.appId + "" + netId;
+    let key = stats.appId + stats.connectionType;
 
     // |cachedAppStats| only keeps the data with the same date.
     // If the incoming date is different from |cachedAppStatsDate|,
     // both |cachedAppStats| and |cachedAppStatsDate| will get updated.
     let diff = (this._db.normalizeDate(stats.date) -
                 this._db.normalizeDate(this.cachedAppStatsDate)) /
                this._db.sampleRate;
     if (diff != 0) {
@@ -546,25 +473,29 @@ this.NetworkStatsService = {
 
     // If new rxBytes or txBytes exceeds MAX_CACHED_TRAFFIC
     // the corresponding row will be saved to indexedDB.
     // Then, the row will be removed from the cached.
     if (appStats.rxBytes > MAX_CACHED_TRAFFIC ||
         appStats.txBytes > MAX_CACHED_TRAFFIC) {
       this._db.saveStats(appStats,
         function (error, result) {
-          debug("Application stats inserted in indexedDB");
+          if (DEBUG) {
+            debug("Application stats inserted in indexedDB");
+          }
         }
       );
       delete this.cachedAppStats[key];
     }
   },
 
-  updateCachedAppStats: function updateCachedAppStats(aCallback) {
-    debug("updateCachedAppStats: " + this.cachedAppStatsDate);
+  updateCachedAppStats: function updateCachedAppStats(callback) {
+    if (DEBUG) {
+      debug("updateCachedAppStats: " + this.cachedAppStatsDate);
+    }
 
     let stats = Object.keys(this.cachedAppStats);
     if (stats.length == 0) {
       // |cachedAppStats| is empty, no need to update.
       return;
     }
 
     let index = 0;
@@ -573,47 +504,47 @@ this.NetworkStatsService = {
         if (DEBUG) {
           debug("Application stats inserted in indexedDB");
         }
 
         // Clean up the |cachedAppStats| after updating.
         if (index == stats.length - 1) {
           this.cachedAppStats = Object.create(null);
 
-          if (!aCallback) {
+          if (!callback) {
             return;
           }
 
           if (error) {
-            aCallback(false, error);
+            callback(false, error);
             return;
           }
 
-          aCallback(true, "ok");
+          callback(true, "ok");
           return;
         }
 
         // Update is not finished, keep updating.
         index += 1;
         this._db.saveStats(this.cachedAppStats[stats[index]],
                            onSavedStats.bind(this, error, result));
       }.bind(this));
   },
 
   get maxCachedTraffic () {
     return MAX_CACHED_TRAFFIC;
   },
 
   logAllRecords: function logAllRecords() {
-    this._db.logAllRecords(function onResult(aError, aResult) {
-      if (aError) {
-        debug("Error: " + aError);
+    this._db.logAllRecords(function onResult(error, result) {
+      if (error) {
+        debug("Error: " + error);
         return;
       }
 
       debug("===== LOG =====");
-      debug("There are " + aResult.length + " items");
-      debug(JSON.stringify(aResult));
+      debug("There are " + result.length + " items");
+      debug(JSON.stringify(result));
     });
-  },
+  }
 };
 
 NetworkStatsService.init();
--- a/dom/network/src/NetworkStatsServiceProxy.js
+++ b/dom/network/src/NetworkStatsServiceProxy.js
@@ -24,24 +24,24 @@ function NetworkStatsServiceProxy() {
   }
 }
 
 NetworkStatsServiceProxy.prototype = {
   /*
    * Function called in the protocol layer (HTTP, FTP, WebSocket ...etc)
    * to pass the per-app stats to NetworkStatsService.
    */
-  saveAppStats: function saveAppStats(aAppId, aNetwork, aTimeStamp,
+  saveAppStats: function saveAppStats(aAppId, aConnectionType, aTimeStamp,
                                       aRxBytes, aTxBytes, aCallback) {
     if (DEBUG) {
-      debug("saveAppStats: " + aAppId + " connectionType " + aNetwork.type +
-            " " + aTimeStamp + " " + aRxBytes + " " + aTxBytes);
+      debug("saveAppStats: " + aAppId + " " + aConnectionType + " " +
+            aTimeStamp + " " + aRxBytes + " " + aTxBytes);
     }
 
-    NetworkStatsService.saveAppStats(aAppId, aNetwork, aTimeStamp,
-                                      aRxBytes, aTxBytes, aCallback);
+    NetworkStatsService.saveAppStats(aAppId, aConnectionType, aTimeStamp,
+                                     aRxBytes, aTxBytes, aCallback);
   },
 
   classID : NETWORKSTATSSERVICEPROXY_CID,
   QueryInterface : XPCOMUtils.generateQI([nsINetworkStatsServiceProxy]),
 }
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkStatsServiceProxy]);
--- a/dom/network/src/TCPSocket.js
+++ b/dom/network/src/TCPSocket.js
@@ -162,17 +162,17 @@ TCPSocket.prototype = {
   _waitingForStartTLS: false,
   _pendingDataAfterStartTLS: [],
 
 #ifdef MOZ_WIDGET_GONK
   // Network statistics (Gonk-specific feature)
   _txBytes: 0,
   _rxBytes: 0,
   _appId: Ci.nsIScriptSecurityManager.NO_APP_ID,
-  _activeNetwork: null,
+  _connectionType: Ci.nsINetworkInterface.NETWORK_TYPE_UNKNOWN,
 #endif
 
   // Public accessors.
   get readyState() {
     return this._readyState;
   },
   get binaryType() {
     return this._binaryType;
@@ -342,17 +342,17 @@ TCPSocket.prototype = {
     }
 
     let nssProxy = Cc["@mozilla.org/networkstatsServiceProxy;1"]
                      .getService(Ci.nsINetworkStatsServiceProxy);
     if (!nssProxy) {
       LOG("Error: Ci.nsINetworkStatsServiceProxy service is not available.");
       return;
     }
-    nssProxy.saveAppStats(this._appId, this._activeNetwork, Date.now(),
+    nssProxy.saveAppStats(this._appId, this._connectionType, Date.now(),
                           this._rxBytes, this._txBytes);
 
     // Reset the counters once the statistics is saved to NetworkStatsServiceProxy.
     this._txBytes = this._rxBytes = 0;
   },
   // End of helper method for network statistics.
 #endif
 
@@ -525,22 +525,22 @@ TCPSocket.prototype = {
       return that;
     }
 
     let transport = that._transport = this._createTransport(host, port, that._ssl);
     transport.setEventSink(that, Services.tm.currentThread);
     that._initStream(that._binaryType);
 
 #ifdef MOZ_WIDGET_GONK
-    // Set _activeNetwork, which is only required for network statistics.
+    // Set _connectionType, which is only required for network statistics.
     // Note that nsINetworkManager, as well as nsINetworkStatsServiceProxy, is
     // Gonk-specific.
     let networkManager = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
-    if (networkManager) {
-      that._activeNetwork = networkManager.active;
+    if (networkManager && networkManager.active) {
+      that._connectionType = networkManager.active.type;
     }
 #endif
 
     return that;
   },
 
   upgradeToSecure: function ts_upgradeToSecure() {
     if (this._readyState !== kOPEN) {
--- a/dom/network/tests/test_networkstats_basics.html
+++ b/dom/network/tests/test_networkstats_basics.html
@@ -7,44 +7,55 @@
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
+// Test for NetworkStats
+function checkInterface(aInterface) {
+  ok(!(aInterface in window), aInterface + " should be prefixed");
+  ok(("Moz" + aInterface) in window, aInterface + " should be prefixed");
+}
+
 function test() {
+  // Test interfaces
+  checkInterface("NetworkStatsManager");
+  checkInterface("NetworkStats");
+  checkInterface("NetworkStatsData");
+
   ok('mozNetworkStats' in navigator, "navigator.mozMozNetworkStats should exist");
   ok(navigator.mozNetworkStats, "navigator.mozNetworkStats returns an object");
 
   netStats = navigator.mozNetworkStats;
 
   // Test IDL attributes
-  ok('availableNetworks' in netStats,
-   "availableNetworks should be a NetworkStats attribute");
-  ok(Array.isArray(netStats.availableNetworks) && netStats.availableNetworks.length > 0,
-   "availableNetworks is an array not empty.");
+  ok('connectionTypes' in netStats,
+   "connectionTypes should be a NetworkStats attribute");
+  ok(Array.isArray(netStats.connectionTypes) && netStats.connectionTypes.length > 0,
+   "connectionTypes is an array not empty.");
 
   ok('sampleRate' in netStats,
    "sampleRate should be a NetworkStats attribute");
   ok(netStats.sampleRate > 0,
    "sampleRate is greater than 0.");
 
-  ok('maxStorageAge' in netStats,
-   "maxStorageAge should be a NetworkStats attribute");
-  ok(netStats.maxStorageAge > 0,
-   "maxStorageAge is greater than 0.");
+  ok('maxStorageSamples' in netStats,
+   "maxStorageSamples should be a NetworkStats attribute");
+  ok(netStats.maxStorageSamples > 0,
+   "maxStorageSamples is greater than 0.");
 
   // Test IDL methods
   next();
   return;
 }
 
-function checkDataDates(data, start, end, sampleRate) {
+function checkDataDates(data, start, end, sampleRate){
   var offset = (new Date()).getTimezoneOffset() * 60 * 1000;
   start = Math.floor((start.getTime() - offset) / sampleRate) * sampleRate + offset;
   end = Math.floor((end.getTime() - offset) / sampleRate) * sampleRate + offset;
 
   var counter = 0;
   var date = start;
   var success = true;
 
@@ -55,195 +66,292 @@ function checkDataDates(data, start, end
     }
     date += sampleRate;
     counter++;
   } while (date <= end);
 
   ok(success, "data result has correct dates");
 }
 
-function compareNetworks(networkA, networkB) {
-  return (networkA.id == networkB.id &&
-          networkA.type == networkB.type);
-}
-
 var req;
 var index = -1;
 var netStats = null;
 
 var steps = [
   function () {
-    // Test clearAllStats
-    req = netStats.clearAllStats();
+    // Test clearAlldata
+    req = netStats.clearAllData();
     req.onsuccess = function () {
-      ok(true, "clearAllStats deleted the database");
+      ok(true, "clearAllData deleted the database");
       next();
     };
     req.onerror = function () {
-      ok(false, "clearAllStats deleted the database");
+      ok(false, "clearAllData deleted the database");
     }
   },
   function () {
-    // Check if getSamples launch exception when start is greather than end
+    // Check if getNetworkStats launch exception when start is greather than end
 
     // Prepare get params
-    var network = netStats.availableNetworks[0];
+    var type = netStats.connectionTypes[0];
     // Get dates
     var endDate = new Date();
     var startDate = new Date(endDate.getTime() + 1000);
 
     try {
-      netStats.getSamples(network, startDate, endDate);
+      netStats.getNetworkStats({start: startDate, end: endDate});
     } catch(ex) {
-      ok(true, "getSamples launch exception when start is greater than end");
-      next();
-      return;
-    }
-
-    ok(false, "getSamples launch exception when start is greater than end");
-    next();
-    return;
-  },
-  function () {
-    // Test if call getSamples with network of type different than
-    // nsIDOMMozNetworkStatsInterface launch an exception
-
-    // Prepare get params
-    var network = "wifi";
-    var endDate = new Date();
-    var startDate = new Date(endDate.getTime() - 1000);
-
-    try {
-      netStats.getSamples(network, new Date(), new Date());
-    } catch(ex) {
-      ok(true, "getSamples launch exception if network is not " +
-               "a nsIDOMMozNetworkStatsInterface");
+      ok(true, "getNetworkStats launch exception when start is greater than end");
       next();
       return;
     }
 
-    ok(false, "getSamples launch exception if network is not " +
-              "a nsIDOMMozNetworkStatsInterface");
+    ok(false, "getNetworkStats launch exceptionwhen start is greater than end");
+    next();
+    return;
   },
   function () {
-    // Test if call getSamples with start parameter type different than Date launch an exception
+    // Test if call getNetworkStats with undefined start param launch an exception
 
     // Prepare get params
-    var network = netStats.availableNetworks[0];
-    var endDate = new Date();
-    var startDate = new Date(endDate.getTime() - 1000);
-    startDate = startDate.toString();
+    var type = netStats.connectionTypes[0];
+    setTimeout(function() {
+      try {
+        netStats.getNetworkStats({end: new Date()});
+      } catch(ex) {
+        ok(true, "getNetworkStats launch exception when start param does not exist");
+        next();
+        return;
+      }
 
-    try {
-      netStats.getSamples(network, startDate, endDate);
-    } catch(ex) {
-      ok(true, "getSamples launch exception when start param is not a Date");
-      next();
-      return;
-    }
+      ok(false, "getNetworkStats launch exception when start param does not exist");
+    }, 1000);
+  },
+  function () {
+    // Test if call getNetworkStats with undefined end param launch an exception
 
-    ok(false, "getSamples launch exception when start param is not a Date");
+    // Prepare get params
+    var type = netStats.connectionTypes[0];
+    setTimeout(function() {
+      try {
+        netStats.getNetworkStats({start: new Date()});
+      } catch(ex) {
+        ok(true, "getNetworkStats launch exception when end param does not exist");
+        next();
+        return;
+      }
+
+      ok(false, "getNetworkStats launch exception when end param does not exist");
+    }, 1000);
   },
   function () {
-    // Test if call getSamples with end parameter type different than Date launch an exception
-
+    ok(true, "Get system stats for a connectionType and dates adapted to samplerate");
     // Prepare get params
-    var network = netStats.availableNetworks[0];
-    var endDate = new Date();
-    var startDate = new Date(endDate.getTime() - 1000);
-    endDate = startDate.toString();
+    var type = netStats.connectionTypes[0];
+    var diff = 2;
+    // Get samplerate in millis
+    var sampleRate = netStats.sampleRate * 1000;
+    // Get date with samplerate's precision
+    var offset = new Date().getTimezoneOffset() * 60 * 1000;
+    var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
+                           * sampleRate + offset);
+    var startDate = new Date(endDate.getTime() - (sampleRate * diff));
+    // Calculate the number of samples that should be returned based on the
+    // the samplerate and including final and initial samples.
+    var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
 
-    try {
-      netStats.getSamples(network, startDate, endDate);
-    } catch(ex) {
-      ok(true, "getSamples launch exception when end param is not a Date");
+    // Launch request
+    req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type});
+    req.onsuccess = function () {
+      ok(true, "Get system stats request ok");
+      ok(req.result.manifestURL == null, "manifestURL should be null");
+      ok(req.result.connectionType == type, "connectionTypes should be equals");
+      ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
+      ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
+      var data = req.result.data;
+      ok(Array.isArray(data) && data.length == samples,
+         "data is an array of length " + samples);
+      checkDataDates(data, startDate, endDate, sampleRate);
       next();
-      return;
+    };
+    req.onerror = function () {
+      ok(false, "Get system stats for a connectionType failure!");
     }
-
-    ok(false, "getSamples launch exception when end param is not a Date");
   },
   function () {
-    ok(true, "Get stats for a network and dates adapted to samplerate");
+    ok(true, "Get system stats for all connectionTypes and dates adapted to samplerate");
     // Prepare get params
-    var network = netStats.availableNetworks[0];
     var diff = 2;
     // Get samplerate in millis
-    var sampleRate = netStats.sampleRate;
+    var sampleRate = netStats.sampleRate * 1000;
     // Get date with samplerate's precision
     var offset = new Date().getTimezoneOffset() * 60 * 1000;
     var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
                            * sampleRate + offset);
     var startDate = new Date(endDate.getTime() - (sampleRate * diff));
     // Calculate the number of samples that should be returned based on the
     // the samplerate and including final and initial samples.
     var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
 
     // Launch request
-    req = netStats.getSamples(network, startDate, endDate);
+    req = netStats.getNetworkStats({start: startDate, end: endDate});
     req.onsuccess = function () {
-      ok(true, "Get system stats request ok");
+      ok(true, "Get stats request ok");
       ok(req.result.manifestURL == null, "manifestURL should be null");
-      ok(compareNetworks(req.result.network, network), "networks should be equals");
+      ok(req.result.connectionType == null, "connectionTypes should be null");
+      ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
+      ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
+      var data = req.result.data;
+      ok(Array.isArray(data) && data.length == samples,
+         "data is an array of length " + samples);
+      checkDataDates(data, startDate, endDate, sampleRate);
+      next();
+    };
+    req.onerror = function () {
+      ok(false, "Get system stats for all connectionTypes failure!");
+    }
+  },
+  function () {
+    ok(true, "Get app stats for a connectionType and dates adapted to samplerate");
+    // Prepare get params
+    var url = 'app://browser.gaiamobile.org/manifest.webapp';
+    var type = netStats.connectionTypes[0];
+    var diff = 2;
+    // Get samplerate in millis
+    var sampleRate = netStats.sampleRate * 1000;
+    // Get date with samplerate's precision
+    var offset = new Date().getTimezoneOffset() * 60 * 1000;
+    var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
+                           * sampleRate + offset);
+    var startDate = new Date(endDate.getTime() - (sampleRate * diff));
+    // Calculate the number of samples that should be returned based on the
+    // the samplerate and including final and initial samples.
+    var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
+
+    // Launch request
+    req = netStats.getNetworkStats({start:          startDate,
+                                    end:            endDate,
+                                    connectionType: type,
+                                    manifestURL:    url});
+    req.onsuccess = function () {
+      ok(true, "Get app stats request ok");
+      ok(req.result.manifestURL == url, "manifestURL should be equals");
+      ok(req.result.connectionType == type, "connectionTypes should be equals");
       ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
       ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
       var data = req.result.data;
       ok(Array.isArray(data) && data.length == samples,
          "data is an array of length " + samples);
       checkDataDates(data, startDate, endDate, sampleRate);
       next();
     };
     req.onerror = function () {
-      ok(false, "Get stats failure!");
+      ok(false, "Get app stats for a connectionType failure!");
     }
   },
   function () {
-    ok(true, "Get system stats for a network and dates not adapted to samplerate");
+    ok(true, "Get app stats for all connectionTypes and dates adapted to samplerate");
     // Prepare get params
-    var network = netStats.availableNetworks[0];
+    var url = 'app://browser.gaiamobile.org/manifest.webapp';
     var diff = 2;
     // Get samplerate in millis
-    var sampleRate = netStats.sampleRate;
+    var sampleRate = netStats.sampleRate * 1000;
+    // Get date with samplerate's precision
+    var offset = new Date().getTimezoneOffset() * 60 * 1000;
+    var endDate = new Date(Math.floor((new Date().getTime() - offset) / sampleRate)
+                           * sampleRate + offset);
+    var startDate = new Date(endDate.getTime() - (sampleRate * diff));
+    // Calculate the number of samples that should be returned based on the
+    // the samplerate and including final and initial samples.
+    var samples = (endDate.getTime() - startDate.getTime()) / sampleRate + 1;
+
+    // Launch request
+    req = netStats.getNetworkStats({start:       startDate,
+                                    end:         endDate,
+                                    manifestURL: url});
+    req.onsuccess = function () {
+      ok(true, "Get app stats request ok");
+      ok(req.result.manifestURL == url, "manifestURL should be equals");
+      ok(req.result.connectionType == null, "connectionTypes should be null");
+      ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
+      ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
+      var data = req.result.data;
+      ok(Array.isArray(data) && data.length == samples,
+         "data is an array of length " + samples);
+      checkDataDates(data, startDate, endDate, sampleRate);
+      next();
+    };
+    req.onerror = function () {
+      ok(false, "Get app stats for all connectionTypes failure!");
+    }
+  },
+  function () {
+    ok(true, "Get system stats for a connectionType and dates not adapted to samplerate");
+    // Prepare get params
+    var type = netStats.connectionTypes[0];
+    var diff = 2;
+    // Get samplerate in millis
+    var sampleRate = netStats.sampleRate * 1000;
     var endDate = new Date();
     var startDate = new Date(endDate.getTime() - (sampleRate * diff));
     // Calculate the number of samples that should be returned based on the
     // the samplerate, including final and initial samples and taking into
     // account that these will be filtered according to precision.
     var samples = (Math.floor(endDate.getTime() / (sampleRate)) * sampleRate -
                    Math.floor(startDate.getTime() / (sampleRate)) * sampleRate) / sampleRate + 1;
 
     // Launch request
-    req = netStats.getSamples(network, startDate, endDate);
+    req = netStats.getNetworkStats({start: startDate, end: endDate, connectionType: type});
     req.onsuccess = function () {
-      ok(true, "Get stats request ok");
+      ok(true, "Get system stats request ok");
       ok(req.result.manifestURL == null, "manifestURL should be null");
-      ok(compareNetworks(req.result.network, network), "networks should be equals");
+      ok(req.result.connectionType == type, "connectionTypes should be equals");
       ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
       ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
       var data = req.result.data;
       ok(Array.isArray(data) && data.length == samples,
          "data is an array of length " + samples);
       checkDataDates(data, startDate, endDate, sampleRate);
       next();
     };
     req.onerror = function () {
-      ok(false, "Get stats failure!");
+      ok(false, "Get system stats for a connectionType failure!");
     }
   },
   function () {
-    // Test clearStats
-    var network = netStats.availableNetworks[0];
+    ok(true, "Get system stats for all connectionTypes and dates not adapted to samplerate");
+    // Prepare get params
+    var diff = 2;
+    // Get samplerate in millis
+    var sampleRate = netStats.sampleRate * 1000;
+    // Get date with samplerate's precision
+    var endDate = new Date();
+    var startDate = new Date(endDate.getTime() - (sampleRate * diff));
+    // Calculate the number of samples that should be returned based on the
+    // the samplerate, including final and initial samples and taking into
+    // account that these will be filtered according to precision.
+    var samples = (Math.floor(endDate.getTime() / (sampleRate)) * sampleRate -
+                   Math.floor(startDate.getTime() / (sampleRate)) * sampleRate) / sampleRate + 1;
 
-    req = netStats.clearStats(network);
+    // Launch request
+    req = netStats.getNetworkStats({start: startDate, end: endDate});
     req.onsuccess = function () {
-      ok(true, "clearStats deleted the database");
+      ok(true, "Get stats request ok");
+      ok(req.result.manifestURL == null, "manifestURL should be null");
+      ok(req.result.connectionType == null, "connectionTypes should be null");
+      ok(req.result.start.getTime() == startDate.getTime(), "starts should be equals");
+      ok(req.result.end.getTime() == endDate.getTime(), "ends should be equals");
+      var data = req.result.data;
+      ok(Array.isArray(data) && data.length == samples,
+         "data is an array of length " + samples);
+      checkDataDates(data, startDate, endDate, sampleRate);
       next();
     };
     req.onerror = function () {
-      ok(false, "clearStats deleted the database");
+      ok(false, "Get system stats for all connectionType failure!");
     }
   },
   function () {
     ok(true, "all done!\n");
     SpecialPowers.removePermission("networkstats-manage", document);
     SimpleTest.finish();
     return;
   }
--- a/dom/network/tests/test_networkstats_enabled_no_perm.html
+++ b/dom/network/tests/test_networkstats_enabled_no_perm.html
@@ -7,29 +7,29 @@
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-// Test to ensure NetworkStats is enabled but mozNetworkStats.availableNetworks
+// Test to ensure NetworkStats is enabled but mozNetworkStats.connectionTypes
 // does not work in content.
 
 SpecialPowers.setBoolPref("dom.mozNetworkStats.enabled", true);
 SpecialPowers.removePermission("networkstats-manage", document);
 
 ok('mozNetworkStats' in navigator, "navigator.mozNetworkStats should be accessible if dom.mozNetworkStats.enabled is true");
 
 var error;
 try {
-  navigator.mozNetworkStats.availableNetworks;
-  ok(false, "Accessing navigator.mozNetworkStats.availableNetworks should have thrown!");
+  navigator.mozNetworkStats.connectionTypes;
+  ok(false, "Accessing navigator.mozNetworkStats.connectionTypes should have thrown!");
 } catch (ex) {
   error = ex;
 }
-ok(error, "Got an exception accessing navigator.mozNetworkStats.availableNetworks");
+ok(error, "Got an exception accessing navigator.mozNetworkStats.connectionTypes");
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/network/tests/unit_stats/test_networkstats_db.js
+++ b/dom/network/tests/unit_stats/test_networkstats_db.js
@@ -1,32 +1,23 @@
 /* Any: copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/NetworkStatsDB.jsm");
 
-const netStatsDb = new NetworkStatsDB();
+const netStatsDb = new NetworkStatsDB(this);
 
 function filterTimestamp(date) {
   var sampleRate = netStatsDb.sampleRate;
   var offset = date.getTimezoneOffset() * 60 * 1000;
   return Math.floor((date.getTime() - offset) / sampleRate) * sampleRate;
 }
 
-function getNetworks() {
-  return [{ id: '0', type: Ci.nsIDOMMozNetworkStatsManager.WIFI },
-          { id: '1234', type: Ci.nsIDOMMozNetworkStatsManager.MOBILE }];
-}
-
-function compareNetworks(networkA, networkB) {
-  return (networkA[0] == networkB[0] && networkA[1] == networkB[1]);
-}
-
 add_test(function test_sampleRate() {
   var sampleRate = netStatsDb.sampleRate;
   do_check_true(sampleRate > 0);
   netStatsDb.sampleRate = 0;
   sampleRate = netStatsDb.sampleRate;
   do_check_true(sampleRate > 0);
 
   run_next_test();
@@ -93,99 +84,80 @@ add_test(function test_fillResultSamples
     aux += sampleRate;
   }
   do_check_true(success);
 
   run_next_test();
 });
 
 add_test(function test_clear() {
-  var networks = getNetworks();
-  netStatsDb.clearStats(networks, function (error, result) {
-    do_check_eq(error, null);
-    run_next_test();
-  });
-});
-
-add_test(function test_clear_interface() {
-  var networks = getNetworks();
-  netStatsDb.clearInterfaceStats(networks[0], function (error, result) {
+  netStatsDb.clear(function (error, result) {
     do_check_eq(error, null);
     run_next_test();
   });
 });
 
 add_test(function test_internalSaveStats_singleSample() {
-  var networks = getNetworks();
-
-  var stats = { appId:        0,
-                network:      [networks[0].id, networks[0].type],
-                timestamp:    Date.now(),
-                rxBytes:      0,
-                txBytes:      0,
-                rxTotalBytes: 1234,
-                txTotalBytes: 1234 };
+  var stats = {appId:          0,
+               connectionType: "wifi",
+               timestamp:      Date.now(),
+               rxBytes:        0,
+               txBytes:        0,
+               rxTotalBytes:   1234,
+               txTotalBytes:   1234};
 
   netStatsDb.dbNewTxn("readwrite", function(txn, store) {
     netStatsDb._saveStats(txn, store, stats);
   }, function(error, result) {
     do_check_eq(error, null);
 
     netStatsDb.logAllRecords(function(error, result) {
       do_check_eq(error, null);
       do_check_eq(result.length, 1);
       do_check_eq(result[0].appId, stats.appId);
-      do_check_true(compareNetworks(result[0].network, stats.network));
+      do_check_eq(result[0].connectionType, stats.connectionType);
       do_check_eq(result[0].timestamp, stats.timestamp);
       do_check_eq(result[0].rxBytes, stats.rxBytes);
       do_check_eq(result[0].txBytes, stats.txBytes);
       do_check_eq(result[0].rxTotalBytes, stats.rxTotalBytes);
       do_check_eq(result[0].txTotalBytes, stats.txTotalBytes);
       run_next_test();
     });
   });
 });
 
 add_test(function test_internalSaveStats_arraySamples() {
-  var networks = getNetworks();
-
-  netStatsDb.clearStats(networks, function (error, result) {
+  netStatsDb.clear(function (error, result) {
     do_check_eq(error, null);
 
-    var network = [networks[0].id, networks[0].type];
-
     var samples = 2;
     var stats = [];
     for (var i = 0; i < samples; i++) {
-      stats.push({ appId:        0,
-                   network:      network,
-                   timestamp:    Date.now() + (10 * i),
-                   rxBytes:      0,
-                   txBytes:      0,
-                   rxTotalBytes: 1234,
-                   txTotalBytes: 1234 });
+      stats.push({appId:          0,
+                  connectionType: "wifi",
+                  timestamp:      Date.now() + (10 * i),
+                  rxBytes:        0,
+                  txBytes:        0,
+                  rxTotalBytes:   1234,
+                  txTotalBytes:   1234});
     }
 
     netStatsDb.dbNewTxn("readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
     }, function(error, result) {
       do_check_eq(error, null);
 
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
-
-        // Result has one sample more than samples because clear inserts
-        // an empty sample to keep totalBytes synchronized with netd counters
-        result.shift();
         do_check_eq(result.length, samples);
 
         var success = true;
-        for (var i = 1; i < samples; i++) {
+        for (var i = 0; i < samples; i++) {
           if (result[i].appId != stats[i].appId ||
-              !compareNetworks(result[i].network, stats[i].network) ||
+              result[i].connectionType != stats[i].connectionType ||
               result[i].timestamp != stats[i].timestamp ||
               result[i].rxBytes != stats[i].rxBytes ||
               result[i].txBytes != stats[i].txBytes ||
               result[i].rxTotalBytes != stats[i].rxTotalBytes ||
               result[i].txTotalBytes != stats[i].txTotalBytes) {
             success = false;
             break;
           }
@@ -193,62 +165,59 @@ add_test(function test_internalSaveStats
         do_check_true(success);
         run_next_test();
       });
     });
   });
 });
 
 add_test(function test_internalRemoveOldStats() {
-  var networks = getNetworks();
-
-  netStatsDb.clearStats(networks, function (error, result) {
+  netStatsDb.clear(function (error, result) {
     do_check_eq(error, null);
 
-    var network = [networks[0].id, networks[0].type];
     var samples = 10;
     var stats = [];
     for (var i = 0; i < samples - 1; i++) {
-      stats.push({ appId:              0,
-                   network:      network, timestamp:    Date.now() + (10 * i),
-                   rxBytes:            0, txBytes:      0,
-                   rxTotalBytes:    1234, txTotalBytes: 1234 });
+      stats.push({appId:               0,
+                  connectionType: "wifi", timestamp:    Date.now() + (10 * i),
+                  rxBytes:             0, txBytes:      0,
+                  rxTotalBytes:     1234, txTotalBytes: 1234});
     }
 
-    stats.push({ appId:              0,
-                 network:      network, timestamp:    Date.now() + (10 * samples),
-                 rxBytes:            0, txBytes:      0,
-                 rxTotalBytes:    1234, txTotalBytes: 1234 });
+    stats.push({appId:               0,
+                connectionType: "wifi", timestamp:    Date.now() + (10 * samples),
+                rxBytes:             0, txBytes:      0,
+                rxTotalBytes:     1234, txTotalBytes: 1234});
 
     netStatsDb.dbNewTxn("readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
-      var date = stats[stats.length - 1].timestamp
+      var date = stats[stats.length -1].timestamp
                  + (netStatsDb.sampleRate * netStatsDb.maxStorageSamples - 1) - 1;
-      netStatsDb._removeOldStats(txn, store, 0, network, date);
+      netStatsDb._removeOldStats(txn, store, 0, "wifi", date);
     }, function(error, result) {
       do_check_eq(error, null);
 
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
         do_check_eq(result.length, 1);
 
         run_next_test();
       });
     });
   });
 });
 
-function processSamplesDiff(networks, lastStat, newStat, callback) {
-  netStatsDb.clearStats(networks, function (error, result){
+function processSamplesDiff(lastStat, newStat, callback) {
+  netStatsDb.clear(function (error, result){
     do_check_eq(error, null);
     netStatsDb.dbNewTxn("readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, lastStat);
     }, function(error, result) {
       netStatsDb.dbNewTxn("readwrite", function(txn, store) {
-        let request = store.index("network").openCursor(newStat.network, "prev");
+        let request = store.index("connectionType").openCursor(newStat.connectionType, "prev");
         request.onsuccess = function onsuccess(event) {
           let cursor = event.target.result;
           do_check_neq(cursor, null);
           netStatsDb._processSamplesDiff(txn, store, cursor, newStat);
         };
       }, function(error, result) {
         do_check_eq(error, null);
         netStatsDb.logAllRecords(function(error, result) {
@@ -256,314 +225,292 @@ function processSamplesDiff(networks, la
           callback(result);
         });
       });
     });
   });
 }
 
 add_test(function test_processSamplesDiffSameSample() {
-  var networks = getNetworks();
-  var network = [networks[0].id, networks[0].type];
-
   var sampleRate = netStatsDb.sampleRate;
   var date = filterTimestamp(new Date());
-
-  var lastStat = { appId:              0,
-                   network:      network, timestamp:    date,
-                   rxBytes:            0, txBytes:      0,
-                   rxTotalBytes:    1234, txTotalBytes: 1234 };
+  var lastStat = {appId:               0,
+                  connectionType: "wifi", timestamp:    date,
+                  rxBytes:             0, txBytes:      0,
+                  rxTotalBytes:     1234, txTotalBytes: 1234};
 
-  var newStat = { appId:              0,
-                  network:      network, timestamp:    date,
-                  rxBytes:            0, txBytes:      0,
-                  rxTotalBytes:    2234, txTotalBytes: 2234 };
+  var newStat = {appId:                 0,
+                 connectionType:   "wifi", timestamp:    date,
+                 rxBytes:               0, txBytes:      0,
+                 rxTotalBytes:       2234, txTotalBytes: 2234};
 
-  processSamplesDiff(networks, lastStat, newStat, function(result) {
+  processSamplesDiff(lastStat, newStat, function(result) {
     do_check_eq(result.length, 1);
     do_check_eq(result[0].appId, newStat.appId);
-    do_check_true(compareNetworks(result[0].network, newStat.network));
+    do_check_eq(result[0].connectionType, newStat.connectionType);
     do_check_eq(result[0].timestamp, newStat.timestamp);
     do_check_eq(result[0].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
     do_check_eq(result[0].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
     do_check_eq(result[0].rxTotalBytes, newStat.rxTotalBytes);
     do_check_eq(result[0].txTotalBytes, newStat.txTotalBytes);
     run_next_test();
   });
 });
 
 add_test(function test_processSamplesDiffNextSample() {
-  var networks = getNetworks();
-  var network = [networks[0].id, networks[0].type];
-
   var sampleRate = netStatsDb.sampleRate;
   var date = filterTimestamp(new Date());
-
-  var lastStat = { appId:              0,
-                   network:      network, timestamp:    date,
-                   rxBytes:            0, txBytes:      0,
-                   rxTotalBytes:    1234, txTotalBytes: 1234 };
+  var lastStat = {appId:               0,
+                  connectionType: "wifi", timestamp:    date,
+                  rxBytes:             0, txBytes:      0,
+                  rxTotalBytes:     1234, txTotalBytes: 1234};
 
-  var newStat = { appId:              0,
-                  network:      network, timestamp:    date + sampleRate,
-                  rxBytes:            0, txBytes:      0,
-                  rxTotalBytes:     500, txTotalBytes: 500 };
+  var newStat = {appId:                 0,
+                 connectionType:   "wifi", timestamp:    date + sampleRate,
+                 rxBytes:               0, txBytes:      0,
+                 rxTotalBytes:        500, txTotalBytes: 500};
 
-  processSamplesDiff(networks, lastStat, newStat, function(result) {
+  processSamplesDiff(lastStat, newStat, function(result) {
     do_check_eq(result.length, 2);
     do_check_eq(result[1].appId, newStat.appId);
-    do_check_true(compareNetworks(result[1].network, newStat.network));
+    do_check_eq(result[1].connectionType, newStat.connectionType);
     do_check_eq(result[1].timestamp, newStat.timestamp);
     do_check_eq(result[1].rxBytes, newStat.rxTotalBytes);
     do_check_eq(result[1].txBytes, newStat.txTotalBytes);
     do_check_eq(result[1].rxTotalBytes, newStat.rxTotalBytes);
     do_check_eq(result[1].txTotalBytes, newStat.txTotalBytes);
     run_next_test();
   });
 });
 
 add_test(function test_processSamplesDiffSamplesLost() {
-  var networks = getNetworks();
-  var network = [networks[0].id, networks[0].type];
   var samples = 5;
   var sampleRate = netStatsDb.sampleRate;
   var date = filterTimestamp(new Date());
-  var lastStat = { appId:             0,
-                   network:     network, timestamp:    date,
-                   rxBytes:           0, txBytes:      0,
-                   rxTotalBytes:   1234, txTotalBytes: 1234 };
+  var lastStat = {appId:               0,
+                  connectionType: "wifi", timestamp:    date,
+                  rxBytes:             0, txBytes:      0,
+                  rxTotalBytes:     1234, txTotalBytes: 1234};
 
-  var newStat = { appId:              0,
-                  network:      network, timestamp:    date + (sampleRate * samples),
-                  rxBytes:            0, txBytes:      0,
-                  rxTotalBytes:    2234, txTotalBytes: 2234 };
+  var newStat = {appId:                0,
+                 connectionType:  "wifi", timestamp:    date + (sampleRate * samples),
+                 rxBytes:              0, txBytes:      0,
+                 rxTotalBytes:      2234, txTotalBytes: 2234};
 
-  processSamplesDiff(networks, lastStat, newStat, function(result) {
+  processSamplesDiff(lastStat, newStat, function(result) {
     do_check_eq(result.length, samples + 1);
     do_check_eq(result[0].appId, newStat.appId);
-    do_check_true(compareNetworks(result[samples].network, newStat.network));
+    do_check_eq(result[samples].connectionType, newStat.connectionType);
     do_check_eq(result[samples].timestamp, newStat.timestamp);
     do_check_eq(result[samples].rxBytes, newStat.rxTotalBytes - lastStat.rxTotalBytes);
     do_check_eq(result[samples].txBytes, newStat.txTotalBytes - lastStat.txTotalBytes);
     do_check_eq(result[samples].rxTotalBytes, newStat.rxTotalBytes);
     do_check_eq(result[samples].txTotalBytes, newStat.txTotalBytes);
     run_next_test();
   });
 });
 
 add_test(function test_saveStats() {
-  var networks = getNetworks();
-  var network = [networks[0].id, networks[0].type];
+  var stats = {appId:          0,
+               connectionType: "wifi",
+               date:           new Date(),
+               rxBytes:        2234,
+               txBytes:        2234};
 
-  var stats = { appId:       0,
-                networkId:   networks[0].id,
-                networkType: networks[0].type,
-                date:        new Date(),
-                rxBytes:     2234,
-                txBytes:     2234};
-
-  netStatsDb.clearStats(networks, function (error, result) {
+  netStatsDb.clear(function (error, result) {
     do_check_eq(error, null);
     netStatsDb.saveStats(stats, function(error, result) {
       do_check_eq(error, null);
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
         do_check_eq(result.length, 1);
         do_check_eq(result[0].appId, stats.appId);
-        do_check_true(compareNetworks(result[0].network, network));
+        do_check_eq(result[0].connectionType, stats.connectionType);
         let timestamp = filterTimestamp(stats.date);
         do_check_eq(result[0].timestamp, timestamp);
         do_check_eq(result[0].rxBytes, 0);
         do_check_eq(result[0].txBytes, 0);
         do_check_eq(result[0].rxTotalBytes, stats.rxBytes);
         do_check_eq(result[0].txTotalBytes, stats.txBytes);
         run_next_test();
       });
     });
   });
 });
 
 add_test(function test_saveAppStats() {
-  var networks = getNetworks();
-  var network = [networks[0].id, networks[0].type];
+  var stats = {appId:          1,
+               connectionType: "wifi",
+               date:           new Date(),
+               rxBytes:        2234,
+               txBytes:        2234};
 
-  var stats = { appId:       1,
-                networkId:   networks[0].id,
-                networkType: networks[0].type,
-                date:        new Date(),
-                rxBytes:     2234,
-                txBytes:     2234};
-
-  netStatsDb.clearStats(networks, function (error, result) {
+  netStatsDb.clear(function (error, result) {
     do_check_eq(error, null);
     netStatsDb.saveStats(stats, function(error, result) {
       do_check_eq(error, null);
       netStatsDb.logAllRecords(function(error, result) {
         do_check_eq(error, null);
-        // The clear function clears all records of the datbase but
-        // inserts a new element for each [appId, connectionId, connectionType]
-        // record to keep the track of rxTotalBytes / txTotalBytes.
-        // So at this point, we have two records, one for the appId 0 used in
-        // past tests and the new one for appId 1
-        do_check_eq(result.length, 2);
-        do_check_eq(result[1].appId, stats.appId);
-        do_check_true(compareNetworks(result[1].network, network));
+        do_check_eq(result.length, 1);
+        do_check_eq(result[0].appId, stats.appId);
+        do_check_eq(result[0].connectionType, stats.connectionType);
         let timestamp = filterTimestamp(stats.date);
-        do_check_eq(result[1].timestamp, timestamp);
-        do_check_eq(result[1].rxBytes, stats.rxBytes);
-        do_check_eq(result[1].txBytes, stats.txBytes);
-        do_check_eq(result[1].rxTotalBytes, 0);
-        do_check_eq(result[1].txTotalBytes, 0);
+        do_check_eq(result[0].timestamp, timestamp);
+        do_check_eq(result[0].rxBytes, stats.rxBytes);
+        do_check_eq(result[0].txBytes, stats.txBytes);
+        do_check_eq(result[0].rxTotalBytes, 0);
+        do_check_eq(result[0].txTotalBytes, 0);
         run_next_test();
       });
     });
   });
 });
 
-function prepareFind(network, stats, callback) {
-  netStatsDb.clearStats(network, function (error, result) {
+function prepareFind(stats, callback) {
+  netStatsDb.clear(function (error, result) {
     do_check_eq(error, null);
     netStatsDb.dbNewTxn("readwrite", function(txn, store) {
       netStatsDb._saveStats(txn, store, stats);
     }, function(error, result) {
         callback(error, result);
     });
   });
 }
 
 add_test(function test_find () {
-  var networks = getNetworks();
-  var networkWifi = [networks[0].id, networks[0].type];
-  var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
-  var appId = 0;
-
   var samples = 5;
   var sampleRate = netStatsDb.sampleRate;
   var start = Date.now();
   var saveDate = filterTimestamp(new Date());
   var end = new Date(start + (sampleRate * (samples - 1)));
   start = new Date(start - sampleRate);
   var stats = [];
   for (var i = 0; i < samples; i++) {
-    stats.push({ appId:              appId,
-                 network:      networkWifi, timestamp:    saveDate + (sampleRate * i),
-                 rxBytes:                0, txBytes:      10,
-                 rxTotalBytes:           0, txTotalBytes: 0 });
+    stats.push({appId:               0,
+                connectionType: "wifi", timestamp:    saveDate + (sampleRate * i),
+                rxBytes:             0, txBytes:      10,
+                rxTotalBytes:        0, txTotalBytes: 0});
 
-    stats.push({ appId:                appId,
-                 network:      networkMobile, timestamp:    saveDate + (sampleRate * i),
-                 rxBytes:                  0, txBytes:      10,
-                 rxTotalBytes:             0, txTotalBytes: 0 });
+    stats.push({appId:                 0,
+                connectionType: "mobile", timestamp:    saveDate + (sampleRate * i),
+                rxBytes:               0, txBytes:      10,
+                rxTotalBytes:          0, txTotalBytes: 0});
   }
 
-  prepareFind(networks[0], stats, function(error, result) {
+  prepareFind(stats, function(error, result) {
     do_check_eq(error, null);
     netStatsDb.find(function (error, result) {
       do_check_eq(error, null);
-      do_check_eq(result.network.id, networks[0].id);
-      do_check_eq(result.network.type, networks[0].type);
+      do_check_eq(result.connectionType, "wifi");
       do_check_eq(result.start.getTime(), start.getTime());
       do_check_eq(result.end.getTime(), end.getTime());
       do_check_eq(result.data.length, samples + 1);
       do_check_eq(result.data[0].rxBytes, null);
       do_check_eq(result.data[1].rxBytes, 0);
       do_check_eq(result.data[samples].rxBytes, 0);
-      run_next_test();
-    }, networks[0], start, end, appId);
+
+      netStatsDb.findAll(function (error, result) {
+        do_check_eq(error, null);
+        do_check_eq(result.connectionType, null);
+        do_check_eq(result.start.getTime(), start.getTime());
+        do_check_eq(result.end.getTime(), end.getTime());
+        do_check_eq(result.data.length, samples + 1);
+        do_check_eq(result.data[0].rxBytes, null);
+        do_check_eq(result.data[1].rxBytes, 0);
+        do_check_eq(result.data[1].txBytes, 20);
+        do_check_eq(result.data[samples].rxBytes, 0);
+        run_next_test();
+      }, {appId: 0, start: start, end: end});
+    }, {start: start, end: end, connectionType: "wifi", appId: 0});
   });
 });
 
 add_test(function test_findAppStats () {
-  var networks = getNetworks();
-  var networkWifi = [networks[0].id, networks[0].type];
-  var networkMobile = [networks[1].id, networks[1].type]; // Fake mobile interface
-
   var samples = 5;
   var sampleRate = netStatsDb.sampleRate;
   var start = Date.now();
   var saveDate = filterTimestamp(new Date());
   var end = new Date(start + (sampleRate * (samples - 1)));
   start = new Date(start - sampleRate);
   var stats = [];
   for (var i = 0; i < samples; i++) {
-    stats.push({ appId:                  1,
-                 network:      networkWifi, timestamp:    saveDate + (sampleRate * i),
-                 rxBytes:                0, txBytes:      10,
-                 rxTotalBytes:           0, txTotalBytes: 0 });
+    stats.push({appId:               1,
+                connectionType: "wifi", timestamp:    saveDate + (sampleRate * i),
+                rxBytes:             0, txBytes:      10,
+                rxTotalBytes:        0, txTotalBytes: 0});
 
-    stats.push({ appId:                    1,
-                 network:      networkMobile, timestamp:    saveDate + (sampleRate * i),
-                 rxBytes:                  0, txBytes:      10,
-                 rxTotalBytes:             0, txTotalBytes: 0 });
+    stats.push({appId:                 1,
+                connectionType: "mobile", timestamp:    saveDate + (sampleRate * i),
+                rxBytes:               0, txBytes:      10,
+                rxTotalBytes:          0, txTotalBytes: 0});
   }
 
-  prepareFind(networks[0], stats, function(error, result) {
+  prepareFind(stats, function(error, result) {
     do_check_eq(error, null);
     netStatsDb.find(function (error, result) {
       do_check_eq(error, null);
-      do_check_eq(result.network.id, networks[0].id);
-      do_check_eq(result.network.type, networks[0].type);
+      do_check_eq(result.connectionType, "wifi");
       do_check_eq(result.start.getTime(), start.getTime());
       do_check_eq(result.end.getTime(), end.getTime());
       do_check_eq(result.data.length, samples + 1);
       do_check_eq(result.data[0].rxBytes, null);
       do_check_eq(result.data[1].rxBytes, 0);
       do_check_eq(result.data[samples].rxBytes, 0);
-      run_next_test();
-    }, networks[0], start, end, 1);
+
+      netStatsDb.findAll(function (error, result) {
+        do_check_eq(error, null);
+        do_check_eq(result.connectionType, null);
+        do_check_eq(result.start.getTime(), start.getTime());
+        do_check_eq(result.end.getTime(), end.getTime());
+        do_check_eq(result.data.length, samples + 1);
+        do_check_eq(result.data[0].rxBytes, null);
+        do_check_eq(result.data[1].rxBytes, 0);
+        do_check_eq(result.data[1].txBytes, 20);
+        do_check_eq(result.data[samples].rxBytes, 0);
+        run_next_test();
+      }, {start: start, end: end, appId: 1});
+    }, {start: start, end: end, connectionType: "wifi", appId: 1});
   });
 });
 
 add_test(function test_saveMultipleAppStats () {
-  var networks = getNetworks();
-  var networkWifi = networks[0];
-  var networkMobile = networks[1]; // Fake mobile interface
-
   var saveDate = filterTimestamp(new Date());
   var cached = Object.create(null);
 
-  cached['1wifi'] = { appId:                      1, date:           new Date(),
-                      networkId:     networkWifi.id, networkType: networkWifi.type,
-                      rxBytes:                    0, txBytes:      10 };
+  cached['1wifi'] = {appId:                 1,
+                     connectionType:   "wifi", date:    new Date(),
+                     rxBytes:               0, txBytes:      10};
 
-  cached['1mobile'] = { appId:                    1, date:           new Date(),
-                        networkId: networkMobile.id, networkType: networkMobile.type,
-                        rxBytes:                  0, txBytes:      10 };
+  cached['1mobile'] = {appId:                 1,
+                       connectionType: "mobile", date:    new Date(),
+                       rxBytes:               0, txBytes:      10};
 
-  cached['2wifi'] = { appId:                      2, date:           new Date(),
-                      networkId:     networkWifi.id, networkType: networkWifi.type,
-                      rxBytes:                    0, txBytes:      10 };
+  cached['2wifi'] = {appId:                 2,
+                     connectionType:   "wifi", date:    new Date(),
+                     rxBytes:               0, txBytes:      10};
 
-  cached['2mobile'] = { appId:                    2, date:           new Date(),
-                        networkId: networkMobile.id, networkType: networkMobile.type,
-                        rxBytes:                  0, txBytes:      10 };
+  cached['2mobile'] = {appId:                 2,
+                       connectionType: "mobile", date:    new Date(),
+                       rxBytes:               0, txBytes:      10};
 
   let keys = Object.keys(cached);
   let index = 0;
 
-  networks.push(networkMobile);
-  netStatsDb.clearStats(networks, function (error, result) {
+  netStatsDb.clear(function (error, result) {
     do_check_eq(error, null);
     netStatsDb.saveStats(cached[keys[index]],
       function callback(error, result) {
         do_check_eq(error, null);
 
         if (index == keys.length - 1) {
           netStatsDb.logAllRecords(function(error, result) {
-          // Again, result has two samples more than expected samples because
-          // clear inserts one empty sample for each network to keep totalBytes
-          // synchronized with netd counters. so the first two samples have to
-          // be discarted.
-          result.shift();
-          result.shift();
-
           do_check_eq(error, null);
           do_check_eq(result.length, 4);
           do_check_eq(result[0].appId, 1);
-          do_check_true(compareNetworks(result[0].network,[networkWifi.id, networkWifi.type]));
+          do_check_eq(result[0].connectionType, 'mobile');
           do_check_eq(result[0].rxBytes, 0);
           do_check_eq(result[0].txBytes, 10);
           run_next_test();
           });
         }
 
         index += 1;
         netStatsDb.saveStats(cached[keys[index]], callback);
--- a/dom/network/tests/unit_stats/test_networkstats_service.js
+++ b/dom/network/tests/unit_stats/test_networkstats_service.js
@@ -1,64 +1,56 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 add_test(function test_clearDB() {
-  var networks = NetworkStatsService.availableNetworks();
-  NetworkStatsService._db.clearStats(networks, function onDBCleared(error, result) {
+  NetworkStatsService._db.clear(function onDBCleared(error, result) {
     do_check_eq(result, null);
     run_next_test();
   });
 });
 
-function getNetworkId() {
-  var network = (NetworkStatsService.availableNetworks())[0];
-  return NetworkStatsService.getNetworkId(network.id, network.type);
-}
 
 add_test(function test_networkStatsAvailable_ok() {
-  var netId = getNetworkId();
   NetworkStatsService.networkStatsAvailable(function (success, msg) {
     do_check_eq(success, true);
     run_next_test();
-  }, netId, true, 1234, 4321, new Date());
+  }, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, true, 1234, 4321, new Date());
 });
 
 add_test(function test_networkStatsAvailable_failure() {
-  var netId = getNetworkId();
   NetworkStatsService.networkStatsAvailable(function (success, msg) {
     do_check_eq(success, false);
     run_next_test();
-  }, netId, false, 1234, 4321, new Date());
+  }, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, false, 1234, 4321, new Date());
 });
 
-add_test(function test_update_invalidNetwork() {
+add_test(function test_update_invalidConnection() {
   NetworkStatsService.update(-1, function (success, msg) {
     do_check_eq(success, false);
-    do_check_eq(msg, "Invalid network -1");
+    do_check_eq(msg, "Invalid network type -1");
     run_next_test();
   });
 });
 
 add_test(function test_update() {
-  var netId = getNetworkId();
-  NetworkStatsService.update(netId, function (success, msg) {
+  NetworkStatsService.update(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, function (success, msg) {
     do_check_eq(success, true);
     run_next_test();
   });
 });
 
 add_test(function test_updateQueueIndex() {
-  NetworkStatsService.updateQueue = [{netId: 0, callbacks: null},
-                                     {netId: 1, callbacks: null},
-                                     {netId: 2, callbacks: null},
-                                     {netId: 3, callbacks: null},
-                                     {netId: 4, callbacks: null}];
+  NetworkStatsService.updateQueue = [{type: 0, callbacks: null},
+                                     {type: 1, callbacks: null},
+                                     {type: 2, callbacks: null},
+                                     {type: 3, callbacks: null},
+                                     {type: 4, callbacks: null}];
   var index = NetworkStatsService.updateQueueIndex(3);
   do_check_eq(index, 3);
   index = NetworkStatsService.updateQueueIndex(10);
   do_check_eq(index, -1);
 
   NetworkStatsService.updateQueue = [];
   run_next_test();
 });
@@ -66,54 +58,48 @@ add_test(function test_updateQueueIndex(
 add_test(function test_updateAllStats() {
   NetworkStatsService.updateAllStats(function(success, msg) {
     do_check_eq(success, true);
     run_next_test();
   });
 });
 
 add_test(function test_updateStats_ok() {
-  var netId = getNetworkId();
-  NetworkStatsService.updateStats(netId, function(success, msg){
+  NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, function(success, msg){
     do_check_eq(success, true);
     run_next_test();
   });
 });
 
 add_test(function test_updateStats_failure() {
   NetworkStatsService.updateStats(-1, function(success, msg){
     do_check_eq(success, false);
     run_next_test();
   });
 });
 
 add_test(function test_queue() {
-  // Fill networks with fake network interfaces
+  // Fill connections with fake network interfaces (wlan0 and rmnet0)
   // to enable netd async requests
-  var network = {id: "1234", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE};
-  var netId1 = NetworkStatsService.getNetworkId(network.id, network.type);
-  NetworkStatsService._networks[netId1] = { network: network,
-                                            interfaceName: "net1" };
+  NetworkStatsService._connectionTypes[Ci.nsINetworkInterface.NETWORK_TYPE_WIFI]
+                     .network.name = 'wlan0';
+  NetworkStatsService._connectionTypes[Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE]
+                     .network.name = 'rmnet0';
 
-  network = {id: "5678", type: Ci.nsIDOMMozNetworkStatsManager.MOBILE};
-  var netId2 = NetworkStatsService.getNetworkId(network.id, network.type);
-  NetworkStatsService._networks[netId2] = { network: network,
-                                            interfaceName: "net2" };
-
-  NetworkStatsService.updateStats(netId1);
-  NetworkStatsService.updateStats(netId2);
+  NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI);
+  NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE);
   do_check_eq(NetworkStatsService.updateQueue.length, 2);
   do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 1);
 
   var callback = function(success, msg) {
     return;
   };
 
-  NetworkStatsService.updateStats(netId1, callback);
-  NetworkStatsService.updateStats(netId2, callback);
+  NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, callback);
+  NetworkStatsService.updateStats(Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, callback);
 
   do_check_eq(NetworkStatsService.updateQueue.length, 2);
   do_check_eq(NetworkStatsService.updateQueue[0].callbacks.length, 2);
   do_check_eq(NetworkStatsService.updateQueue[0].callbacks[0], null);
   do_check_neq(NetworkStatsService.updateQueue[0].callbacks[1], null);
 
   run_next_test();
 });
--- a/dom/network/tests/unit_stats/test_networkstats_service_proxy.js
+++ b/dom/network/tests/unit_stats/test_networkstats_service_proxy.js
@@ -4,154 +4,116 @@
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "nssProxy",
                                    "@mozilla.org/networkstatsServiceProxy;1",
                                    "nsINetworkStatsServiceProxy");
 
-function mokConvertNetworkInterface() {
-  NetworkStatsService.convertNetworkInterface = function(aNetwork) {
-    if (aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE &&
-        aNetwork.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
-      return null;
-    }
-
-    let id = '0';
-    if (aNetwork.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
-      id = '1234'
-    }
-
-    let netId = this.getNetworkId(id, aNetwork.type);
-
-    if (!this._networks[netId]) {
-      this._networks[netId] = Object.create(null);
-      this._networks[netId].network = { id: id,
-                                        type: aNetwork.type };
-    }
-
-    return netId;
-  };
-}
-
 add_test(function test_saveAppStats() {
   var cachedAppStats = NetworkStatsService.cachedAppStats;
   var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
   var samples = 5;
 
-  // Create to fake nsINetworkInterfaces. As nsINetworkInterface can not
-  // be instantiated, these two vars will emulate it by filling the properties
-  // that will be used.
-  var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
-  var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
-
-  // Insert fake mobile network interface in NetworkStatsService
-  var mobileNetId = NetworkStatsService.getNetworkId(mobile.id, mobile.type);
-
   do_check_eq(Object.keys(cachedAppStats).length, 0);
 
   for (var i = 0; i < samples; i++) {
-    nssProxy.saveAppStats(1, wifi, timestamp, 10, 20);
+    nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
+                          timestamp, 10, 20);
 
-    nssProxy.saveAppStats(1, mobile, timestamp, 10, 20);
+    nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
+                          timestamp, 10, 20);
   }
 
-  var key1 = 1 + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
-  var key2 = 1 + mobileNetId;
+  var key1 = 1 + 'wifi';
+  var key2 = 1 + 'mobile';
 
   do_check_eq(Object.keys(cachedAppStats).length, 2);
   do_check_eq(cachedAppStats[key1].appId, 1);
-  do_check_eq(cachedAppStats[key1].networkId, wifi.id);
-  do_check_eq(cachedAppStats[key1].networkType, wifi.type);
+  do_check_eq(cachedAppStats[key1].connectionType, 'wifi');
   do_check_eq(new Date(cachedAppStats[key1].date).getTime() / 1000,
               Math.floor(timestamp / 1000));
   do_check_eq(cachedAppStats[key1].rxBytes, 50);
   do_check_eq(cachedAppStats[key1].txBytes, 100);
   do_check_eq(cachedAppStats[key2].appId, 1);
-  do_check_eq(cachedAppStats[key2].networkId, mobile.id);
-  do_check_eq(cachedAppStats[key2].networkType, mobile.type);
+  do_check_eq(cachedAppStats[key2].connectionType, 'mobile');
   do_check_eq(new Date(cachedAppStats[key2].date).getTime() / 1000,
               Math.floor(timestamp / 1000));
   do_check_eq(cachedAppStats[key2].rxBytes, 50);
   do_check_eq(cachedAppStats[key2].txBytes, 100);
 
   run_next_test();
 });
 
 add_test(function test_saveAppStatsWithDifferentDates() {
   var today = NetworkStatsService.cachedAppStatsDate;
   var tomorrow = new Date(today.getTime() + (24 * 60 * 60 * 1000));
-
-  var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
-  var mobile = {type: Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE, id: "1234"};
-
-  var key = 1 + NetworkStatsService.getNetworkId(wifi.id, wifi.type);
+  var key = 1 + 'wifi';
 
   NetworkStatsService.updateCachedAppStats(
     function (success, msg) {
       do_check_eq(success, true);
 
       do_check_eq(Object.keys(NetworkStatsService.cachedAppStats).length, 0);
 
-      nssProxy.saveAppStats(1, wifi, today.getTime(), 10, 20);
+      nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
+                            today.getTime(), 10, 20);
 
-      nssProxy.saveAppStats(1, mobile, today.getTime(), 10, 20);
+      nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
+                            today.getTime(), 10, 20);
 
       var saveAppStatsCb = {
         notify: function notify(success, message) {
           do_check_eq(success, true);
 
           var cachedAppStats = NetworkStatsService.cachedAppStats;
-          var key = 2 + NetworkStatsService.getNetworkId(mobile.id, mobile.type);
+          var key = 2 + 'mobile';
           do_check_eq(Object.keys(cachedAppStats).length, 1);
           do_check_eq(cachedAppStats[key].appId, 2);
-          do_check_eq(cachedAppStats[key].networkId, mobile.id);
-          do_check_eq(cachedAppStats[key].networkType, mobile.type);
+          do_check_eq(cachedAppStats[key].connectionType, 'mobile');
           do_check_eq(new Date(cachedAppStats[key].date).getTime() / 1000,
                       Math.floor(tomorrow.getTime() / 1000));
           do_check_eq(cachedAppStats[key].rxBytes, 30);
           do_check_eq(cachedAppStats[key].txBytes, 40);
 
           run_next_test();
         }
       };
 
-      nssProxy.saveAppStats(2, mobile, tomorrow.getTime(), 30, 40, saveAppStatsCb);
+      nssProxy.saveAppStats(2, Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE,
+                            tomorrow.getTime(), 30, 40, saveAppStatsCb);
     }
   );
 });
 
 add_test(function test_saveAppStatsWithMaxCachedTraffic() {
   var timestamp = NetworkStatsService.cachedAppStatsDate.getTime();
   var maxtraffic = NetworkStatsService.maxCachedTraffic;
-  var wifi = {type: Ci.nsINetworkInterface.NETWORK_TYPE_WIFI, id: "0"};
 
   NetworkStatsService.updateCachedAppStats(
     function (success, msg) {
       do_check_eq(success, true);
 
       var cachedAppStats = NetworkStatsService.cachedAppStats;
       do_check_eq(Object.keys(cachedAppStats).length, 0);
 
-      nssProxy.saveAppStats(1, wifi, timestamp, 10, 20);
+      nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
+                            timestamp, 10, 20);
 
       do_check_eq(Object.keys(cachedAppStats).length, 1);
 
-      nssProxy.saveAppStats(1, wifi, timestamp, maxtraffic, 20);
+      nssProxy.saveAppStats(1, Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
+                            timestamp, maxtraffic, 20);
 
       do_check_eq(Object.keys(cachedAppStats).length, 0);
 
       run_next_test();
   });
 });
 
 function run_test() {
   do_get_profile();
 
   Cu.import("resource://gre/modules/NetworkStatsService.jsm");
 
-  // Function convertNetworkInterface of NetworkStatsService causes errors when dealing
-  // with RIL to get the iccid, so overwrite it.
-  mokConvertNetworkInterface();
-
   run_next_test();
 }
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -72,17 +72,17 @@ class nsGeolocationRequest
                        const GeoPositionCallback& aCallback,
                        const GeoPositionErrorCallback& aErrorCallback,
                        PositionOptions* aOptions,
                        bool aWatchPositionRequest = false,
                        int32_t aWatchId = 0);
   void Shutdown();
 
   void SendLocation(nsIDOMGeoPosition* location);
-  bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
+  bool WantsHighAccuracy() {return mOptions && mOptions->mEnableHighAccuracy;}
   void SetTimeoutTimer();
   nsIPrincipal* GetPrincipal();
 
   ~nsGeolocationRequest();
 
   virtual bool Recv__delete__(const bool& allow) MOZ_OVERRIDE;
   virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
 
@@ -440,17 +440,17 @@ nsGeolocationRequest::Allow()
   // if the user has specified a maximumAge, return a cached value.
 
   uint32_t maximumAge = 0;
   if (mOptions) {
     if (mOptions->mMaximumAge > 0) {
       maximumAge = mOptions->mMaximumAge;
     }
   }
-  gs->UpdateAccuracy(WantsHighAccuracy());
+  gs->SetHigherAccuracy(mOptions && mOptions->mEnableHighAccuracy);
 
   bool canUseCache = lastPosition && maximumAge > 0 &&
     (PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
     PRTime(cachedPositionTime));
 
   if (canUseCache) {
     // okay, we can return a cached position
     // getCurrentPosition requests serviced by the cache
@@ -580,22 +580,22 @@ nsGeolocationRequest::Shutdown()
   MOZ_ASSERT(!mShutdown, "request shutdown twice");
   mShutdown = true;
 
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
     mTimeoutTimer = nullptr;
   }
 
-  // If there are no other high accuracy requests, the geolocation service will
-  // notify the provider to switch to the default accuracy.
+  // This should happen last, to ensure that this request isn't taken into consideration
+  // when deciding whether existing requests still require high accuracy.
   if (mOptions && mOptions->mEnableHighAccuracy) {
     nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
     if (gs) {
-      gs->UpdateAccuracy();
+      gs->SetHigherAccuracy(false);
     }
   }
 }
 
 bool nsGeolocationRequest::Recv__delete__(const bool& allow)
 {
   if (allow) {
     (void) Allow();
@@ -896,19 +896,19 @@ nsGeolocationService::HighAccuracyReques
     if (mGeolocators[i]->HighAccuracyRequested()) {
       return true;
     }
   }
   return false;
 }
 
 void
-nsGeolocationService::UpdateAccuracy(bool aForceHigh)
+nsGeolocationService::SetHigherAccuracy(bool aEnable)
 {
-  bool highRequired = aForceHigh || HighAccuracyRequested();
+  bool highRequired = aEnable || HighAccuracyRequested();
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     ContentChild* cpc = ContentChild::GetSingleton();
     cpc->SendSetGeolocationHigherAccuracy(highRequired);
     return;
   }
 
   if (!mHigherAccuracy && highRequired) {
@@ -1056,17 +1056,16 @@ void
 Geolocation::Shutdown()
 {
   // Release all callbacks
   mPendingCallbacks.Clear();
   mWatchingCallbacks.Clear();
 
   if (mService) {
     mService->RemoveLocator(this);
-    mService->UpdateAccuracy();
   }
 
   mService = nullptr;
   mPrincipal = nullptr;
 }
 
 nsIDOMWindow*
 Geolocation::GetParentObject() const {
--- a/dom/src/geolocation/nsGeolocation.h
+++ b/dom/src/geolocation/nsGeolocation.h
@@ -80,18 +80,18 @@ public:
   nsresult StartDevice(nsIPrincipal* aPrincipal);
 
   // Stop the started geolocation device (gps, nmea, etc.)
   void     StopDevice();
 
   // create, or reinitalize the callback timer
   void     SetDisconnectTimer();
 
-  // Update the accuracy and notify the provider if changed
-  void     UpdateAccuracy(bool aForceHigh = false);
+  // request higher accuracy, if possible
+  void     SetHigherAccuracy(bool aEnable);
   bool     HighAccuracyRequested();
 
 private:
 
   ~nsGeolocationService();
 
   // Disconnect timer.  When this timer expires, it clears all pending callbacks
   // and closes down the provider, unless we are watching a point, and in that
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -68,19 +68,16 @@ function WifiGeoPositionProvider() {
   try {
     gUseScanning = Services.prefs.getBoolPref("geo.wifi.scan");
   } catch (e) {}
 
   this.wifiService = null;
   this.timer = null;
   this.hasSeenWiFi = false;
   this.started = false;
-  // this is only used when logging is enabled, to debug interactions with the
-  // geolocation service
-  this.highAccuracy = false;
 }
 
 WifiGeoPositionProvider.prototype = {
   classID:          Components.ID("{77DA64D3-7458-4920-9491-86CC9914F904}"),
   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIGeolocationProvider,
                                            Ci.nsIWifiListener,
                                            Ci.nsITimerCallback]),
   startup:  function() {
@@ -130,22 +127,20 @@ WifiGeoPositionProvider.prototype = {
       this.timer.cancel();
       this.timer = null;
     }
 
     this.started = false;
   },
 
   setHighAccuracy: function(enable) {
-    this.highAccuracy = enable;
-    LOG("setting highAccuracy to " + (this.highAccuracy?"TRUE":"FALSE"));
   },
 
   onChange: function(accessPoints) {
-    LOG("onChange called, highAccuracy = " + (this.highAccuracy?"TRUE":"FALSE"));
+    LOG("onChange called");
     this.hasSeenWiFi = true;
 
     let url = Services.urlFormatter.formatURLPref("geo.wifi.uri");
 
     function isPublic(ap) {
         let mask = "_nomap"
         let result = ap.ssid.indexOf(mask, ap.ssid.length - mask.length) == -1;
         if (result != -1) {
deleted file mode 100644
--- a/dom/tests/unit/test_geolocation_reset_accuracy.js
+++ /dev/null
@@ -1,104 +0,0 @@
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}");
-const providerContract = "@mozilla.org/geolocation/provider;1";
-
-const categoryName = "geolocation-provider";
-
-var provider = {
-  QueryInterface: function eventsink_qi(iid) {
-    if (iid.equals(Components.interfaces.nsISupports) ||
-        iid.equals(Components.interfaces.nsIFactory) ||
-        iid.equals(Components.interfaces.nsIGeolocationProvider))
-      return this;
-    throw Components.results.NS_ERROR_NO_INTERFACE;
-  },
-  createInstance: function eventsink_ci(outer, iid) {
-    if (outer)
-      throw Components.results.NS_ERROR_NO_AGGREGATION;
-    return this.QueryInterface(iid);
-  },
-  lockFactory: function eventsink_lockf(lock) {
-    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-  },
-  startup: function() {
-  },
-  watch: function() {
-  },
-  shutdown: function() {
-  },
-  setHighAccuracy: function(enable) {
-    this._isHigh = enable;
-    if (enable) {
-      this._seenHigh = true;
-    }
-  },
-  _isHigh: false,
-  _seenHigh: false
-};
-
-let runningInParent = true;
-try {
-  runningInParent = Components.classes["@mozilla.org/xre/runtime;1"].
-                    getService(Components.interfaces.nsIXULRuntime).processType
-                    == Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-}
-catch (e) { }
-
-function successCallback()
-{
-  do_check_true(false);
-  do_test_finished();
-}
-
-function errorCallback()
-{
-  do_check_true(false);
-  do_test_finished();
-}
-
-function run_test()
-{
-  if (runningInParent) {
-    // XPCShell does not get a profile by default. The geolocation service
-    // depends on the settings service which uses IndexedDB and IndexedDB
-    // needs a place where it can store databases.
-    do_get_profile();
-
-    Components.manager.nsIComponentRegistrar.registerFactory(providerCID,
-      "Unit test geo provider", providerContract, provider);
-    var catMan = Components.classes["@mozilla.org/categorymanager;1"]
-                           .getService(Components.interfaces.nsICategoryManager);
-    catMan.nsICategoryManager.addCategoryEntry(categoryName, "unit test",
-                                               providerContract, false, true);
-
-    var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
-    prefs.setBoolPref("geo.testing.ignore_ipc_principal", true);
-    prefs.setBoolPref("geo.wifi.scan", false);
-  }
-
-  let geolocation = Cc["@mozilla.org/geolocation;1"].createInstance(Ci.nsISupports);
-
-  do_test_pending();
-
-  let watchID1 = geolocation.watchPosition(successCallback, errorCallback);
-  let watchID2 = geolocation.watchPosition(successCallback, errorCallback,
-                                           {enableHighAccuracy: true});
-
-  do_timeout(1000, function() {
-    geolocation.clearWatch(watchID2);
-    do_timeout(1000, check_results);
-  });
-}
-
-function check_results()
-{
-  if (runningInParent) {
-    // check the provider was set to high accuracy during the test
-    do_check_true(provider._seenHigh);
-    // check the provider is not currently set to high accuracy
-    do_check_false(provider._isHigh);
-  }
-  do_test_finished();
-}
deleted file mode 100644
--- a/dom/tests/unit/test_geolocation_reset_accuracy_wrap.js
+++ /dev/null
@@ -1,70 +0,0 @@
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}");
-const providerContract = "@mozilla.org/geolocation/provider;1";
-
-const categoryName = "geolocation-provider";
-
-var provider = {
-  QueryInterface: function eventsink_qi(iid) {
-    if (iid.equals(Components.interfaces.nsISupports) ||
-        iid.equals(Components.interfaces.nsIFactory) ||
-        iid.equals(Components.interfaces.nsIGeolocationProvider))
-      return this;
-    throw Components.results.NS_ERROR_NO_INTERFACE;
-  },
-  createInstance: function eventsink_ci(outer, iid) {
-    if (outer)
-      throw Components.results.NS_ERROR_NO_AGGREGATION;
-    return this.QueryInterface(iid);
-  },
-  lockFactory: function eventsink_lockf(lock) {
-    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-  },
-  startup: function() {
-  },
-  watch: function() {
-  },
-  shutdown: function() {
-  },
-  setHighAccuracy: function(enable) {
-    this._isHigh = enable;
-    if (enable) {
-      this._seenHigh = true;
-    }
-  },
-  _isHigh: false,
-  _seenHigh: false
-};
-
-function run_test()
-{
-  // XPCShell does not get a profile by default. The geolocation service
-  // depends on the settings service which uses IndexedDB and IndexedDB
-  // needs a place where it can store databases.
-  do_get_profile();
-
-  Components.manager.nsIComponentRegistrar.registerFactory(providerCID,
-    "Unit test geo provider", providerContract, provider);
-  var catMan = Components.classes["@mozilla.org/categorymanager;1"]
-                         .getService(Components.interfaces.nsICategoryManager);
-  catMan.nsICategoryManager.addCategoryEntry(categoryName, "unit test",
-                                             providerContract, false, true);
-
-  var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
-  prefs.setBoolPref("geo.testing.ignore_ipc_principal", true);
-  prefs.setBoolPref("geo.wifi.scan", false);
-
-  run_test_in_child("test_geolocation_reset_accuracy.js", check_results);
-}
-
-function check_results()
-{
-  // check the provider was set to high accuracy during the test
-  do_check_true(provider._seenHigh);
-  // check the provider is not currently set to high accuracy
-  do_check_false(provider._isHigh);
-
-  do_test_finished();
-}
--- a/dom/tests/unit/xpcshell.ini
+++ b/dom/tests/unit/xpcshell.ini
@@ -7,13 +7,8 @@ tail =
 [test_geolocation_provider.js]
 # Bug 684962: test hangs consistently on Android
 skip-if = os == "android"
 [test_geolocation_timeout.js]
 # Bug 919946: test hangs consistently on Android
 skip-if = os == "android"
 [test_geolocation_timeout_wrap.js]
 skip-if = os == "mac" || os == "android"
-[test_geolocation_reset_accuracy.js]
-# Bug 919946: test hangs consistently on Android
-skip-if = os == "android"
-[test_geolocation_reset_accuracy_wrap.js]
-skip-if = os == "mac" || os == "android"
--- a/image/test/mochitest/test_animSVGImage.html
+++ b/image/test/mochitest/test_animSVGImage.html
@@ -49,64 +49,56 @@ function takeReferenceSnapshot() {
 
   // Re-hide reference div, and take another snapshot to be sure it's gone
   referenceDiv.style.display = "none";
   let blankSnapshot2 = snapshotWindow(window, false);
   ok(compareSnapshots(blankSnapshot, blankSnapshot2, true)[0],
      "reference div should disappear when it becomes display:none");
 }
 
-function myOnStopFrame(aRequest) {
+function myOnStopFrame() {
   gOnStopFrameCounter++;
   ok(true, "myOnStopFrame called");
   let currentSnapshot = snapshotWindow(window, false);
   if (compareSnapshots(currentSnapshot, gReferenceSnapshot, true)[0]) {
     // SUCCESS!
     ok(true, "Animated image looks correct, " +
              "at call #" + gOnStopFrameCounter + " to onStopFrame");
     cleanUpAndFinish();
   }
-  setTimeout(function() { myOnStopFrame(0, 0); }, 1000);
+  else
+    setTimeout(myOnStopFrame, 1);
 }
 
 function failTest() {
   ok(false, "timing out after " + FAILURE_TIMEOUT + "ms.  " +
             "Animated image still doesn't look correct, " +
             "after call #" + gOnStopFrameCounter + " to onStopFrame");
   cleanUpAndFinish();
 }
 
 function cleanUpAndFinish() {
   // On the off chance that failTest and myOnStopFrame are triggered
   // back-to-back, use a flag to prevent multiple calls to SimpleTest.finish.
   if (gIsTestFinished) {
     return;
   }
-  let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
-  imgLoadingContent.removeObserver(gMyDecoderObserver);
   SimpleTest.finish();
   gIsTestFinished = true;
 }
 
 function main() {
   takeReferenceSnapshot();
 
-  // Create, customize & attach decoder observer
-  observer = new ImageDecoderObserverStub();
-  observer.frameComplete = myOnStopFrame;
-  gMyDecoderObserver =
-    Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
-      .createScriptedObserver(observer);
-  let imgLoadingContent = gImg.QueryInterface(Ci.nsIImageLoadingContent);
-  imgLoadingContent.addObserver(gMyDecoderObserver);
-
   // We want to test the cold loading behavior, so clear cache in case an
   // earlier test got our image in there already.
   clearImageCache();
 
+  setTimeout(myOnStopFrame, 1);
+
   // kick off image-loading! myOnStopFrame handles the rest.
   gImg.setAttribute("src", "lime-anim-100x100.svg");
 
   // In case something goes wrong, fail earlier than mochitest timeout,
   // and with more information.
   setTimeout(failTest, FAILURE_TIMEOUT);
 }
 
--- a/js/public/TypeDecls.h
+++ b/js/public/TypeDecls.h
@@ -45,21 +45,17 @@ class JSString;
 #endif
 
 #ifdef JS_USE_JSID_STRUCT_TYPES
 struct jsid;
 #else
 typedef ptrdiff_t jsid;
 #endif
 
-#ifdef WIN32
-typedef wchar_t  jschar;
-#else
-typedef uint16_t jschar;
-#endif
+typedef char16_t jschar;
 
 namespace JS {
 
 class Value;
 template <typename T> class Handle;
 template <typename T> class MutableHandle;
 
 typedef Handle<JSFunction*> HandleFunction;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -1,14 +1,24 @@
 # -*- Mode: makefile -*-
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+ifndef .PYMAKE
+ifeq (,$(MAKE_VERSION))
+$(error GNU Make is required)
+endif
+make_min_ver := 3.81
+ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
+$(error GNU Make $(make_min_ver) or higher is required)
+endif
+endif
+
 TOPLEVEL_BUILD := 1
 
 run_for_side_effects := $(shell echo "MAKE: $(MAKE)")
 STATIC_LIBRARY_NAME = js_static
 LIBS		= $(NSPR_LIBS)
 
 # JavaScript must be built shared, even for static builds, as it is used by
 # other modules which are always built shared. Failure to do so results in
--- a/js/src/assembler/jit/ExecutableAllocatorPosix.cpp
+++ b/js/src/assembler/jit/ExecutableAllocatorPosix.cpp
@@ -27,27 +27,35 @@
 
 #if ENABLE_ASSEMBLER && WTF_OS_UNIX && !WTF_OS_SYMBIAN
 
 #include <sys/mman.h>
 #include <unistd.h>
 
 #include "assembler/wtf/Assertions.h"
 #include "assembler/wtf/VMTags.h"
+#include "js/Utility.h"
 
 namespace JSC {
 
 size_t ExecutableAllocator::determinePageSize()
 {
     return getpagesize();
 }
 
 ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
 {
-    void* allocation = mmap(NULL, n, INITIAL_PROTECTION_FLAGS, MAP_PRIVATE | MAP_ANON, VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, 0);
+    void* allocation;
+#ifdef JSGC_ROOT_ANALYSIS
+    do {
+#endif
+        allocation = mmap(NULL, n, INITIAL_PROTECTION_FLAGS, MAP_PRIVATE | MAP_ANON, VM_TAG_FOR_EXECUTABLEALLOCATOR_MEMORY, 0);
+#ifdef JSGC_ROOT_ANALYSIS
+    } while (allocation && JS::IsPoisonedPtr(allocation));
+#endif
     if (allocation == MAP_FAILED)
         allocation = NULL;
     ExecutablePool::Allocation alloc = { reinterpret_cast<char*>(allocation), n };
     return alloc;
 }
 
 void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
 { 
--- a/js/src/build/cl.py
+++ b/js/src/build/cl.py
@@ -71,18 +71,19 @@ def InvokeClWithDependencyGeneration(cmd
     def on_line(line):
         # cl -showIncludes prefixes every header with "Note: including file:"
         # and an indentation corresponding to the depth (which we don't need)
         if line.startswith(CL_INCLUDES_PREFIX):
             dep = line[len(CL_INCLUDES_PREFIX):].strip()
             # We can't handle pathes with spaces properly in mddepend.pl, but
             # we can assume that anything in a path with spaces is a system
             # header and throw it away.
+            dep = normcase(dep)
             if ' ' not in dep:
-                rule.add_dependencies([normcase(dep)])
+                rule.add_dependencies([dep])
         else:
             # Make sure we preserve the relevant output from cl. mozprocess
             # swallows the newline delimiter, so we need to re-add it.
             sys.stdout.write(line)
             sys.stdout.write('\n')
 
     # We need to ignore children because MSVC can fire up a background process
     # during compilation. This process is cleaned up on its own. If we kill it,
--- a/js/src/config/config.mk
+++ b/js/src/config/config.mk
@@ -627,17 +627,17 @@ GARBAGE		+= $(DEPENDENCIES) core $(wildc
 
 ifeq ($(OS_ARCH),Darwin)
 ifndef NSDISTMODE
 NSDISTMODE=absolute_symlink
 endif
 PWD := $(CURDIR)
 endif
 
-NSINSTALL_PY := $(PYTHON) $(call core_abspath,$(topsrcdir)/config/nsinstall.py)
+NSINSTALL_PY := $(PYTHON) $(abspath $(topsrcdir)/config/nsinstall.py)
 # For Pymake, wherever we use nsinstall.py we're also going to try to make it
 # a native command where possible. Since native commands can't be used outside
 # of single-line commands, we continue to provide INSTALL for general use.
 # Single-line commands should be switched over to install_cmd.
 NSINSTALL_NATIVECMD := %nsinstall nsinstall
 
 ifdef NSINSTALL_BIN
 NSINSTALL = $(NSINSTALL_BIN)
@@ -686,17 +686,17 @@ sysinstall_cmd = install_cmd
 AB_CD = $(MOZ_UI_LOCALE)
 
 ifndef L10NBASEDIR
   L10NBASEDIR = $(error L10NBASEDIR not defined by configure)
 else
   IS_LANGUAGE_REPACK = 1
 endif
 
-EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(call core_realpath,$(L10NBASEDIR))/$(AB_CD)/$(subst /locales,,$(1)))
+EXPAND_LOCALE_SRCDIR = $(if $(filter en-US,$(AB_CD)),$(topsrcdir)/$(1)/en-US,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/$(subst /locales,,$(1)))
 
 ifdef relativesrcdir
 LOCALE_SRCDIR ?= $(call EXPAND_LOCALE_SRCDIR,$(relativesrcdir))
 endif
 
 ifdef relativesrcdir
 MAKE_JARS_FLAGS += --relativesrcdir=$(relativesrcdir)
 ifneq (en-US,$(AB_CD))
@@ -737,17 +737,17 @@ endif # ! OS2
 
 # Make sure any compiled classes work with at least JVM 1.4
 JAVAC_FLAGS += -source 1.4
 
 ifdef MOZ_DEBUG
 JAVAC_FLAGS += -g
 endif
 
-CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
+CREATE_PRECOMPLETE_CMD = $(PYTHON) $(abspath $(topsrcdir)/config/createprecomplete.py)
 
 # MDDEPDIR is the subdirectory where dependency files are stored
 MDDEPDIR := .deps
 
 EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/expandlibs_exec.py $(if $@,--depend $(MDDEPDIR)/$(dir $@)/$(@F).pp --target $@)
 EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/expandlibs_gen.py $(if $@,--depend $(MDDEPDIR)/$(dir $@)/$(@F).pp)
 EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR)
 EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC)
--- a/js/src/config/makefiles/functions.mk
+++ b/js/src/config/makefiles/functions.mk
@@ -11,12 +11,12 @@
 #
 
 # Define an include-at-most-once flag
 ifdef INCLUDED_FUNCTIONS_MK
 $(error Do not include functions.mk twice!)
 endif
 INCLUDED_FUNCTIONS_MK = 1
 
-core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1)))
-core_realpath = $(if $(realpath $(1)),$(realpath $(1)),$(call core_abspath,$(1)))
+core_abspath = $(error core_abspath is unsupported, use $$(abspath) instead)
+core_realpath = $(error core_realpath is unsupported)
 
-core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1)))))))
+core_winabspath = $(error core_winabspath is unsupported)
--- a/js/src/config/makefiles/java-build.mk
+++ b/js/src/config/makefiles/java-build.mk
@@ -3,16 +3,17 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 ifndef INCLUDED_JAVA_BUILD_MK #{
 
 ifdef ANDROID_RESFILES #{
+ifndef IGNORE_ANDROID_RESFILES #{
 res-dep := .deps-copy-java-res
 
 GENERATED_DIRS += res
 GARBAGE        += $(res-dep)
 
 export:: $(res-dep)
 
 res-dep-preqs := \
@@ -20,16 +21,17 @@ res-dep-preqs := \
   $(call mkdir_deps,res) \
   $(if $(IS_LANGUAGE_REPACK),FORCE) \
   $(NULL)
 
 # nop-build: only copy res/ files when needed
 $(res-dep): $(res-dep-preqs)
 	$(call copy_dir,$(srcdir)/res,$(CURDIR)/res)
 	@$(TOUCH) $@
+endif #} IGNORE_ANDROID_RESFILES
 endif #} ANDROID_RESFILES
 
 
 ifdef JAVAFILES #{
 GENERATED_DIRS += classes
 
 export:: classes
 classes: $(call mkdir_deps,classes)
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -16,16 +16,18 @@ ifdef INCLUDED_RULES_MK
 endif
 INCLUDED_RULES_MK = 1
 
 # Integrate with mozbuild-generated make files. We first verify that no
 # variables provided by the automatically generated .mk files are
 # present. If they are, this is a violation of the separation of
 # responsibility between Makefile.in and mozbuild files.
 _MOZBUILD_EXTERNAL_VARIABLES := \
+  ANDROID_GENERATED_RESFILES \
+  ANDROID_RESFILES \
   CMMSRCS \
   CPP_UNIT_TESTS \
   DIRS \
   EXTRA_PP_COMPONENTS \
   EXTRA_PP_JS_MODULES \
   GTEST_CMMSRCS \
   GTEST_CPPSRCS \
   GTEST_CSRCS \
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -4071,21 +4071,23 @@ dnl ====================================
 
 if test "$OS_ARCH" = "Darwin"; then
   AC_DEFINE(XP_MACOSX)
   AC_DEFINE(XP_UNIX)
 elif test "$OS_ARCH" != "WINNT" -a "$OS_ARCH" != "OS2"; then
   AC_DEFINE(XP_UNIX)
 fi
 
-AC_ARG_ENABLE(threadsafe,
-    [  --enable-threadsafe     Enable support for multiple threads.],
-    [if test "x$enableval" = "xyes"; then
-        AC_DEFINE(JS_THREADSAFE)
-    fi],)
+JS_THREADSAFE=1
+MOZ_ARG_DISABLE_BOOL(threadsafe,
+[  --disable-threadsafe Disable support for multiple threads.],
+    JS_THREADSAFE= )
+if test -n "$JS_THREADSAFE"; then
+    AC_DEFINE(JS_THREADSAFE)
+fi
 
 if test "$MOZ_DEBUG"; then
     AC_DEFINE(MOZ_REFLOW_PERF)
     AC_DEFINE(MOZ_REFLOW_PERF_DSP)
 fi
 
 if test "$ACCESSIBILITY" -a "$MOZ_ENABLE_GTK" ; then
     AC_DEFINE(MOZ_ACCESSIBILITY_ATK)
--- a/js/src/jit-test/tests/asm.js/testExpressions.js
+++ b/js/src/jit-test/tests/asm.js/testExpressions.js
@@ -248,18 +248,18 @@ var f = asmLink(asmCompile(USE_ASM + "fu
 assertEq(f(1,true), 1);
 assertEq(f(-1,true), 0);
 assertEq(f(-5,true), 1);
 assertEq(f(1,false), 0);
 assertEq(f(-1,false), 0);
 assertEq(f(-5,false), 1);
 
 assertAsmTypeFail('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return (i32[0]+1)|0 } return f");
-assertAsmTypeFail('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] + 1.0); } return f");
 new Float64Array(BUF_64KB)[0] = 2.3;
+assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] + 2.0) } return f"), this, null, BUF_64KB)(), 2.3+2);
 assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] - 2.0) } return f"), this, null, BUF_64KB)(), 2.3-2);
 assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] * 2.0) } return f"), this, null, BUF_64KB)(), 2.3*2);
 assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] / 2.0) } return f"), this, null, BUF_64KB)(), 2.3/2);
 assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +(f64[0] % 2.0) } return f"), this, null, BUF_64KB)(), 2.3%2);
 assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "function f() { return +-f64[0] } return f"), this, null, BUF_64KB)(), -2.3);
 assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "var sqrt=glob.Math.sqrt; function f() { return +sqrt(f64[0]) } return f"), this, null, BUF_64KB)(), Math.sqrt(2.3));
 new Int32Array(BUF_64KB)[0] = 42;
 assertEq(asmLink(asmCompile('glob','imp','b', USE_ASM + HEAP_IMPORTS + "var imul=glob.Math.imul; function f() { return imul(i32[0], 2)|0 } return f"), this, null, BUF_64KB)(), 84);
--- a/js/src/jit-test/tests/asm.js/testHeapAccess.js
+++ b/js/src/jit-test/tests/asm.js/testHeapAccess.js
@@ -86,16 +86,22 @@ assertEq(f(0xff),0xff);
 assertEq(f(0x100),0);
 
 var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=+j; f64[i>>3] = j; return (~~+f64[i>>3])|0}; return f');
 var f = asmLink(code, this, null, new ArrayBuffer(4096));
 assertEq(f(0, 1.3), 1);
 assertEq(f(4088, 2.5), 2);
 assertEq(f(4096, 3.8), 0);
 
+var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=+j; f64[i>>3] = j; return (~~f64[i>>3])|0}; return f');
+var f = asmLink(code, this, null, new ArrayBuffer(4096));
+assertEq(f(0, 1.3), 1);
+assertEq(f(4088, 2.5), 2);
+assertEq(f(4096, 3.8), 0);
+
 var code = asmCompile('glob', 'imp', 'b', USE_ASM + HEAP_IMPORTS + 'function f(i,j) {i=i|0;j=+j; f64[i>>3] = j; return +f64[i>>3]}; return f');
 var f = asmLink(code, this, null, new ArrayBuffer(4096));
 assertEq(f(0, 1.3), 1.3);
 assertEq(f(4088, 2.5), 2.5);
 assertEq(f(4096, 3.8), NaN);
 
 var i32 = new Int32Array(4096);
 i32[0] = 42;
--- a/js/src/jit-test/tests/basic/testBug756919.js
+++ b/js/src/jit-test/tests/basic/testBug756919.js
@@ -1,8 +1,10 @@
 // |jit-test| allow-oom
 
 gcparam("maxBytes", gcparam("gcBytes") + 1024);
 test();
 function test() {
+  var upvar = "";
+  function f() { upvar += ""; }
   test();
   eval('');
 }
--- a/js/src/jit-test/tests/basic/testBug840012.js
+++ b/js/src/jit-test/tests/basic/testBug840012.js
@@ -1,15 +1,17 @@
 // |jit-test| error:out of memory
 
 gcPreserveCode();
 evaluate("gcparam(\"maxBytes\", gcparam(\"gcBytes\") + 4*1024);");
 evaluate("\
 function testDontEnum(F) { \
   function test() {\
+    var upvar = \"\";\
+    function f() { upvar += \"\"; }\
     typeof (new test(\"1\")) != 'function'\
   }\
   test();\
 } \
 var list = [];\
 for (i in list)\
   var F = this[list[i]];\
 actual = testDontEnum(F);\
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug921035.js
@@ -0,0 +1,14 @@
+// |jit-test| error: TypeError
+
+function $ERROR() {}
+function testMultipleArgumentsObjects() {
+    var testargs = arguments;
+    var f = function (which) {
+        var args = [ testargs ];
+        return args[which][0];
+    };
+    var arr = [0, 0, 0, 0, 1];
+    for (var i = 0; i < arr.length; i++)
+        $ERROR[i] = f(arr[i]);
+}
+testMultipleArgumentsObjects()
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -382,18 +382,17 @@ class Type
     enum Which {
         Double,
         Doublish,
         Fixnum,
         Int,
         Signed,
         Unsigned,
         Intish,
-        Void,
-        Unknown
+        Void
     };
 
   private:
     Which which_;
 
   public:
     Type() : which_(Which(-1)) {}
     Type(Which w) : which_(w) {}
@@ -409,25 +408,25 @@ class Type
         return which_ == Unsigned || which_ == Fixnum;
     }
 
     bool isInt() const {
         return isSigned() || isUnsigned() || which_ == Int;
     }
 
     bool isIntish() const {
-        return isInt() || which_ == Intish || which_ == Unknown;
+        return isInt() || which_ == Intish;
     }
 
     bool isDouble() const {
         return which_ == Double;
     }
 
     bool isDoublish() const {
-        return isDouble() || which_ == Doublish || which_ == Unknown;
+        return isDouble() || which_ == Doublish;
     }
 
     bool isVoid() const {
         return which_ == Void;
     }
 
     bool isExtern() const {
         return isDouble() || isSigned();
@@ -444,33 +443,31 @@ class Type
             return MIRType_Double;
           case Fixnum:
           case Int:
           case Signed:
           case Unsigned:
           case Intish:
             return MIRType_Int32;
           case Void:
-          case Unknown:
             return MIRType_None;
         }
         MOZ_ASSUME_UNREACHABLE("Invalid Type");
     }
 
     const char *toChars() const {
         switch (which_) {
           case Double:    return "double";
           case Doublish:  return "doublish";
           case Fixnum:    return "fixnum";
           case Int:       return "int";
           case Signed:    return "signed";
           case Unsigned:  return "unsigned";
           case Intish:    return "intish";
           case Void:      return "void";
-          case Unknown:   return "unknown";
         }
         MOZ_ASSUME_UNREACHABLE("Invalid Type");
     }
 };
 
 } /* anonymous namespace */
 
 // Represents the subset of Type that can be used as the return type of a
@@ -3942,24 +3939,24 @@ CheckCoerceToInt(FunctionCompiler &f, Pa
     JS_ASSERT(expr->isKind(PNK_BITNOT));
     ParseNode *operand = UnaryKid(expr);
 
     MDefinition *operandDef;
     Type operandType;
     if (!CheckExpr(f, operand, &operandDef, &operandType))
         return false;
 
-    if (operandType.isDouble()) {
+    if (operandType.isDoublish()) {
         *def = f.unary<MTruncateToInt32>(operandDef);
         *type = Type::Signed;
         return true;
     }
 
     if (!operandType.isIntish())
-        return f.failf(operand, "%s is not a subtype of double or intish", operandType.toChars());
+        return f.failf(operand, "%s is not a subtype of doublish or intish", operandType.toChars());
 
     *def = operandDef;
     *type = Type::Signed;
     return true;
 }
 
 static bool
 CheckBitNot(FunctionCompiler &f, ParseNode *neg, MDefinition **def, Type *type)
@@ -4155,38 +4152,29 @@ CheckAddOrSub(FunctionCompiler &f, Parse
             return false;
         rhsNumAddOrSub = 0;
     }
 
     unsigned numAddOrSub = lhsNumAddOrSub + rhsNumAddOrSub + 1;
     if (numAddOrSub > (1<<20))
         return f.fail(expr, "too many + or - without intervening coercion");
 
-    if (expr->isKind(PNK_ADD)) {
-        if (lhsType.isInt() && rhsType.isInt()) {
-            *def = f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32);
-            *type = Type::Intish;
-        } else if (lhsType.isDouble() && rhsType.isDouble()) {
-            *def = f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double);
-            *type = Type::Double;
-        } else {
-            return f.failf(expr, "operands to + must both be int or double, got %s and %s",
-                           lhsType.toChars(), rhsType.toChars());
-        }
+    if (lhsType.isInt() && rhsType.isInt()) {
+        *def = expr->isKind(PNK_ADD)
+               ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Int32)
+               : f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
+        *type = Type::Intish;
+    } else if (lhsType.isDoublish() && rhsType.isDoublish()) {
+        *def = expr->isKind(PNK_ADD)
+               ? f.binary<MAdd>(lhsDef, rhsDef, MIRType_Double)
+               : f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
+        *type = Type::Double;
     } else {
-        if (lhsType.isInt() && rhsType.isInt()) {
-            *def = f.binary<MSub>(lhsDef, rhsDef, MIRType_Int32);
-            *type = Type::Intish;
-        } else if (lhsType.isDoublish() && rhsType.isDoublish()) {
-            *def = f.binary<MSub>(lhsDef, rhsDef, MIRType_Double);
-            *type = Type::Double;
-        } else {
-            return f.failf(expr, "operands to - must both be int or doublish, got %s and %s",
-                           lhsType.toChars(), rhsType.toChars());
-        }
+        return f.failf(expr, "operands to +/- must both be int or doublish, got %s and %s",
+                       lhsType.toChars(), rhsType.toChars());
     }
 
     if (numAddOrSubOut)
         *numAddOrSubOut = numAddOrSub;
     return true;
 }
 
 static bool
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -8840,16 +8840,21 @@ DoInstanceOfFallback(JSContext *cx, ICIn
 
     if (!rhs.isObject()) {
         js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rhs, NullPtr());
         return false;
     }
 
     RootedObject obj(cx, &rhs.toObject());
 
+    // For functions, keep track of the |prototype| property in type information,
+    // for use during Ion compilation.
+    if (obj->is<JSFunction>() && IsIonEnabled(cx))
+        types::EnsureTrackPropertyTypes(cx, obj, NameToId(cx->names().prototype));
+
     bool cond = false;
     if (!HasInstance(cx, obj, lhs, &cond))
         return false;
 
     res.setBoolean(cond);
     return true;
 }
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6051,25 +6051,27 @@ CodeGenerator::addSetPropertyCache(LInst
       default:
         MOZ_ASSUME_UNREACHABLE("Bad execution mode");
     }
 }
 
 bool
 CodeGenerator::addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex,
                                   Register temp, FloatRegister tempFloat, ValueOperand index,
-                                  ConstantOrRegister value, bool strict)
+                                  ConstantOrRegister value, bool strict, bool guardHoles)
 {
     switch (gen->info().executionMode()) {
       case SequentialExecution: {
-        SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict);
+        SetElementIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
+                           guardHoles);
         return addCache(ins, allocateCache(cache));
       }
       case ParallelExecution: {
-        SetElementParIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict);
+        SetElementParIC cache(obj, unboxIndex, temp, tempFloat, index, value, strict,
+                              guardHoles);
         return addCache(ins, allocateCache(cache));
       }
       default:
         MOZ_ASSUME_UNREACHABLE("Bad execution mode");
     }
 }
 
 bool
@@ -6213,17 +6215,17 @@ CodeGenerator::visitSetElementCacheV(LSe
     Register obj = ToRegister(ins->object());
     Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
     Register temp = ToRegister(ins->temp());
     FloatRegister tempFloat = ToFloatRegister(ins->tempFloat());
     ValueOperand index = ToValue(ins, LSetElementCacheV::Index);
     ConstantOrRegister value = TypedOrValueRegister(ToValue(ins, LSetElementCacheV::Value));
 
     return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
-                              ins->mir()->strict());
+                              ins->mir()->strict(), ins->mir()->guardHoles());
 }
 
 bool
 CodeGenerator::visitSetElementCacheT(LSetElementCacheT *ins)
 {
     Register obj = ToRegister(ins->object());
     Register unboxIndex = ToTempUnboxRegister(ins->tempToUnboxIndex());
     Register temp = ToRegister(ins->temp());
@@ -6232,17 +6234,17 @@ CodeGenerator::visitSetElementCacheT(LSe
     ConstantOrRegister value;
     const LAllocation *tmp = ins->value();
     if (tmp->isConstant())
         value = *tmp->toConstant();
     else
         value = TypedOrValueRegister(ins->mir()->value()->type(), ToAnyRegister(tmp));
 
     return addSetElementCache(ins, obj, unboxIndex, temp, tempFloat, index, value,
-                              ins->mir()->strict());
+                              ins->mir()->strict(), ins->mir()->guardHoles());
 }
 
 typedef bool (*SetElementICFn)(JSContext *, size_t, HandleObject, HandleValue, HandleValue);
 const VMFunction SetElementIC::UpdateInfo =
     FunctionInfo<SetElementICFn>(SetElementIC::update);
 
 bool
 CodeGenerator::visitSetElementIC(OutOfLineUpdateCache *ool, DataPtr<SetElementIC> &ic)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -338,17 +338,17 @@ class CodeGenerator : public CodeGenerat
                              bool allowGetters);
     bool addGetElementCache(LInstruction *ins, Register obj, ConstantOrRegister index,
                             TypedOrValueRegister output, bool monitoredResult);
     bool addSetPropertyCache(LInstruction *ins, RegisterSet liveRegs, Register objReg,
                              PropertyName *name, ConstantOrRegister value, bool strict,
                              bool needsTypeBarrier);
     bool addSetElementCache(LInstruction *ins, Register obj, Register unboxIndex, Register temp,
                             FloatRegister tempFloat, ValueOperand index, ConstantOrRegister value,
-                            bool strict);
+                            bool strict, bool guardHoles);
     bool checkForAbortPar(LInstruction *lir);
 
     bool generateBranchV(const ValueOperand &value, Label *ifTrue, Label *ifFalse, FloatRegister fr);
 
     bool emitAllocateGCThingPar(LInstruction *lir, const Register &objReg, const Register &sliceReg,
                                 const Register &tempReg1, const Register &tempReg2,
                                 JSObject *templateObj);
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -206,26 +206,35 @@ IsPhiObservable(MPhi *phi, Observability
         for (MUseIterator iter(phi->usesBegin()); iter != phi->usesEnd(); iter++) {
             if (!iter->consumer()->isDefinition() ||
                 !iter->consumer()->toDefinition()->isPhi())
                 return true;
         }
         break;
     }
 
-    // If the Phi is of the |this| value, it must always be observable.
     uint32_t slot = phi->slot();
     CompileInfo &info = phi->block()->info();
-    if (info.fun() && slot == info.thisSlot())
+    JSFunction *fun = info.fun();
+
+    // If the Phi is of the |this| value, it must always be observable.
+    if (fun && slot == info.thisSlot())
+        return true;
+
+    // If the function is heavyweight, and the Phi is of the |scopeChain|
+    // value, and the function may need an argu