Merge m-c to fx-team.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 07 Aug 2013 14:46:19 -0400
changeset 154576 ab34f6dd82d1d8f8c7cd4fc6bb9f7aff9209e869
parent 154575 f8879459be3549eb139cabf78c58ffbb99c228e9 (current diff)
parent 154536 79b5c74ef97be205dfaf9248154632af1f806dd6 (diff)
child 154577 325745823114bd5cbe9008bcb96a8e9c5e2cf868
push id2961
push userlsblakk@mozilla.com
push dateMon, 28 Oct 2013 21:59:28 +0000
treeherdermozilla-beta@73ef4f13486f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.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 m-c to fx-team.
browser/modules/AboutHomeUtils.jsm
mobile/android/base/resources/layout/awesomebar_actionbar.xml
modules/libpref/src/init/all.js
widget/gtk2/compat/gtk/gtkcolorselectiondialog.h
xpcom/typelib/xpidl/Makefile.in
xpcom/typelib/xpidl/moz.build
--- 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/
 #
 
-Removal of XPIDL for bug 893117 requires a clobber to make sure interfaces aren't generated.
+Add an WebIDL interface for bug 892978 requires a clobber for Windows.
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -30,21 +30,16 @@ let gBrowserThumbnails = {
    */
   _tabEvents: ["TabClose", "TabSelect"],
 
   init: function Thumbnails_init() {
     // Bug 863512 - Make page thumbnails work in electrolysis
     if (gMultiProcessBrowser)
       return;
 
-    try {
-      if (Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled"))
-        return;
-    } catch (e) {}
-
     PageThumbs.addExpirationFilter(this);
     gBrowser.addTabsProgressListener(this);
     Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this, false);
 
     this._sslDiskCacheEnabled =
       Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
 
     this._tabEvents.forEach(function (aEvent) {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -79,19 +79,16 @@ this.__defineGetter__("PluralForm", func
 this.__defineSetter__("PluralForm", function (val) {
   delete this.PluralForm;
   return this.PluralForm = val;
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
   "resource://gre/modules/TelemetryStopwatch.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
-  "resource:///modules/AboutHomeUtils.jsm");
-
 #ifdef MOZ_SERVICES_SYNC
 XPCOMUtils.defineLazyModuleGetter(this, "Weave",
   "resource://services-sync/main.js");
 #endif
 
 XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
   let tmp = {};
   Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
@@ -2308,74 +2305,16 @@ function SetPageProxyState(aState)
 
 function PageProxyClickHandler(aEvent)
 {
   if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
     middleMousePaste(aEvent);
 }
 
 /**
- *  Handle load of some pages (about:*) so that we can make modifications
- *  to the DOM for unprivileged pages.
- */
-function BrowserOnAboutPageLoad(doc) {
-  if (doc.documentURI.toLowerCase() == "about:home") {
-    // XXX bug 738646 - when Marketplace is launched, remove this statement and
-    // the hidden attribute set on the apps button in aboutHome.xhtml
-    if (getBoolPref("browser.aboutHome.apps", false))
-      doc.getElementById("apps").removeAttribute("hidden");
-
-    let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
-             getService(Components.interfaces.nsISessionStore);
-    if (ss.canRestoreLastSession &&
-        !PrivateBrowsingUtils.isWindowPrivate(window))
-      doc.getElementById("launcher").setAttribute("session", "true");
-
-    // Inject search engine and snippets URL.
-    let docElt = doc.documentElement;
-    // set the following attributes BEFORE searchEngineURL, which triggers to
-    // show the snippets when it's set.
-    docElt.setAttribute("snippetsURL", AboutHomeUtils.snippetsURL);
-    if (AboutHomeUtils.showKnowYourRights) {
-      docElt.setAttribute("showKnowYourRights", "true");
-      // Set pref to indicate we've shown the notification.
-      let currentVersion = Services.prefs.getIntPref("browser.rights.version");
-      Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
-    }
-    docElt.setAttribute("snippetsVersion", AboutHomeUtils.snippetsVersion);
-
-    let updateSearchEngine = function() {
-      let engine = AboutHomeUtils.defaultSearchEngine;
-      docElt.setAttribute("searchEngineName", engine.name);
-      docElt.setAttribute("searchEnginePostData", engine.postDataString || "");
-      // Again, keep the searchEngineURL as the last attribute, because the
-      // mutation observer in aboutHome.js is counting on that.
-      docElt.setAttribute("searchEngineURL", engine.searchURL);
-    };
-    updateSearchEngine();
-
-    // Listen for the event that's triggered when the user changes search engine.
-    // At this point we simply reload about:home to reflect the change.
-    Services.obs.addObserver(updateSearchEngine, "browser-search-engine-modified", false);
-
-    // Remove the observer when the page is reloaded or closed.
-    doc.defaultView.addEventListener("pagehide", function removeObserver() {
-      doc.defaultView.removeEventListener("pagehide", removeObserver);
-      Services.obs.removeObserver(updateSearchEngine, "browser-search-engine-modified");
-    }, false);
-
-#ifdef MOZ_SERVICES_HEALTHREPORT
-    doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
-      BrowserSearch.recordSearchInHealthReport(e.detail, "abouthome");
-    }, true, true);
-#endif
-  }
-}
-
-/**
  * Handle command events bubbling up from error page content
  */
 let BrowserOnClick = {
   handleEvent: function BrowserOnClick_handleEvent(aEvent) {
     if (!aEvent.isTrusted || // Don't trust synthetic events
         aEvent.button == 2 || aEvent.target.localName != "button") {
       return;
     }
@@ -2389,19 +2328,16 @@ let BrowserOnClick = {
       this.onAboutCertError(originalTarget, ownerDoc);
     }
     else if (ownerDoc.documentURI.startsWith("about:blocked")) {
       this.onAboutBlocked(originalTarget, ownerDoc);
     }
     else if (ownerDoc.documentURI.startsWith("about:neterror")) {
       this.onAboutNetError(originalTarget, ownerDoc);
     }
-    else if (ownerDoc.documentURI.toLowerCase() == "about:home") {
-      this.onAboutHome(originalTarget, ownerDoc);
-    }
   },
 
   onAboutCertError: function BrowserOnClick_onAboutCertError(aTargetElm, aOwnerDoc) {
     let elmId = aTargetElm.getAttribute("id");
     let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
     let isTopFrame = (aOwnerDoc.defaultView.parent === aOwnerDoc.defaultView);
 
     switch (elmId) {
@@ -2568,59 +2504,16 @@ let BrowserOnClick = {
   },
 
   onAboutNetError: function BrowserOnClick_onAboutNetError(aTargetElm, aOwnerDoc) {
     let elmId = aTargetElm.getAttribute("id");
     if (elmId != "errorTryAgain" || !/e=netOffline/.test(aOwnerDoc.documentURI))
       return;
     Services.io.offline = false;
   },
-
-  onAboutHome: function BrowserOnClick_onAboutHome(aTargetElm, aOwnerDoc) {
-    let elmId = aTargetElm.getAttribute("id");
-
-    switch (elmId) {
-      case "restorePreviousSession":
-        let ss = Cc["@mozilla.org/browser/sessionstore;1"].
-                 getService(Ci.nsISessionStore);
-        if (ss.canRestoreLastSession) {
-          ss.restoreLastSession();
-        }
-        aOwnerDoc.getElementById("launcher").removeAttribute("session");
-        break;
-
-      case "downloads":
-        BrowserDownloadsUI();
-        break;
-
-      case "bookmarks":
-        PlacesCommandHook.showPlacesOrganizer("AllBookmarks");
-        break;
-
-      case "history":
-        PlacesCommandHook.showPlacesOrganizer("History");
-        break;
-
-      case "apps":
-        openUILinkIn("https://marketplace.mozilla.org/", "tab");
-        break;
-
-      case "addons":
-        BrowserOpenAddonsMgr();
-        break;
-
-      case "sync":
-        openPreferences("paneSync");
-        break;
-
-      case "settings":
-        openPreferences();
-        break;
-    }
-  },
 };
 
 /**
  * Re-direct the browser to a known-safe page.  This function is
  * used when, for example, the user browses to a known malware page
  * and is presented with about:blocked.  The "Get me out of here!"
  * button should take the user to the default start page so that even
  * when their own homepage is infected, we can get them somewhere safe.
@@ -4290,32 +4183,30 @@ var TabsProgressListener = {
     // document URI is not yet the about:-uri of the error page.
 
     let doc = gMultiProcessBrowser ? null : aWebProgress.DOMWindow.document;
     if (!gMultiProcessBrowser &&
         aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
         Components.isSuccessCode(aStatus) &&
         doc.documentURI.startsWith("about:") &&
         !doc.documentURI.toLowerCase().startsWith("about:blank") &&
+        !doc.documentURI.toLowerCase().startsWith("about:home") &&
         !doc.documentElement.hasAttribute("hasBrowserHandlers")) {
       // STATE_STOP may be received twice for documents, thus store an
       // attribute to ensure handling it just once.
       doc.documentElement.setAttribute("hasBrowserHandlers", "true");
       aBrowser.addEventListener("click", BrowserOnClick, true);
       aBrowser.addEventListener("pagehide", function onPageHide(event) {
         if (event.target.defaultView.frameElement)
           return;
         aBrowser.removeEventListener("click", BrowserOnClick, true);
         aBrowser.removeEventListener("pagehide", onPageHide, true);
         if (event.target.documentElement)
           event.target.documentElement.removeAttribute("hasBrowserHandlers");
       }, true);
-
-      // We also want to make changes to page UI for unprivileged about pages.
-      BrowserOnAboutPageLoad(doc);
     }
   },
 
   onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI,
                               aFlags) {
     // Filter out location changes caused by anchor navigation
     // or history.push/pop/replaceState.
     if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -8,16 +8,18 @@ let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this,
   "LoginManagerContent", "resource://gre/modules/LoginManagerContent.jsm");
 XPCOMUtils.defineLazyModuleGetter(this,
   "InsecurePasswordUtils", "resource://gre/modules/InsecurePasswordUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 // Bug 671101 - directly using webNavigation in this context
 // causes docshells to leak
 this.__defineGetter__("webNavigation", function () {
   return docShell.QueryInterface(Ci.nsIWebNavigation);
 });
 
 addMessageListener("WebNavigation:LoadURI", function (message) {
@@ -45,8 +47,147 @@ if (!Services.prefs.getBoolPref("browser
   });
   addEventListener("DOMAutoComplete", function(event) {
     LoginManagerContent.onUsernameInput(event);
   });
   addEventListener("blur", function(event) {
     LoginManagerContent.onUsernameInput(event);
   });
 }
+
+let AboutHomeListener = {
+  init: function() {
+    let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                              .getInterface(Ci.nsIWebProgress);
+    webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
+
+    addMessageListener("AboutHome:Update", this);
+  },
+
+  receiveMessage: function(aMessage) {
+    switch (aMessage.name) {
+    case "AboutHome:Update":
+      this.onUpdate(aMessage.data);
+      break;
+    }
+  },
+
+  onUpdate: function(aData) {
+    let doc = content.document;
+    if (doc.documentURI.toLowerCase() != "about:home")
+      return;
+
+    if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isWindowPrivate(content))
+      doc.getElementById("launcher").setAttribute("session", "true");
+
+    // Inject search engine and snippets URL.
+    let docElt = doc.documentElement;
+    // set the following attributes BEFORE searchEngineURL, which triggers to
+    // show the snippets when it's set.
+    docElt.setAttribute("snippetsURL", aData.snippetsURL);
+    if (aData.showKnowYourRights)
+      docElt.setAttribute("showKnowYourRights", "true");
+    docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
+
+    let engine = aData.defaultSearchEngine;
+    docElt.setAttribute("searchEngineName", engine.name);
+    docElt.setAttribute("searchEnginePostData", engine.postDataString || "");
+    // Again, keep the searchEngineURL as the last attribute, because the
+    // mutation observer in aboutHome.js is counting on that.
+    docElt.setAttribute("searchEngineURL", engine.searchURL);
+  },
+
+  onPageLoad: function(aDocument) {
+    // XXX bug 738646 - when Marketplace is launched, remove this statement and
+    // the hidden attribute set on the apps button in aboutHome.xhtml
+    if (Services.prefs.getPrefType("browser.aboutHome.apps") == Services.prefs.PREF_BOOL &&
+        Services.prefs.getBoolPref("browser.aboutHome.apps"))
+      doc.getElementById("apps").removeAttribute("hidden");
+
+    sendAsyncMessage("AboutHome:RequestUpdate");
+
+    aDocument.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
+      sendAsyncMessage("AboutHome:Search", { engineName: e.detail });
+    }, true, true);
+  },
+
+  onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
+    let doc = aWebProgress.DOMWindow.document;
+    if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+        aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
+        Components.isSuccessCode(aStatus) &&
+        doc.documentURI.toLowerCase() == "about:home" &&
+        !doc.documentElement.hasAttribute("hasBrowserHandlers")) {
+      // STATE_STOP may be received twice for documents, thus store an
+      // attribute to ensure handling it just once.
+      doc.documentElement.setAttribute("hasBrowserHandlers", "true");
+      addEventListener("click", this.onClick, true);
+      addEventListener("pagehide", function onPageHide(event) {
+        if (event.target.defaultView.frameElement)
+          return;
+        removeEventListener("click", this.onClick, true);
+        removeEventListener("pagehide", onPageHide, true);
+        if (event.target.documentElement)
+          event.target.documentElement.removeAttribute("hasBrowserHandlers");
+      }, true);
+
+      // We also want to make changes to page UI for unprivileged about pages.
+      this.onPageLoad(doc);
+    }
+  },
+
+  onClick: function(aEvent) {
+    if (!aEvent.isTrusted || // Don't trust synthetic events
+        aEvent.button == 2 || aEvent.target.localName != "button") {
+      return;
+    }
+
+    let originalTarget = aEvent.originalTarget;
+    let ownerDoc = originalTarget.ownerDocument;
+    let elmId = originalTarget.getAttribute("id");
+
+    switch (elmId) {
+      case "restorePreviousSession":
+        sendAsyncMessage("AboutHome:RestorePreviousSession");
+        ownerDoc.getElementById("launcher").removeAttribute("session");
+        break;
+
+      case "downloads":
+        sendAsyncMessage("AboutHome:Downloads");
+        break;
+
+      case "bookmarks":
+        sendAsyncMessage("AboutHome:Bookmarks");
+        break;
+
+      case "history":
+        sendAsyncMessage("AboutHome:History");
+        break;
+
+      case "apps":
+        sendAsyncMessage("AboutHome:Apps");
+        break;
+
+      case "addons":
+        sendAsyncMessage("AboutHome:Addons");
+        break;
+
+      case "sync":
+        sendAsyncMessage("AboutHome:Sync");
+        break;
+
+      case "settings":
+        sendAsyncMessage("AboutHome:Settings");
+        break;
+    }
+  },
+
+  QueryInterface: function QueryInterface(aIID) {
+    if (aIID.equals(Ci.nsIWebProgressListener) ||
+        aIID.equals(Ci.nsISupportsWeakReference) ||
+        aIID.equals(Ci.nsISupports)) {
+      return this;
+    }
+
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  }
+};
+AboutHomeListener.init();
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -139,20 +139,16 @@ Site.prototype = {
   },
 
   /**
    * Refreshes the thumbnail for the site.
    */
   refreshThumbnail: function Site_refreshThumbnail() {
     let thumbnailURL = PageThumbs.getThumbnailURL(this.url);
     let thumbnail = this._querySelector(".newtab-thumbnail");
-    // if this is being called due to the thumbnail being updated we will
-    // be setting it to the same value it had before.  To be confident the
-    // change wont be optimized away we remove the property first.
-    thumbnail.style.removeProperty("backgroundImage");
     thumbnail.style.backgroundImage = "url(" + thumbnailURL + ")";
   },
 
   /**
    * Adds event handlers for the site and its buttons.
    */
   _addEventHandlers: function Site_addEventHandlers() {
     // Register drag-and-drop event handlers.
--- a/browser/base/content/test/browser_aboutHome.js
+++ b/browser/base/content/test/browser_aboutHome.js
@@ -2,17 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
   "resource://gre/modules/commonjs/sdk/core/promise.js");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
-  "resource:///modules/AboutHomeUtils.jsm");
+  "resource:///modules/AboutHome.jsm");
 
 let gRightsVersion = Services.prefs.getIntPref("browser.rights.version");
 
 registerCleanupFunction(function() {
   // Ensure we don't pollute prefs for next tests.
   Services.prefs.clearUserPref("network.cookies.cookieBehavior");
   Services.prefs.clearUserPref("network.cookie.lifetimePolicy");
   Services.prefs.clearUserPref("browser.rights.override");
@@ -101,24 +101,27 @@ let gTests = [
       return Promise.resolve();
     }
 
     let numSearchesBefore = 0;
     let deferred = Promise.defer();
     let doc = gBrowser.contentDocument;
     let engineName = doc.documentElement.getAttribute("searchEngineName");
 
-    // We rely on the listener in browser.js being installed and fired before
-    // this one. If this ever changes, we should add an executeSoon() or similar.
     doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
       is(e.detail, engineName, "Detail is search engine name");
 
-      getNumberOfSearches(engineName).then(num => {
-        is(num, numSearchesBefore + 1, "One more search recorded.");
-        deferred.resolve();
+      // We use executeSoon() to ensure that this code runs after the
+      // count has been updated in browser.js, since it uses the same
+      // event.
+      executeSoon(function () {
+        getNumberOfSearches(engineName).then(num => {
+          is(num, numSearchesBefore + 1, "One more search recorded.");
+          deferred.resolve();
+        });
       });
     }, true, true);
 
     // Get the current number of recorded searches.
     getNumberOfSearches(engineName).then(num => {
       numSearchesBefore = num;
 
       info("Perform a search.");
@@ -270,40 +273,52 @@ let gTests = [
       info("Observer: " + aData + " for " + engine.name);
 
       if (aData != "engine-added")
         return;
 
       if (engine.name != "POST Search")
         return;
 
+      // Ready to execute the tests!
+      let needle = "Search for something awesome.";
+      let document = gBrowser.selectedTab.linkedBrowser.contentDocument;
+      let searchText = document.getElementById("searchText");
+
+      // We're about to change the search engine. Once the change has
+      // propagated to the about:home content, we want to perform a search.
+      let mutationObserver = new MutationObserver(function (mutations) {
+        for (let mutation of mutations) {
+          if (mutation.attributeName == "searchEngineURL") {
+            searchText.value = needle;
+            searchText.focus();
+            EventUtils.synthesizeKey("VK_RETURN", {});
+          }
+        }
+      });
+      mutationObserver.observe(document.documentElement, { attributes: true });
+
+      // Change the search engine, triggering the observer above.
       Services.search.defaultEngine = engine;
 
       registerCleanupFunction(function() {
+        mutationObserver.disconnect();
         Services.search.removeEngine(engine);
         Services.search.defaultEngine = currEngine;
       });
 
 
-      // Ready to execute the tests!
-      let needle = "Search for something awesome.";
-      let document = gBrowser.selectedTab.linkedBrowser.contentDocument;
-      let searchText = document.getElementById("searchText");
-
+      // When the search results load, check them for correctness.
       waitForLoad(function() {
         let loadedText = gBrowser.contentDocument.body.textContent;
         ok(loadedText, "search page loaded");
         is(loadedText, "searchterms=" + escape(needle.replace(/\s/g, "+")),
            "Search text should arrive correctly");
         deferred.resolve();
       });
-
-      searchText.value = needle;
-      searchText.focus();
-      EventUtils.synthesizeKey("VK_RETURN", {});
     };
     Services.obs.addObserver(searchObserver, "browser-search-engine-modified", false);
     registerCleanupFunction(function () {
       Services.obs.removeObserver(searchObserver, "browser-search-engine-modified");
     });
     Services.search.addEngine("http://test:80/browser/browser/base/content/test/POSTSearchEngine.xml",
                               Ci.nsISearchEngine.DATA_XML, null, false);
     return deferred.promise;
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -9,16 +9,19 @@ const Cr = Components.results;
 const Cu = Components.utils;
 
 const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/SignInToWebsite.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "AboutHome",
+                                  "resource:///modules/AboutHome.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
@@ -457,16 +460,17 @@ BrowserGlue.prototype = {
 
     webappsUI.init();
     PageThumbs.init();
     NewTabUtils.init();
     BrowserNewTabPreloader.init();
     SignInToWebsiteUX.init();
     PdfJs.init();
     webrtcUI.init();
+    AboutHome.init();
 
     Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
   },
 
   _checkForOldBuildUpdates: function () {
     // check for update if our build is old
     if (Services.prefs.getBoolPref("app.update.enabled") &&
         Services.prefs.getBoolPref("app.update.checkInstallTime")) {
rename from browser/modules/AboutHomeUtils.jsm
rename to browser/modules/AboutHome.jsm
--- a/browser/modules/AboutHomeUtils.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -1,36 +1,43 @@
 /* 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";
 
-this.EXPORTED_SYMBOLS = [ "AboutHomeUtils" ];
+let Cc = Components.classes;
+let Ci = Components.interfaces;
+let Cu = Components.utils;
+
+this.EXPORTED_SYMBOLS = [ "AboutHomeUtils", "AboutHome" ];
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
 // Url to fetch snippets, in the urlFormatter service format.
 const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
 
 // Should be bumped up if the snippets content format changes.
 const STARTPAGE_VERSION = 4;
 
 this.AboutHomeUtils = {
   get snippetsVersion() STARTPAGE_VERSION,
 
   /**
    * Returns an object containing the name and searchURL of the original default
    * search engine.
    */
   get defaultSearchEngine() {
     let defaultEngine = Services.search.defaultEngine;
     let submission = defaultEngine.getSubmission("_searchTerms_", null, "homepage");
-  
+
     return Object.freeze({
       name: defaultEngine.name,
       searchURL: submission.uri.spec,
       postDataString: submission.postDataString
     });
   },
 
   /*
@@ -78,8 +85,125 @@ this.AboutHomeUtils = {
  * Returns the URL to fetch snippets from, in the urlFormatter service format.
  */
 XPCOMUtils.defineLazyGetter(AboutHomeUtils, "snippetsURL", function() {
   let updateURL = Services.prefs
                           .getCharPref(SNIPPETS_URL_PREF)
                           .replace("%STARTPAGE_VERSION%", STARTPAGE_VERSION);
   return Services.urlFormatter.formatURL(updateURL);
 });
+
+/**
+ * This code provides services to the about:home page. Whenever
+ * about:home needs to do something chrome-privileged, it sends a
+ * message that's handled here.
+ */
+let AboutHome = {
+  MESSAGES: [
+    "AboutHome:RestorePreviousSession",
+    "AboutHome:Downloads",
+    "AboutHome:Bookmarks",
+    "AboutHome:History",
+    "AboutHome:Apps",
+    "AboutHome:Addons",
+    "AboutHome:Sync",
+    "AboutHome:Settings",
+    "AboutHome:RequestUpdate",
+    "AboutHome:Search",
+  ],
+
+  init: function() {
+    let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
+
+    for (let msg of this.MESSAGES) {
+      mm.addMessageListener(msg, this);
+    }
+
+    Services.obs.addObserver(this, "browser-search-engine-modified", false);
+  },
+
+  observe: function(aEngine, aTopic, aVerb) {
+    switch (aTopic) {
+      case "browser-search-engine-modified":
+        this.sendAboutHomeData(null);
+        break;
+    }
+  },
+
+  receiveMessage: function(aMessage) {
+    let window = aMessage.target.ownerDocument.defaultView;
+
+    switch (aMessage.name) {
+      case "AboutHome:RestorePreviousSession":
+        let ss = Cc["@mozilla.org/browser/sessionstore;1"].
+                 getService(Ci.nsISessionStore);
+        if (ss.canRestoreLastSession) {
+          ss.restoreLastSession();
+        }
+        break;
+
+      case "AboutHome:Downloads":
+        window.BrowserDownloadsUI();
+        break;
+
+      case "AboutHome:Bookmarks":
+        window.PlacesCommandHook.showPlacesOrganizer("AllBookmarks");
+        break;
+
+      case "AboutHome:History":
+        window.PlacesCommandHook.showPlacesOrganizer("History");
+        break;
+
+      case "AboutHome:Apps":
+        window.openUILinkIn("https://marketplace.mozilla.org/", "tab");
+        break;
+
+      case "AboutHome:Addons":
+        window.BrowserOpenAddonsMgr();
+        break;
+
+      case "AboutHome:Sync":
+        window.openPreferences("paneSync");
+        break;
+
+      case "AboutHome:Settings":
+        window.openPreferences();
+        break;
+
+      case "AboutHome:RequestUpdate":
+        this.sendAboutHomeData(aMessage.target);
+        break;
+
+      case "AboutHome:Search":
+#ifdef MOZ_SERVICES_HEALTHREPORT
+        window.BrowserSearch.recordSearchInHealthReport(aMessage.data.engineName, "abouthome");
+#endif
+        break;
+    }
+  },
+
+  // Send all the chrome-privileged data needed by about:home. This
+  // gets re-sent when the search engine changes.
+  sendAboutHomeData: function(target) {
+    let ss = Cc["@mozilla.org/browser/sessionstore;1"].
+               getService(Ci.nsISessionStore);
+    let data = {
+      showRestoreLastSession: ss.canRestoreLastSession,
+      snippetsURL: AboutHomeUtils.snippetsURL,
+      showKnowYourRights: AboutHomeUtils.showKnowYourRights,
+      snippetsVersion: AboutHomeUtils.snippetsVersion,
+      defaultSearchEngine: AboutHomeUtils.defaultSearchEngine
+    };
+
+    if (AboutHomeUtils.showKnowYourRights) {
+      // Set pref to indicate we've shown the notification.
+      let currentVersion = Services.prefs.getIntPref("browser.rights.version");
+      Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
+    }
+
+    if (target) {
+      target.messageManager.sendAsyncMessage("AboutHome:Update", data);
+    } else {
+      let mm = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
+      mm.broadcastAsyncMessage("AboutHome:Update", data);
+    }
+  },
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -21,12 +21,12 @@ EXTRA_JS_MODULES += [
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXTRA_JS_MODULES += [
         'WindowsJumpLists.jsm',
         'WindowsPreviewPerTab.jsm',
     ]
 
 EXTRA_PP_JS_MODULES += [
-    'AboutHomeUtils.jsm',
+    'AboutHome.jsm',
     'RecentWindow.jsm',
 ]
 
--- a/build/pymake/pymake/data.py
+++ b/build/pymake/pymake/data.py
@@ -568,17 +568,18 @@ class Pattern(object):
     This insane behavior probably doesn't matter, but we're compatible just for shits and giggles.
     """
 
     __slots__ = ('data')
 
     def __init__(self, s):
         r = []
         i = 0
-        while i < len(s):
+        slen = len(s)
+        while i < slen:
             c = s[i]
             if c == '\\':
                 nc = s[i + 1]
                 if nc == '%':
                     r.append('%')
                     i += 1
                 elif nc == '\\':
                     r.append('\\')
--- a/caps/src/nsSecurityManagerFactory.cpp
+++ b/caps/src/nsSecurityManagerFactory.cpp
@@ -42,17 +42,17 @@ nsSecurityNameSet::nsSecurityNameSet()
 }
 
 nsSecurityNameSet::~nsSecurityNameSet()
 {
 }
 
 NS_IMPL_ISUPPORTS1(nsSecurityNameSet, nsIScriptExternalNameSet)
 
-static JSBool
+static bool
 netscape_security_enablePrivilege(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     Telemetry::Accumulate(Telemetry::ENABLE_PRIVILEGE_EVER_CALLED, true);
     return xpc::EnableUniversalXPConnect(cx);
 }
 
 static const JSFunctionSpec PrivilegeManager_static_methods[] = {
     JS_FS("enablePrivilege", netscape_security_enablePrivilege, 1, 0),
--- a/config/check_spidermonkey_style.py
+++ b/config/check_spidermonkey_style.py
@@ -179,17 +179,17 @@ class FileKind(object):
         if filename.endswith('.msg'):
             return FileKind.MSG
 
         error(filename, None, 'unknown file kind')
 
 
 def get_all_filenames():
     """Get a list of all the files in the (Mercurial or Git) repository."""
-    cmds = [['hg', 'manifest'], ['git', 'ls-files']]
+    cmds = [['hg', 'manifest', '-q'], ['git', 'ls-files']]
     for cmd in cmds:
         try:
             all_filenames = subprocess.check_output(cmd, universal_newlines=True,
                                                     stderr=subprocess.PIPE).split('\n')
             return all_filenames
         except:
             continue
     else:
--- a/config/makefiles/precompile/Makefile.in
+++ b/config/makefiles/precompile/Makefile.in
@@ -20,15 +20,18 @@ define make_subtier_dir
 $(MAKE) -C $(2) $(3)
 @echo "BUILDSTATUS SUBTIER_FINISH precompile $(1)"
 
 endef
 
 export::
 	@echo "BUILDSTATUS SUBTIERS IPDL WebIDL"
 
-export:: ipdl webidl
+export:: ipdl webidl xpidl-parser
 
 ipdl:
 	$(call make_subtier_dir,IPDL,$(DEPTH)/ipc/ipdl,ipdl)
 
 webidl:
 	$(call make_subtier_dir,WebIDL,$(DEPTH)/dom/bindings,webidl)
+
+xpidl-parser:
+	$(call make_subtier_dir,XPIDLParser,$(DEPTH)/xpcom/idl-parser,xpidl-parser)
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -39,16 +39,17 @@ class nsAutoScriptBlockerSuppressNodeRem
 class nsDragEvent;
 class nsEvent;
 class nsEventListenerManager;
 class nsHtml5StringParser;
 class nsIChannel;
 class nsIConsoleService;
 class nsIContent;
 class nsIContentPolicy;
+class nsIContentSecurityPolicy;
 class nsIDocShell;
 class nsIDocument;
 class nsIDocumentLoaderFactory;
 class nsIDocumentObserver;
 class nsIDOMDocument;
 class nsIDOMDocumentFragment;
 class nsIDOMEvent;
 class nsIDOMHTMLFormElement;
@@ -466,16 +467,22 @@ public:
    * Get the cache security manager service. Can return null if the layout
    * module has been shut down.
    */
   static nsIScriptSecurityManager* GetSecurityManager()
   {
     return sSecurityManager;
   }
 
+  /**
+   * Get the ContentSecurityPolicy for a JS context.
+   **/
+  static bool GetContentSecurityPolicy(JSContext* aCx,
+                                       nsIContentSecurityPolicy** aCSP);
+
   // Returns the subject principal. Guaranteed to return non-null. May only
   // be called when nsContentUtils is initialized.
   static nsIPrincipal* GetSubjectPrincipal();
 
   // Returns the principal of the given JS object. This should never be null
   // for any object in the XPConnect runtime.
   //
   // In general, being interested in the principal of an object is enough to
--- a/content/base/public/nsISelectionPrivate.idl
+++ b/content/base/public/nsISelectionPrivate.idl
@@ -9,21 +9,23 @@
 
 interface nsRange;
 interface nsIDOMNode;
 interface nsISelectionListener;
 interface nsIContent;
 interface nsINode;
 
 %{C++
+class nsIFrame;
 struct nsTextRangeStyle;
 struct nsPoint;
 struct ScrollAxis;
+#include "nsDirection.h"
 #include "nsTArray.h"
-#include "nsIFrame.h"
+#include "nsIPresShell.h" // TODO: Remove this include
 %}
 
 [ptr] native nsIFrame(nsIFrame);
 [ptr] native RangeArray(nsTArray<nsRange*>);
 [ref] native constTextRangeStyleRef(const nsTextRangeStyle);
 [ref] native nsPointRef(nsPoint);
 native nsDirection(nsDirection);
 native ScrollAxis(nsIPresShell::ScrollAxis);
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -8,16 +8,17 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 LIBRARY_NAME	= gkconbase_s
 MSVC_ENABLE_PGO := 1
 LIBXUL_LIBRARY	= 1
+FAIL_ON_WARNINGS = 1
 
 ifdef MOZ_WEBRTC
 LOCAL_INCLUDES += \
 		-I$(topsrcdir)/netwerk/sctp/datachannel \
 		$(NULL)
 endif
 
 GQI_SRCS = contentbase.gqi
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6021,16 +6021,44 @@ nsContentUtils::FindInternalContentViewe
       *aLoaderType = TYPE_CONTENT;
     }
     return docFactory.forget();
   }
 
   return nullptr;
 }
 
+bool
+nsContentUtils::GetContentSecurityPolicy(JSContext* aCx,
+                                         nsIContentSecurityPolicy** aCSP)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+  // Get the security manager
+  nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
+
+  if (!ssm) {
+    NS_ERROR("Failed to get security manager service");
+    return false;
+  }
+
+  nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx);
+  NS_ASSERTION(subjectPrincipal, "Failed to get subjectPrincipal");
+
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  nsresult rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
+  if (NS_FAILED(rv)) {
+    NS_ERROR("CSP: Failed to get CSP from principal.");
+    return false;
+  }
+
+  csp.forget(aCSP);
+  return true;
+}
+
 // static
 bool
 nsContentUtils::IsPatternMatching(nsAString& aValue, nsAString& aPattern,
                                   nsIDocument* aDocument)
 {
   NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aDocument->GetWindow());
   NS_ENSURE_TRUE(sgo, true);
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -5150,17 +5150,17 @@ nsIDocument::CreateAttributeNS(const nsA
     return nullptr;
   }
 
   nsRefPtr<Attr> attribute = new Attr(nullptr, nodeInfo.forget(),
                                       EmptyString(), true);
   return attribute.forget();
 }
 
-static JSBool
+static bool
 CustomElementConstructor(JSContext *aCx, unsigned aArgc, JS::Value* aVp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
 
   JS::Rooted<JSObject*> global(aCx,
     JS_GetGlobalForObject(aCx, &args.callee()));
   nsCOMPtr<nsPIDOMWindow> window = do_QueryWrapper(aCx, global);
   MOZ_ASSERT(window, "Should have a non-null window");
--- a/content/base/src/nsDocumentEncoder.cpp
+++ b/content/base/src/nsDocumentEncoder.cpp
@@ -457,17 +457,18 @@ nsDocumentEncoder::SerializeToStringRecu
     mNodeFixup->FixupNode(domNodeIn, &serializeClonedChildren, getter_AddRefs(domNodeOut));
     fixedNodeKungfuDeathGrip = do_QueryInterface(domNodeOut);
     maybeFixedNode = fixedNodeKungfuDeathGrip;
   }
 
   if (!maybeFixedNode)
     maybeFixedNode = aNode;
 
-  if (mFlags & SkipInvisibleContent & ~OutputNonTextContentAsPlaceholder) {
+  if ((mFlags & SkipInvisibleContent) &&
+      !(mFlags & OutputNonTextContentAsPlaceholder)) {
     if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
       nsIFrame* frame = static_cast<nsIContent*>(aNode)->GetPrimaryFrame();
       if (frame) {
         bool isSelectable;
         frame->IsSelectable(&isSelectable, nullptr);
         if (!isSelectable){
           aDontSerializeRoot = true;
         }
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -457,33 +457,16 @@ nsFrameLoader::ReallyStartLoadingInterna
   // Just to be safe, recheck uri.
   rv = CheckURILoad(mURIToLoad);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
   mDocShell->CreateLoadInfo(getter_AddRefs(loadInfo));
   NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE);
 
-  // Does this frame have a parent which is already sandboxed or is this
-  // an <iframe> with a sandbox attribute?
-  uint32_t sandboxFlags = 0;
-  uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags();
-
-  HTMLIFrameElement* iframe = HTMLIFrameElement::FromContent(mOwnerContent);
-
-  if (iframe) {
-    sandboxFlags = iframe->GetSandboxFlags();
-  }
-
-  if (sandboxFlags || parentSandboxFlags) {
-    // The child can only add restrictions, never remove them.
-    sandboxFlags |= parentSandboxFlags;
-    mDocShell->SetSandboxFlags(sandboxFlags);
-  }
-
   // If this frame is sandboxed with respect to origin we will set it up with
   // a null principal later in nsDocShell::DoURILoad.
   // We do it there to correctly sandbox content that was loaded into
   // the frame via other methods than the src attribute.
   // We'll use our principal, not that of the document loaded inside us.  This
   // is very important; needed to prevent XSS attacks on documents loaded in
   // subframes!
   loadInfo->SetOwner(mOwnerContent->NodePrincipal());
@@ -1564,16 +1547,25 @@ nsFrameLoader::MaybeCreateDocShell()
     doc->GetContainer();
   nsCOMPtr<nsIWebNavigation> parentAsWebNav = do_QueryInterface(container);
   NS_ENSURE_STATE(parentAsWebNav);
 
   // Create the docshell...
   mDocShell = do_CreateInstance("@mozilla.org/docshell;1");
   NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
 
+  // Apply sandbox flags even if our owner is not an iframe, as this copies
+  // flags from our owning content's owning document.
+  uint32_t sandboxFlags = 0;
+  HTMLIFrameElement* iframe = HTMLIFrameElement::FromContent(mOwnerContent);
+  if (iframe) {
+    sandboxFlags = iframe->GetSandboxFlags();
+  }
+  ApplySandboxFlags(sandboxFlags);
+
   if (!mNetworkCreated) {
     if (mDocShell) {
       mDocShell->SetCreatedDynamically(true);
     }
   }
 
   // Get the frame name and tell the docshell about it.
   NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
@@ -2466,16 +2458,28 @@ nsFrameLoader::SetDetachedSubdocView(nsV
 
 nsView*
 nsFrameLoader::GetDetachedSubdocView(nsIDocument** aContainerDoc) const
 {
   NS_IF_ADDREF(*aContainerDoc = mContainerDocWhileDetached);
   return mDetachedSubdocViews;
 }
 
+void
+nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags)
+{
+  if (mDocShell) {
+    uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags();
+
+    // The child can only add restrictions, never remove them.
+    sandboxFlags |= parentSandboxFlags;
+    mDocShell->SetSandboxFlags(sandboxFlags);
+  }
+}
+
 /* virtual */ void
 nsFrameLoader::AttributeChanged(nsIDocument* aDocument,
                                 mozilla::dom::Element* aElement,
                                 int32_t      aNameSpaceID,
                                 nsIAtom*     aAttribute,
                                 int32_t      aModType)
 {
   MOZ_ASSERT(mObservingOwnerContent);
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -298,16 +298,23 @@ public:
                              nsIDocument* aContainerDoc);
 
   /**
    * Retrieves the detached view and the document containing the view,
    * as set by SetDetachedSubdocView().
    */
   nsView* GetDetachedSubdocView(nsIDocument** aContainerDoc) const;
 
+  /**
+   * Applies a new set of sandbox flags. These are merged with the sandbox
+   * flags from our owning content's owning document with a logical OR, this
+   * ensures that we can only add restrictions and never remove them.
+   */
+  void ApplySandboxFlags(uint32_t sandboxFlags);
+
 private:
 
   void SetOwnerContent(mozilla::dom::Element* aContent);
 
   bool ShouldUseRemoteProcess();
 
   /**
    * Is this a frameloader for a bona fide <iframe mozbrowser> or
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -55,16 +55,17 @@
 #include "nsGkAtoms.h"
 #include "nsThreadUtils.h"
 #include "nsNetUtil.h"
 #include "nsMimeTypes.h"
 #include "nsStyleUtil.h"
 #include "nsGUIEvent.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/Preferences.h"
+#include "nsSandboxFlags.h"
 
 // Concrete classes
 #include "nsFrameLoader.h"
 
 #include "nsObjectLoadingContent.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIChannelPolicy.h"
@@ -2284,19 +2285,30 @@ nsObjectLoadingContent::OpenChannel()
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Referrer
   nsCOMPtr<nsIHttpChannel> httpChan(do_QueryInterface(chan));
   if (httpChan) {
     httpChan->SetReferrer(doc->GetDocumentURI());
   }
 
-  // Set up the channel's principal and such, like nsDocShell::DoURILoad does
-  nsContentUtils::SetUpChannelOwner(thisContent->NodePrincipal(),
-                                    chan, mURI, true);
+  // Set up the channel's principal and such, like nsDocShell::DoURILoad does.
+  // If the content being loaded should be sandboxed with respect to origin we
+  // create a new null principal here. nsContentUtils::SetUpChannelOwner is
+  // used with a flag to force it to be set as the channel owner.
+  nsCOMPtr<nsIPrincipal> ownerPrincipal;
+  uint32_t sandboxFlags = doc->GetSandboxFlags();
+  if (sandboxFlags & SANDBOXED_ORIGIN) {
+    ownerPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1");
+  } else {
+    // Not sandboxed - we allow the content to assume its natural owner.
+    ownerPrincipal = thisContent->NodePrincipal();
+  }
+  nsContentUtils::SetUpChannelOwner(ownerPrincipal, chan, mURI, true,
+                                    sandboxFlags & SANDBOXED_ORIGIN);
 
   nsCOMPtr<nsIScriptChannel> scriptChannel = do_QueryInterface(chan);
   if (scriptChannel) {
     // Allow execution against our context if the principals match
     scriptChannel->SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
   }
 
   // AsyncOpen can fail if a file does not exist.
--- a/content/base/src/nsPlainTextSerializer.cpp
+++ b/content/base/src/nsPlainTextSerializer.cpp
@@ -97,16 +97,18 @@ nsPlainTextSerializer::nsPlainTextSerial
   mTagStackIndex = 0;
   mIgnoreAboveIndex = (uint32_t)kNotFound;
 
   // initialize the OL stack, where numbers for ordered lists are kept
   mOLStack = new int32_t[OLStackSize];
   mOLStackIndex = 0;
 
   mULCount = 0;
+
+  mIgnoredChildNodeLevel = 0;
 }
 
 nsPlainTextSerializer::~nsPlainTextSerializer()
 {
   delete[] mTagStack;
   delete[] mOLStack;
   NS_WARN_IF_FALSE(mHeadLevel == 0, "Wrong head level!");
 }
@@ -227,16 +229,37 @@ nsPlainTextSerializer::PopBool(nsTArray<
   uint32_t size = aStack.Length();
   if (size > 0) {
     returnValue = aStack.ElementAt(size-1);
     aStack.RemoveElementAt(size-1);
   }
   return returnValue;
 }
 
+bool
+nsPlainTextSerializer::ShouldReplaceContainerWithPlaceholder(nsIAtom* aTag)
+{
+  // If nsIDocumentEncoder::OutputNonTextContentAsPlaceholder is set,
+  // non-textual container element should be serialized as placeholder
+  // character and its child nodes should be ignored. See bug 895239.
+  if (!(mFlags & nsIDocumentEncoder::OutputNonTextContentAsPlaceholder)) {
+    return false;
+  }
+
+  return
+    (aTag == nsGkAtoms::audio) ||
+    (aTag == nsGkAtoms::canvas) ||
+    (aTag == nsGkAtoms::iframe) ||
+    (aTag == nsGkAtoms::meter) ||
+    (aTag == nsGkAtoms::progress) ||
+    (aTag == nsGkAtoms::object) ||
+    (aTag == nsGkAtoms::svg) ||
+    (aTag == nsGkAtoms::video);
+}
+
 NS_IMETHODIMP 
 nsPlainTextSerializer::AppendText(nsIContent* aText,
                                   int32_t aStartOffset,
                                   int32_t aEndOffset, 
                                   nsAString& aStr)
 {
   if (mIgnoreAboveIndex != (uint32_t)kNotFound) {
     return NS_OK;
@@ -398,16 +421,28 @@ nsPlainTextSerializer::AppendDocumentSta
                                            nsAString& aStr)
 {
   return NS_OK;
 }
 
 nsresult
 nsPlainTextSerializer::DoOpenContainer(nsIAtom* aTag)
 {
+  // Check if we need output current node as placeholder character and ignore
+  // child nodes.
+  if (ShouldReplaceContainerWithPlaceholder(mElement->Tag())) {
+    if (mIgnoredChildNodeLevel == 0) {
+      // Serialize current node as placeholder character
+      Write(NS_LITERAL_STRING("\xFFFC"));
+    }
+    // Ignore child nodes.
+    mIgnoredChildNodeLevel++;
+    return NS_OK;
+  }
+
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
@@ -723,16 +758,21 @@ nsPlainTextSerializer::DoOpenContainer(n
   mInWhitespace = true;
 
   return NS_OK;
 }
 
 nsresult
 nsPlainTextSerializer::DoCloseContainer(nsIAtom* aTag)
 {
+  if (ShouldReplaceContainerWithPlaceholder(mElement->Tag())) {
+    mIgnoredChildNodeLevel--;
+    return NS_OK;
+  }
+
   if (mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
@@ -918,16 +958,20 @@ nsPlainTextSerializer::DoCloseContainer(
   }
 
   return NS_OK;
 }
 
 bool
 nsPlainTextSerializer::MustSuppressLeaf()
 {
+  if (mIgnoredChildNodeLevel > 0) {
+    return true;
+  }
+
   if ((mTagStackIndex > 1 &&
        mTagStack[mTagStackIndex-2] == nsGkAtoms::select) ||
       (mTagStackIndex > 0 &&
         mTagStack[mTagStackIndex-1] == nsGkAtoms::select)) {
     // Don't output the contents of SELECT elements;
     // Might be nice, eventually, to output just the selected element.
     // Read more in bug 31994.
     return true;
@@ -1026,17 +1070,17 @@ nsPlainTextSerializer::DoAddLeaf(nsIAtom
     while (line.Length() < width) {
       line.Append(PRUnichar('-'));
     }
     Write(line);
 
     EnsureVerticalSpace(0);
   }
   else if (mFlags & nsIDocumentEncoder::OutputNonTextContentAsPlaceholder) {
-    Write(NS_LITERAL_STRING("\uFFFC"));
+    Write(NS_LITERAL_STRING("\xFFFC"));
   }
   else if (aTag == nsGkAtoms::img) {
     /* Output (in decreasing order of preference)
        alt, title or nothing */
     // See <http://www.w3.org/TR/REC-html40/struct/objects.html#edef-IMG>
     nsAutoString imageDescription;
     if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::alt,
                                        imageDescription))) {
--- a/content/base/src/nsPlainTextSerializer.h
+++ b/content/base/src/nsPlainTextSerializer.h
@@ -103,17 +103,19 @@ protected:
     return mHeadLevel == 0;
   }
 
   // Stack handling functions
   bool GetLastBool(const nsTArray<bool>& aStack);
   void SetLastBool(nsTArray<bool>& aStack, bool aValue);
   void PushBool(nsTArray<bool>& aStack, bool aValue);
   bool PopBool(nsTArray<bool>& aStack);
-  
+
+  bool ShouldReplaceContainerWithPlaceholder(nsIAtom* aTag);
+
 protected:
   nsString         mCurrentLine;
   uint32_t         mHeadLevel;
   bool             mAtFirstColumn;
 
   // Handling of quoted text (for mail):
   // Quotes need to be wrapped differently from non-quoted text,
   // because quoted text has a few extra characters (e.g. ">> ")
@@ -203,14 +205,23 @@ protected:
   uint32_t         mULCount;
 
   nsString                     mLineBreak;
   nsCOMPtr<nsILineBreaker>     mLineBreaker;
 
   // Conveniance constant. It would be nice to have it as a const static
   // variable, but that causes issues with OpenBSD and module unloading.
   const nsString          kSpace;
+
+  // If nsIDocumentEncoder::OutputNonTextContentAsPlaceholder is set, the child
+  // nodes of specific nodes - <iframe>, <canvas>, etc. should be ignored.
+  // mIgnoredChildNodeLevel is used to tell if current node is an ignorable
+  // child node. The initial value of mIgnoredChildNodeLevel is 0. When
+  // serializer enters those specific nodes, mIgnoredChildNodeLevel increases
+  // and is greater than 0. Otherwise when serializer leaves those nodes,
+  // mIgnoredChildNodeLevel decreases.
+  uint32_t mIgnoredChildNodeLevel;
 };
 
 nsresult
 NS_NewPlainTextSerializer(nsIContentSerializer** aSerializer);
 
 #endif
--- a/content/base/src/nsTextFragmentImpl.h
+++ b/content/base/src/nsTextFragmentImpl.h
@@ -11,14 +11,22 @@
 template<size_t size> struct Non8BitParameters;
 template<> struct Non8BitParameters<4> {
   static inline size_t mask() { return 0xff00ff00; }
   static inline uint32_t alignMask() { return 0x3; }
   static inline uint32_t numUnicharsPerWord() { return 2; }
 };
 
 template<> struct Non8BitParameters<8> {
-  static inline size_t mask() { return 0xff00ff00ff00ff00; }
+  static inline size_t mask() {
+    static const uint64_t maskAsUint64 = 0xff00ff00ff00ff00ULL;
+    // We have to explicitly cast this 64-bit value to a size_t, or else
+    // compilers for 32-bit platforms will warn about it being too large to fit
+    // in the size_t return type. (Fortunately, this code isn't actually
+    // invoked on 32-bit platforms -- they'll use the <4> specialization above.
+    // So it is, in fact, OK that this value is too large for a 32-bit size_t.)
+    return (size_t)maskAsUint64;
+  }
   static inline uint32_t alignMask() { return 0x7; }
   static inline uint32_t numUnicharsPerWord() { return 4; }
 };
 
 #endif
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -237,16 +237,17 @@ MOCHITEST_FILES_A = \
 		bug282547.sjs \
 		test_domparser_null_char.html \
 		test_bug811701.html \
 		test_bug811701.xhtml \
 		test_bug820909.html \
 		test_bug704063.html \
 		test_bug894874.html \
 		test_bug895974.html \
+		test_bug895239.html \
 		$(NULL)
 
 MOCHITEST_FILES_B = \
 		test_bug459424.html \
 		bug461735-redirect1.sjs \
 		bug461735-redirect2.sjs \
 		bug461735-post-redirect.js \
 		test_bug513194.html \
@@ -358,16 +359,28 @@ MOCHITEST_FILES_B = \
 		file_CSP_evalscript_main.html \
 		file_CSP_evalscript_main.html^headers^ \
 		file_CSP_evalscript_main.js \
 		file_CSP_evalscript_main_allowed.js \
 		file_CSP_evalscript_main_spec_compliant.html \
 	 	file_CSP_evalscript_main_spec_compliant.html^headers^ \
 		file_CSP_evalscript_main_spec_compliant_allowed.html \
 		file_CSP_evalscript_main_spec_compliant_allowed.html^headers^ \
+		test_CSP_evalscript_getCRMFRequest.html \
+		file_CSP_evalscript_main_getCRMFRequest.html \
+		file_CSP_evalscript_main_getCRMFRequest.html^headers^ \
+		file_CSP_evalscript_main_getCRMFRequest.js \
+		file_CSP_evalscript_main_allowed_getCRMFRequest.js \
+		file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html \
+	 	file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html^headers^ \
+		file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html \
+		file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html^headers^ \
+		file_CSP_evalscript_no_CSP_at_all.html \
+		file_CSP_evalscript_no_CSP_at_all.html^headers^ \
+		file_CSP_evalscript_no_CSP_at_all.js \
 		test_CSP_inlinestyle.html \
 		file_CSP_inlinestyle_main.html \
 		file_CSP_inlinestyle_main.html^headers^ \
 		file_CSP_inlinestyle_main_spec_compliant.html \
 		file_CSP_inlinestyle_main_spec_compliant.html^headers^ \
 		file_CSP_inlinestyle_main_spec_compliant_allowed.html \
 		file_CSP_inlinestyle_main_spec_compliant_allowed.html^headers^ \
 		file_csp_bug768029.html \
--- a/content/base/test/file_CSP_evalscript_main.js
+++ b/content/base/test/file_CSP_evalscript_main.js
@@ -25,17 +25,16 @@ var onevalblocked = (function(window) {
       window.parent.scriptBlocked(shouldrun, what, data);
       logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
     };})(window);
 
 
 // Defer until document is loaded so that we can write the pretty result boxes
 // out.
 addEventListener('load', function() {
-
   // setTimeout(String) test -- mutate something in the window._testResults
   // obj, then check it.
   {
     var str_setTimeoutWithStringRan = 'onevalexecuted(false, "setTimeout(String)", "setTimeout with a string was enabled.");';
     function fcn_setTimeoutWithStringCheck() {
       if (this._testResults["setTimeout(String)"] !== "ran") {
         onevalblocked(false, "setTimeout(String)",
                       "setTimeout with a string was blocked");
--- a/content/base/test/file_CSP_evalscript_main_allowed.js
+++ b/content/base/test/file_CSP_evalscript_main_allowed.js
@@ -23,17 +23,16 @@ var onevalblocked = (function(window) {
       window.parent.scriptBlocked(shouldrun, what, data);
       logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
     };})(window);
 
 
 // Defer until document is loaded so that we can write the pretty result boxes
 // out.
 addEventListener('load', function() {
-
   // setTimeout(String) test  -- should pass
   try {
     setTimeout('onevalexecuted(true, "setTimeout(String)", "setTimeout with a string was enabled.");', 10);
   } catch (e) {
     onevalblocked(true, "setTimeout(String)",
                   "setTimeout with a string was blocked");
   }
 
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_allowed_getCRMFRequest.js
@@ -0,0 +1,42 @@
+// some javascript for the CSP eval() tests
+// all of these evals should succeed, as the document loading this script
+// has script-src 'self' 'unsafe-eval'
+
+function logResult(str, passed) {
+  var elt = document.createElement('div');
+  var color = passed ? "#cfc;" : "#fcc";
+  elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+  elt.innerHTML = str;
+  document.body.appendChild(elt);
+}
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptRan(shouldrun, what, data);
+      logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+    };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptBlocked(shouldrun, what, data);
+      logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+    };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+  // test that allows crypto.generateCRMFRequest eval to run
+  try {
+      var script =
+        'console.log("dynamic script passed to crypto.generateCRMFRequest should execute")';
+      crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+      onevalexecuted(true, "eval(script) inside crypto.generateCRMFRequest",
+                     "eval executed during crypto.generateCRMFRequest");
+  } catch (e) {
+    onevalblocked(true, "eval(script) inside crypto.generateCRMFRequest",
+                  "eval was blocked during crypto.generateCRMFRequest");
+  }
+}, false);
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_getCRMFRequest.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_main_getCRMFRequest.js"></script>
+  </head>
+  <body>
+
+    Foo.
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_getCRMFRequest.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+X-Content-Security-Policy: default-src 'self'
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_getCRMFRequest.js
@@ -0,0 +1,48 @@
+// some javascript for the CSP eval() tests
+
+function logResult(str, passed) {
+  var elt = document.createElement('div');
+  var color = passed ? "#cfc;" : "#fcc";
+  elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+  elt.innerHTML = str;
+  document.body.appendChild(elt);
+}
+
+window._testResults = {};
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+    return function(shouldrun, what, data) {
+      window._testResults[what] = "ran";
+      window.parent.scriptRan(shouldrun, what, data);
+      logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+    };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+    return function(shouldrun, what, data) {
+      window._testResults[what] = "blocked";
+      window.parent.scriptBlocked(shouldrun, what, data);
+      logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+    };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+  // generateCRMFRequest test -- make sure we cannot eval the callback if CSP is in effect
+  try {
+    var script = 'console.log("dynamic script eval\'d in crypto.generateCRMFRequest should be disallowed")';
+    crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+    onevalexecuted(false, "crypto.generateCRMFRequest()",
+                   "crypto.generateCRMFRequest() should not run!");
+  } catch (e) {
+    onevalblocked(false, "eval(script) inside crypto.generateCRMFRequest",
+                  "eval was blocked during crypto.generateCRMFRequest");
+  }
+
+
+}, false);
+
+
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_main_allowed_getCRMFRequest.js"></script>
+  </head>
+  <body>
+
+    Foo.
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-eval'
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_main_getCRMFRequest.js"></script>
+  </head>
+  <body>
+
+    Foo.
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Content-Security-Policy: default-src 'self'
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_no_CSP_at_all.html
@@ -0,0 +1,12 @@
+<html>
+  <head>
+    <title>CSP eval script tests: no CSP specified</title>
+    <script type="application/javascript"
+             src="file_CSP_evalscript_no_CSP_at_all.js"></script>
+  </head>
+  <body>
+
+    Foo. See bug 824652
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_no_CSP_at_all.html^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-cache
new file mode 100644
--- /dev/null
+++ b/content/base/test/file_CSP_evalscript_no_CSP_at_all.js
@@ -0,0 +1,42 @@
+// some javascript for the CSP eval() tests
+// all of these evals should succeed, as the document loading this script
+// has script-src 'self' 'unsafe-eval'
+
+function logResult(str, passed) {
+  var elt = document.createElement('div');
+  var color = passed ? "#cfc;" : "#fcc";
+  elt.setAttribute('style', 'background-color:' + color + '; width:100%; border:1px solid black; padding:3px; margin:4px;');
+  elt.innerHTML = str;
+  document.body.appendChild(elt);
+}
+
+// callback for when stuff is allowed by CSP
+var onevalexecuted = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptRan(shouldrun, what, data);
+      logResult((shouldrun ? "PASS: " : "FAIL: ") + what + " : " + data, shouldrun);
+    };})(window);
+
+// callback for when stuff is blocked
+var onevalblocked = (function(window) {
+    return function(shouldrun, what, data) {
+      window.parent.scriptBlocked(shouldrun, what, data);
+      logResult((shouldrun ? "FAIL: " : "PASS: ") + what + " : " + data, !shouldrun);
+    };})(window);
+
+
+// Defer until document is loaded so that we can write the pretty result boxes
+// out.
+addEventListener('load', function() {
+  // test that allows crypto.generateCRMFRequest eval to run when there is no CSP at all in place
+  try {
+      var script =
+        'console.log("dynamic script passed to crypto.generateCRMFRequest should execute")';
+      crypto.generateCRMFRequest('CN=0', 0, 0, null, script, 384, null, 'rsa-dual-use');
+      onevalexecuted(true, "eval(script) inside crypto.generateCRMFRequest: no CSP at all",
+                     "eval executed during crypto.generateCRMFRequest where no CSP is set at all");
+  } catch (e) {
+    onevalblocked(true, "eval(script) inside crypto.generateCRMFRequest",
+                  "eval was blocked during crypto.generateCRMFRequest");
+  }
+}, false);
--- a/content/base/test/test_CSP_evalscript.html
+++ b/content/base/test/test_CSP_evalscript.html
@@ -56,13 +56,14 @@ SimpleTest.waitForExplicitFinish();
 SpecialPowers.pushPrefEnv(
   {'set':[["security.csp.speccompliant", true]]},
     function() {
       // save this for last so that our listeners are registered.
       // ... this loads the testbed of good and bad requests.
       document.getElementById('cspframe').src = 'file_CSP_evalscript_main.html';
       document.getElementById('cspframe2').src = 'file_CSP_evalscript_main_spec_compliant.html';
       document.getElementById('cspframe3').src = 'file_CSP_evalscript_main_spec_compliant_allowed.html';
+      // document.getElementById('cspframe4').src = 'file_CSP_evalscript_no_CSP_at_all.html';
     });
 </script>
 </pre>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_CSP_evalscript_getCRMFRequest.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Content Security Policy "no eval" in crypto.getCRMFRequest()</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+  
+</div>
+
+<iframe style="width:100%;height:300px;" id='cspframe'></iframe>
+<iframe style="width:100%;height:300px;" id='cspframe2'></iframe>
+<iframe style="width:100%;height:300px;" id='cspframe3'></iframe>
+<iframe style="width:100%;height:300px;" id='cspframe4'></iframe>
+<script class="testbody" type="text/javascript">
+
+var path = "/tests/content/base/test/";
+
+var evalScriptsThatRan = 0;
+var evalScriptsBlocked = 0;
+var evalScriptsTotal = 4;
+
+
+// called by scripts that run
+var scriptRan = function(shouldrun, testname, data) {
+  evalScriptsThatRan++;
+  ok(shouldrun, 'EVAL SCRIPT RAN: ' + testname + '(' + data + ')');
+  checkTestResults();
+}
+
+// called when a script is blocked
+var scriptBlocked = function(shouldrun, testname, data) {
+  evalScriptsBlocked++;
+  ok(!shouldrun, 'EVAL SCRIPT BLOCKED: ' + testname + '(' + data + ')');
+  checkTestResults();
+}
+
+
+// Check to see if all the tests have run
+var checkTestResults = function() {
+  // if any test is incomplete, keep waiting
+  if (evalScriptsTotal - evalScriptsBlocked - evalScriptsThatRan > 0)
+    return;
+
+  // ... otherwise, finish
+  SimpleTest.finish();
+}
+
+//////////////////////////////////////////////////////////////////////
+// set up and go
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv(
+  {'set':[["security.csp.speccompliant", true]]},
+    function() {
+      // save this for last so that our listeners are registered.
+      // ... this loads the testbed of good and bad requests.
+      document.getElementById('cspframe').src = 'file_CSP_evalscript_main_getCRMFRequest.html';
+      document.getElementById('cspframe2').src = 'file_CSP_evalscript_main_spec_compliant_getCRMFRequest.html';
+      document.getElementById('cspframe3').src = 'file_CSP_evalscript_main_spec_compliant_allowed_getCRMFRequest.html';
+      document.getElementById('cspframe4').src = 'file_CSP_evalscript_no_CSP_at_all.html';
+    });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug895239.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=895239
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 895239</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+  function testPaintextSerializerWithPlaceHolder() {
+
+    const de = SpecialPowers.Ci.nsIDocumentEncoder;
+    const Cc = SpecialPowers.Cc;
+
+    // Create a plaintext encoder with the flag OutputNonTextContentAsPlaceholder.
+    var encoder = Cc["@mozilla.org/layout/documentEncoder;1?type=text/plain"]
+                  .createInstance(de);
+    var flags = de.OutputRaw |
+                de.OutputNonTextContentAsPlaceholder;
+    encoder.init(document, "text/plain", flags);
+
+    function toPlaintext(id) {
+      var element = document.getElementById(id);
+      var range = document.createRange();
+      range.selectNodeContents(element);
+      encoder.setRange(range);
+      return encoder.encodeToString();
+    }
+
+    // Test cases to serialize all nodes including invisible nodes.
+    is(toPlaintext("case1"), "This is an audio \uFFFC! ", "test with <audio>");
+    is(toPlaintext("case2"), "This is a canvas \uFFFC! ", "test with <canvas>");
+    is(toPlaintext("case3"), "This is an iframe \uFFFC! ", "test with one <iframe>");
+    is(toPlaintext("case4"), "One iframe \uFFFC with another iframe \uFFFC. ", "test with two <iframes>");
+    is(toPlaintext("case5"), "This is a meter \uFFFC! ", "test with <meter>");
+    is(toPlaintext("case6"), "This is a progress \uFFFC! ", "test with <progress>");
+    is(toPlaintext("case7"), "This is an object \uFFFC! ", "test with <object>");
+    is(toPlaintext("case8"), "This is a svg \uFFFC! ", "test with <svg>");
+    is(toPlaintext("case9"), "This is a video \uFFFC! ", "test with <video>");
+    is(toPlaintext("case10"), "This is a video \uFFFC! ", "test with nested tags");
+
+    // Test cases to serialize visible nodes only.
+    encoder.init(document, "text/plain", flags | de.SkipInvisibleContent);
+    is(toPlaintext("case1"), "This is an audio \uFFFC! ", "test with <audio> for visible nodes");
+    is(toPlaintext("case2"), "This is a canvas \uFFFC! ", "test with <canvas> for visible nodes");
+    is(toPlaintext("case3"), "This is an iframe \uFFFC! ", "test with one <iframe> for visible nodes");
+    is(toPlaintext("case4"), "One iframe \uFFFC with another iframe . ", "test with two <iframes> for visible nodes");
+    is(toPlaintext("case5"), "This is a meter \uFFFC! ", "test with <meter> for visible nodes");
+    is(toPlaintext("case6"), "This is a progress \uFFFC! ", "test with <progress> for visible nodes");
+    is(toPlaintext("case7"), "This is an object \uFFFC! ", "test with <object> for visible nodes");
+    is(toPlaintext("case8"), "This is a svg \uFFFC! ", "test with <svg> for visible nodes");
+    is(toPlaintext("case9"), "This is a video \uFFFC! ", "test with <video> for visible nodes");
+    is(toPlaintext("case10"), "This is a video \uFFFC! ", "test with nested tags for visible nodes");
+    SimpleTest.finish();
+  }
+
+  addLoadEvent(testPaintextSerializerWithPlaceHolder);
+  SimpleTest.waitForExplicitFinish();
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=895239">Mozilla Bug 895239</a>
+<p id="display"></p>
+<div id="content">
+  <span id="case1">This is an audio
+    <audio controls="controls">
+      Your browser does not support <code>audio</code> element.
+    </audio>!
+  </span>
+  <span id="case2">This is a canvas
+    <canvas height="100" width="100">
+      Your browser does not support canvas element.
+    </canvas>!
+  </span>
+  <span id="case3">This is an iframe
+    <iframe src="about:blank">
+      Your browser does not support iframes.
+    </iframe>!
+  </span>
+  <span id="case4">One iframe
+    <iframe src="about:blank">
+      Your browser does not support iframes.
+    </iframe> with another iframe
+    <iframe src="about:blank" style="display: none"></iframe>.
+  </span>
+  <span id="case5">This is a meter
+    <meter min="0" max="100" value="50">
+      50%
+    </meter>!
+  </span>
+  <span id="case6">This is a progress
+    <progress max="100" value="70">
+      70%
+    </progress>!
+  </span>
+  <span id="case7">This is an object
+    <object type="application/x-shockware-flash">
+      <a href="#">Download the plugin.</a>
+    </object>!
+  </span>
+  <span id="case8">This is a svg
+    <svg height="100" width="100">
+      Your browser does not support svg.
+      <circle cx="100" cy="100" r="80" fill="green"></circle>
+    </svg>!
+  </span>
+  <span id="case9">This is a video
+    <video>
+      Your browser does not support videos.
+    </video>!
+  </span>
+  <span id="case10">This is a video
+    <video>
+      Your browser does not support videos.<iframe src="about:blank"></iframe>
+    </video>!
+  </span>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/content/base/test/test_ipc_messagemanager_blob.html
+++ b/content/base/test/test_ipc_messagemanager_blob.html
@@ -91,15 +91,15 @@
     addEventListener("load", function() {
       info("Got load event.");
 
       SpecialPowers.addPermission("browser", true, document);
       SpecialPowers.pushPrefEnv({
         "set": [
           ["dom.ipc.browser_frames.oop_by_default", true],
           ["dom.mozBrowserFramesEnabled", true],
-          ["browser.pageThumbs.enabled", false]
+          ["browser.pagethumbnails.capturing_disabled", false]
         ]
       }, runTests);
     });
   </script>
 </body>
 </html>
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -8,16 +8,17 @@
 #include "WebGLObjectModel.h"
 #include "WebGLExtensions.h"
 #include "WebGLContextUtils.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLMemoryMultiReporterWrapper.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLVertexArray.h"
+#include "WebGLQuery.h"
 
 #include "AccessCheck.h"
 #include "nsIConsoleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsError.h"
@@ -191,18 +192,19 @@ WebGLContext::WebGLContext()
     if (mMaxWarnings < -1)
     {
         GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
         mMaxWarnings = 0;
     }
 
     mLastUseIndex = 0;
 
-    mMinInUseAttribArrayLengthCached = false;
-    mMinInUseAttribArrayLength = 0;
+    mBufferFetchingIsVerified = false;
+    mMaxFetchedVertices = 0;
+    mMaxFetchedInstances = 0;
 
     mIsScreenCleared = false;
 
     mDisableFragHighP = false;
 
     mDrawCallsSinceLastFlush = 0;
 }
 
@@ -232,16 +234,17 @@ WebGLContext::DestroyResourcesAndContext
 
     gl->MakeCurrent();
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
     mBoundArrayBuffer = nullptr;
     mCurrentProgram = nullptr;
     mBoundFramebuffer = nullptr;
+    mActiveOcclusionQuery = nullptr;
     mBoundRenderbuffer = nullptr;
     mBoundVertexArray = nullptr;
     mDefaultVertexArray = nullptr;
 
     while (!mTextures.isEmpty())
         mTextures.getLast()->DeleteOnce();
     while (!mVertexArrays.isEmpty())
         mVertexArrays.getLast()->DeleteOnce();
@@ -250,16 +253,18 @@ WebGLContext::DestroyResourcesAndContext
     while (!mRenderbuffers.isEmpty())
         mRenderbuffers.getLast()->DeleteOnce();
     while (!mFramebuffers.isEmpty())
         mFramebuffers.getLast()->DeleteOnce();
     while (!mShaders.isEmpty())
         mShaders.getLast()->DeleteOnce();
     while (!mPrograms.isEmpty())
         mPrograms.getLast()->DeleteOnce();
+    while (!mQueries.isEmpty())
+        mQueries.getLast()->DeleteOnce();
 
     if (mBlackTexturesAreInitialized) {
         gl->fDeleteTextures(1, &mBlackTexture2D);
         gl->fDeleteTextures(1, &mBlackTextureCubeMap);
         mBlackTexturesAreInitialized = false;
     }
 
     if (mFakeVertexAttrib0BufferObject) {
@@ -1594,26 +1599,27 @@ WebGLContext::GetSupportedExtensions(JSC
 
 //
 // XPCOM goop
 //
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_9(WebGLContext,
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_10(WebGLContext,
   mCanvasElement,
   mExtensions,
   mBound2DTextures,
   mBoundCubeMapTextures,
   mBoundArrayBuffer,
   mCurrentProgram,
   mBoundFramebuffer,
   mBoundRenderbuffer,
-  mBoundVertexArray)
+  mBoundVertexArray,
+  mActiveOcclusionQuery)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMWebGLRenderingContext)
   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   // If the exact way we cast to nsISupports here ever changes, fix our
   // ToSupports() method.
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -66,16 +66,17 @@ namespace mozilla {
 class WebGLMemoryPressureObserver;
 class WebGLContextBoundObject;
 class WebGLActiveInfo;
 class WebGLExtensionBase;
 class WebGLBuffer;
 class WebGLVertexAttribData;
 class WebGLShader;
 class WebGLProgram;
+class WebGLQuery;
 class WebGLUniformLocation;
 class WebGLFramebuffer;
 class WebGLRenderbuffer;
 class WebGLShaderPrecisionFormat;
 class WebGLTexture;
 class WebGLVertexArray;
 
 namespace dom {
@@ -384,20 +385,18 @@ public:
     void DeleteShader(WebGLShader *shader);
     void DeleteVertexArray(WebGLVertexArray *vao);
     void DeleteTexture(WebGLTexture *tex);
     void DepthFunc(WebGLenum func);
     void DepthMask(WebGLboolean b);
     void DepthRange(WebGLclampf zNear, WebGLclampf zFar);
     void DetachShader(WebGLProgram *program, WebGLShader *shader);
     void Disable(WebGLenum cap);
-    void DisableVertexAttribArray(WebGLuint index);
     void DrawBuffers(const dom::Sequence<GLenum>& buffers);
     void Enable(WebGLenum cap);
-    void EnableVertexAttribArray(WebGLuint index);
     void Flush() {
         if (!IsContextStable())
             return;
         MakeContextCurrent();
         gl->fFlush();
     }
     void Finish() {
         if (!IsContextStable())
@@ -457,19 +456,16 @@ public:
     JS::Value GetTexParameter(JSContext * /* unused */, WebGLenum target,
                               WebGLenum pname) {
         return GetTexParameter(target, pname);
     }
     JS::Value GetUniform(JSContext* cx, WebGLProgram *prog,
                          WebGLUniformLocation *location, ErrorResult& rv);
     already_AddRefed<WebGLUniformLocation>
       GetUniformLocation(WebGLProgram *prog, const nsAString& name);
-    JS::Value GetVertexAttrib(JSContext* cx, WebGLuint index, WebGLenum pname,
-                              ErrorResult& rv);
-    WebGLsizeiptr GetVertexAttribOffset(WebGLuint index, WebGLenum pname);
     void Hint(WebGLenum target, WebGLenum mode);
     bool IsBuffer(WebGLBuffer *buffer);
     bool IsEnabled(WebGLenum cap);
     bool IsFramebuffer(WebGLFramebuffer *fb);
     bool IsProgram(WebGLProgram *prog);
     bool IsRenderbuffer(WebGLRenderbuffer *rb);
     bool IsShader(WebGLShader *shader);
     bool IsTexture(WebGLTexture *tex);
@@ -740,79 +736,102 @@ public:
                                           WebGLboolean aTranspose);
     bool ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location);
     void ValidateProgram(WebGLProgram *prog);
     bool ValidateUniformLocation(const char* info, WebGLUniformLocation *location_object);
     bool ValidateSamplerUniformSetter(const char* info,
                                     WebGLUniformLocation *location,
                                     WebGLint value);
 
+    void Viewport(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height);
+
+// -----------------------------------------------------------------------------
+// Asynchronous Queries (WebGLContextAsyncQueries.cpp)
+public:
+    already_AddRefed<WebGLQuery> CreateQuery();
+    void DeleteQuery(WebGLQuery *query);
+    void BeginQuery(WebGLenum target, WebGLQuery *query);
+    void EndQuery(WebGLenum target);
+    bool IsQuery(WebGLQuery *query);
+    already_AddRefed<WebGLQuery> GetQuery(WebGLenum target, WebGLenum pname);
+    JS::Value GetQueryObject(JSContext* cx, WebGLQuery *query, WebGLenum pname);
+
+private:
+    bool ValidateTargetParameter(WebGLenum target, const char* infos);
+    WebGLRefPtr<WebGLQuery>& GetActiveQueryByTarget(WebGLenum target);
+
+// -----------------------------------------------------------------------------
+// Vertices Feature (WebGLContextVertices.cpp)
+public:
+    void DrawArrays(GLenum mode, WebGLint first, WebGLsizei count);
+    void DrawArraysInstanced(GLenum mode, WebGLint first, WebGLsizei count, WebGLsizei primcount);
+    void DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, WebGLintptr byteOffset);
+    void DrawElementsInstanced(WebGLenum mode, WebGLsizei count, WebGLenum type,
+                               WebGLintptr byteOffset, WebGLsizei primcount);
+
+    void EnableVertexAttribArray(WebGLuint index);
+    void DisableVertexAttribArray(WebGLuint index);
+
+    JS::Value GetVertexAttrib(JSContext* cx, WebGLuint index, WebGLenum pname,
+                              ErrorResult& rv);
+    WebGLsizeiptr GetVertexAttribOffset(WebGLuint index, WebGLenum pname);
+
     void VertexAttrib1f(WebGLuint index, WebGLfloat x0);
     void VertexAttrib2f(WebGLuint index, WebGLfloat x0, WebGLfloat x1);
     void VertexAttrib3f(WebGLuint index, WebGLfloat x0, WebGLfloat x1,
                         WebGLfloat x2);
     void VertexAttrib4f(WebGLuint index, WebGLfloat x0, WebGLfloat x1,
                         WebGLfloat x2, WebGLfloat x3);
 
     void VertexAttrib1fv(WebGLuint idx, const dom::Float32Array &arr) {
         VertexAttrib1fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib1fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
         VertexAttrib1fv_base(idx, arr.Length(), arr.Elements());
     }
-    void VertexAttrib1fv_base(WebGLuint idx, uint32_t arrayLength,
-                              const WebGLfloat* ptr);
 
     void VertexAttrib2fv(WebGLuint idx, const dom::Float32Array &arr) {
         VertexAttrib2fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib2fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
         VertexAttrib2fv_base(idx, arr.Length(), arr.Elements());
     }
-    void VertexAttrib2fv_base(WebGLuint idx, uint32_t arrayLength,
-                              const WebGLfloat* ptr);
 
     void VertexAttrib3fv(WebGLuint idx, const dom::Float32Array &arr) {
         VertexAttrib3fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib3fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
         VertexAttrib3fv_base(idx, arr.Length(), arr.Elements());
     }
-    void VertexAttrib3fv_base(WebGLuint idx, uint32_t arrayLength,
-                              const WebGLfloat* ptr);
 
     void VertexAttrib4fv(WebGLuint idx, const dom::Float32Array &arr) {
         VertexAttrib4fv_base(idx, arr.Length(), arr.Data());
     }
     void VertexAttrib4fv(WebGLuint idx, const dom::Sequence<WebGLfloat>& arr) {
         VertexAttrib4fv_base(idx, arr.Length(), arr.Elements());
     }
-    void VertexAttrib4fv_base(WebGLuint idx, uint32_t arrayLength,
-                              const WebGLfloat* ptr);
-    
+
     void VertexAttribPointer(WebGLuint index, WebGLint size, WebGLenum type,
                              WebGLboolean normalized, WebGLsizei stride,
                              WebGLintptr byteOffset);
-    void Viewport(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height);
-
-// -----------------------------------------------------------------------------
-// Vertices Feature (WebGLContextVertices.cpp)
-public:
-    void DrawArrays(GLenum mode, WebGLint first, WebGLsizei count);
-    void DrawArraysInstanced(GLenum mode, WebGLint first, WebGLsizei count, WebGLsizei primcount);
-    void DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, WebGLintptr byteOffset);
-    void DrawElementsInstanced(WebGLenum mode, WebGLsizei count, WebGLenum type,
-                               WebGLintptr byteOffset, WebGLsizei primcount);
+    void VertexAttribDivisor(WebGLuint index, WebGLuint divisor);
 
 private:
     bool DrawArrays_check(WebGLint first, WebGLsizei count, WebGLsizei primcount, const char* info);
     bool DrawElements_check(WebGLsizei count, WebGLenum type, WebGLintptr byteOffset,
                             WebGLsizei primcount, const char* info);
     void Draw_cleanup();
 
+    void VertexAttrib1fv_base(WebGLuint idx, uint32_t arrayLength, const WebGLfloat* ptr);
+    void VertexAttrib2fv_base(WebGLuint idx, uint32_t arrayLength, const WebGLfloat* ptr);
+    void VertexAttrib3fv_base(WebGLuint idx, uint32_t arrayLength, const WebGLfloat* ptr);
+    void VertexAttrib4fv_base(WebGLuint idx, uint32_t arrayLength, const WebGLfloat* ptr);
+
+    bool ValidateBufferFetching(const char *info);
+
 // -----------------------------------------------------------------------------
 // PROTECTED
 protected:
     void SetDontKnowIfNeedFakeBlack() {
         mFakeBlackStatus = DontKnowIfNeedFakeBlack;
     }
 
     bool NeedFakeBlack();
@@ -871,25 +890,27 @@ protected:
     int32_t mGLMaxTextureImageUnits;
     int32_t mGLMaxVertexTextureImageUnits;
     int32_t mGLMaxVaryingVectors;
     int32_t mGLMaxFragmentUniformVectors;
     int32_t mGLMaxVertexUniformVectors;
     int32_t mGLMaxColorAttachments;
     int32_t mGLMaxDrawBuffers;
 
-    // Cache the max number of elements that can be read from bound VBOs
-    // (result of ValidateBuffers).
-    bool mMinInUseAttribArrayLengthCached;
-    uint32_t mMinInUseAttribArrayLength;
+    // Cache the max number of vertices and isntances that can be read from
+    // bound VBOs (result of ValidateBuffers).
+    bool mBufferFetchingIsVerified;
+    uint32_t mMaxFetchedVertices;
+    uint32_t mMaxFetchedInstances;
 
-    inline void InvalidateCachedMinInUseAttribArrayLength()
+    inline void InvalidateBufferFetching()
     {
-        mMinInUseAttribArrayLengthCached = false;
-        mMinInUseAttribArrayLength = 0;
+        mBufferFetchingIsVerified = false;
+        mMaxFetchedVertices = 0;
+        mMaxFetchedInstances = 0;
     }
 
     // Represents current status, or state, of the context. That is, is it lost
     // or stable and what part of the context lost process are we currently at.
     // This is used to support the WebGL spec's asyncronous nature in handling
     // context loss.
     enum ContextStatus {
         // The context is stable; there either are none or we don't know of any.
@@ -932,18 +953,19 @@ protected:
     bool IsExtensionEnabled(WebGLExtensionID ext) const;
 
     // returns true if the extension is supported for this JSContext (this decides what getSupportedExtensions exposes)
     bool IsExtensionSupported(JSContext *cx, WebGLExtensionID ext) const;
     bool IsExtensionSupported(WebGLExtensionID ext) const;
 
     nsTArray<WebGLenum> mCompressedTextureFormats;
 
+    // -------------------------------------------------------------------------
+    // Validation functions (implemented in WebGLContextValidate.cpp)
     bool InitAndValidateGL();
-    bool ValidateBuffers(uint32_t *maxAllowedCount, const char *info);
     bool ValidateCapabilityEnum(WebGLenum cap, const char *info);
     bool ValidateBlendEquationEnum(WebGLenum cap, const char *info);
     bool ValidateBlendFuncDstEnum(WebGLenum mode, const char *info);
     bool ValidateBlendFuncSrcEnum(WebGLenum mode, const char *info);
     bool ValidateBlendFuncEnumsCompatibility(WebGLenum sfactor, WebGLenum dfactor, const char *info);
     bool ValidateTextureTargetEnum(WebGLenum target, const char *info);
     bool ValidateComparisonEnum(WebGLenum target, const char *info);
     bool ValidateStencilOpEnum(WebGLenum action, const char *info);
@@ -1083,20 +1105,22 @@ protected:
 
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
 
     uint32_t mMaxFramebufferColorAttachments;
 
     WebGLRefPtr<WebGLFramebuffer> mBoundFramebuffer;
     WebGLRefPtr<WebGLRenderbuffer> mBoundRenderbuffer;
     WebGLRefPtr<WebGLVertexArray> mBoundVertexArray;
+    WebGLRefPtr<WebGLQuery> mActiveOcclusionQuery;
 
     LinkedList<WebGLTexture> mTextures;
     LinkedList<WebGLBuffer> mBuffers;
     LinkedList<WebGLProgram> mPrograms;
+    LinkedList<WebGLQuery> mQueries;
     LinkedList<WebGLShader> mShaders;
     LinkedList<WebGLRenderbuffer> mRenderbuffers;
     LinkedList<WebGLFramebuffer> mFramebuffers;
     LinkedList<WebGLVertexArray> mVertexArrays;
 
     WebGLRefPtr<WebGLVertexArray> mDefaultVertexArray;
 
     // PixelStore parameters
@@ -1175,16 +1199,17 @@ public:
     // console logging helpers
     void GenerateWarning(const char *fmt, ...);
     void GenerateWarning(const char *fmt, va_list ap);
 
     friend class WebGLTexture;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
+    friend class WebGLQuery;
     friend class WebGLBuffer;
     friend class WebGLShader;
     friend class WebGLUniformLocation;
     friend class WebGLVertexArray;
 };
 
 // used by DOM bindings in conjunction with GetParentObject
 inline nsISupports*
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLContextAsyncQueries.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "WebGLContext.h"
+#include "WebGLQuery.h"
+
+using namespace mozilla;
+
+/*
+ * We fake ANY_SAMPLES_PASSED and ANY_SAMPLES_PASSED_CONSERVATIVE with 
+ * SAMPLES_PASSED on desktop.
+ *
+ * OpenGL ES 3.0 spec 4.1.6
+ *  If the target of the query is ANY_SAMPLES_PASSED_CONSERVATIVE, an implementation
+ *  may choose to use a less precise version of the test which can additionally set
+ *  the samples-boolean state to TRUE in some other implementation-dependent cases.
+ */
+
+static const char*
+GetQueryTargetEnumString(WebGLenum target)
+{
+    switch (target)
+    {
+        case LOCAL_GL_ANY_SAMPLES_PASSED:
+            return "ANY_SAMPLES_PASSED";
+        case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+            return "ANY_SAMPLES_PASSED_CONSERVATIVE";
+        default:
+            break;
+    }
+    
+    MOZ_ASSERT(false, "Unknown query `target`.");
+    return "UNKNOWN_QUERY_TARGET";
+}
+
+already_AddRefed<WebGLQuery>
+WebGLContext::CreateQuery()
+{
+    if (!IsContextStable())
+        return nullptr;
+
+    if (mActiveOcclusionQuery && !gl->IsGLES2()) {
+        /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt
+         * Calling either GenQueriesARB or DeleteQueriesARB while any query of
+         * any target is active causes an INVALID_OPERATION error to be
+         * generated.
+         */
+        GenerateWarning("createQuery: the WebGL 2 prototype might generate INVALID_OPERATION"
+                        "when creating a query object while one other is active.");
+        /*
+         * We *need* to lock webgl2 to GL>=3.0 on desktop, but we don't have a good
+         * mechanism to do this yet. See bug 898404.
+         */
+    }
+
+    nsRefPtr<WebGLQuery> globj = new WebGLQuery(this);
+
+    return globj.forget();
+}
+
+void
+WebGLContext::DeleteQuery(WebGLQuery *query)
+{
+    if (!IsContextStable())
+        return;
+
+    if (!query)
+        return;
+
+    if (query->IsDeleted())
+        return;
+
+    if (query->IsActive()) {
+        EndQuery(query->mType);
+    }
+
+    if (mActiveOcclusionQuery && !gl->IsGLES2()) {
+        /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt
+         * Calling either GenQueriesARB or DeleteQueriesARB while any query of
+         * any target is active causes an INVALID_OPERATION error to be
+         * generated.
+         */
+        GenerateWarning("deleteQuery: the WebGL 2 prototype might generate INVALID_OPERATION"
+                        "when deleting a query object while one other is active.");
+    }
+
+    query->RequestDelete();
+}
+
+void
+WebGLContext::BeginQuery(WebGLenum target, WebGLQuery *query)
+{
+    if (!IsContextStable())
+        return;
+
+    if (!ValidateTargetParameter(target, "beginQuery")) {
+        return;
+    }
+
+    if (!query) {
+        /* SPECS BeginQuery.1
+         * http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt
+         * BeginQueryEXT sets the active query object name for the query type given
+         * by <target> to <id>. If BeginQueryEXT is called with an <id> of zero, if
+         * the active query object name for <target> is non-zero (for the targets
+         * ANY_SAMPLES_PASSED_EXT and ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, if the
+         * active query for either target is non-zero), if <id> is the name of an
+         * existing query object whose type does not match <target>, or if <id> is the
+         * active query object name for any query type, the error INVALID_OPERATION is
+         * generated.
+         */
+        ErrorInvalidOperation("beginQuery: query should not be null");
+        return;
+    }
+
+    if (query->IsDeleted()) {
+        /* http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt
+         * BeginQueryEXT fails and an INVALID_OPERATION error is generated if <id>
+         * is not a name returned from a previous call to GenQueriesEXT, or if such
+         * a name has since been deleted with DeleteQueriesEXT.
+         */
+        ErrorInvalidOperation("beginQuery: query has been deleted");
+        return;
+    }
+
+    if (query->HasEverBeenActive() &&
+        query->mType != target)
+    {
+        /*
+         * See SPECS BeginQuery.1
+         */
+        ErrorInvalidOperation("beginQuery: target doesn't match with the query type");
+        return;
+    }
+
+    if (GetActiveQueryByTarget(target)) {
+        /*
+         * See SPECS BeginQuery.1
+         */
+        ErrorInvalidOperation("beginQuery: an other query already active");
+        return;
+    }
+
+    if (!query->HasEverBeenActive()) {
+        query->mType = target;
+    }
+
+    MakeContextCurrent();
+
+    if (!gl->IsGLES2()) {
+        gl->fBeginQuery(LOCAL_GL_SAMPLES_PASSED, query->mGLName);
+    } else {
+        gl->fBeginQuery(target, query->mGLName);
+    }
+
+    GetActiveQueryByTarget(target) = query;
+}
+
+void
+WebGLContext::EndQuery(WebGLenum target)
+{
+    if (!IsContextStable())
+        return;
+
+    if (!ValidateTargetParameter(target, "endQuery")) {
+        return;
+    }
+
+    if (!GetActiveQueryByTarget(target) ||
+        target != GetActiveQueryByTarget(target)->mType)
+    {
+        /* http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt
+         * marks the end of the sequence of commands to be tracked for the query type
+         * given by <target>. The active query object for <target> is updated to
+         * indicate that query results are not available, and the active query object
+         * name for <target> is reset to zero. When the commands issued prior to
+         * EndQueryEXT have completed and a final query result is available, the
+         * query object active when EndQueryEXT is called is updated by the GL. The
+         * query object is updated to indicate that the query results are available
+         * and to contain the query result. If the active query object name for
+         * <target> is zero when EndQueryEXT is called, the error INVALID_OPERATION
+         * is generated.
+         */
+        ErrorInvalidOperation("endQuery: There is no active query of type %s.",
+                              GetQueryTargetEnumString(target));
+        return;
+    }
+
+    MakeContextCurrent();
+
+    if (!gl->IsGLES2()) {
+        gl->fEndQuery(LOCAL_GL_SAMPLES_PASSED);
+    } else {
+        gl->fEndQuery(target);
+    }
+
+    GetActiveQueryByTarget(target) = nullptr;
+}
+
+bool
+WebGLContext::IsQuery(WebGLQuery *query)
+{
+    if (!IsContextStable())
+        return false;
+
+    if (!query)
+        return false;
+
+    return ValidateObjectAllowDeleted("isQuery", query) &&
+           !query->IsDeleted() &&
+           query->HasEverBeenActive();
+}
+
+already_AddRefed<WebGLQuery>
+WebGLContext::GetQuery(WebGLenum target, WebGLenum pname)
+{
+    if (!IsContextStable())
+        return nullptr;
+
+    if (!ValidateTargetParameter(target, "getQuery")) {
+        return nullptr;
+    }
+
+    if (pname != LOCAL_GL_CURRENT_QUERY) {
+        /* OpenGL ES 3.0 spec 6.1.7
+         *  pname must be CURRENT_QUERY.
+         */
+        ErrorInvalidEnum("getQuery: pname must be CURRENT_QUERY");
+        return nullptr;
+    }
+
+    nsRefPtr<WebGLQuery> tmp = GetActiveQueryByTarget(target).get();
+    return tmp.forget();
+}
+
+JS::Value
+WebGLContext::GetQueryObject(JSContext* cx, WebGLQuery *query, WebGLenum pname)
+{
+    if (!IsContextStable())
+        return JS::NullValue();
+
+    if (!query) {
+        /* OpenGL ES 3.0 spec 6.1.7 (spec getQueryObject 1)
+         *  If id is not the name of a query object, or if the query object named by id is
+         *  currently active, then an INVALID_OPERATION error is generated. pname must be
+         *  QUERY_RESULT or QUERY_RESULT_AVAILABLE.
+         */
+        ErrorInvalidOperation("getQueryObject: query should not be null");
+        return JS::NullValue();
+    }
+
+    if (query->IsDeleted()) {
+        // See (spec getQueryObject 1)
+        ErrorInvalidOperation("getQueryObject: query has been deleted");
+        return JS::NullValue();
+    }
+
+    if (query->IsActive()) {
+        // See (spec getQueryObject 1)
+        ErrorInvalidOperation("getQueryObject: query is active");
+        return JS::NullValue();
+    }
+
+    if (!query->HasEverBeenActive()) {
+        /* See (spec getQueryObject 1)
+         *  If this instance of WebGLQuery has never been active before, that mean that
+         *  query->mGLName is not a query object yet.
+         */
+        ErrorInvalidOperation("getQueryObject: query has never been active");
+        return JS::NullValue();
+    }
+
+    switch (pname)
+    {
+        case LOCAL_GL_QUERY_RESULT_AVAILABLE:
+        {
+            GLuint returned = 0;
+
+            MakeContextCurrent();
+            gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned);
+
+            return JS::BooleanValue(returned != 0);
+        }
+
+        case LOCAL_GL_QUERY_RESULT:
+        {
+            GLuint returned = 0;
+
+            MakeContextCurrent();
+            gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT, &returned);
+
+            /*
+             * test (returned != 0) is important because ARB_occlusion_query on desktop drivers
+             * return the number of samples drawed when the OpenGL ES extension
+             * ARB_occlusion_query_boolean return only a boolean if a sample has been drawed.
+             */
+            return JS::BooleanValue(returned != 0);
+        }
+
+        default:
+            break;
+    }
+
+    ErrorInvalidEnum("getQueryObject: pname must be QUERY_RESULT{_AVAILABLE}");
+    return JS::NullValue();
+}
+
+bool
+WebGLContext::ValidateTargetParameter(WebGLenum target, const char* infos)
+{
+    if (target != LOCAL_GL_ANY_SAMPLES_PASSED &&
+        target != LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE)
+    {
+        ErrorInvalidEnum("%s: target must be ANY_SAMPLES_PASSED{_CONSERVATIVE}", infos);
+        return false;
+    }
+
+    return true;
+}
+
+WebGLRefPtr<WebGLQuery>&
+WebGLContext::GetActiveQueryByTarget(WebGLenum target)
+{
+    MOZ_ASSERT(ValidateTargetParameter(target, "private WebGLContext::GetActiveQueryByTarget"));
+
+    return mActiveOcclusionQuery;
+}
+
+
--- a/content/canvas/src/WebGLContextFramebufferOperations.cpp
+++ b/content/canvas/src/WebGLContextFramebufferOperations.cpp
@@ -17,16 +17,20 @@ WebGLContext::Clear(WebGLbitfield mask)
         return;
 
     MakeContextCurrent();
 
     uint32_t m = mask & (LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT | LOCAL_GL_STENCIL_BUFFER_BIT);
     if (mask != m)
         return ErrorInvalidValue("clear: invalid mask bits");
 
+    if (mask == 0) {
+        GenerateWarning("Calling gl.clear(0) has no effect.");
+    }
+
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers())
             return ErrorInvalidFramebufferOperation("clear: incomplete framebuffer");
 
         gl->fClear(mask);
         return;
     }
 
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -378,17 +378,17 @@ WebGLContext::BufferData(WebGLenum targe
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
     void* zeroBuffer = calloc(size, 1);
     if (!zeroBuffer)
         return ErrorOutOfMemory("bufferData: out of memory");
 
     MakeContextCurrent();
-    InvalidateCachedMinInUseAttribArrayLength();
+    InvalidateBufferFetching();
 
     GLenum error = CheckedBufferData(target, size, zeroBuffer, usage);
     free(zeroBuffer);
 
     if (error) {
         GenerateWarning("bufferData generated error %s", ErrorName(error));
         return;
     }
@@ -426,17 +426,17 @@ WebGLContext::BufferData(WebGLenum targe
 
     if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
         return;
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
     MakeContextCurrent();
-    InvalidateCachedMinInUseAttribArrayLength();
+    InvalidateBufferFetching();
 
     GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage);
 
     if (error) {
         GenerateWarning("bufferData generated error %s", ErrorName(error));
         return;
     }
 
@@ -464,17 +464,17 @@ WebGLContext::BufferData(WebGLenum targe
     }
 
     if (!ValidateBufferUsageEnum(usage, "bufferData: usage"))
         return;
 
     if (!boundBuffer)
         return ErrorInvalidOperation("bufferData: no buffer bound!");
 
-    InvalidateCachedMinInUseAttribArrayLength();
+    InvalidateBufferFetching();
     MakeContextCurrent();
 
     GLenum error = CheckedBufferData(target, data.Length(), data.Data(), usage);
     if (error) {
         GenerateWarning("bufferData generated error %s", ErrorName(error));
         return;
     }
 
@@ -1109,34 +1109,16 @@ WebGLContext::DepthRange(WebGLfloat zNea
 
     if (zNear > zFar)
         return ErrorInvalidOperation("depthRange: the near value is greater than the far value!");
 
     MakeContextCurrent();
     gl->fDepthRange(zNear, zFar);
 }
 
-void
-WebGLContext::DisableVertexAttribArray(WebGLuint index)
-{
-    if (!IsContextStable())
-        return;
-
-    if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
-        return;
-
-    MakeContextCurrent();
-    InvalidateCachedMinInUseAttribArrayLength();
-
-    if (index || gl->IsGLES2())
-        gl->fDisableVertexAttribArray(index);
-
-    mBoundVertexArray->mAttribBuffers[index].enabled = false;
-}
-
 int
 WebGLContext::WhatDoesVertexAttrib0Need()
 {
   // here we may assume that mCurrentProgram != null
 
     // work around Mac OSX crash, see bug 631420
 #ifdef XP_MACOSX
     if (gl->WorkAroundDriverBugs() &&
@@ -1390,32 +1372,16 @@ WebGLContext::Disable(WebGLenum cap)
             break;
     }
 
     MakeContextCurrent();
     gl->fDisable(cap);
 }
 
 void
-WebGLContext::EnableVertexAttribArray(WebGLuint index)
-{
-    if (!IsContextStable())
-        return;
-
-    if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
-        return;
-
-    MakeContextCurrent();
-    InvalidateCachedMinInUseAttribArrayLength();
-
-    gl->fEnableVertexAttribArray(index);
-    mBoundVertexArray->mAttribBuffers[index].enabled = true;
-}
-
-void
 WebGLContext::FramebufferRenderbuffer(WebGLenum target, WebGLenum attachment, WebGLenum rbtarget, WebGLRenderbuffer *wrb)
 {
     if (!IsContextStable())
         return;
 
     if (!mBoundFramebuffer)
         return ErrorInvalidOperation("framebufferRenderbuffer: cannot modify framebuffer 0");
 
@@ -2666,109 +2632,16 @@ WebGLContext::GetUniformLocation(WebGLPr
         loc = new WebGLUniformLocation(this,
                                        prog,
                                        intlocation,
                                        info);
     }
     return loc.forget();
 }
 
-JS::Value
-WebGLContext::GetVertexAttrib(JSContext* cx, WebGLuint index, WebGLenum pname,
-                              ErrorResult& rv)
-{
-    if (!IsContextStable())
-        return JS::NullValue();
-
-    if (!mBoundVertexArray->EnsureAttribIndex(index, "getVertexAttrib"))
-        return JS::NullValue();
-
-    MakeContextCurrent();
-
-    switch (pname) {
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
-        {
-            return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribBuffers[index].buf.get(), rv);
-        }
-
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE:
-            return JS::Int32Value(mBoundVertexArray->mAttribBuffers[index].stride);
-
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE:
-        {
-            if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
-                return JS::NullValue();
-
-            if (!mBoundVertexArray->mAttribBuffers[index].enabled)
-                return JS::Int32Value(4);
-
-            // Don't break; fall through.
-        }
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE:
-        {
-            GLint i = 0;
-            gl->fGetVertexAttribiv(index, pname, &i);
-            if (pname == LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE)
-                return JS::Int32Value(i);
-            MOZ_ASSERT(pname == LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE);
-            return JS::NumberValue(uint32_t(i));
-        }
-
-        case LOCAL_GL_CURRENT_VERTEX_ATTRIB:
-        {
-            WebGLfloat vec[4] = {0, 0, 0, 1};
-            if (index) {
-                gl->fGetVertexAttribfv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, &vec[0]);
-            } else {
-                vec[0] = mVertexAttrib0Vector[0];
-                vec[1] = mVertexAttrib0Vector[1];
-                vec[2] = mVertexAttrib0Vector[2];
-                vec[3] = mVertexAttrib0Vector[3];
-            }
-            JSObject* obj = Float32Array::Create(cx, this, 4, vec);
-            if (!obj) {
-                rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-            }
-            return JS::ObjectOrNullValue(obj);
-        }
-
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED:
-        {
-            return JS::BooleanValue(mBoundVertexArray->mAttribBuffers[index].enabled);
-        }
-
-        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
-        {
-            return JS::BooleanValue(mBoundVertexArray->mAttribBuffers[index].normalized);
-        }
-
-        default:
-            ErrorInvalidEnumInfo("getVertexAttrib: parameter", pname);
-    }
-
-    return JS::NullValue();
-}
-
-WebGLsizeiptr
-WebGLContext::GetVertexAttribOffset(WebGLuint index, WebGLenum pname)
-{
-    if (!IsContextStable())
-        return 0;
-
-    if (!ValidateAttribIndex(index, "getVertexAttribOffset"))
-        return 0;
-
-    if (pname != LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER) {
-        ErrorInvalidEnum("getVertexAttribOffset: bad parameter");
-        return 0;
-    }
-
-    return mBoundVertexArray->mAttribBuffers[index].byteOffset;
-}
-
 void
 WebGLContext::Hint(WebGLenum target, WebGLenum mode)
 {
     if (!IsContextStable())
         return;
 
     bool isValid = false;
 
@@ -2868,17 +2741,17 @@ void
 WebGLContext::LinkProgram(WebGLProgram *program)
 {
     if (!IsContextStable())
         return;
 
     if (!ValidateObject("linkProgram", program))
         return;
 
-    InvalidateCachedMinInUseAttribArrayLength(); // we do it early in this function
+    InvalidateBufferFetching(); // we do it early in this function
     // as some of the validation below changes program state
 
     GLuint progname = program->GLName();
 
     if (!program->NextGeneration()) {
         // XXX throw?
         return;
     }
@@ -3783,188 +3656,27 @@ WebGLContext::UniformMatrix4fv_base(WebG
                                          numElementsToUpload, arrayLength, aTranspose)) {
         return;
     }
     MakeContextCurrent();
     gl->fUniformMatrix4fv(location, numElementsToUpload, false, data);
 }
 
 void
-WebGLContext::VertexAttrib1f(WebGLuint index, WebGLfloat x0)
-{
-    if (!IsContextStable())
-        return;
-
-    MakeContextCurrent();
-
-    if (index) {
-        gl->fVertexAttrib1f(index, x0);
-    } else {
-        mVertexAttrib0Vector[0] = x0;
-        mVertexAttrib0Vector[1] = 0;
-        mVertexAttrib0Vector[2] = 0;
-        mVertexAttrib0Vector[3] = 1;
-        if (gl->IsGLES2())
-            gl->fVertexAttrib1f(index, x0);
-    }
-}
-
-void
-WebGLContext::VertexAttrib2f(WebGLuint index, WebGLfloat x0, WebGLfloat x1)
-{
-    if (!IsContextStable())
-        return;
-
-    MakeContextCurrent();
-
-    if (index) {
-        gl->fVertexAttrib2f(index, x0, x1);
-    } else {
-        mVertexAttrib0Vector[0] = x0;
-        mVertexAttrib0Vector[1] = x1;
-        mVertexAttrib0Vector[2] = 0;
-        mVertexAttrib0Vector[3] = 1;
-        if (gl->IsGLES2())
-            gl->fVertexAttrib2f(index, x0, x1);
-    }
-}
-
-void
-WebGLContext::VertexAttrib3f(WebGLuint index, WebGLfloat x0, WebGLfloat x1, WebGLfloat x2)
-{
-    if (!IsContextStable())
-        return;
-
-    MakeContextCurrent();
-
-    if (index) {
-        gl->fVertexAttrib3f(index, x0, x1, x2);
-    } else {
-        mVertexAttrib0Vector[0] = x0;
-        mVertexAttrib0Vector[1] = x1;
-        mVertexAttrib0Vector[2] = x2;
-        mVertexAttrib0Vector[3] = 1;
-        if (gl->IsGLES2())
-            gl->fVertexAttrib3f(index, x0, x1, x2);
-    }
-}
-
-void
-WebGLContext::VertexAttrib4f(WebGLuint index, WebGLfloat x0, WebGLfloat x1,
-                                              WebGLfloat x2, WebGLfloat x3)
-{
-    if (!IsContextStable())
-        return;
-
-    MakeContextCurrent();
-
-    if (index) {
-        gl->fVertexAttrib4f(index, x0, x1, x2, x3);
-    } else {
-        mVertexAttrib0Vector[0] = x0;
-        mVertexAttrib0Vector[1] = x1;
-        mVertexAttrib0Vector[2] = x2;
-        mVertexAttrib0Vector[3] = x3;
-        if (gl->IsGLES2())
-            gl->fVertexAttrib4f(index, x0, x1, x2, x3);
-    }
-}
-
-
-void
-WebGLContext::VertexAttrib1fv_base(WebGLuint idx, uint32_t arrayLength,
-                                   const WebGLfloat* ptr)
-{
-    if (!ValidateAttribArraySetter("VertexAttrib1fv", 1, arrayLength))
-        return;
-
-    MakeContextCurrent();
-    if (idx) {
-        gl->fVertexAttrib1fv(idx, ptr);
-    } else {
-        mVertexAttrib0Vector[0] = ptr[0];
-        mVertexAttrib0Vector[1] = WebGLfloat(0);
-        mVertexAttrib0Vector[2] = WebGLfloat(0);
-        mVertexAttrib0Vector[3] = WebGLfloat(1);
-        if (gl->IsGLES2())
-            gl->fVertexAttrib1fv(idx, ptr);
-    }
-}
-
-void
-WebGLContext::VertexAttrib2fv_base(WebGLuint idx, uint32_t arrayLength,
-                                   const WebGLfloat* ptr)
-{
-    if (!ValidateAttribArraySetter("VertexAttrib2fv", 2, arrayLength))
-        return;
-
-    MakeContextCurrent();
-    if (idx) {
-        gl->fVertexAttrib2fv(idx, ptr);
-    } else {
-        mVertexAttrib0Vector[0] = ptr[0];
-        mVertexAttrib0Vector[1] = ptr[1];
-        mVertexAttrib0Vector[2] = WebGLfloat(0);
-        mVertexAttrib0Vector[3] = WebGLfloat(1);
-        if (gl->IsGLES2())
-            gl->fVertexAttrib2fv(idx, ptr);
-    }
-}
-
-void
-WebGLContext::VertexAttrib3fv_base(WebGLuint idx, uint32_t arrayLength,
-                                   const WebGLfloat* ptr)
-{
-    if (!ValidateAttribArraySetter("VertexAttrib3fv", 3, arrayLength))
-        return;
-
-    MakeContextCurrent();
-    if (idx) {
-        gl->fVertexAttrib3fv(idx, ptr);
-    } else {
-        mVertexAttrib0Vector[0] = ptr[0];
-        mVertexAttrib0Vector[1] = ptr[1];
-        mVertexAttrib0Vector[2] = ptr[2];
-        mVertexAttrib0Vector[3] = WebGLfloat(1);
-        if (gl->IsGLES2())
-            gl->fVertexAttrib3fv(idx, ptr);
-    }
-}
-
-void
-WebGLContext::VertexAttrib4fv_base(WebGLuint idx, uint32_t arrayLength,
-                                   const WebGLfloat* ptr)
-{
-    if (!ValidateAttribArraySetter("VertexAttrib4fv", 4, arrayLength))
-        return;
-
-    MakeContextCurrent();
-    if (idx) {
-        gl->fVertexAttrib4fv(idx, ptr);
-    } else {
-        mVertexAttrib0Vector[0] = ptr[0];
-        mVertexAttrib0Vector[1] = ptr[1];
-        mVertexAttrib0Vector[2] = ptr[2];
-        mVertexAttrib0Vector[3] = ptr[3];
-        if (gl->IsGLES2())
-            gl->fVertexAttrib4fv(idx, ptr);
-    }
-}
-
-void
 WebGLContext::UseProgram(WebGLProgram *prog)
 {
     if (!IsContextStable())
         return;
 
     if (!ValidateObjectAllowNull("useProgram", prog))
         return;
 
     MakeContextCurrent();
-    InvalidateCachedMinInUseAttribArrayLength();
+
+    InvalidateBufferFetching();
 
     WebGLuint progname = prog ? prog->GLName() : 0;
 
     if (prog && !prog->LinkStatus())
         return ErrorInvalidOperation("useProgram: program was not linked successfully");
 
     gl->fUseProgram(progname);
 
@@ -4599,95 +4311,16 @@ WebGLContext::ShaderSource(WebGLShader *
     if (!ValidateGLSLString(cleanSource, "compileShader"))
         return;
 
     shader->SetSource(source);
 
     shader->SetNeedsTranslation();
 }
 
-void
-WebGLContext::VertexAttribPointer(WebGLuint index, WebGLint size, WebGLenum type,
-                                  WebGLboolean normalized, WebGLsizei stride,
-                                  WebGLintptr byteOffset)
-{
-    if (!IsContextStable())
-        return;
-
-    if (mBoundArrayBuffer == nullptr)
-        return ErrorInvalidOperation("vertexAttribPointer: must have valid GL_ARRAY_BUFFER binding");
-
-    WebGLsizei requiredAlignment = 1;
-    switch (type) {
-      case LOCAL_GL_BYTE:
-      case LOCAL_GL_UNSIGNED_BYTE:
-          requiredAlignment = 1;
-          break;
-      case LOCAL_GL_SHORT:
-      case LOCAL_GL_UNSIGNED_SHORT:
-          requiredAlignment = 2;
-          break;
-      // XXX case LOCAL_GL_FIXED:
-      case LOCAL_GL_FLOAT:
-          requiredAlignment = 4;
-          break;
-      default:
-          return ErrorInvalidEnumInfo("vertexAttribPointer: type", type);
-    }
-
-    // requiredAlignment should always be a power of two.
-    WebGLsizei requiredAlignmentMask = requiredAlignment - 1;
-
-    if ( !mBoundVertexArray->EnsureAttribIndex(index, "vertexAttribPointer") ) {
-        return;
-    }
-
-    if (size < 1 || size > 4)
-        return ErrorInvalidValue("vertexAttribPointer: invalid element size");
-
-    if (stride < 0 || stride > 255) // see WebGL spec section 6.6 "Vertex Attribute Data Stride"
-        return ErrorInvalidValue("vertexAttribPointer: negative or too large stride");
-
-    if (byteOffset < 0)
-        return ErrorInvalidValue("vertexAttribPointer: negative offset");
-
-    if (stride & requiredAlignmentMask) {
-        return ErrorInvalidOperation("vertexAttribPointer: stride doesn't satisfy the alignment "
-                                     "requirement of given type");
-    }
-
-    if (byteOffset & requiredAlignmentMask) {
-        return ErrorInvalidOperation("vertexAttribPointer: byteOffset doesn't satisfy the alignment "
-                                     "requirement of given type");
-
-    }
-
-    InvalidateCachedMinInUseAttribArrayLength();
-
-    /* XXX make work with bufferSubData & heterogeneous types 
-    if (type != mBoundArrayBuffer->GLType())
-        return ErrorInvalidOperation("vertexAttribPointer: type must match bound VBO type: %d != %d", type, mBoundArrayBuffer->GLType());
-    */
-
-    WebGLVertexAttribData &vd = mBoundVertexArray->mAttribBuffers[index];
-
-    vd.buf = mBoundArrayBuffer;
-    vd.stride = stride;
-    vd.size = size;
-    vd.byteOffset = byteOffset;
-    vd.type = type;
-    vd.normalized = normalized;
-
-    MakeContextCurrent();
-
-    gl->fVertexAttribPointer(index, size, type, normalized,
-                             stride,
-                             reinterpret_cast<void*>(byteOffset));
-}
-
 GLenum WebGLContext::CheckedTexImage2D(GLenum target,
                                        GLint level,
                                        GLenum internalFormat,
                                        GLsizei width,
                                        GLsizei height,
                                        GLint border,
                                        GLenum format,
                                        GLenum type,
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -84,97 +84,16 @@ WebGLProgram::UpdateInfo()
 	        mUniformInfoMap->Put(uniform.mapped, info);
             }
         }
     }
 
     return true;
 }
 
-/*
- * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
- * that will be legal to be read from bound VBOs.
- */
-
-bool
-WebGLContext::ValidateBuffers(uint32_t *maxAllowedCount, const char *info)
-{
-#ifdef DEBUG
-    GLint currentProgram = 0;
-    MakeContextCurrent();
-    gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram);
-    NS_ASSERTION(GLuint(currentProgram) == mCurrentProgram->GLName(),
-                 "WebGL: current program doesn't agree with GL state");
-    if (GLuint(currentProgram) != mCurrentProgram->GLName())
-        return false;
-#endif
-
-    if (mMinInUseAttribArrayLengthCached) {
-        *maxAllowedCount = mMinInUseAttribArrayLength;
-        return true;
-    }
-
-    uint32_t maxAllowed = UINT32_MAX;
-    uint32_t attribs = mBoundVertexArray->mAttribBuffers.Length();
-    for (uint32_t i = 0; i < attribs; ++i) {
-        const WebGLVertexAttribData& vd = mBoundVertexArray->mAttribBuffers[i];
-
-        // If the attrib array isn't enabled, there's nothing to check;
-        // it's a static value.
-        if (!vd.enabled)
-            continue;
-
-        if (vd.buf == nullptr) {
-            ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i);
-            return false;
-        }
-
-        // If the attrib is not in use, then we don't have to validate
-        // it, just need to make sure that the binding is non-null.
-        if (!mCurrentProgram->IsAttribInUse(i))
-            continue;
-
-        // the base offset
-        CheckedUint32 checked_byteLength
-            = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset;
-        CheckedUint32 checked_sizeOfLastElement
-            = CheckedUint32(vd.componentSize()) * vd.size;
-
-        if (!checked_byteLength.isValid() ||
-            !checked_sizeOfLastElement.isValid())
-        {
-            ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
-            return false;
-        }
-
-        if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
-            maxAllowed = 0;
-            break;
-        } else {
-            CheckedUint32 checked_maxAllowedCount
-                = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
-
-            if (!checked_maxAllowedCount.isValid()) {
-                ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
-                return false;
-            }
-
-            if (maxAllowed > checked_maxAllowedCount.value())
-                maxAllowed = checked_maxAllowedCount.value();
-        }
-    }
-
-    *maxAllowedCount = maxAllowed;
-
-    mMinInUseAttribArrayLengthCached = true;
-    mMinInUseAttribArrayLength = *maxAllowedCount;
-
-    return true;
-}
-
 bool WebGLContext::ValidateCapabilityEnum(WebGLenum cap, const char *info)
 {
     switch (cap) {
         case LOCAL_GL_BLEND:
         case LOCAL_GL_CULL_FACE:
         case LOCAL_GL_DEPTH_TEST:
         case LOCAL_GL_DITHER:
         case LOCAL_GL_POLYGON_OFFSET_FILL:
@@ -1073,19 +992,22 @@ WebGLContext::InitAndValidateGL()
         return false;
     }
 
     if (IsWebGL2() &&
         (!IsExtensionSupported(OES_vertex_array_object) ||
          !IsExtensionSupported(WEBGL_draw_buffers) ||
          !gl->IsExtensionSupported(gl::GLContext::EXT_gpu_shader4) ||
          !gl->IsExtensionSupported(gl::GLContext::EXT_blend_minmax) ||
-         !gl->IsExtensionSupported(gl::GLContext::XXX_draw_instanced)
+         !gl->IsExtensionSupported(gl::GLContext::XXX_draw_instanced) ||
+         !gl->IsExtensionSupported(gl::GLContext::XXX_instanced_arrays) ||
+         (gl->IsGLES2() && !gl->IsExtensionSupported(gl::GLContext::EXT_occlusion_query_boolean))
         ))
     {
+        // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
         return false;
     }
 
     mMemoryPressureObserver
         = new WebGLMemoryPressureObserver(this);
     nsCOMPtr<nsIObserverService> observerService
         = mozilla::services::GetObserverService();
     if (observerService) {
--- a/content/canvas/src/WebGLContextVertexArray.cpp
+++ b/content/canvas/src/WebGLContextVertexArray.cpp
@@ -25,17 +25,17 @@ WebGLContext::BindVertexArray(WebGLVerte
          * generated if array is not a name returned from a previous call to
          * GenVertexArraysOES, or if such a name has since been deleted with
          * DeleteVertexArraysOES
          */
         ErrorInvalidOperation("bindVertexArray: can't bind a deleted array!");
         return;
     }
 
-    InvalidateCachedMinInUseAttribArrayLength();
+    InvalidateBufferFetching();
 
     MakeContextCurrent();
 
     if (array) {
         gl->fBindVertexArray(array->GLName());
         array->SetHasEverBeenBound(true);
         mBoundVertexArray = array;
     }
--- a/content/canvas/src/WebGLContextVertices.cpp
+++ b/content/canvas/src/WebGLContextVertices.cpp
@@ -5,62 +5,476 @@
 
 #include "WebGLContext.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLVertexArray.h"
 #include "WebGLTexture.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLFramebuffer.h"
+#include "WebGLUniformInfo.h"
+#include "WebGLShader.h"
+#include "WebGLProgram.h"
 
 using namespace mozilla;
+using namespace dom;
 
 // For a Tegra workaround.
 static const int MAX_DRAW_CALLS_SINCE_FLUSH = 100;
 
+void
+WebGLContext::VertexAttrib1f(WebGLuint index, WebGLfloat x0)
+{
+    if (!IsContextStable())
+        return;
+
+    MakeContextCurrent();
+
+    if (index) {
+        gl->fVertexAttrib1f(index, x0);
+    } else {
+        mVertexAttrib0Vector[0] = x0;
+        mVertexAttrib0Vector[1] = 0;
+        mVertexAttrib0Vector[2] = 0;
+        mVertexAttrib0Vector[3] = 1;
+        if (gl->IsGLES2())
+            gl->fVertexAttrib1f(index, x0);
+    }
+}
+
+void
+WebGLContext::VertexAttrib2f(WebGLuint index, WebGLfloat x0, WebGLfloat x1)
+{
+    if (!IsContextStable())
+        return;
+
+    MakeContextCurrent();
+
+    if (index) {
+        gl->fVertexAttrib2f(index, x0, x1);
+    } else {
+        mVertexAttrib0Vector[0] = x0;
+        mVertexAttrib0Vector[1] = x1;
+        mVertexAttrib0Vector[2] = 0;
+        mVertexAttrib0Vector[3] = 1;
+        if (gl->IsGLES2())
+            gl->fVertexAttrib2f(index, x0, x1);
+    }
+}
+
+void
+WebGLContext::VertexAttrib3f(WebGLuint index, WebGLfloat x0, WebGLfloat x1, WebGLfloat x2)
+{
+    if (!IsContextStable())
+        return;
+
+    MakeContextCurrent();
+
+    if (index) {
+        gl->fVertexAttrib3f(index, x0, x1, x2);
+    } else {
+        mVertexAttrib0Vector[0] = x0;
+        mVertexAttrib0Vector[1] = x1;
+        mVertexAttrib0Vector[2] = x2;
+        mVertexAttrib0Vector[3] = 1;
+        if (gl->IsGLES2())
+            gl->fVertexAttrib3f(index, x0, x1, x2);
+    }
+}
+
+void
+WebGLContext::VertexAttrib4f(WebGLuint index, WebGLfloat x0, WebGLfloat x1,
+                             WebGLfloat x2, WebGLfloat x3)
+{
+    if (!IsContextStable())
+        return;
+
+    MakeContextCurrent();
+
+    if (index) {
+        gl->fVertexAttrib4f(index, x0, x1, x2, x3);
+    } else {
+        mVertexAttrib0Vector[0] = x0;
+        mVertexAttrib0Vector[1] = x1;
+        mVertexAttrib0Vector[2] = x2;
+        mVertexAttrib0Vector[3] = x3;
+        if (gl->IsGLES2())
+            gl->fVertexAttrib4f(index, x0, x1, x2, x3);
+    }
+}
+
+
+void
+WebGLContext::VertexAttrib1fv_base(WebGLuint idx, uint32_t arrayLength,
+                                   const WebGLfloat* ptr)
+{
+    if (!ValidateAttribArraySetter("VertexAttrib1fv", 1, arrayLength))
+        return;
+
+    MakeContextCurrent();
+    if (idx) {
+        gl->fVertexAttrib1fv(idx, ptr);
+    } else {
+        mVertexAttrib0Vector[0] = ptr[0];
+        mVertexAttrib0Vector[1] = WebGLfloat(0);
+        mVertexAttrib0Vector[2] = WebGLfloat(0);
+        mVertexAttrib0Vector[3] = WebGLfloat(1);
+        if (gl->IsGLES2())
+            gl->fVertexAttrib1fv(idx, ptr);
+    }
+}
+
+void
+WebGLContext::VertexAttrib2fv_base(WebGLuint idx, uint32_t arrayLength,
+                                   const WebGLfloat* ptr)
+{
+    if (!ValidateAttribArraySetter("VertexAttrib2fv", 2, arrayLength))
+        return;
+
+    MakeContextCurrent();
+    if (idx) {
+        gl->fVertexAttrib2fv(idx, ptr);
+    } else {
+        mVertexAttrib0Vector[0] = ptr[0];
+        mVertexAttrib0Vector[1] = ptr[1];
+        mVertexAttrib0Vector[2] = WebGLfloat(0);
+        mVertexAttrib0Vector[3] = WebGLfloat(1);
+        if (gl->IsGLES2())
+            gl->fVertexAttrib2fv(idx, ptr);
+    }
+}
+
+void
+WebGLContext::VertexAttrib3fv_base(WebGLuint idx, uint32_t arrayLength,
+                                   const WebGLfloat* ptr)
+{
+    if (!ValidateAttribArraySetter("VertexAttrib3fv", 3, arrayLength))
+        return;
+
+    MakeContextCurrent();
+    if (idx) {
+        gl->fVertexAttrib3fv(idx, ptr);
+    } else {
+        mVertexAttrib0Vector[0] = ptr[0];
+        mVertexAttrib0Vector[1] = ptr[1];
+        mVertexAttrib0Vector[2] = ptr[2];
+        mVertexAttrib0Vector[3] = WebGLfloat(1);
+        if (gl->IsGLES2())
+            gl->fVertexAttrib3fv(idx, ptr);
+    }
+}
+
+void
+WebGLContext::VertexAttrib4fv_base(WebGLuint idx, uint32_t arrayLength,
+                                   const WebGLfloat* ptr)
+{
+    if (!ValidateAttribArraySetter("VertexAttrib4fv", 4, arrayLength))
+        return;
+
+    MakeContextCurrent();
+    if (idx) {
+        gl->fVertexAttrib4fv(idx, ptr);
+    } else {
+        mVertexAttrib0Vector[0] = ptr[0];
+        mVertexAttrib0Vector[1] = ptr[1];
+        mVertexAttrib0Vector[2] = ptr[2];
+        mVertexAttrib0Vector[3] = ptr[3];
+        if (gl->IsGLES2())
+            gl->fVertexAttrib4fv(idx, ptr);
+    }
+}
+
+void
+WebGLContext::EnableVertexAttribArray(WebGLuint index)
+{
+    if (!IsContextStable())
+        return;
+
+    if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
+        return;
+
+    MakeContextCurrent();
+    InvalidateBufferFetching();
+
+    gl->fEnableVertexAttribArray(index);
+    mBoundVertexArray->mAttribBuffers[index].enabled = true;
+}
+
+void
+WebGLContext::DisableVertexAttribArray(WebGLuint index)
+{
+    if (!IsContextStable())
+        return;
+
+    if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
+        return;
+
+    MakeContextCurrent();
+    InvalidateBufferFetching();
+
+    if (index || gl->IsGLES2())
+        gl->fDisableVertexAttribArray(index);
+
+    mBoundVertexArray->mAttribBuffers[index].enabled = false;
+}
+
+
+JS::Value
+WebGLContext::GetVertexAttrib(JSContext* cx, WebGLuint index, WebGLenum pname,
+                              ErrorResult& rv)
+{
+    if (!IsContextStable())
+        return JS::NullValue();
+
+    if (!mBoundVertexArray->EnsureAttribIndex(index, "getVertexAttrib"))
+        return JS::NullValue();
+
+    MakeContextCurrent();
+
+    switch (pname) {
+        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
+        {
+            return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribBuffers[index].buf.get(), rv);
+        }
+
+        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE:
+        {
+            return JS::Int32Value(mBoundVertexArray->mAttribBuffers[index].stride);
+        }
+
+        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE:
+        {
+            if (!ValidateAttribIndex(index, "getVertexAttrib"))
+                return JS::NullValue();
+            
+            if (!mBoundVertexArray->mAttribBuffers[index].enabled)
+                return JS::Int32Value(4);
+            
+            // Don't break; fall through.
+        }
+        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE:
+        {
+            GLint i = 0;
+            gl->fGetVertexAttribiv(index, pname, &i);
+            if (pname == LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE)
+                return JS::Int32Value(i);
+            MOZ_ASSERT(pname == LOCAL_GL_VERTEX_ATTRIB_ARRAY_TYPE);
+            return JS::NumberValue(uint32_t(i));
+        }
+
+        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_DIVISOR:
+        {
+            if (IsWebGL2())
+            {
+                return JS::Int32Value(mBoundVertexArray->mAttribBuffers[index].divisor);
+            }
+            break;
+        }
+
+        case LOCAL_GL_CURRENT_VERTEX_ATTRIB:
+        {
+            WebGLfloat vec[4] = {0, 0, 0, 1};
+            if (index) {
+                gl->fGetVertexAttribfv(index, LOCAL_GL_CURRENT_VERTEX_ATTRIB, &vec[0]);
+            } else {
+                vec[0] = mVertexAttrib0Vector[0];
+                vec[1] = mVertexAttrib0Vector[1];
+                vec[2] = mVertexAttrib0Vector[2];
+                vec[3] = mVertexAttrib0Vector[3];
+            }
+            JSObject* obj = Float32Array::Create(cx, this, 4, vec);
+            if (!obj) {
+                rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+            }
+            return JS::ObjectOrNullValue(obj);
+        }
+
+        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED:
+        {
+            return JS::BooleanValue(mBoundVertexArray->mAttribBuffers[index].enabled);
+        }
+
+        case LOCAL_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED:
+        {
+            return JS::BooleanValue(mBoundVertexArray->mAttribBuffers[index].normalized);
+        }
+
+        default:
+            break;
+    }
+
+    ErrorInvalidEnumInfo("getVertexAttrib: parameter", pname);
+
+    return JS::NullValue();
+}
+
+WebGLsizeiptr
+WebGLContext::GetVertexAttribOffset(WebGLuint index, WebGLenum pname)
+{
+    if (!IsContextStable())
+        return 0;
+
+    if (!ValidateAttribIndex(index, "getVertexAttribOffset"))
+        return 0;
+
+    if (pname != LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER) {
+        ErrorInvalidEnum("getVertexAttribOffset: bad parameter");
+        return 0;
+    }
+
+    return mBoundVertexArray->mAttribBuffers[index].byteOffset;
+}
+
+void
+WebGLContext::VertexAttribPointer(WebGLuint index, WebGLint size, WebGLenum type,
+                                  WebGLboolean normalized, WebGLsizei stride,
+                                  WebGLintptr byteOffset)
+{
+    if (!IsContextStable())
+        return;
+
+    if (mBoundArrayBuffer == nullptr)
+        return ErrorInvalidOperation("vertexAttribPointer: must have valid GL_ARRAY_BUFFER binding");
+
+    WebGLsizei requiredAlignment = 1;
+    switch (type) {
+        case LOCAL_GL_BYTE:
+        case LOCAL_GL_UNSIGNED_BYTE:
+            requiredAlignment = 1;
+            break;
+        case LOCAL_GL_SHORT:
+        case LOCAL_GL_UNSIGNED_SHORT:
+            requiredAlignment = 2;
+            break;
+            // XXX case LOCAL_GL_FIXED:
+        case LOCAL_GL_FLOAT:
+            requiredAlignment = 4;
+            break;
+        default:
+            return ErrorInvalidEnumInfo("vertexAttribPointer: type", type);
+    }
+
+    // requiredAlignment should always be a power of two.
+    WebGLsizei requiredAlignmentMask = requiredAlignment - 1;
+
+    if ( !mBoundVertexArray->EnsureAttribIndex(index, "vertexAttribPointer") ) {
+        return;
+    }
+
+    if (size < 1 || size > 4)
+        return ErrorInvalidValue("vertexAttribPointer: invalid element size");
+
+    if (stride < 0 || stride > 255) // see WebGL spec section 6.6 "Vertex Attribute Data Stride"
+        return ErrorInvalidValue("vertexAttribPointer: negative or too large stride");
+
+    if (byteOffset < 0)
+        return ErrorInvalidValue("vertexAttribPointer: negative offset");
+
+    if (stride & requiredAlignmentMask) {
+        return ErrorInvalidOperation("vertexAttribPointer: stride doesn't satisfy the alignment "
+                                     "requirement of given type");
+    }
+
+    if (byteOffset & requiredAlignmentMask) {
+        return ErrorInvalidOperation("vertexAttribPointer: byteOffset doesn't satisfy the alignment "
+                                     "requirement of given type");
+        
+    }
+
+    InvalidateBufferFetching();
+
+    /* XXX make work with bufferSubData & heterogeneous types
+     if (type != mBoundArrayBuffer->GLType())
+     return ErrorInvalidOperation("vertexAttribPointer: type must match bound VBO type: %d != %d", type, mBoundArrayBuffer->GLType());
+     */
+
+    WebGLVertexAttribData &vd = mBoundVertexArray->mAttribBuffers[index];
+
+    vd.buf = mBoundArrayBuffer;
+    vd.stride = stride;
+    vd.size = size;
+    vd.byteOffset = byteOffset;
+    vd.type = type;
+    vd.normalized = normalized;
+
+    MakeContextCurrent();
+
+    gl->fVertexAttribPointer(index, size, type, normalized,
+                             stride,
+                             reinterpret_cast<void*>(byteOffset));
+}
+
+void
+WebGLContext::VertexAttribDivisor(WebGLuint index, WebGLuint divisor)
+{
+    if (!IsContextStable())
+        return;
+
+    if ( !mBoundVertexArray->EnsureAttribIndex(index, "vertexAttribDivisor") ) {
+        return;
+    }
+
+    WebGLVertexAttribData& vd = mBoundVertexArray->mAttribBuffers[index];
+    vd.divisor = divisor;
+
+    InvalidateBufferFetching();
+
+    MakeContextCurrent();
+
+    gl->fVertexAttribDivisor(index, divisor);
+}
 
 bool WebGLContext::DrawArrays_check(WebGLint first, WebGLsizei count, WebGLsizei primcount, const char* info)
 {
     if (first < 0 || count < 0) {
         ErrorInvalidValue("%s: negative first or count", info);
         return false;
     }
 
+    if (primcount < 0) {
+        ErrorInvalidValue("%s: negative primcount", info);
+        return false;
+    }
+
     if (!ValidateStencilParamsForDrawCall()) {
         return false;
     }
 
     // If count is 0, there's nothing to do.
     if (count == 0 || primcount == 0) {
         return false;
     }
 
     // If there is no current program, this is silently ignored.
     // Any checks below this depend on a program being available.
     if (!mCurrentProgram) {
         return false;
     }
 
-    uint32_t maxAllowedCount = 0;
-    if (!ValidateBuffers(&maxAllowedCount, info)) {
+    if (!ValidateBufferFetching(info)) {
         return false;
     }
 
     CheckedInt<GLsizei> checked_firstPlusCount = CheckedInt<GLsizei>(first) + count;
 
     if (!checked_firstPlusCount.isValid()) {
         ErrorInvalidOperation("%s: overflow in first+count", info);
         return false;
     }
 
-    if (uint32_t(checked_firstPlusCount.value()) > maxAllowedCount) {
+    if (uint32_t(checked_firstPlusCount.value()) > mMaxFetchedVertices) {
         ErrorInvalidOperation("%s: bound vertex attribute buffers do not have sufficient size for given first and count", info);
         return false;
     }
 
+    if (uint32_t(primcount) > mMaxFetchedInstances) {
+        ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
+        return false;
+    }
+
     MakeContextCurrent();
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers()) {
             ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
             return false;
         }
     }
@@ -112,16 +526,21 @@ WebGLContext::DrawArraysInstanced(GLenum
 bool
 WebGLContext::DrawElements_check(WebGLsizei count, WebGLenum type, WebGLintptr byteOffset, WebGLsizei primcount, const char* info)
 {
     if (count < 0 || byteOffset < 0) {
         ErrorInvalidValue("%s: negative count or offset", info);
         return false;
     }
 
+    if (primcount < 0) {
+        ErrorInvalidValue("%s: negative primcount", info);
+        return false;
+    }
+
     if (!ValidateStencilParamsForDrawCall()) {
         return false;
     }
 
     // If count is 0, there's nothing to do.
     if (count == 0 || primcount == 0) {
         return false;
     }
@@ -183,39 +602,43 @@ WebGLContext::DrawElements_check(WebGLsi
         return false;
     }
 
     if (uint32_t(checked_neededByteCount.value()) > mBoundVertexArray->mBoundElementArrayBuffer->ByteLength()) {
         ErrorInvalidOperation("%s: bound element array buffer is too small for given count and offset", info);
         return false;
     }
 
-    uint32_t maxAllowedCount = 0;
-    if (!ValidateBuffers(&maxAllowedCount, info))
+    if (!ValidateBufferFetching(info))
         return false;
 
-    if (!maxAllowedCount ||
-        !mBoundVertexArray->mBoundElementArrayBuffer->Validate(type, maxAllowedCount - 1, first, count))
+    if (!mMaxFetchedVertices ||
+        !mBoundVertexArray->mBoundElementArrayBuffer->Validate(type, mMaxFetchedVertices - 1, first, count))
     {
         ErrorInvalidOperation(
                               "%s: bound vertex attribute buffers do not have sufficient "
                               "size for given indices from the bound element array", info);
         return false;
     }
 
+    if (uint32_t(primcount) > mMaxFetchedInstances) {
+        ErrorInvalidOperation("%s: bound instance attribute buffers do not have sufficient size for given primcount", info);
+        return false;
+    }
+
     MakeContextCurrent();
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeRenderbuffers()) {
             ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
             return false;
         }
     }
 
-    if (!DoFakeVertexAttrib0(maxAllowedCount)) {
+    if (!DoFakeVertexAttrib0(mMaxFetchedVertices)) {
         return false;
     }
     BindFakeBlackTextures();
 
     return true;
 }
 
 void
@@ -242,17 +665,17 @@ WebGLContext::DrawElementsInstanced(WebG
                                         WebGLintptr byteOffset, WebGLsizei primcount)
 {
     if (!IsContextStable())
         return;
 
     if (!ValidateDrawModeEnum(mode, "drawElementsInstanced: mode"))
         return;
 
-    if (!DrawElements_check(count, type, byteOffset, 1, "drawElementsInstanced"))
+    if (!DrawElements_check(count, type, byteOffset, primcount, "drawElementsInstanced"))
         return;
 
     SetupContextLossTimer();
     gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset));
 
     Draw_cleanup();
 }
 
@@ -274,8 +697,87 @@ void WebGLContext::Draw_cleanup()
             if (mDrawCallsSinceLastFlush >= MAX_DRAW_CALLS_SINCE_FLUSH) {
                 gl->fFlush();
                 mDrawCallsSinceLastFlush = 0;
             }
         }
     }
 }
 
+/*
+ * Verify that state is consistent for drawing, and compute max number of elements (maxAllowedCount)
+ * that will be legal to be read from bound VBOs.
+ */
+
+bool
+WebGLContext::ValidateBufferFetching(const char *info)
+{
+#ifdef DEBUG
+    GLint currentProgram = 0;
+    MakeContextCurrent();
+    gl->fGetIntegerv(LOCAL_GL_CURRENT_PROGRAM, &currentProgram);
+    MOZ_ASSERT(GLuint(currentProgram) == mCurrentProgram->GLName(),
+               "WebGL: current program doesn't agree with GL state");
+#endif
+
+    if (mBufferFetchingIsVerified) {
+        return true;
+    }
+
+    uint32_t maxVertices = UINT32_MAX;
+    uint32_t maxInstances = UINT32_MAX;
+    uint32_t attribs = mBoundVertexArray->mAttribBuffers.Length();
+
+    for (uint32_t i = 0; i < attribs; ++i) {
+        const WebGLVertexAttribData& vd = mBoundVertexArray->mAttribBuffers[i];
+
+        // If the attrib array isn't enabled, there's nothing to check;
+        // it's a static value.
+        if (!vd.enabled)
+            continue;
+
+        if (vd.buf == nullptr) {
+            ErrorInvalidOperation("%s: no VBO bound to enabled vertex attrib index %d!", info, i);
+            return false;
+        }
+
+        // If the attrib is not in use, then we don't have to validate
+        // it, just need to make sure that the binding is non-null.
+        if (!mCurrentProgram->IsAttribInUse(i))
+            continue;
+
+        // the base offset
+        CheckedUint32 checked_byteLength = CheckedUint32(vd.buf->ByteLength()) - vd.byteOffset;
+        CheckedUint32 checked_sizeOfLastElement = CheckedUint32(vd.componentSize()) * vd.size;
+
+        if (!checked_byteLength.isValid() ||
+            !checked_sizeOfLastElement.isValid())
+        {
+            ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
+            return false;
+        }
+
+        if (checked_byteLength.value() < checked_sizeOfLastElement.value()) {
+            maxVertices = 0;
+            maxInstances = 0;
+            break;
+        }
+
+        CheckedUint32 checked_maxAllowedCount = ((checked_byteLength - checked_sizeOfLastElement) / vd.actualStride()) + 1;
+
+        if (!checked_maxAllowedCount.isValid()) {
+            ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
+            return false;
+        }
+
+        if (vd.divisor == 0)
+            maxVertices = std::min(maxVertices, checked_maxAllowedCount.value());
+        else
+            maxInstances = std::min(maxInstances, checked_maxAllowedCount.value() / vd.divisor);
+    }
+
+    mBufferFetchingIsVerified = true;
+    mMaxFetchedVertices = maxVertices;
+    mMaxFetchedInstances = maxInstances;
+
+    return true;
+}
+
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLQuery.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "WebGLContext.h"
+#include "WebGLQuery.h"
+#include "mozilla/dom/WebGL2RenderingContextBinding.h"
+#include "nsContentUtils.h"
+
+using namespace mozilla;
+
+JSObject*
+WebGLQuery::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope) {
+    return dom::WebGLQueryBinding::Wrap(cx, scope, this);
+}
+
+WebGLQuery::WebGLQuery(WebGLContext* context)
+    : WebGLContextBoundObject(context)
+    , mGLName(0)
+    , mType(0)
+{
+    SetIsDOMBinding();
+    mContext->mQueries.insertBack(this);
+
+    mContext->MakeContextCurrent();
+    mContext->gl->fGenQueries(1, &mGLName);
+}
+
+void WebGLQuery::Delete() {
+    mContext->MakeContextCurrent();
+    mContext->gl->fDeleteQueries(1, &mGLName);
+    LinkedListElement<WebGLQuery>::removeFrom(mContext->mQueries);
+}
+
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLQuery)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLQuery)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLQuery)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLQuery)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLQuery.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 WEBGLQUERY_H_
+#define WEBGLQUERY_H_
+
+#include "WebGLObjectModel.h"
+#include "WebGLContext.h"
+
+#include "nsWrapperCache.h"
+
+#include "mozilla/LinkedList.h"
+
+namespace mozilla {
+
+class WebGLQuery MOZ_FINAL
+    : public nsISupports
+    , public WebGLRefCountedObject<WebGLQuery>
+    , public LinkedListElement<WebGLQuery>
+    , public WebGLContextBoundObject
+    , public nsWrapperCache
+{
+// -----------------------------------------------------------------------------
+// PUBLIC
+public:
+
+    // -------------------------------------------------------------------------
+    // CONSTRUCTOR & DESTRUCTOR
+
+    WebGLQuery(WebGLContext *context);
+
+    ~WebGLQuery() {
+        DeleteOnce();
+    };
+
+
+    // -------------------------------------------------------------------------
+    // MEMBER FUNCTIONS
+
+    bool IsActive() const
+    {
+        return mContext->GetActiveQueryByTarget(mType) == this;
+    }
+
+    bool HasEverBeenActive()
+    {
+        return mType != 0;
+    }
+
+
+    // -------------------------------------------------------------------------
+    // IMPLEMENT WebGLRefCountedObject and WebGLContextBoundObject
+
+    void Delete();
+
+    WebGLContext* GetParentObject() const
+    {
+        return Context();
+    }
+
+
+    // -------------------------------------------------------------------------
+    // IMPLEMENT NS
+    virtual JSObject* WrapObject(JSContext *cx,
+                                 JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
+
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLQuery)
+
+
+// -----------------------------------------------------------------------------
+// PRIVATE
+private:
+
+    // -------------------------------------------------------------------------
+    // MEMBERS
+    WebGLuint mGLName;
+    WebGLenum mType;
+
+    // -------------------------------------------------------------------------
+    // FRIENDSHIPS
+    friend class WebGLContext;
+};
+
+} // namespace mozilla
+
+#endif
--- a/content/canvas/src/WebGLVertexArray.h
+++ b/content/canvas/src/WebGLVertexArray.h
@@ -18,46 +18,70 @@ namespace mozilla {
 
 class WebGLVertexArray MOZ_FINAL
     : public nsISupports
     , public WebGLRefCountedObject<WebGLVertexArray>
     , public LinkedListElement<WebGLVertexArray>
     , public WebGLContextBoundObject
     , public nsWrapperCache
 {
+// -----------------------------------------------------------------------------
+// PUBLIC
 public:
+
+    // -------------------------------------------------------------------------
+    // CONSTRUCTOR & DESTRUCTOR
+
     WebGLVertexArray(WebGLContext *context);
 
     ~WebGLVertexArray() {
         DeleteOnce();
     };
 
-    void Delete();
 
-    bool HasEverBeenBound() { return mHasEverBeenBound; }
-    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
-    WebGLuint GLName() const { return mGLName; }
+    // -------------------------------------------------------------------------
+    // IMPLMENET PARENT CLASSES
+
+    void Delete();
 
     WebGLContext* GetParentObject() const {
         return Context();
     }
 
-    bool EnsureAttribIndex(WebGLuint index, const char *info);
-
     virtual JSObject* WrapObject(JSContext *cx,
                                  JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
 
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLVertexArray)
 
+
+    // -------------------------------------------------------------------------
+    // MEMBER FUNCTIONS
+
+    bool HasEverBeenBound() { return mHasEverBeenBound; }
+    void SetHasEverBeenBound(bool x) { mHasEverBeenBound = x; }
+    WebGLuint GLName() const { return mGLName; }
+
+    bool EnsureAttribIndex(WebGLuint index, const char *info);
+
+
+// -----------------------------------------------------------------------------
+// PRIVATE
+private:
+
+    // -------------------------------------------------------------------------
+    // MEMBERS
+
     WebGLuint mGLName;
     bool mHasEverBeenBound;
-
     nsTArray<WebGLVertexAttribData> mAttribBuffers;
     WebGLRefPtr<WebGLBuffer> mBoundElementArrayBuffer;
 
+
+    // -------------------------------------------------------------------------
+    // FRIENDSHIPS
+
     friend class WebGLContext;
-    friend class WebGLExtensionVertexArray;
 };
 
 } // namespace mozilla
 
 #endif
--- a/content/canvas/src/WebGLVertexAttribData.h
+++ b/content/canvas/src/WebGLVertexAttribData.h
@@ -8,23 +8,30 @@
 
 namespace mozilla {
 
 class WebGLBuffer;
 
 struct WebGLVertexAttribData {
     // note that these initial values are what GL initializes vertex attribs to
     WebGLVertexAttribData()
-        : buf(0), stride(0), size(4), byteOffset(0),
-          type(LOCAL_GL_FLOAT), enabled(false), normalized(false)
+        : buf(0)
+        , stride(0)
+        , size(4)
+        , divisor(0) // OpenGL ES 3.0 specs paragraphe 6.2 p240
+        , byteOffset(0)
+        , type(LOCAL_GL_FLOAT)
+        , enabled(false)
+        , normalized(false)
     { }
 
     WebGLRefPtr<WebGLBuffer> buf;
     WebGLuint stride;
     WebGLuint size;
+    WebGLuint divisor;
     GLuint byteOffset;
     GLenum type;
     bool enabled;
     bool normalized;
 
     GLuint componentSize() const {
         switch(type) {
             case LOCAL_GL_BYTE:
--- a/content/canvas/src/moz.build
+++ b/content/canvas/src/moz.build
@@ -26,16 +26,17 @@ CPP_SOURCES += [
 
 if CONFIG['MOZ_WEBGL']:
     CPP_SOURCES += [
         'WebGLActiveInfo.cpp',
         'WebGLBuffer.cpp',
         'WebGL1Context.cpp',
         'WebGL2Context.cpp',
         'WebGLContext.cpp',
+        'WebGLContextAsyncQueries.cpp',
         'WebGLContextGL.cpp',
         'WebGLContextUtils.cpp',
         'WebGLContextReporter.cpp',
         'WebGLContextValidate.cpp',
         'WebGLContextFramebufferOperations.cpp',
         'WebGLContextVertexArray.cpp',
         'WebGLContextVertices.cpp',
         'WebGLElementArrayCache.cpp',
@@ -51,16 +52,17 @@ if CONFIG['MOZ_WEBGL']:
         'WebGLExtensionStandardDerivatives.cpp',
         'WebGLExtensionTextureFilterAnisotropic.cpp',
         'WebGLExtensionTextureFloat.cpp',
         'WebGLExtensionTextureFloatLinear.cpp',
         'WebGLExtensionVertexArray.cpp',
         'WebGLFramebuffer.cpp',
         'WebGLObjectModel.cpp',
         'WebGLProgram.cpp',
+        'WebGLQuery.cpp',
         'WebGLRenderbuffer.cpp',
         'WebGLShader.cpp',
         'WebGLShaderPrecisionFormat.cpp',
         'WebGLTexelConversions.cpp',
         'WebGLTexture.cpp',
         'WebGLUniformLocation.cpp',
         'WebGLVertexArray.cpp',
     ]
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -904,16 +904,19 @@ protected:
   // These events get re-dispatched when the bfcache is exited.
   nsTArray<nsString> mPendingEvents;
 
   // Media loading flags. See:
   //   http://www.whatwg.org/specs/web-apps/current-work/#video)
   nsMediaNetworkState mNetworkState;
   nsMediaReadyState mReadyState;
 
+  // Last value passed from codec or stream source to UpdateReadyStateForData.
+  NextFrameStatus mLastNextFrameStatus;
+
   enum LoadAlgorithmState {
     // No load algorithm instance is waiting for a source to be added to the
     // media in order to continue loading.
     NOT_WAITING,
     // We've run the load algorithm, and we tried all source children of the
     // media element, and failed to load any successfully. We're waiting for
     // another source element to be added to the media element, and will try
     // to load any such element when its added.
@@ -1116,16 +1119,19 @@ protected:
   bool mMediaSecurityVerified;
 
   // The CORS mode when loading the media element
   CORSMode mCORSMode;
 
   // True if the media has an audio track
   bool mHasAudio;
 
+  // True if the media has a video track
+  bool mHasVideo;
+
   // True if the media's channel's download has been suspended.
   bool mDownloadSuspendedByCache;
 
   // Audio Channel Type.
   AudioChannelType mAudioChannelType;
 
   // Is this media element playing?
   bool mPlayingThroughTheAudioChannel;
--- a/content/html/content/src/HTMLIFrameElement.cpp
+++ b/content/html/content/src/HTMLIFrameElement.cpp
@@ -217,33 +217,28 @@ HTMLIFrameElement::SetAttr(int32_t aName
 }
 
 nsresult
 HTMLIFrameElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue,
                                 bool aNotify)
 {
   if (aName == nsGkAtoms::sandbox && aNameSpaceID == kNameSpaceID_None) {
-    // Parse the new value of the sandbox attribute, and if we have a docshell
-    // set its sandbox flags appropriately.
+    // If we have an nsFrameLoader, parse the new value of the sandbox
+    // attribute and apply the new sandbox flags.
     if (mFrameLoader) {
-      nsCOMPtr<nsIDocShell> docshell = mFrameLoader->GetExistingDocShell();
-
-      if (docshell) {
-        uint32_t newFlags = 0;
-        // If a nullptr aValue is passed in, we want to clear the sandbox flags
-        // which we will do by setting them to 0.
-        if (aValue) {
-          nsAutoString strValue;
-          aValue->ToString(strValue);
-          newFlags = nsContentUtils::ParseSandboxAttributeToFlags(
-            strValue);
-        }   
-        docshell->SetSandboxFlags(newFlags);
-      }
+      // If a nullptr aValue is passed in, we want to clear the sandbox flags
+      // which we will do by setting them to 0.
+      uint32_t newFlags = 0;
+      if (aValue) {
+        nsAutoString strValue;
+        aValue->ToString(strValue);
+        newFlags = nsContentUtils::ParseSandboxAttributeToFlags(strValue);
+      }   
+      mFrameLoader->ApplySandboxFlags(newFlags);
     }
   }
   return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
                                             aNotify);
 }
 
 nsresult
 HTMLIFrameElement::UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -63,16 +63,18 @@
 #include "nsMediaFragmentURIParser.h"
 #include "nsURIHashKey.h"
 #include "nsJSUtils.h"
 #include "MediaStreamGraph.h"
 #include "nsIScriptError.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "mozilla/dom/MediaSource.h"
 #include "MediaMetadataManager.h"
+#include "AudioStreamTrack.h"
+#include "VideoStreamTrack.h"
 
 #include "AudioChannelService.h"
 
 #include "nsCSSParser.h"
 #include "nsIMediaList.h"
 
 #include "ImageContainer.h"
 #include "nsIPowerManagerService.h"
@@ -616,17 +618,20 @@ void HTMLMediaElement::AbortExistingLoad
   mLoadedFirstFrame = false;
   mAutoplaying = true;
   mIsLoadingFromSourceChildren = false;
   mSuspendedAfterFirstFrame = false;
   mAllowSuspendAfterFirstFrame = true;
   mHaveQueuedSelectResource = false;
   mSuspendedForPreloadNone = false;
   mDownloadSuspendedByCache = false;
+  mHasAudio = false;
+  mHasVideo = false;
   mSourcePointer = nullptr;
+  mLastNextFrameStatus = NEXT_FRAME_UNINITIALIZED;
 
   mChannels = 0;
   mRate = 0;
   mTags = nullptr;
 
   if (mNetworkState != nsIDOMHTMLMediaElement::NETWORK_EMPTY) {
     mNetworkState = nsIDOMHTMLMediaElement::NETWORK_EMPTY;
     NS_ASSERTION(!mDecoder && !mSrcStream, "How did someone setup a new stream/decoder already?");
@@ -1901,16 +1906,17 @@ HTMLMediaElement::LookupMediaElementURIT
 }
 
 HTMLMediaElement::HTMLMediaElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
     mSrcStreamListener(nullptr),
     mCurrentLoadID(0),
     mNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY),
     mReadyState(nsIDOMHTMLMediaElement::HAVE_NOTHING),
+    mLastNextFrameStatus(NEXT_FRAME_UNINITIALIZED),
     mLoadWaitStatus(NOT_WAITING),
     mVolume(1.0),
     mChannels(0),
     mRate(0),
     mPreloadAction(PRELOAD_UNDEFINED),
     mMediaSize(-1,-1),
     mLastCurrentTime(0.0),
     mFragmentStart(-1.0),
@@ -1941,16 +1947,17 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mAllowSuspendAfterFirstFrame(true),
     mHasPlayedOrSeeked(false),
     mHasSelfReference(false),
     mShuttingDown(false),
     mSuspendedForPreloadNone(false),
     mMediaSecurityVerified(false),
     mCORSMode(CORS_NONE),
     mHasAudio(false),
+    mHasVideo(false),
     mDownloadSuspendedByCache(false),
     mAudioChannelType(AUDIO_CHANNEL_NORMAL),
     mPlayingThroughTheAudioChannel(false)
 {
 #ifdef PR_LOGGING
   if (!gMediaElementLog) {
     gMediaElementLog = PR_NewLogModule("nsMediaElement");
   }
@@ -2509,42 +2516,50 @@ nsresult HTMLMediaElement::FinishDecoder
 
   // Force a same-origin check before allowing events for this media resource.
   mMediaSecurityVerified = false;
 
   // The new stream has not been suspended by us.
   mPausedForInactiveDocumentOrChannel = false;
   mEventDeliveryPaused = false;
   mPendingEvents.Clear();
-
+  // Set mDecoder now so if methods like GetCurrentSrc get called between
+  // here and Load(), they work.
+  mDecoder = aDecoder;
+
+  // Tell aDecoder about its MediaResource now so things like principals are
+  // available immediately.
+  aDecoder->SetResource(aStream);
   aDecoder->SetAudioChannelType(mAudioChannelType);
   aDecoder->SetAudioCaptured(mAudioCaptured);
   aDecoder->SetVolume(mMuted ? 0.0 : mVolume);
   aDecoder->SetPreservesPitch(mPreservesPitch);
   aDecoder->SetPlaybackRate(mPlaybackRate);
+  // Update decoder principal before we start decoding, since it
+  // can affect how we feed data to MediaStreams
+  NotifyDecoderPrincipalChanged();
 
   for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) {
     OutputMediaStream* ms = &mOutputStreams[i];
     aDecoder->AddOutputStream(ms->mStream->GetStream()->AsProcessedStream(),
         ms->mFinishWhenEnded);
   }
 
-  nsresult rv = aDecoder->Load(aStream, aListener, aCloneDonor);
+  nsresult rv = aDecoder->Load(aListener, aCloneDonor);
   if (NS_FAILED(rv)) {
+    mDecoder = nullptr;
     LOG(PR_LOG_DEBUG, ("%p Failed to load for decoder %p", this, aDecoder));
     return rv;
   }
 
   // Decoder successfully created, the decoder now owns the MediaResource
   // which owns the channel.
   mChannel = nullptr;
 
-  mDecoder = aDecoder;
   AddMediaElementToURITable();
-  NotifyDecoderPrincipalChanged();
 
   // We may want to suspend the new stream now.
   // This will also do an AddRemoveSelfReference.
   NotifyOwnerDocumentActivityChanged();
 
   if (!mPaused) {
     SetPlayedOrSeeked(true);
     if (!mPausedForInactiveDocumentOrChannel) {
@@ -2680,28 +2695,37 @@ void HTMLMediaElement::SetupSrcMediaStre
   mSrcStreamListener = new StreamListener(this);
   GetSrcMediaStream()->AddListener(mSrcStreamListener);
   if (mPaused) {
     GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
   }
   if (mPausedForInactiveDocumentOrChannel) {
     GetSrcMediaStream()->ChangeExplicitBlockerCount(1);
   }
+
+  nsAutoTArray<nsRefPtr<AudioStreamTrack>,1> audioTracks;
+  aStream->GetAudioTracks(audioTracks);
+  nsAutoTArray<nsRefPtr<VideoStreamTrack>,1> videoTracks;
+  aStream->GetVideoTracks(videoTracks);
+
+  // Clear aChannels, aRate and aTags, but set mHasAudio and mHasVideo
+  MetadataLoaded(0, 0,
+                 !audioTracks.IsEmpty(), !videoTracks.IsEmpty(),
+                 nullptr);
+  DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
+  mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
+
   ChangeDelayLoadStatus(false);
   GetSrcMediaStream()->AddAudioOutput(this);
   GetSrcMediaStream()->SetAudioOutputVolume(this, float(mMuted ? 0.0 : mVolume));
   VideoFrameContainer* container = GetVideoFrameContainer();
   if (container) {
     GetSrcMediaStream()->AddVideoOutput(container);
   }
-  ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
-  DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
-  DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
-  DispatchAsyncEvent(NS_LITERAL_STRING("suspend"));
-  mNetworkState = nsIDOMHTMLMediaElement::NETWORK_IDLE;
+
   AddRemoveSelfReference();
   // FirstFrameLoaded(false) will be called when the stream has current data,
   // to complete the setup by entering the HAVE_CURRENT_DATA state.
 }
 
 void HTMLMediaElement::EndSrcMediaStreamPlayback()
 {
   MediaStream* stream = GetSrcMediaStream();
@@ -2748,16 +2772,17 @@ void HTMLMediaElement::MetadataLoaded(in
                                       int aRate,
                                       bool aHasAudio,
                                       bool aHasVideo,
                                       const MetadataTags* aTags)
 {
   mChannels = aChannels;
   mRate = aRate;
   mHasAudio = aHasAudio;
+  mHasVideo = aHasVideo;
   mTags = aTags;
   ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_METADATA);
   DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   DispatchAsyncEvent(NS_LITERAL_STRING("loadedmetadata"));
   if (mDecoder && mDecoder->IsTransportSeekable() && mDecoder->IsMediaSeekable()) {
     ProcessMediaFragmentURI();
     mDecoder->SetFragmentEndTime(mFragmentEnd);
   }
@@ -2767,20 +2792,18 @@ void HTMLMediaElement::MetadataLoaded(in
   // audio only file.
   if (!aHasVideo) {
     mVideoFrameContainer = nullptr;
   }
 }
 
 void HTMLMediaElement::FirstFrameLoaded(bool aResourceFullyLoaded)
 {
-  ChangeReadyState(aResourceFullyLoaded ?
-    nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA :
-    nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
   ChangeDelayLoadStatus(false);
+  UpdateReadyStateForData(NEXT_FRAME_UNAVAILABLE);
 
   NS_ASSERTION(!mSuspendedAfterFirstFrame, "Should not have already suspended");
 
   if (mDecoder && mAllowSuspendAfterFirstFrame && mPaused &&
       !aResourceFullyLoaded &&
       !HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay) &&
       mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
     mSuspendedAfterFirstFrame = true;
@@ -2957,16 +2980,18 @@ void HTMLMediaElement::DownloadStalled()
 
 bool HTMLMediaElement::ShouldCheckAllowOrigin()
 {
   return mCORSMode != CORS_NONE;
 }
 
 void HTMLMediaElement::UpdateReadyStateForData(MediaDecoderOwner::NextFrameStatus aNextFrame)
 {
+  mLastNextFrameStatus = aNextFrame;
+
   if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
     // aNextFrame might have a next frame because the decoder can advance
     // on its own thread before ResourceLoaded or MetadataLoaded gets
     // a chance to run.
     // The arrival of more data can't change us out of this readyState.
     return;
   }
 
@@ -2980,16 +3005,24 @@ void HTMLMediaElement::UpdateReadyStateF
     // transition, we will never fire the "canplaythrough" event if the
     // media cache is too small, and scripts are bound to fail. Don't force
     // this transition if the decoder is in ended state; the readyState
     // should remain at HAVE_CURRENT_DATA in this case.
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_ENOUGH_DATA);
     return;
   }
 
+  if (mReadyState < nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA && mHasVideo) {
+    VideoFrameContainer* container = GetVideoFrameContainer();
+    if (container && mMediaSize == nsIntSize(-1,-1)) {
+      // No frame has been set yet. Don't advance.
+      return;
+    }
+  }
+
   if (aNextFrame != MediaDecoderOwner::NEXT_FRAME_AVAILABLE) {
     ChangeReadyState(nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA);
     if (!mWaitingFired && aNextFrame == MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING) {
       FireTimeUpdate(false);
       DispatchAsyncEvent(NS_LITERAL_STRING("waiting"));
       mWaitingFired = true;
     }
     return;
@@ -3114,36 +3147,31 @@ void HTMLMediaElement::CheckAutoplayData
       GetSrcMediaStream()->ChangeExplicitBlockerCount(-1);
     }
     DispatchAsyncEvent(NS_LITERAL_STRING("play"));
   }
 }
 
 VideoFrameContainer* HTMLMediaElement::GetVideoFrameContainer()
 {
-  // If we have loaded the metadata, and the size of the video is still
-  // (-1, -1), the media has no video. Don't go a create a video frame
-  // container.
-  if (mReadyState >= nsIDOMHTMLMediaElement::HAVE_METADATA &&
-      mMediaSize == nsIntSize(-1, -1)) {
-    return nullptr;
+  if (mVideoFrameContainer) {
+    return mVideoFrameContainer;
   }
 
-  if (mVideoFrameContainer)
-    return mVideoFrameContainer;
-
   // If we have a print surface, this is just a static image so
   // no image container is required
-  if (mPrintSurface)
+  if (mPrintSurface) {
     return nullptr;
+  }
 
   // Only video frames need an image container.
   nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
-  if (!video)
+  if (!video) {
     return nullptr;
+  }
 
   mVideoFrameContainer =
     new VideoFrameContainer(this, LayerManager::CreateAsynchronousImageContainer());
 
   return mVideoFrameContainer;
 }
 
 nsresult HTMLMediaElement::DispatchAudioAvailableEvent(float* aFrameBuffer,
@@ -3248,26 +3276,32 @@ already_AddRefed<nsIPrincipal> HTMLMedia
     nsRefPtr<nsIPrincipal> principal = mSrcStream->GetPrincipal();
     return principal.forget();
   }
   return nullptr;
 }
 
 void HTMLMediaElement::NotifyDecoderPrincipalChanged()
 {
+  nsRefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
+
+  bool subsumes;
+  mDecoder->UpdateSameOriginStatus(
+    NS_SUCCEEDED(NodePrincipal()->Subsumes(principal, &subsumes)) && subsumes);
+
   for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) {
     OutputMediaStream* ms = &mOutputStreams[i];
-    nsRefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
     ms->mStream->CombineWithPrincipal(principal);
   }
 }
 
 void HTMLMediaElement::UpdateMediaSize(nsIntSize size)
 {
   mMediaSize = size;
+  UpdateReadyStateForData(mLastNextFrameStatus);
 }
 
 void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents)
 {
   if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
     mPausedForInactiveDocumentOrChannel = aPauseElement;
     if (aPauseElement) {
       if (mDecoder) {
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -283,16 +283,23 @@ MOCHITEST_FILES = \
 		file_iframe_sandbox_a_if5.html \
 		file_iframe_sandbox_a_if6.html \
 		file_iframe_sandbox_a_if7.html \
 		file_iframe_sandbox_a_if8.html \
 		file_iframe_sandbox_a_if9.html \
 		file_iframe_sandbox_a_if10.html \
 		file_iframe_sandbox_a_if11.html \
 		file_iframe_sandbox_a_if12.html \
+		file_iframe_sandbox_a_if13.html \
+		file_iframe_sandbox_a_if14.html \
+		file_iframe_sandbox_a_if15.html \
+		file_iframe_sandbox_a_if16.html \
+		file_iframe_sandbox_a_if17.html \
+		file_iframe_sandbox_a_if18.html \
+		file_iframe_sandbox_a_if19.html \
 		test_iframe_sandbox_same_origin.html \
 		file_iframe_sandbox_b_if1.html \
 		file_iframe_sandbox_b_if2.html \
 		file_iframe_sandbox_b_if3.html \
 		test_iframe_sandbox_general.html \
 		file_iframe_sandbox_c_if1.html \
 		file_iframe_sandbox_c_if2.html \
 		file_iframe_sandbox_c_if3.html \
--- a/content/html/content/test/file_iframe_sandbox_a_if10.html
+++ b/content/html/content/test/file_iframe_sandbox_a_if10.html
@@ -2,10 +2,11 @@
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 341604</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <frameset>
   <frame src="file_iframe_sandbox_a_if11.html">
+  <frame src="file_iframe_sandbox_a_if16.html">
 </frameset>
 </html>
--- a/content/html/content/test/file_iframe_sandbox_a_if11.html
+++ b/content/html/content/test/file_iframe_sandbox_a_if11.html
@@ -13,11 +13,11 @@
         window.parent.parent.postMessage({ok: true, desc: "a frame inside a sandboxed iframe is not same origin with the iframe's parent"}, "*");
       }
      }
   </script>
 </head>
 <frameset>
   <frame onload='doStuff()' src="file_iframe_sandbox_a_if12.html">
 </frameset>
-I'm a &lt;frame&gt; inside an iframe which is sandboxed with 'allow-scripts'
+I'm a &lt;frame&gt; inside an iframe which is sandboxed with 'allow-scripts allow-forms'
 </html>
 
--- a/content/html/content/test/file_iframe_sandbox_a_if12.html
+++ b/content/html/content/test/file_iframe_sandbox_a_if12.html
@@ -12,12 +12,12 @@ function doStuff() {
   }
   catch (e) {
     dump("caught some e if12\n");
     window.parent.parent.parent.postMessage({ok: true, desc: "a frame inside a frame inside a sandboxed iframe is not same origin with the iframe's parent"}, "*");
   }
 }
 </script>
 <body onload='doStuff()'>
-  I'm a &lt;frame&gt; inside a &lt;frame&gt; inside an iframe which is sandboxed with 'allow-scripts'
+  I'm a &lt;frame&gt; inside a &lt;frame&gt; inside an iframe which is sandboxed with 'allow-scripts allow-forms'
 </body>
 </html>
 
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_a_if13.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 886262</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<body>
+  <object data="file_iframe_sandbox_a_if14.html"></object>
+</body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_a_if14.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 886262</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+  window.addEventListener("message", receiveMessage, false);
+
+  function receiveMessage(event)
+  {
+    window.parent.parent.postMessage({ok: event.data.ok, desc: "objects containing " + event.data.desc}, "*");
+  }
+
+  function doStuff() {
+    try {
+      window.parent.parent.ok_wrapper(false, "an object inside a sandboxed iframe should NOT be same origin with the iframe's parent");
+    }
+    catch (e) {
+      window.parent.parent.postMessage({ok: true, desc: "an object inside a sandboxed iframe is not same origin with the iframe's parent"}, "*");
+    }
+  }
+</script>
+
+<body onload='doStuff()'>
+I'm a &lt;object&gt; inside an iframe which is sandboxed with 'allow-scripts allow-forms'
+
+  <object data="file_iframe_sandbox_a_if15.html"></object>
+</body>
+
+</html>
+
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_a_if15.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 886262</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+function doStuff() {
+  try {
+    window.parent.parent.parent.ok_wrapper(false, "an object inside a frame or object inside a sandboxed iframe should NOT be same origin with the iframe's parent");
+  }
+  catch (e) {
+    window.parent.parent.parent.postMessage({ok: true, desc: "an object inside a frame or object inside a sandboxed iframe is not same origin with the iframe's parent"}, "*");
+  }
+
+  // Check that sandboxed forms browsing context flag NOT set by attempting to submit a form.
+  document.getElementById('a_form').submit();
+}
+</script>
+
+<body onload='doStuff()'>
+  I'm a &lt;object&gt; inside a &lt;frame&gt; or &lt;object&gt; inside an iframe which is sandboxed with 'allow-scripts allow-forms'
+
+  <form method="get" action="file_iframe_sandbox_form_pass.html" id="a_form">
+    First name: <input type="text" name="firstname">
+    Last name: <input type="text" name="lastname">
+    <input type="submit" id="a_button">
+  </form>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_a_if16.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 886262</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+  window.addEventListener("message", receiveMessage, false);
+
+  function receiveMessage(event)
+  {
+    window.parent.parent.postMessage({ok: event.data.ok, desc: "objects containing " + event.data.desc}, "*");
+  }
+</script>
+
+<body>
+I'm a &lt;frame&gt; inside an iframe which is sandboxed with 'allow-scripts allow-forms'
+
+  <object data="file_iframe_sandbox_a_if15.html"></object>
+</body>
+
+</html>
+
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_a_if17.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 886262</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+  function doTest() {
+    var if_18_19 = document.getElementById('if_18_19');
+    if_18_19.sandbox = "allow-scripts allow-same-origin";
+    if_18_19.contentWindow.postMessage("go", "*");
+  }
+</script>
+
+<body onload="doTest()">
+  I am sandboxed but with "allow-scripts". I change the sandbox flags on if_18_19 to
+  "allow-scripts allow-same-origin" then get it to re-navigate itself to
+  file_iframe_sandbox_a_if18.html, which attemps to call a function in my parent.
+  This should fail since my sandbox flags should be copied to it when the sandbox
+  flags are changed.
+
+  <iframe sandbox="allow-scripts" id="if_18_19" src="file_iframe_sandbox_a_if19.html" height="10" width="10"></iframe>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_a_if18.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 886262</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+
+<script>
+  function doTest() {
+    try {
+      window.parent.parent.ok_wrapper(false, "an iframe in an iframe SHOULD copy its parent's sandbox flags when its sandbox flags are changed");
+    }
+    catch (e) {
+      window.parent.parent.postMessage({ok: true, desc: "an iframe in an iframe copies its parent's sandbox flags when its sandbox flags are changed"}, "*");
+    }
+  }
+</script>
+
+<body onload="doTest()">
+  I'm an iframe whose sandbox flags have been changed to include allow-same-origin.
+  I should not be able to call a function in my parent's parent because my parent's
+  iframe does not have allow-same-origin set.
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/file_iframe_sandbox_a_if19.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 886262</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+
+<script>
+  window.addEventListener("message", function(e){
+     window.open("file_iframe_sandbox_a_if18.html", "_self");
+  }, false);
+</script>
+
+<body>
+  I'm just here to navigate to file_iframe_sandbox_a_if18.html after my owning
+  iframe has had allow-same-origin added.
+</body>
+</html>
+
--- a/content/html/content/test/test_iframe_sandbox_inheritance.html
+++ b/content/html/content/test/test_iframe_sandbox_inheritance.html
@@ -9,16 +9,19 @@ Implement HTML5 sandbox attribute for IF
   <title>Test for Bug 341604</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <script type="application/javascript">
 /** Test for Bug 341604 - Implement HTML5 sandbox attribute for IFRAMEs **/
 /** Inheritance Tests **/
 
+// Assertion failure in docshell/shistory/src/nsSHEntry.cpp (currently line 625).
+// Bug 901876 raised.
+SimpleTest.expectAssertions(1);
 SimpleTest.waitForExplicitFinish();
   
 // A postMessage handler that is used by sandboxed iframes without
 // 'allow-same-origin' to communicate pass/fail back to this main page.
 // It expects to be called with an object like {ok: true/false, desc:
 // <description of the test> which it then forwards to ok().
 window.addEventListener("message", receiveMessage, false);
 
@@ -34,18 +37,18 @@ function ok_wrapper(result, desc) {
   ok(result, desc);
 
   completedTests++;
 
   if (result) {
 	  passedTests++;
   } 
 
-  if (completedTests == 6) {
-    is(passedTests, 6, "there should be 6 passed inheritance tests");
+  if (completedTests == 13) {
+    is(passedTests, completedTests, "there should be " + completedTests + " passed inheritance tests");
     SimpleTest.finish();
   }
 }
 
 function doTest() {
   // fails if bad
   // 1) an iframe with no sandbox attribute inside an iframe that has sandbox = ""
   // should not be able to execute scripts (cannot ever loosen permissions)
@@ -97,22 +100,69 @@ function doTest() {
   // 10) a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
   // origin with this document
   // done by file_iframe_sandbox_a_if11.html which is contained with file_iframe_sandbox_a_if10.html
 
   // passes if good
   // 11) a <frame> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
   // origin with its parent frame or this document
   // done by file_iframe_sandbox_a_if12.html which is contained with file_iframe_sandbox_a_if11.html
+
+  // passes if good, fails if bad
+  // 12) An <object> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+  // origin with this document
+  // Done by file_iframe_sandbox_a_if14.html which is contained within file_iframe_sandbox_a_if13.html
+
+  // passes if good, fails if bad
+  // 13) An <object> inside an <object> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+  // origin with its parent frame or this document
+  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if14.html
+
+  // passes if good, fails if bad
+  // 14) An <object> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts' should not be same
+  // origin with its parent frame or this document
+  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if16.html
+  // which is contained within file_iframe_sandbox_a_if10.html
+
+  // passes if good
+  // 15) An <object> inside an <object> inside an <iframe> sandboxed with 'allow-scripts allow-forms'
+  // should be able to submit forms.
+  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if14.html
+
+  // passes if good
+  // 16) An <object> inside a <frame> inside an <iframe> sandboxed with 'allow-scripts allow-forms'
+  // should be able to submit forms.
+  // Done by file_iframe_sandbox_a_if15.html which is contained within file_iframe_sandbox_a_if16.html
+  // which is contained within file_iframe_sandbox_a_if10.html
+
+  // fails if bad
+  // 17) An <object> inside an <iframe> sandboxed with 'allow-same-origin'
+  // should not be able to run scripts.
+  // Done by iframe "if_no_scripts" using a data: load.
+
+  // passes if good
+  // 18) An <object> inside an <iframe> sandboxed with 'allow-scripts allow-same-origin'
+  // should be able to run scripts and be same origin with this document.
+  // Done by iframe "if_scripts" using a data: load.
+
+  // passes if good, fails if bad
+  // 19) Make sure that the parent's document's sandboxing flags are copied when
+  // changing the sandbox flags on an iframe inside an iframe.
+  // Done in file_iframe_sandbox_a_if17.html and file_iframe_sandbox_a_if18.html
 }
 
 addLoadEvent(doTest);
 </script>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=341604">Mozilla Bug 341604</a> - Implement HTML5 sandbox attribute for IFRAMEs
 <p id="display"></p>
 <div id="content">
 <iframe sandbox="" id="if_1" src="file_iframe_sandbox_a_if1.html" height="10" width="10"></iframe>
 <iframe sandbox="allow-scripts" id="if_3" src="file_iframe_sandbox_a_if3.html" height="10" width="10"></iframe>
 <iframe sandbox="allow-scripts allow-same-origin" id="if_5" src="file_iframe_sandbox_a_if5.html" height="10" width="10"></iframe>
 <iframe sandbox="allow-scripts allow-same-origin" id="if_8" src="file_iframe_sandbox_a_if8.html" height="10" width="10"></iframe>
-<iframe sandbox="allow-scripts" id="if_10" src="file_iframe_sandbox_a_if10.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-forms" id="if_10" src="file_iframe_sandbox_a_if10.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-forms" id="if_13" src="file_iframe_sandbox_a_if13.html" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" id="if_no_scripts" src="data:text/html,<object%20data='data:text/html,<script>parent.parent.ok_wrapper(false, &quot;an object inside an iframe sandboxed with only allow-same-origin should not be able to run scripts&quot;)</script>'></object>" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts allow-same-origin" id="if_scripts" src="data:text/html,<object%20data='data:text/html,<script>parent.parent.ok_wrapper(true, &quot;an object inside an iframe sandboxed with allow-scripts allow-same-origin should be able to run scripts and call functions in the parent of the iframe&quot;)</script>'></object>" height="10" width="10"></iframe>
+<iframe sandbox="allow-same-origin" id="if_19" src="data:text/html,<iframe%20data='data:text/html,<script>parent.parent.ok_wrapper(true, &quot;an object inside an iframe sandboxed with allow-scripts allow-same-origin should be able to run scripts and call functions in the parent of the iframe&quot;)</script>'></object>" height="10" width="10"></iframe>
+<iframe sandbox="allow-scripts" id="if_17" src="file_iframe_sandbox_a_if17.html" height="10" width="10"></iframe>
 </div>
new file mode 100644
--- /dev/null
+++ b/content/media/AudioNodeExternalInputStream.cpp
@@ -0,0 +1,492 @@
+/* -*- 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 "MediaStreamGraphImpl.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeExternalInputStream.h"
+#include "speex/speex_resampler.h"
+
+using namespace mozilla::dom;
+
+namespace mozilla {
+
+AudioNodeExternalInputStream::AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate)
+  : AudioNodeStream(aEngine, MediaStreamGraph::INTERNAL_STREAM, aSampleRate)
+  , mCurrentOutputPosition(0)
+{
+  MOZ_COUNT_CTOR(AudioNodeExternalInputStream);
+}
+
+AudioNodeExternalInputStream::~AudioNodeExternalInputStream()
+{
+  MOZ_COUNT_DTOR(AudioNodeExternalInputStream);
+}
+
+AudioNodeExternalInputStream::TrackMapEntry::~TrackMapEntry()
+{
+  if (mResampler) {
+    speex_resampler_destroy(mResampler);
+  }
+}
+
+uint32_t
+AudioNodeExternalInputStream::GetTrackMapEntry(const StreamBuffer::Track& aTrack,
+                                               GraphTime aFrom)
+{
+  AudioSegment* segment = aTrack.Get<AudioSegment>();
+
+  // Check the map for an existing entry corresponding to the input track.
+  for (uint32_t i = 0; i < mTrackMap.Length(); ++i) {
+    TrackMapEntry* map = &mTrackMap[i];
+    if (map->mTrackID == aTrack.GetID()) {
+      return i;
+    }
+  }
+
+  // Determine channel count by finding the first entry with non-silent data.
+  AudioSegment::ChunkIterator ci(*segment);
+  while (!ci.IsEnded() && ci->IsNull()) {
+    ci.Next();
+  }
+  if (ci.IsEnded()) {
+    // The track is entirely silence so far, we can ignore it for now.
+    return nsTArray<TrackMapEntry>::NoIndex;
+  }
+
+  // Create a speex resampler with the same sample rate and number of channels
+  // as the track.
+  SpeexResamplerState* resampler = nullptr;
+  uint32_t channelCount = (*ci).mChannelData.Length();
+  if (aTrack.GetRate() != mSampleRate) {
+    resampler = speex_resampler_init(channelCount,
+      aTrack.GetRate(), mSampleRate, SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr);
+    speex_resampler_skip_zeros(resampler);
+  }
+
+  TrackMapEntry* map = mTrackMap.AppendElement();
+  map->mEndOfConsumedInputTicks = 0;
+  map->mEndOfLastInputIntervalInInputStream = -1;
+  map->mEndOfLastInputIntervalInOutputStream = -1;
+  map->mSamplesPassedToResampler =
+    TimeToTicksRoundUp(aTrack.GetRate(), GraphTimeToStreamTime(aFrom));
+  map->mResampler = resampler;
+  map->mResamplerChannelCount = channelCount;
+  map->mTrackID = aTrack.GetID();
+  return mTrackMap.Length() - 1;
+}
+
+static const uint32_t SPEEX_RESAMPLER_PROCESS_MAX_OUTPUT = 1000;
+
+template <typename T> static int
+SpeexResamplerProcess(SpeexResamplerState* aResampler,
+                      uint32_t aChannel,
+                      const T* aInput, uint32_t* aIn,
+                      float* aOutput, uint32_t* aOut);
+
+template <> int
+SpeexResamplerProcess<float>(SpeexResamplerState* aResampler,
+                             uint32_t aChannel,
+                             const float* aInput, uint32_t* aIn,
+                             float* aOutput, uint32_t* aOut)
+{
+  NS_ASSERTION(*aOut <= SPEEX_RESAMPLER_PROCESS_MAX_OUTPUT, "Bad aOut");
+  return speex_resampler_process_float(aResampler, aChannel, aInput, aIn, aOutput, aOut);
+}
+
+template <> int
+SpeexResamplerProcess<int16_t>(SpeexResamplerState* aResampler,
+                               uint32_t aChannel,
+                               const int16_t* aInput, uint32_t* aIn,
+                               float* aOutput, uint32_t* aOut)
+{
+  NS_ASSERTION(*aOut <= SPEEX_RESAMPLER_PROCESS_MAX_OUTPUT, "Bad aOut");
+  int16_t tmp[SPEEX_RESAMPLER_PROCESS_MAX_OUTPUT];
+  int result = speex_resampler_process_int(aResampler, aChannel, aInput, aIn, tmp, aOut);
+  if (result == RESAMPLER_ERR_SUCCESS) {
+    for (uint32_t i = 0; i < *aOut; ++i) {
+      aOutput[i] = AudioSampleToFloat(tmp[i]);
+    }
+  }
+  return result;
+}
+
+template <typename T> static void
+ResampleChannelBuffer(SpeexResamplerState* aResampler, uint32_t aChannel,
+                      const T* aInput, uint32_t aInputDuration,
+                      nsTArray<float>* aOutput)
+{
+  if (!aResampler) {
+    float* out = aOutput->AppendElements(aInputDuration);
+    for (uint32_t i = 0; i < aInputDuration; ++i) {
+      out[i] = AudioSampleToFloat(aInput[i]);
+    }
+    return;
+  }
+
+  uint32_t processed = 0;
+  while (processed < aInputDuration) {
+    uint32_t prevLength = aOutput->Length();
+    float* output = aOutput->AppendElements(SPEEX_RESAMPLER_PROCESS_MAX_OUTPUT);
+    uint32_t in = aInputDuration - processed;
+    uint32_t out = aOutput->Length() - prevLength;
+    SpeexResamplerProcess(aResampler, aChannel,
+                          aInput + processed, &in,
+                          output, &out);
+    processed += in;
+    aOutput->SetLength(prevLength + out);
+  }
+}
+
+class SharedChannelArrayBuffer : public ThreadSharedObject {
+public:
+  SharedChannelArrayBuffer(nsTArray<nsTArray<float> >* aBuffers)
+  {
+    mBuffers.SwapElements(*aBuffers);
+  }
+  nsTArray<nsTArray<float> > mBuffers;
+};
+
+void
+AudioNodeExternalInputStream::TrackMapEntry::ResampleChannels(const nsTArray<const void*>& aBuffers,
+                                                              uint32_t aInputDuration,
+                                                              AudioSampleFormat aFormat,
+                                                              float aVolume)
+{
+  NS_ASSERTION(aBuffers.Length() == mResamplerChannelCount,
+               "Channel count must be correct here");
+
+  nsAutoTArray<nsTArray<float>,2> resampledBuffers;
+  resampledBuffers.SetLength(aBuffers.Length());
+  nsTArray<float> samplesAdjustedForVolume;
+  nsAutoTArray<const float*,2> bufferPtrs;
+  bufferPtrs.SetLength(aBuffers.Length());
+
+  for (uint32_t i = 0; i < aBuffers.Length(); ++i) {
+    AudioSampleFormat format = aFormat;
+    const void* buffer = aBuffers[i];
+
+    if (aVolume != 1.0f) {
+      format = AUDIO_FORMAT_FLOAT32;
+      samplesAdjustedForVolume.SetLength(aInputDuration);
+      switch (aFormat) {
+      case AUDIO_FORMAT_FLOAT32:
+        ConvertAudioSamplesWithScale(static_cast<const float*>(buffer),
+                                     samplesAdjustedForVolume.Elements(),
+                                     aInputDuration, aVolume);
+        break;
+      case AUDIO_FORMAT_S16:
+        ConvertAudioSamplesWithScale(static_cast<const int16_t*>(buffer),
+                                     samplesAdjustedForVolume.Elements(),
+                                     aInputDuration, aVolume);
+        break;
+      default:
+        MOZ_ASSERT(false);
+        return;
+      }
+      buffer = samplesAdjustedForVolume.Elements();
+    }
+
+    switch (format) {
+    case AUDIO_FORMAT_FLOAT32:
+      ResampleChannelBuffer(mResampler, i,
+                            static_cast<const float*>(buffer),
+                            aInputDuration, &resampledBuffers[i]);
+      break;
+    case AUDIO_FORMAT_S16:
+      ResampleChannelBuffer(mResampler, i,
+                            static_cast<const int16_t*>(buffer),
+                            aInputDuration, &resampledBuffers[i]);
+      break;
+    default:
+      MOZ_ASSERT(false);
+      return;
+    }
+    bufferPtrs[i] = resampledBuffers[i].Elements();
+    NS_ASSERTION(i == 0 ||
+                 resampledBuffers[i].Length() == resampledBuffers[0].Length(),
+                 "Resampler made different decisions for different channels!");
+  }
+
+  uint32_t length = resampledBuffers[0].Length();
+  nsRefPtr<ThreadSharedObject> buf = new SharedChannelArrayBuffer(&resampledBuffers);
+  mResampledData.AppendFrames(buf.forget(), bufferPtrs, length);
+}
+
+void
+AudioNodeExternalInputStream::TrackMapEntry::ResampleInputData(AudioSegment* aSegment)
+{
+  AudioSegment::ChunkIterator ci(*aSegment);
+  while (!ci.IsEnded()) {
+    const AudioChunk& chunk = *ci;
+    nsAutoTArray<const void*,2> channels;
+    if (chunk.GetDuration() > UINT32_MAX) {
+      // This will cause us to OOM or overflow below. So let's just bail.
+      NS_ERROR("Chunk duration out of bounds");
+      return;
+    }
+    uint32_t duration = uint32_t(chunk.GetDuration());
+
+    if (chunk.IsNull()) {
+      nsAutoTArray<AudioDataValue,1024> silence;
+      silence.SetLength(duration);
+      PodZero(silence.Elements(), silence.Length());
+      channels.SetLength(mResamplerChannelCount);
+      for (uint32_t i = 0; i < channels.Length(); ++i) {
+        channels[i] = silence.Elements();
+      }
+      ResampleChannels(channels, duration, AUDIO_OUTPUT_FORMAT, 0.0f);
+    } else if (chunk.mChannelData.Length() == mResamplerChannelCount) {
+      // Common case, since mResamplerChannelCount is set to the first chunk's
+      // number of channels.
+      channels.AppendElements(chunk.mChannelData);
+      ResampleChannels(channels, duration, chunk.mBufferFormat, chunk.mVolume);
+    } else {
+      // Uncommon case. Since downmixing requires channels to be floats,
+      // convert everything to floats now.
+      uint32_t upChannels = GetAudioChannelsSuperset(chunk.mChannelData.Length(), mResamplerChannelCount);
+      nsTArray<float> buffer;
+      if (chunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
+        channels.AppendElements(chunk.mChannelData);
+      } else {
+        NS_ASSERTION(chunk.mBufferFormat == AUDIO_FORMAT_S16, "Unknown format");
+        if (duration > UINT32_MAX/chunk.mChannelData.Length()) {
+          NS_ERROR("Chunk duration out of bounds");
+          return;
+        }
+        buffer.SetLength(chunk.mChannelData.Length()*duration);
+        for (uint32_t i = 0; i < chunk.mChannelData.Length(); ++i) {
+          const int16_t* samples = static_cast<const int16_t*>(chunk.mChannelData[i]);
+          float* converted = &buffer[i*duration];
+          for (uint32_t j = 0; j < duration; ++j) {
+            converted[j] = AudioSampleToFloat(samples[j]);
+          }
+          channels.AppendElement(converted);
+        }
+      }
+      nsTArray<float> zeroes;
+      if (channels.Length() < upChannels) {
+        zeroes.SetLength(duration);
+        PodZero(zeroes.Elements(), zeroes.Length());
+        AudioChannelsUpMix(&channels, upChannels, zeroes.Elements());
+      }
+      if (channels.Length() == mResamplerChannelCount) {
+        ResampleChannels(channels, duration, AUDIO_FORMAT_FLOAT32, chunk.mVolume);
+      } else {
+        nsTArray<float> output;
+        if (duration > UINT32_MAX/mResamplerChannelCount) {
+          NS_ERROR("Chunk duration out of bounds");
+          return;
+        }
+        output.SetLength(duration*mResamplerChannelCount);
+        nsAutoTArray<float*,2> outputPtrs;
+        nsAutoTArray<const void*,2> outputPtrsConst;
+        for (uint32_t i = 0; i < mResamplerChannelCount; ++i) {
+          outputPtrs.AppendElement(output.Elements() + i*duration);
+          outputPtrsConst.AppendElement(outputPtrs[i]);
+        }
+        AudioChannelsDownMix(channels, outputPtrs.Elements(), outputPtrs.Length(), duration);
+        ResampleChannels(outputPtrsConst, duration, AUDIO_FORMAT_FLOAT32, chunk.mVolume);
+      }
+    }
+    ci.Next();
+  }
+}
+
+/**
+ * Copies the data in aInput to aOffsetInBlock within aBlock. All samples must
+ * be float. Both chunks must have the same number of channels (or else
+ * aInput is null). aBlock must have been allocated with AllocateInputBlock.
+ */
+static void
+CopyChunkToBlock(const AudioChunk& aInput, AudioChunk *aBlock, uint32_t aOffsetInBlock)
+{
+  uint32_t d = aInput.GetDuration();
+  for (uint32_t i = 0; i < aBlock->mChannelData.Length(); ++i) {
+    float* out = static_cast<float*>(const_cast<void*>(aBlock->mChannelData[i])) +
+      aOffsetInBlock;
+    if (aInput.IsNull()) {
+      PodZero(out, d);
+    } else {
+      const float* in = static_cast<const float*>(aInput.mChannelData[i]);
+      ConvertAudioSamplesWithScale(in, out, d, aInput.mVolume);
+    }
+  }
+}
+
+/**
+ * Converts the data in aSegment to a single chunk aChunk. Every chunk in
+ * aSegment must have the same number of channels (or be null). aSegment must have
+ * duration WEBAUDIO_BLOCK_SIZE. Every chunk in aSegment must be in float format.
+ */
+static void
+ConvertSegmentToAudioBlock(AudioSegment* aSegment, AudioChunk* aBlock)
+{
+  NS_ASSERTION(aSegment->GetDuration() == WEBAUDIO_BLOCK_SIZE, "Bad segment duration");
+
+  {
+    AudioSegment::ChunkIterator ci(*aSegment);
+    NS_ASSERTION(!ci.IsEnded(), "Segment must have at least one chunk");
+    AudioChunk& firstChunk = *ci;
+    ci.Next();
+    if (ci.IsEnded()) {
+      *aBlock = firstChunk;
+      return;
+    }
+
+    while (ci->IsNull() && !ci.IsEnded()) {
+      ci.Next();
+    }
+    if (ci.IsEnded()) {
+      // All null.
+      aBlock->SetNull(WEBAUDIO_BLOCK_SIZE);
+      return;
+    }
+
+    AllocateAudioBlock(ci->mChannelData.Length(), aBlock);
+  }
+
+  AudioSegment::ChunkIterator ci(*aSegment);
+  uint32_t duration = 0;
+  while (!ci.IsEnded()) {
+    CopyChunkToBlock(*ci, aBlock, duration);
+    duration += ci->GetDuration();
+    ci.Next();
+  }
+}
+
+void
+AudioNodeExternalInputStream::ProduceOutput(GraphTime aFrom, GraphTime aTo)
+{
+  // According to spec, number of outputs is always 1.
+  mLastChunks.SetLength(1);
+
+  // GC stuff can result in our input stream being destroyed before this stream.
+  // Handle that.
+  if (mInputs.IsEmpty()) {
+    mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
+    AdvanceOutputSegment();
+    return;
+  }
+
+  MOZ_ASSERT(mInputs.Length() == 1);
+
+  MediaStream* source = mInputs[0]->GetSource();
+  nsAutoTArray<AudioSegment,1> audioSegments;
+  nsAutoTArray<bool,1> trackMapEntriesUsed;
+  uint32_t inputChannels = 0;
+  for (StreamBuffer::TrackIter tracks(source->mBuffer, MediaSegment::AUDIO);
+       !tracks.IsEnded(); tracks.Next()) {
+    const StreamBuffer::Track& inputTrack = *tracks;
+    // Create a TrackMapEntry if necessary.
+    uint32_t trackMapIndex = GetTrackMapEntry(inputTrack, aFrom);
+    // Maybe there's nothing in this track yet. If so, ignore it. (While the
+    // track is only playing silence, we may not be able to determine the
+    // correct number of channels to start resampling.)
+    if (trackMapIndex == nsTArray<TrackMapEntry>::NoIndex) {
+      continue;
+    }
+
+    while (trackMapEntriesUsed.Length() <= trackMapIndex) {
+      trackMapEntriesUsed.AppendElement(false);
+    }
+    trackMapEntriesUsed[trackMapIndex] = true;
+
+    TrackMapEntry* trackMap = &mTrackMap[trackMapIndex];
+    AudioSegment segment;
+    GraphTime next;
+    TrackRate inputTrackRate = inputTrack.GetRate();
+    for (GraphTime t = aFrom; t < aTo; t = next) {
+      MediaInputPort::InputInterval interval = mInputs[0]->GetNextInputInterval(t);
+      interval.mEnd = std::min(interval.mEnd, aTo);
+      if (interval.mStart >= interval.mEnd)
+        break;
+      next = interval.mEnd;
+
+      // Ticks >= startTicks and < endTicks are in the interval
+      StreamTime outputEnd = GraphTimeToStreamTime(interval.mEnd);
+      TrackTicks startTicks = trackMap->mSamplesPassedToResampler + segment.GetDuration();
+      StreamTime outputStart = GraphTimeToStreamTime(interval.mStart);
+      NS_ASSERTION(startTicks == TimeToTicksRoundUp(inputTrackRate, outputStart),
+                   "Samples missing");
+      TrackTicks endTicks = TimeToTicksRoundUp(inputTrackRate, outputEnd);
+      TrackTicks ticks = endTicks - startTicks;
+
+      if (interval.mInputIsBlocked) {
+        segment.AppendNullData(ticks);
+      } else {
+        // See comments in TrackUnionStream::CopyTrackData
+        StreamTime inputStart = source->GraphTimeToStreamTime(interval.mStart);
+        StreamTime inputEnd = source->GraphTimeToStreamTime(interval.mEnd);
+        TrackTicks inputTrackEndPoint =
+            inputTrack.IsEnded() ? inputTrack.GetEnd() : TRACK_TICKS_MAX;
+
+        if (trackMap->mEndOfLastInputIntervalInInputStream != inputStart ||
+            trackMap->mEndOfLastInputIntervalInOutputStream != outputStart) {
+          // Start of a new series of intervals where neither stream is blocked.
+          trackMap->mEndOfConsumedInputTicks = TimeToTicksRoundDown(inputTrackRate, inputStart) - 1;
+        }
+        TrackTicks inputStartTicks = trackMap->mEndOfConsumedInputTicks;
+        TrackTicks inputEndTicks = inputStartTicks + ticks;
+        trackMap->mEndOfConsumedInputTicks = inputEndTicks;
+        trackMap->mEndOfLastInputIntervalInInputStream = inputEnd;
+        trackMap->mEndOfLastInputIntervalInOutputStream = outputEnd;
+
+        if (inputStartTicks < 0) {
+          // Data before the start of the track is just null.
+          segment.AppendNullData(-inputStartTicks);
+          inputStartTicks = 0;
+        }
+        if (inputEndTicks > inputStartTicks) {
+          segment.AppendSlice(*inputTrack.GetSegment(),
+                              std::min(inputTrackEndPoint, inputStartTicks),
+                              std::min(inputTrackEndPoint, inputEndTicks));
+        }
+        // Pad if we're looking past the end of the track
+        segment.AppendNullData(std::max<TrackTicks>(0, inputEndTicks - inputTrackEndPoint));
+      }
+    }
+
+    trackMap->mSamplesPassedToResampler += segment.GetDuration();
+    trackMap->ResampleInputData(&segment);
+
+    if (trackMap->mResampledData.GetDuration() < mCurrentOutputPosition + WEBAUDIO_BLOCK_SIZE) {
+      // We don't have enough data. Delay it.
+      trackMap->mResampledData.InsertNullDataAtStart(
+        mCurrentOutputPosition + WEBAUDIO_BLOCK_SIZE - trackMap->mResampledData.GetDuration());
+    }
+    audioSegments.AppendElement()->AppendSlice(trackMap->mResampledData,
+      mCurrentOutputPosition, mCurrentOutputPosition + WEBAUDIO_BLOCK_SIZE);
+    trackMap->mResampledData.ForgetUpTo(mCurrentOutputPosition + WEBAUDIO_BLOCK_SIZE);
+    inputChannels = GetAudioChannelsSuperset(inputChannels, trackMap->mResamplerChannelCount);
+  }
+
+  for (int32_t i = mTrackMap.Length() - 1; i >= 0; --i) {
+    if (i >= int32_t(trackMapEntriesUsed.Length()) || !trackMapEntriesUsed[i]) {
+      mTrackMap.RemoveElementAt(i);
+    }
+  }
+
+  uint32_t outputChannels = ComputeFinalOuputChannelCount(inputChannels);
+
+  if (outputChannels) {
+    AllocateAudioBlock(outputChannels, &mLastChunks[0]);
+    nsAutoTArray<float,GUESS_AUDIO_CHANNELS*WEBAUDIO_BLOCK_SIZE> downmixBuffer;
+    for (uint32_t i = 0; i < audioSegments.Length(); ++i) {
+      AudioChunk tmpChunk;
+      ConvertSegmentToAudioBlock(&audioSegments[i], &tmpChunk);
+      if (!tmpChunk.IsNull()) {
+        AccumulateInputChunk(i, tmpChunk, &mLastChunks[0], &downmixBuffer);
+      }
+    }
+  } else {
+    mLastChunks[0].SetNull(WEBAUDIO_BLOCK_SIZE);
+  }
+  mCurrentOutputPosition += WEBAUDIO_BLOCK_SIZE;
+
+  // Using AudioNodeStream's AdvanceOutputSegment to push the media stream graph along with null data.
+  AdvanceOutputSegment();
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/content/media/AudioNodeExternalInputStream.h
@@ -0,0 +1,111 @@
+/* -*- 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 MOZILLA_AUDIONODEEXTERNALINPUTSTREAM_H_
+#define MOZILLA_AUDIONODEEXTERNALINPUTSTREAM_H_
+
+#include "MediaStreamGraph.h"
+#include "AudioChannelFormat.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeStream.h"
+#include "mozilla/dom/AudioParam.h"
+#include <deque>
+
+#ifdef PR_LOGGING
+#define LOG(type, msg) PR_LOG(gMediaStreamGraphLog, type, msg)
+#else
+#define LOG(type, msg)
+#endif
+
+// Forward declaration for mResamplerMap
+typedef struct SpeexResamplerState_ SpeexResamplerState;
+
+namespace mozilla {
+
+/**
+ * This is a MediaStream implementation that acts for a Web Audio node but
+ * unlike other AudioNodeStreams, supports any kind of MediaStream as an
+ * input --- handling any number of audio tracks, resampling them from whatever
+ * sample rate they're using, and handling blocking of the input MediaStream.
+ */
+class AudioNodeExternalInputStream : public AudioNodeStream {
+public:
+  AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate);
+  ~AudioNodeExternalInputStream();
+
+  virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo) MOZ_OVERRIDE;
+
+private:
+  // For storing pointers and data about input tracks, like the last TrackTick which
+  // was read, and the associated speex resampler.
+  struct TrackMapEntry {
+    ~TrackMapEntry();
+
+    /**
+     * Resamples data from all chunks in aIterator and following, using mResampler,
+     * adding the results to mResampledData.
+     */
+    void ResampleInputData(AudioSegment* aSegment);
+    /**
+     * Resamples a set of channel buffers using mResampler, adding the results
+     * to mResampledData.
+     */
+    void ResampleChannels(const nsTArray<const void*>& aBuffers,
+                          uint32_t aInputDuration,
+                          AudioSampleFormat aFormat,
+                          float aVolume);
+
+    // mEndOfConsumedInputTicks is the end of the input ticks that we've consumed.
+    // 0 if we haven't consumed any yet.
+    TrackTicks mEndOfConsumedInputTicks;
+    // mEndOfLastInputIntervalInInputStream is the timestamp for the end of the
+    // previous interval which was unblocked for both the input and output
+    // stream, in the input stream's timeline, or -1 if there wasn't one.
+    StreamTime mEndOfLastInputIntervalInInputStream;
+    // mEndOfLastInputIntervalInOutputStream is the timestamp for the end of the
+    // previous interval which was unblocked for both the input and output
+    // stream, in the output stream's timeline, or -1 if there wasn't one.
+    StreamTime mEndOfLastInputIntervalInOutputStream;
+    /**
+     * Number of samples passed to the resampler so far.
+     */
+    TrackTicks mSamplesPassedToResampler;
+    /**
+     * Resampler being applied to this track.
+     */
+    SpeexResamplerState* mResampler;
+    /**
+     * The track data that has been resampled to the rate of the
+     * AudioNodeExternalInputStream. All data in these chunks is in floats (or null),
+     * and has the number of channels given in mResamplerChannelCount.
+     * mResampledData starts at zero in the stream's output track (so generally
+     * it will consist of null data followed by actual data).
+     */
+    AudioSegment mResampledData;
+    /**
+     * Number of channels used to create mResampler.
+     */
+    uint32_t mResamplerChannelCount;
+    /**
+     * The ID for the track of the input stream this entry is for.
+     */
+    TrackID mTrackID;
+  };
+
+  nsTArray<TrackMapEntry> mTrackMap;
+  // Amount of track data produced so far. A multiple of WEBAUDIO_BLOCK_SIZE.
+  TrackTicks mCurrentOutputPosition;
+
+  /**
+   * Creates a TrackMapEntry for the track, if needed. Returns the index
+   * of the TrackMapEntry or NoIndex if no entry is needed yet.
+   */
+  uint32_t GetTrackMapEntry(const StreamBuffer::Track& aTrack,
+                            GraphTime aFrom);
+};
+
+}
+
+#endif /* MOZILLA_AUDIONODESTREAM_H_ */
--- a/content/media/AudioNodeStream.cpp
+++ b/content/media/AudioNodeStream.cpp
@@ -243,16 +243,34 @@ AudioNodeStream::AllInputsFinished() con
   for (uint32_t i = 0; i < inputCount; ++i) {
     if (!mInputs[i]->GetSource()->IsFinishedOnGraphThread()) {
       return false;
     }
   }
   return !!inputCount;
 }
 
+uint32_t
+AudioNodeStream::ComputeFinalOuputChannelCount(uint32_t aInputChannelCount)
+{
+  switch (mChannelCountMode) {
+  case ChannelCountMode::Explicit:
+    // Disregard the channel count we've calculated from inputs, and just use
+    // mNumberOfInputChannels.
+    return mNumberOfInputChannels;
+  case ChannelCountMode::Clamped_max:
+    // Clamp the computed output channel count to mNumberOfInputChannels.
+    return std::min(aInputChannelCount, mNumberOfInputChannels);
+  default:
+  case ChannelCountMode::Max:
+    // Nothing to do here, just shut up the compiler warning.
+    return aInputChannelCount;
+  }
+}
+
 void
 AudioNodeStream::ObtainInputBlock(AudioChunk& aTmpChunk, uint32_t aPortIndex)
 {
   uint32_t inputCount = mInputs.Length();
   uint32_t outputChannelCount = 1;
   nsAutoTArray<AudioChunk*,250> inputChunks;
   for (uint32_t i = 0; i < inputCount; ++i) {
     if (aPortIndex != mInputs[i]->InputNumber()) {
@@ -272,30 +290,17 @@ AudioNodeStream::ObtainInputBlock(AudioC
       continue;
     }
 
     inputChunks.AppendElement(chunk);
     outputChannelCount =
       GetAudioChannelsSuperset(outputChannelCount, chunk->mChannelData.Length());
   }
 
-  switch (mChannelCountMode) {
-  case ChannelCountMode::Explicit:
-    // Disregard the output channel count that we've calculated, and just use
-    // mNumberOfInputChannels.
-    outputChannelCount = mNumberOfInputChannels;
-    break;
-  case ChannelCountMode::Clamped_max:
-    // Clamp the computed output channel count to mNumberOfInputChannels.
-    outputChannelCount = std::min(outputChannelCount, mNumberOfInputChannels);
-    break;
-  case ChannelCountMode::Max:
-    // Nothing to do here, just shut up the compiler warning.
-    break;
-  }
+  outputChannelCount = ComputeFinalOuputChannelCount(outputChannelCount);
 
   uint32_t inputChunkCount = inputChunks.Length();
   if (inputChunkCount == 0 ||
       (inputChunkCount == 1 && inputChunks[0]->mChannelData.Length() == 0)) {
     aTmpChunk.SetNull(WEBAUDIO_BLOCK_SIZE);
     return;
   }
 
@@ -306,91 +311,106 @@ AudioNodeStream::ObtainInputBlock(AudioC
   }
 
   if (outputChannelCount == 0) {
     aTmpChunk.SetNull(WEBAUDIO_BLOCK_SIZE);
     return;
   }
 
   AllocateAudioBlock(outputChannelCount, &aTmpChunk);
-  float silenceChannel[WEBAUDIO_BLOCK_SIZE] = {0.f};
   // The static storage here should be 1KB, so it's fine
   nsAutoTArray<float, GUESS_AUDIO_CHANNELS*WEBAUDIO_BLOCK_SIZE> downmixBuffer;
 
   for (uint32_t i = 0; i < inputChunkCount; ++i) {
-    AudioChunk* chunk = inputChunks[i];
-    nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channels;
-    channels.AppendElements(chunk->mChannelData);
-    if (channels.Length() < outputChannelCount) {
-      if (mChannelInterpretation == ChannelInterpretation::Speakers) {
-        AudioChannelsUpMix(&channels, outputChannelCount, nullptr);
-        NS_ASSERTION(outputChannelCount == channels.Length(),
-                     "We called GetAudioChannelsSuperset to avoid this");
+    AccumulateInputChunk(i, *inputChunks[i], &aTmpChunk, &downmixBuffer);
+  }
+}
+
+void
+AudioNodeStream::AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aChunk,
+                                      AudioChunk* aBlock,
+                                      nsTArray<float>* aDownmixBuffer)
+{
+  nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channels;
+  UpMixDownMixChunk(&aChunk, aBlock->mChannelData.Length(), channels, *aDownmixBuffer);
+
+  for (uint32_t c = 0; c < channels.Length(); ++c) {
+    const float* inputData = static_cast<const float*>(channels[c]);
+    float* outputData = static_cast<float*>(const_cast<void*>(aBlock->mChannelData[c]));
+    if (inputData) {
+      if (aInputIndex == 0) {
+        AudioBlockCopyChannelWithScale(inputData, aChunk.mVolume, outputData);
       } else {
-        // Fill up the remaining channels by zeros
-        for (uint32_t j = channels.Length(); j < outputChannelCount; ++j) {
-          channels.AppendElement(silenceChannel);
-        }
+        AudioBlockAddChannelWithScale(inputData, aChunk.mVolume, outputData);
       }
-    } else if (channels.Length() > outputChannelCount) {
-      if (mChannelInterpretation == ChannelInterpretation::Speakers) {
-        nsAutoTArray<float*,GUESS_AUDIO_CHANNELS> outputChannels;
-        outputChannels.SetLength(outputChannelCount);
-        downmixBuffer.SetLength(outputChannelCount * WEBAUDIO_BLOCK_SIZE);
-        for (uint32_t j = 0; j < outputChannelCount; ++j) {
-          outputChannels[j] = &downmixBuffer[j * WEBAUDIO_BLOCK_SIZE];
-        }
-
-        AudioChannelsDownMix(channels, outputChannels.Elements(),
-                             outputChannelCount, WEBAUDIO_BLOCK_SIZE);
-
-        channels.SetLength(outputChannelCount);
-        for (uint32_t j = 0; j < channels.Length(); ++j) {
-          channels[j] = outputChannels[j];
-        }
-      } else {
-        // Drop the remaining channels
-        channels.RemoveElementsAt(outputChannelCount,
-                                  channels.Length() - outputChannelCount);
+    } else {
+      if (aInputIndex == 0) {
+        PodZero(outputData, WEBAUDIO_BLOCK_SIZE);
       }
     }
+  }
+}
 
-    for (uint32_t c = 0; c < channels.Length(); ++c) {
-      const float* inputData = static_cast<const float*>(channels[c]);
-      float* outputData = static_cast<float*>(const_cast<void*>(aTmpChunk.mChannelData[c]));
-      if (inputData) {
-        if (i == 0) {
-          AudioBlockCopyChannelWithScale(inputData, chunk->mVolume, outputData);
-        } else {
-          AudioBlockAddChannelWithScale(inputData, chunk->mVolume, outputData);
-        }
-      } else {
-        if (i == 0) {
-          memset(outputData, 0, WEBAUDIO_BLOCK_SIZE*sizeof(float));
-        }
+void
+AudioNodeStream::UpMixDownMixChunk(const AudioChunk* aChunk,
+                                   uint32_t aOutputChannelCount,
+                                   nsTArray<const void*>& aOutputChannels,
+                                   nsTArray<float>& aDownmixBuffer)
+{
+  static const float silenceChannel[WEBAUDIO_BLOCK_SIZE] = {0.f};
+
+  aOutputChannels.AppendElements(aChunk->mChannelData);
+  if (aOutputChannels.Length() < aOutputChannelCount) {
+    if (mChannelInterpretation == ChannelInterpretation::Speakers) {
+      AudioChannelsUpMix(&aOutputChannels, aOutputChannelCount, nullptr);
+      NS_ASSERTION(aOutputChannelCount == aOutputChannels.Length(),
+                   "We called GetAudioChannelsSuperset to avoid this");
+    } else {
+      // Fill up the remaining aOutputChannels by zeros
+      for (uint32_t j = aOutputChannels.Length(); j < aOutputChannelCount; ++j) {
+        aOutputChannels.AppendElement(silenceChannel);
       }
     }
+  } else if (aOutputChannels.Length() > aOutputChannelCount) {
+    if (mChannelInterpretation == ChannelInterpretation::Speakers) {
+      nsAutoTArray<float*,GUESS_AUDIO_CHANNELS> outputChannels;
+      outputChannels.SetLength(aOutputChannelCount);
+      aDownmixBuffer.SetLength(aOutputChannelCount * WEBAUDIO_BLOCK_SIZE);
+      for (uint32_t j = 0; j < aOutputChannelCount; ++j) {
+        outputChannels[j] = &aDownmixBuffer[j * WEBAUDIO_BLOCK_SIZE];
+      }
+
+      AudioChannelsDownMix(aOutputChannels, outputChannels.Elements(),
+                           aOutputChannelCount, WEBAUDIO_BLOCK_SIZE);
+
+      aOutputChannels.SetLength(aOutputChannelCount);
+      for (uint32_t j = 0; j < aOutputChannels.Length(); ++j) {
+        aOutputChannels[j] = outputChannels[j];
+      }
+    } else {
+      // Drop the remaining aOutputChannels
+      aOutputChannels.RemoveElementsAt(aOutputChannelCount,
+        aOutputChannels.Length() - aOutputChannelCount);
+    }
   }
 }
 
 // The MediaStreamGraph guarantees that this is actually one block, for
 // AudioNodeStreams.
 void
 AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo)
 {
   if (mMarkAsFinishedAfterThisBlock) {
     // This stream was finished the last time that we looked at it, and all
     // of the depending streams have finished their output as well, so now
     // it's time to mark this stream as finished.
     FinishOutput();
   }
 
-  StreamBuffer::Track* track = EnsureTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate);
-
-  AudioSegment* segment = track->Get<AudioSegment>();
+  EnsureTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate);
 
   uint16_t outputCount = std::max(uint16_t(1), mEngine->OutputCount());
   mLastChunks.SetLength(outputCount);
 
   if (mInCycle) {
     // XXX DelayNode not supported yet so just produce silence
     for (uint16_t i = 0; i < outputCount; ++i) {
       mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
@@ -419,16 +439,25 @@ AudioNodeStream::ProduceOutput(GraphTime
   }
 
   if (mDisabledTrackIDs.Contains(AUDIO_NODE_STREAM_TRACK_ID)) {
     for (uint32_t i = 0; i < mLastChunks.Length(); ++i) {
       mLastChunks[i].SetNull(WEBAUDIO_BLOCK_SIZE);
     }
   }
 
+  AdvanceOutputSegment();
+}
+
+void
+AudioNodeStream::AdvanceOutputSegment()
+{
+  StreamBuffer::Track* track = EnsureTrack(AUDIO_NODE_STREAM_TRACK_ID, mSampleRate);
+  AudioSegment* segment = track->Get<AudioSegment>();
+
   if (mKind == MediaStreamGraph::EXTERNAL_STREAM) {
     segment->AppendAndConsumeChunk(&mLastChunks[0]);
   } else {
     segment->AppendNullData(mLastChunks[0].GetDuration());
   }
 
   for (uint32_t j = 0; j < mListeners.Length(); ++j) {
     MediaStreamListener* l = mListeners[j];
--- a/content/media/AudioNodeStream.h
+++ b/content/media/AudioNodeStream.h
@@ -109,24 +109,36 @@ public:
     return mLastChunks;
   }
   virtual bool MainThreadNeedsUpdates() const MOZ_OVERRIDE
   {
     // Only source and external streams need updates on the main thread.
     return (mKind == MediaStreamGraph::SOURCE_STREAM && mFinished) ||
            mKind == MediaStreamGraph::EXTERNAL_STREAM;
   }
+  virtual bool IsIntrinsicallyConsumed() const MOZ_OVERRIDE
+  {
+    return true;
+  }
 
   // Any thread
   AudioNodeEngine* Engine() { return mEngine; }
   TrackRate SampleRate() const { return mSampleRate; }
 
 protected:
+  void AdvanceOutputSegment();
   void FinishOutput();
+  void AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aChunk,
+                            AudioChunk* aBlock,
+                            nsTArray<float>* aDownmixBuffer);
+  void UpMixDownMixChunk(const AudioChunk* aChunk, uint32_t aOutputChannelCount,
+                         nsTArray<const void*>& aOutputChannels,
+                         nsTArray<float>& aDownmixBuffer);
 
+  uint32_t ComputeFinalOuputChannelCount(uint32_t aInputChannelCount);
   void ObtainInputBlock(AudioChunk& aTmpChunk, uint32_t aPortIndex);
 
   // The engine that will generate output for this node.
   nsAutoPtr<AudioNodeEngine> mEngine;
   // The last block produced by this node.
   OutputChunks mLastChunks;
   // The stream's sampling rate
   const TrackRate mSampleRate;
--- a/content/media/DOMMediaStream.cpp
+++ b/content/media/DOMMediaStream.cpp
@@ -26,21 +26,23 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMMedia
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMMediaStream)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMediaStream)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMMediaStream)
   tmp->Destroy();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMMediaStream)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DOMMediaStream)
 
 NS_IMPL_ISUPPORTS_INHERITED1(DOMLocalMediaStream, DOMMediaStream,
                              nsIDOMLocalMediaStream)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED_1(DOMAudioNodeMediaStream, DOMMediaStream,
@@ -283,16 +285,26 @@ DOMMediaStream::GetDOMTrackFor(TrackID a
 
 void
 DOMMediaStream::NotifyMediaStreamGraphShutdown()
 {
   // No more tracks will ever be added, so just clear these callbacks now
   // to prevent leaks.
   mNotifiedOfMediaStreamGraphShutdown = true;
   mRunOnTracksAvailable.Clear();
+
+  mConsumersToKeepAlive.Clear();
+}
+
+void
+DOMMediaStream::NotifyStreamStateChanged()
+{
+  if (IsFinished()) {
+    mConsumersToKeepAlive.Clear();
+  }
 }
 
 void
 DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable)
 {
   if (mNotifiedOfMediaStreamGraphShutdown) {
     // No more tracks will ever be added, so just delete the callback now.
     delete aRunnable;
--- a/content/media/DOMMediaStream.h
+++ b/content/media/DOMMediaStream.h
@@ -63,20 +63,22 @@ public:
   {
     return mWindow;
   }
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   // WebIDL
   double CurrentTime();
+
   void GetAudioTracks(nsTArray<nsRefPtr<AudioStreamTrack> >& aTracks);
   void GetVideoTracks(nsTArray<nsRefPtr<VideoStreamTrack> >& aTracks);
 
-  MediaStream* GetStream() { return mStream; }
+  MediaStream* GetStream() const { return mStream; }
+
   bool IsFinished();
   /**
    * Returns a principal indicating who may access this stream. The stream contents
    * can only be accessed by principals subsuming this principal.
    */
   nsIPrincipal* GetPrincipal() { return mPrincipal; }
 
   /**
@@ -88,16 +90,20 @@ public:
   bool CombineWithPrincipal(nsIPrincipal* aPrincipal);
 
   /**
    * Called when this stream's MediaStreamGraph has been shut down. Normally
    * MSGs are only shut down when all streams have been removed, so this
    * will only be called during a forced shutdown due to application exit.
    */
   void NotifyMediaStreamGraphShutdown();
+  /**
+   * Called when the main-thread state of the MediaStream changed.
+   */
+  void NotifyStreamStateChanged();
 
   // Indicate what track types we eventually expect to add to this stream
   enum {
     HINT_CONTENTS_AUDIO = 1 << 0,
     HINT_CONTENTS_VIDEO = 1 << 1
   };
   TrackTypeHints GetHintContents() const { return mHintContents; }
   void SetHintContents(TrackTypeHints aHintContents) { mHintContents = aHintContents; }
@@ -140,16 +146,27 @@ public:
   // It is allowed to do anything, including run script.
   // aCallback may run immediately during this call if tracks are already
   // available!
   // We only care about track additions, we'll fire the notification even if
   // some of the tracks have been removed.
   // Takes ownership of aCallback.
   void OnTracksAvailable(OnTracksAvailableCallback* aCallback);
 
+  /**
+   * Add an nsISupports object that this stream will keep alive as long as
+   * the stream is not finished.
+   */
+  void AddConsumerToKeepAlive(nsISupports* aConsumer)
+  {
+    if (!IsFinished() && !mNotifiedOfMediaStreamGraphShutdown) {
+      mConsumersToKeepAlive.AppendElement(aConsumer);
+    }
+  }
+
 protected:
   void Destroy();
   void InitSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
   void InitTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents);
   void InitStreamCommon(MediaStream* aStream);
   void CheckTracksAvailable();
 
   class StreamListener;
@@ -168,16 +185,19 @@ protected:
   // If null, this stream can be used by anyone because it has no content yet.
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   nsAutoTArray<nsRefPtr<MediaStreamTrack>,2> mTracks;
   nsRefPtr<StreamListener> mListener;
 
   nsTArray<nsAutoPtr<OnTracksAvailableCallback> > mRunOnTracksAvailable;
 
+  // Keep these alive until the stream finishes
+  nsTArray<nsCOMPtr<nsISupports> > mConsumersToKeepAlive;
+
   // Indicate what track types we eventually expect to add to this stream
   uint8_t mHintContents;
   // Indicate what track types have been added to this stream
   uint8_t mTrackTypesAvailable;
   bool mNotifiedOfMediaStreamGraphShutdown;
 };
 
 class DOMLocalMediaStream : public DOMMediaStream,
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -363,16 +363,17 @@ MediaDecoder::MediaDecoder() :
   mCurrentTime(0.0),
   mInitialVolume(0.0),
   mInitialPlaybackRate(1.0),
   mInitialPreservesPitch(true),
   mRequestedSeekTime(-1.0),
   mDuration(-1),
   mTransportSeekable(true),
   mMediaSeekable(true),
+  mSameOriginMedia(false),
   mReentrantMonitor("media.decoder"),
   mIsDormant(false),
   mPlayState(PLAY_STATE_PAUSED),
   mNextState(PLAY_STATE_PAUSED),
   mCalledResourceLoaded(false),
   mIgnoreProgressData(false),
   mInfiniteStream(false),
   mTriggerPlaybackEndedWhenSourceStreamFinishes(false),
@@ -440,48 +441,44 @@ void MediaDecoder::Shutdown()
 MediaDecoder::~MediaDecoder()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryReporter::RemoveMediaDecoder(this);
   UnpinForSeek();
   MOZ_COUNT_DTOR(MediaDecoder);
 }
 
-nsresult MediaDecoder::OpenResource(MediaResource* aResource,
-                                    nsIStreamListener** aStreamListener)
+nsresult MediaDecoder::OpenResource(nsIStreamListener** aStreamListener)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (aStreamListener) {
     *aStreamListener = nullptr;
   }
 
   {
     // Hold the lock while we do this to set proper lock ordering
     // expectations for dynamic deadlock detectors: decoder lock(s)
     // should be grabbed before the cache lock
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
-    nsresult rv = aResource->Open(aStreamListener);
+    nsresult rv = mResource->Open(aStreamListener);
     if (NS_FAILED(rv)) {
       LOG(PR_LOG_DEBUG, ("%p Failed to open stream!", this));
       return rv;
     }
-
-    mResource = aResource;
   }
   return NS_OK;
 }
 
-nsresult MediaDecoder::Load(MediaResource* aResource,
-                                nsIStreamListener** aStreamListener,
-                                MediaDecoder* aCloneDonor)
+nsresult MediaDecoder::Load(nsIStreamListener** aStreamListener,
+                            MediaDecoder* aCloneDonor)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  nsresult rv = OpenResource(aResource, aStreamListener);
+  nsresult rv = OpenResource(aStreamListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mDecoderStateMachine = CreateStateMachine();
   if (!mDecoderStateMachine) {
     LOG(PR_LOG_DEBUG, ("%p Failed to create state machine!", this));
     return NS_ERROR_FAILURE;
   }
 
@@ -837,16 +834,28 @@ void MediaDecoder::DecodeError()
     return;
 
   if (mOwner)
     mOwner->DecodeError();
 
   Shutdown();
 }
 
+void MediaDecoder::UpdateSameOriginStatus(bool aSameOrigin)
+{
+  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  mSameOriginMedia = aSameOrigin;
+}
+
+bool MediaDecoder::IsSameOriginMedia()
+{
+  GetReentrantMonitor().AssertCurrentThreadIn();
+  return mSameOriginMedia;
+}
+
 bool MediaDecoder::IsSeeking() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mPlayState == PLAY_STATE_SEEKING;
 }
 
 bool MediaDecoder::IsEnded() const
 {
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -272,26 +272,22 @@ public:
   virtual bool Init(MediaDecoderOwner* aOwner);
 
   // Cleanup internal data structures. Must be called on the main
   // thread by the owning object before that object disposes of this object.
   virtual void Shutdown();
 
   // Start downloading the media. Decode the downloaded data up to the
   // point of the first frame of data.
-  // aResource is the media stream to use. Ownership of aResource passes to
-  // the decoder, even if Load returns an error.
   // This is called at most once per decoder, after Init().
-  virtual nsresult Load(MediaResource* aResource,
-                        nsIStreamListener** aListener,
+  virtual nsresult Load(nsIStreamListener** aListener,
                         MediaDecoder* aCloneDonor);
 
-  // Called in |Load| to open the media resource.
-  nsresult OpenResource(MediaResource* aResource,
-                        nsIStreamListener** aStreamListener);
+  // Called in |Load| to open mResource.
+  nsresult OpenResource(nsIStreamListener** aStreamListener);
 
   // Called when the video file has completed downloading.
   virtual void ResourceLoaded();
 
   // Called if the media file encounters a network error.
   virtual void NetworkError();
 
   // Get the current MediaResource being used. Its URI will be returned
@@ -301,16 +297,21 @@ public:
   // refcounting, *unless* you need to store and use the reference after the
   // MediaDecoder has been destroyed. You might need to do this if you're
   // wrapping the MediaResource in some kind of byte stream interface to be
   // passed to a platform decoder.
   MediaResource* GetResource() const MOZ_FINAL MOZ_OVERRIDE
   {
     return mResource;
   }
+  void SetResource(MediaResource* aResource)
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Should only be called on main thread");
+    mResource = aResource;
+  }
 
   // Return the principal of the current URI being played or downloaded.
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal();
 
   // Return the time position in the video stream being
   // played measured in seconds.
   virtual double GetCurrentTime();
 
@@ -635,16 +636,20 @@ public:
   virtual void NotifyPlaybackStopped() {
     GetReentrantMonitor().AssertCurrentThreadIn();
     mPlaybackStatistics.Stop();
   }
 
   // The actual playback rate computation. The monitor must be held.
   virtual double ComputePlaybackRate(bool* aReliable);
 
+  // Return true when the media is same-origin with the element. The monitor
+  // must be held.
+  bool IsSameOriginMedia();
+
   // Returns true if we can play the entire media through without stopping
   // to buffer, given the current download and playback rates.
   bool CanPlayThrough();
 
   // Make the decoder state machine update the playback position. Called by
   // the reader on the decoder thread (Assertions for this checked by
   // mDecoderStateMachine). This must be called with the decode monitor
   // held.
@@ -731,16 +736,19 @@ public:
   // Called when a "MozAudioAvailable" event listener is added. This enables
   // the decoder to only dispatch "MozAudioAvailable" events when a
   // handler exists, reducing overhead. Called on the main thread.
   virtual void NotifyAudioAvailableListener();
 
   // Notifies the element that decoding has failed.
   virtual void DecodeError();
 
+  // Indicate whether the media is same-origin with the element.
+  void UpdateSameOriginStatus(bool aSameOrigin);
+
   MediaDecoderOwner* GetOwner() MOZ_OVERRIDE;
 
 #ifdef MOZ_RAW
   static bool IsRawEnabled();
 #endif
 
 #ifdef MOZ_OGG
   static bool IsOggEnabled();
@@ -953,16 +961,20 @@ public:
 
   // True if the resource is seekable at a transport level (server supports byte
   // range requests, local file, etc.).
   bool mTransportSeekable;
 
   // True if the media is seekable (i.e. supports random access).
   bool mMediaSeekable;
 
+  // True if the media is same-origin with the element. Data can only be
+  // passed to MediaStreams when this is true.
+  bool mSameOriginMedia;
+
   /******
    * The following member variables can be accessed from any thread.
    ******/
 
   // The state machine object for handling the decoding. It is safe to
   // call methods of this object from other threads. Its internal data
   // is synchronised on a monitor. The lifetime of this object is
   // after mPlayState is LOADING and before mPlayState is SHUTDOWN. It
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -614,16 +614,20 @@ void MediaDecoderStateMachine::SendStrea
 
   DecodedStreamData* stream = mDecoder->GetDecodedStream();
   if (!stream)
     return;
 
   if (mState == DECODER_STATE_DECODING_METADATA)
     return;
 
+  if (!mDecoder->IsSameOriginMedia()) {
+    return;
+  }
+
   // If there's still an audio thread alive, then we can't send any stream
   // data yet since both SendStreamData and the audio thread want to be in
   // charge of popping the audio queue. We're waiting for the audio thread
   // to die before sending anything to our stream.
   if (mAudioThread)
     return;
 
   int64_t minLastAudioPacketTime = INT64_MAX;
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -171,17 +171,17 @@ MediaRecorder::Start(const Optional<int3
     }
     mTimeSlice = aTimeSlice.Value();
   } else {
     mTimeSlice = 0;
   }
 
   // Create a TrackUnionStream to support Pause/Resume by using ChangeExplicitBlockerCount
   MediaStreamGraph* gm = mStream->GetStream()->Graph();
-  mTrackUnionStream = gm->CreateTrackUnionStream(mStream);
+  mTrackUnionStream = gm->CreateTrackUnionStream(nullptr);
   MOZ_ASSERT(mTrackUnionStream, "CreateTrackUnionStream failed");
 
   if (!CheckPrincipal()) {
     aResult.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   if (mEncodedBufferCache == nullptr) {
--- a/content/media/MediaResource.cpp
+++ b/content/media/MediaResource.cpp
@@ -200,16 +200,26 @@ ChannelMediaResource::OnStartRequest(nsI
       CloseChannel();
       return NS_OK;
     }
 
     nsAutoCString ranges;
     hc->GetResponseHeader(NS_LITERAL_CSTRING("Accept-Ranges"),
                           ranges);
     bool acceptsRanges = ranges.EqualsLiteral("bytes");
+    // True if this channel will not return an unbounded amount of data
+    bool dataIsBounded = false;
+
+    int64_t contentLength = -1;
+    hc->GetContentLength(&contentLength);
+    if (contentLength >= 0 && responseStatus == HTTP_OK_CODE) {
+      // "OK" status means Content-Length is for the whole resource.
+      // Since that's bounded, we know we have a finite-length resource.
+      dataIsBounded = true;
+    }
 
     if (mOffset == 0) {
       // Look for duration headers from known Ogg content systems.
       // In the case of multiple options for obtaining the duration
       // the order of precedence is:
       // 1) The Media resource metadata if possible (done by the decoder itself).
       // 2) Content-Duration message header.
       // 3) X-AMZ-Meta-Content-Duration.
@@ -220,30 +230,31 @@ ChannelMediaResource::OnStartRequest(nsI
       rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("Content-Duration"), durationText);
       if (NS_FAILED(rv)) {
         rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("X-AMZ-Meta-Content-Duration"), durationText);
       }
       if (NS_FAILED(rv)) {
         rv = hc->GetResponseHeader(NS_LITERAL_CSTRING("X-Content-Duration"), durationText);
       }
 
-      // If there is no Content-Duration header, or if the value for this header
-      // is not valid, set the media as being infinite.
+      // If there is a Content-Duration header with a valid value, record
+      // the duration.
       if (NS_SUCCEEDED(rv)) {
         double duration = durationText.ToDouble(&ec);
         if (ec == NS_OK && duration >= 0) {
           mDecoder->SetDuration(duration);
-        } else {
-          mDecoder->SetInfinite(true);
+          // We know the resource must be bounded.
+          dataIsBounded = true;
         }
-      } else {
-        mDecoder->SetInfinite(true);
       }
     }
 
+    // Assume Range requests have a bounded upper limit unless the
+    // Content-Range header tells us otherwise.
+    bool boundedSeekLimit = true;
     // Check response code for byte-range requests (seeking, chunk requests).
     if (!mByteRange.IsNull() && (responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
       // Parse Content-Range header.
       int64_t rangeStart = 0;
       int64_t rangeEnd = 0;
       int64_t rangeTotal = 0;
       rv = ParseContentRangeHeader(hc, rangeStart, rangeEnd, rangeTotal);
       if (NS_FAILED(rv)) {
@@ -262,20 +273,20 @@ ChannelMediaResource::OnStartRequest(nsI
                        "response range start does not match request");
       NS_WARN_IF_FALSE(mOffset == rangeStart,
                        "response range start does not match current offset");
       NS_WARN_IF_FALSE(mByteRange.mEnd == rangeEnd,
                        "response range end does not match request");
       // Notify media cache about the length and start offset of data received.
       // Note: If aRangeTotal == -1, then the total bytes is unknown at this stage.
       //       For now, tell the decoder that the stream is infinite.
-      if (rangeTotal != -1) {
+      if (rangeTotal == -1) {
+        boundedSeekLimit = false;
+      } else {
         mCacheStream.NotifyDataLength(rangeTotal);
-      } else {
-        mDecoder->SetInfinite(true);
       }
       mCacheStream.NotifyDataStarted(rangeStart);
 
       mOffset = rangeStart;
       // We received 'Content-Range', so the server accepts range requests.
       acceptsRanges = true;
     } else if (((mOffset > 0) || !mByteRange.IsNull())
                && (responseStatus == HTTP_OK_CODE)) {
@@ -285,37 +296,35 @@ ChannelMediaResource::OnStartRequest(nsI
       mCacheStream.NotifyDataStarted(0);
       mOffset = 0;
 
       // The server claimed it supported range requests.  It lied.
       acceptsRanges = false;
     } else if (mOffset == 0 &&
                (responseStatus == HTTP_OK_CODE ||
                 responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) {
-      // We weren't seeking and got a valid response status,
-      // set the length of the content.
-      int64_t cl = -1;
-      hc->GetContentLength(&cl);
-
-      if (cl >= 0) {
-        mCacheStream.NotifyDataLength(cl);
+      if (contentLength >= 0) {
+        mCacheStream.NotifyDataLength(contentLength);
       }
     }
     // XXX we probably should examine the Content-Range header in case
     // the server gave us a range which is not quite what we asked for
 
     // If we get an HTTP_OK_CODE response to our byte range request,
     // and the server isn't sending Accept-Ranges:bytes then we don't
     // support seeking.
     seekable =
       responseStatus == HTTP_PARTIAL_RESPONSE_CODE || acceptsRanges;
+    if (seekable && boundedSeekLimit) {
+      // If range requests are supported, and we did not see an unbounded
+      // upper range limit, we assume the resource is bounded.
+      dataIsBounded = true;
+    }
 
-    if (seekable) {
-      mDecoder->SetInfinite(false);
-    }
+    mDecoder->SetInfinite(!dataIsBounded);
   }
   mDecoder->SetTransportSeekable(seekable);
   mCacheStream.SetTransportSeekable(seekable);
 
   {
     MutexAutoLock lock(mLock);
     mIsTransportSeekable = seekable;
     mChannelStatistics->Start();
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -17,16 +17,17 @@
 #include "prlog.h"
 #include "VideoUtils.h"
 #include "mozilla/Attributes.h"
 #include "TrackUnionStream.h"
 #include "ImageContainer.h"
 #include "AudioChannelCommon.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
+#include "AudioNodeExternalInputStream.h"
 #include <algorithm>
 #include "DOMMediaStream.h"
 #include "GeckoProfiler.h"
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
 namespace mozilla {
@@ -515,17 +516,17 @@ MediaStreamGraphImpl::UpdateStreamOrder(
     if (ps) {
       ps->mInCycle = false;
     }
   }
 
   mozilla::LinkedList<MediaStream> stack;
   for (uint32_t i = 0; i < mOldStreams.Length(); ++i) {
     nsRefPtr<MediaStream>& s = mOldStreams[i];
-    if (!s->mAudioOutputs.IsEmpty() || !s->mVideoOutputs.IsEmpty()) {
+    if (s->IsIntrinsicallyConsumed()) {
       MarkConsumed(s);
     }
     if (!s->mHasBeenOrdered) {
       UpdateStreamOrderForStream(&stack, s.forget());
     }
   }
 }
 
@@ -1226,16 +1227,19 @@ MediaStreamGraphImpl::ApplyStreamUpdate(
   mMonitor.AssertCurrentThreadOwns();
 
   MediaStream* stream = aUpdate->mStream;
   if (!stream)
     return;
   stream->mMainThreadCurrentTime = aUpdate->mNextMainThreadCurrentTime;
   stream->mMainThreadFinished = aUpdate->mNextMainThreadFinished;
 
+  if (stream->mWrapper) {
+    stream->mWrapper->NotifyStreamStateChanged();
+  }
   for (int32_t i = stream->mMainThreadListeners.Length() - 1; i >= 0; --i) {
     stream->mMainThreadListeners[i]->NotifyMainThreadStateChanged();
   }
 }
 
 void
 MediaStreamGraphImpl::ShutdownThreads()
 {
@@ -1538,16 +1542,40 @@ MediaStreamGraphImpl::AppendMessage(Cont
   mCurrentTaskMessageQueue.AppendElement(aMessage);
   // Do not start running the non-realtime graph unless processing has
   // explicitly started.
   if (mRealtime || mNonRealtimeProcessing) {
     EnsureRunInStableState();
   }
 }
 
+MediaStream::MediaStream(DOMMediaStream* aWrapper)
+  : mBufferStartTime(0)
+  , mExplicitBlockerCount(0)
+  , mBlocked(false)
+  , mGraphUpdateIndices(0)
+  , mFinished(false)
+  , mNotifiedFinished(false)
+  , mNotifiedBlocked(false)
+  , mHasCurrentData(false)
+  , mNotifiedHasCurrentData(false)
+  , mWrapper(aWrapper)
+  , mMainThreadCurrentTime(0)
+  , mMainThreadFinished(false)
+  , mMainThreadDestroyed(false)
+  , mGraph(nullptr)
+{
+  MOZ_COUNT_CTOR(MediaStream);
+  // aWrapper should not already be connected to a MediaStream! It needs
+  // to be hooked up to this stream, and since this stream is only just
+  // being created now, aWrapper must not be connected to anything.
+  NS_ASSERTION(!aWrapper || !aWrapper->GetStream(),
+               "Wrapper already has another media stream hooked up to it!");
+}
+
 void
 MediaStream::Init()
 {
   MediaStreamGraphImpl* graph = GraphImpl();
   mBlocked.SetAtAndAfter(graph->mCurrentTime, true);
   mExplicitBlockerCount.SetAtAndAfter(graph->mCurrentTime, true);
   mExplicitBlockerCount.SetAtAndAfter(graph->mStateComputedTime, false);
 }
@@ -2311,16 +2339,31 @@ MediaStreamGraph::CreateTrackUnionStream
   TrackUnionStream* stream = new TrackUnionStream(aWrapper);
   NS_ADDREF(stream);
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   stream->SetGraphImpl(graph);
   graph->AppendMessage(new CreateMessage(stream));
   return stream;
 }
 
+AudioNodeExternalInputStream*
+MediaStreamGraph::CreateAudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!aSampleRate) {
+    aSampleRate = aEngine->NodeMainThread()->Context()->SampleRate();
+  }
+  AudioNodeExternalInputStream* stream = new AudioNodeExternalInputStream(aEngine, aSampleRate);
+  NS_ADDREF(stream);
+  MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
+  stream->SetGraphImpl(graph);
+  graph->AppendMessage(new CreateMessage(stream));
+  return stream;
+}
+
 AudioNodeStream*
 MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine,
                                         AudioNodeStreamKind aKind,
                                         TrackRate aSampleRate)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!aSampleRate) {
     aSampleRate = aEngine->NodeMainThread()->Context()->SampleRate();
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -186,18 +186,19 @@ class MainThreadMediaStreamListener {
 public:
   virtual void NotifyMainThreadStateChanged() = 0;
 };
 
 class MediaStreamGraphImpl;
 class SourceMediaStream;
 class ProcessedMediaStream;
 class MediaInputPort;
+class AudioNodeEngine;
+class AudioNodeExternalInputStream;
 class AudioNodeStream;
-class AudioNodeEngine;
 struct AudioChunk;
 
 /**
  * A stream of synchronized audio and video data. All (not blocked) streams
  * progress at the same rate --- "real time". Streams cannot seek. The only
  * operation readers can perform on a stream is to read the next data.
  *
  * Consumers of a stream can be reading from it at different offsets, but that
@@ -257,34 +258,17 @@ struct AudioChunk;
  * collected via cycle collection. Destroy messages will be sent
  * for those objects in arbitrary order and the MediaStreamGraph has to be able
  * to handle this.
  */
 class MediaStream : public mozilla::LinkedListElement<MediaStream> {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
 
-  MediaStream(DOMMediaStream* aWrapper)
-    : mBufferStartTime(0)
-    , mExplicitBlockerCount(0)
-    , mBlocked(false)
-    , mGraphUpdateIndices(0)
-    , mFinished(false)
-    , mNotifiedFinished(false)
-    , mNotifiedBlocked(false)
-    , mHasCurrentData(false)
-    , mNotifiedHasCurrentData(false)
-    , mWrapper(aWrapper)
-    , mMainThreadCurrentTime(0)
-    , mMainThreadFinished(false)
-    , mMainThreadDestroyed(false)
-    , mGraph(nullptr)
-  {
-    MOZ_COUNT_CTOR(MediaStream);
-  }
+  MediaStream(DOMMediaStream* aWrapper);
   virtual ~MediaStream()
   {
     MOZ_COUNT_DTOR(MediaStream);
     NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
     NS_ASSERTION(mMainThreadListeners.IsEmpty(),
                  "All main thread listeners should have been removed");
   }
 
@@ -355,16 +339,17 @@ public:
   bool IsDestroyed()
   {
     NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
     return mMainThreadDestroyed;
   }
 
   friend class MediaStreamGraphImpl;
   friend class MediaInputPort;
+  friend class AudioNodeExternalInputStream;
 
   virtual SourceMediaStream* AsSourceStream() { return nullptr; }
   virtual ProcessedMediaStream* AsProcessedStream() { return nullptr; }
   virtual AudioNodeStream* AsAudioNodeStream() { return nullptr; }
 
   // media graph thread only
   void Init();
   // These Impl methods perform the core functionality of the control methods
@@ -395,16 +380,26 @@ public:
   void ChangeExplicitBlockerCountImpl(GraphTime aTime, int32_t aDelta)
   {
     mExplicitBlockerCount.SetAtAndAfter(aTime, mExplicitBlockerCount.GetAt(aTime) + aDelta);
   }
   void AddListenerImpl(already_AddRefed<MediaStreamListener> aListener);
   void RemoveListenerImpl(MediaStreamListener* aListener);
   void RemoveAllListenersImpl();
   void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled);
+  /**
+   * Returns true when this stream requires the contents of its inputs even if
+   * its own outputs are not being consumed. This is used to signal inputs to
+   * this stream that they are being consumed; when they're not being consumed,
+   * we make some optimizations.
+   */
+  virtual bool IsIntrinsicallyConsumed() const
+  {
+    return !mAudioOutputs.IsEmpty() || !mVideoOutputs.IsEmpty();
+  }
 
   void AddConsumer(MediaInputPort* aPort)
   {
     mConsumers.AppendElement(aPort);
   }
   void RemoveConsumer(MediaInputPort* aPort)
   {
     mConsumers.RemoveElement(aPort);
@@ -759,20 +754,20 @@ public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaInputPort)
 
   /**
    * The FLAG_BLOCK_INPUT and FLAG_BLOCK_OUTPUT flags can be used to control
    * exactly how the blocking statuses of the input and output streams affect
    * each other.
    */
   enum {
-    // When set, blocking on the input stream forces blocking on the output
+    // When set, blocking on the output stream forces blocking on the input
     // stream.
     FLAG_BLOCK_INPUT = 0x01,
-    // When set, blocking on the output stream forces blocking on the input
+    // When set, blocking on the input stream forces blocking on the output
     // stream.
     FLAG_BLOCK_OUTPUT = 0x02
   };
   ~MediaInputPort()
   {
     MOZ_COUNT_DTOR(MediaInputPort);
   }
 
@@ -953,16 +948,21 @@ public:
    * Create a stream that will process audio for an AudioNode.
    * Takes ownership of aEngine.  aSampleRate is the sampling rate used
    * for the stream.  If 0 is passed, the sampling rate of the engine's
    * node will get used.
    */
   AudioNodeStream* CreateAudioNodeStream(AudioNodeEngine* aEngine,
                                          AudioNodeStreamKind aKind,
                                          TrackRate aSampleRate = 0);
+
+  AudioNodeExternalInputStream*
+  CreateAudioNodeExternalInputStream(AudioNodeEngine* aEngine,
+                                     TrackRate aSampleRate = 0);
+
   /**
    * Returns the number of graph updates sent. This can be used to track
    * whether a given update has been processed by the graph thread and reflected
    * in main-thread stream state.
    */
   int64_t GetCurrentGraphUpdateIndex() { return mGraphUpdatesSent; }
   /**
    * Start processing non-realtime for a specific number of ticks.
--- a/content/media/TrackUnionStream.h
+++ b/content/media/TrackUnionStream.h
@@ -115,16 +115,27 @@ public:
     mFilterCallback = aCallback;
   }
 
 protected:
   TrackIDFilterCallback mFilterCallback;
 
   // Only non-ended tracks are allowed to persist in this map.
   struct TrackMapEntry {
+    // mEndOfConsumedInputTicks is the end of the input ticks that we've consumed.
+    // 0 if we haven't consumed any yet.
+    TrackTicks mEndOfConsumedInputTicks;
+    // mEndOfLastInputIntervalInInputStream is the timestamp for the end of the
+    // previous interval which was unblocked for both the input and output
+    // stream, in the input stream's timeline, or -1 if there wasn't one.
+    StreamTime mEndOfLastInputIntervalInInputStream;
+    // mEndOfLastInputIntervalInOutputStream is the timestamp for the end of the
+    // previous interval which was unblocked for both the input and output
+    // stream, in the output stream's timeline, or -1 if there wasn't one.
+    StreamTime mEndOfLastInputIntervalInOutputStream;
     MediaInputPort* mInputPort;
     // We keep track IDs instead of track pointers because
     // tracks can be removed without us being notified (e.g.
     // when a finished track is forgotten.) When we need a Track*,
     // we call StreamBuffer::FindTrack, which will return null if
     // the track has been deleted.
     TrackID mInputTrackID;
     TrackID mOutputTrackID;
@@ -156,16 +167,19 @@ protected:
     segment->AppendNullData(outputStart);
     StreamBuffer::Track* track =
       &mBuffer.AddTrack(id, rate, outputStart, segment.forget());
     LOG(PR_LOG_DEBUG, ("TrackUnionStream %p adding track %d for input stream %p track %d, start ticks %lld",
                        this, id, aPort->GetSource(), aTrack->GetID(),
                        (long long)outputStart));
 
     TrackMapEntry* map = mTrackMap.AppendElement();
+    map->mEndOfConsumedInputTicks = 0;
+    map->mEndOfLastInputIntervalInInputStream = -1;
+    map->mEndOfLastInputIntervalInOutputStream = -1;
     map->mInputPort = aPort;
     map->mInputTrackID = aTrack->GetID();
     map->mOutputTrackID = track->GetID();
     map->mSegment = aTrack->GetSegment()->CreateEmptyClone();
     return mTrackMap.Length() - 1;
   }
   void EndTrack(uint32_t aIndex)
   {
@@ -203,24 +217,22 @@ protected:
       interval.mEnd = std::min(interval.mEnd, aTo);
       if (interval.mStart >= interval.mEnd)
         break;
       next = interval.mEnd;
 
       // Ticks >= startTicks and < endTicks are in the interval
       StreamTime outputEnd = GraphTimeToStreamTime(interval.mEnd);
       TrackTicks startTicks = outputTrack->GetEnd();
-#ifdef DEBUG
       StreamTime outputStart = GraphTimeToStreamTime(interval.mStart);
-#endif
       NS_ASSERTION(startTicks == TimeToTicksRoundUp(rate, outputStart),
                    "Samples missing");
       TrackTicks endTicks = TimeToTicksRoundUp(rate, outputEnd);
       TrackTicks ticks = endTicks - startTicks;
-      // StreamTime inputStart = source->GraphTimeToStreamTime(interval.mStart);
+      StreamTime inputStart = source->GraphTimeToStreamTime(interval.mStart);
       StreamTime inputEnd = source->GraphTimeToStreamTime(interval.mEnd);
       TrackTicks inputTrackEndPoint = TRACK_TICKS_MAX;
 
       if (aInputTrack->IsEnded()) {
         TrackTicks inputEndTicks = aInputTrack->TimeToTicksRoundDown(inputEnd);
         if (aInputTrack->GetEnd() <= inputEndTicks) {
           inputTrackEndPoint = aInputTrack->GetEnd();
           *aOutputTrackFinished = true;
@@ -234,22 +246,76 @@ protected:
             this, (long long)ticks, outputTrack->GetID()));
       } else {
         // Figuring out which samples to use from the input stream is tricky
         // because its start time and our start time may differ by a fraction
         // of a tick. Assuming the input track hasn't ended, we have to ensure
         // that 'ticks' samples are gathered, even though a tick boundary may
         // occur between outputStart and outputEnd but not between inputStart
         // and inputEnd.
-        // We'll take the latest samples we can.
-        TrackTicks inputEndTicks = TimeToTicksRoundUp(rate, inputEnd);
-        TrackTicks inputStartTicks = inputEndTicks - ticks;
-        segment->AppendSlice(*aInputTrack->GetSegment(),
-                             std::min(inputTrackEndPoint, inputStartTicks),
-                             std::min(inputTrackEndPoint, inputEndTicks));
+        // These are the properties we need to ensure:
+        // 1) Exactly 'ticks' ticks of output are produced, i.e.
+        // inputEndTicks - inputStartTicks = ticks.
+        // 2) inputEndTicks <= aInputTrack->GetSegment()->GetDuration().
+        // 3) In any sequence of intervals where neither stream is blocked,
+        // the content of the input track we use is a contiguous sequence of
+        // ticks with no gaps or overlaps.
+        if (map->mEndOfLastInputIntervalInInputStream != inputStart ||
+            map->mEndOfLastInputIntervalInOutputStream != outputStart) {
+          // Start of a new series of intervals where neither stream is blocked.
+          map->mEndOfConsumedInputTicks = TimeToTicksRoundDown(rate, inputStart) - 1;
+        }
+        TrackTicks inputStartTicks = map->mEndOfConsumedInputTicks;
+        TrackTicks inputEndTicks = inputStartTicks + ticks;
+        map->mEndOfConsumedInputTicks = inputEndTicks;
+        map->mEndOfLastInputIntervalInInputStream = inputEnd;
+        map->mEndOfLastInputIntervalInOutputStream = outputEnd;
+        // Now we prove that the above properties hold:
+        // Property #1: trivial by construction.
+        // Property #3: trivial by construction. Between every two
+        // intervals where both streams are not blocked, the above if condition
+        // is false and mEndOfConsumedInputTicks advances exactly to match
+        // the ticks that were consumed.
+        // Property #2:
+        // Let originalOutputStart be the value of outputStart and originalInputStart
+        // be the value of inputStart when the body of the "if" block was last
+        // executed.
+        // Let allTicks be the sum of the values of 'ticks' computed since then.
+        // The interval [originalInputStart/rate, inputEnd/rate) is the
+        // same length as the interval [originalOutputStart/rate, outputEnd/rate),
+        // so the latter interval can have at most one more integer in it. Thus
+        // TimeToTicksRoundUp(rate, outputEnd) - TimeToTicksRoundUp(rate, originalOutputStart)
+        //   <= TimeToTicksRoundDown(rate, inputEnd) - TimeToTicksRoundDown(rate, originalInputStart) + 1
+        // Then
+        // inputEndTicks = TimeToTicksRoundDown(rate, originalInputStart) - 1 + allTicks
+        //   = TimeToTicksRoundDown(rate, originalInputStart) - 1 + TimeToTicksRoundUp(rate, outputEnd) - TimeToTicksRoundUp(rate, originalOutputStart)
+        //   <= TimeToTicksRoundDown(rate, originalInputStart) - 1 + TimeToTicksRoundDown(rate, inputEnd) - TimeToTicksRoundDown(rate, originalInputStart) + 1
+        //   = TimeToTicksRoundDown(rate, inputEnd)
+        //   <= inputEnd/rate
+        // (now using the fact that inputEnd <= track->GetEndTimeRoundDown() for a non-ended track)
+        //   <= TicksToTimeRoundDown(rate, aInputTrack->GetSegment()->GetDuration())/rate
+        //   <= rate*aInputTrack->GetSegment()->GetDuration()/rate
+        //   = aInputTrack->GetSegment()->GetDuration()
+        // as required.
+
+        if (inputStartTicks < 0) {
+          // Data before the start of the track is just null.
+          // We have to add a small amount of delay to ensure that there is
+          // always a sample available if we see an interval that contains a
+          // tick boundary on the output stream's timeline but does not contain
+          // a tick boundary on the input stream's timeline. 1 tick delay is
+          // necessary and sufficient.
+          segment->AppendNullData(-inputStartTicks);
+          inputStartTicks = 0;
+        }
+        if (inputEndTicks > inputStartTicks) {
+          segment->AppendSlice(*aInputTrack->GetSegment(),
+                               std::min(inputTrackEndPoint, inputStartTicks),
+                               std::min(inputTrackEndPoint, inputEndTicks));
+        }
         LOG(PR_LOG_DEBUG+1, ("TrackUnionStream %p appending %lld ticks of input data to track %d",
             this, (long long)(std::min(inputTrackEndPoint, inputEndTicks) - std::min(inputTrackEndPoint, inputStartTicks)),
             outputTrack->GetID()));
       }
       ApplyTrackDisabling(outputTrack->GetID(), segment);
       for (uint32_t j = 0; j < mListeners.Length(); ++j) {
         MediaStreamListener* l = mListeners[j];
         l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
--- a/content/media/dash/DASHDecoder.cpp
+++ b/content/media/dash/DASHDecoder.cpp
@@ -190,25 +190,24 @@ DASHDecoder::ReleaseStateMachine()
     mAudioRepDecoders[i]->ReleaseStateMachine();
   }
   for (uint i = 0; i < mVideoRepDecoders.Length(); i++) {
     mVideoRepDecoders[i]->ReleaseStateMachine();
   }
 }
 
 nsresult
-DASHDecoder::Load(MediaResource* aResource,
-                  nsIStreamListener** aStreamListener,
+DASHDecoder::Load(nsIStreamListener** aStreamListener,
                   MediaDecoder* aCloneDonor)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   mDASHReader = new DASHReader(this);
 
-  nsresult rv = OpenResource(aResource, aStreamListener);
+  nsresult rv = OpenResource(aStreamListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mDecoderStateMachine = CreateStateMachine();
   if (!mDecoderStateMachine) {
     LOG1("Failed to create state machine!");
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
--- a/content/media/dash/DASHDecoder.h
+++ b/content/media/dash/DASHDecoder.h
@@ -52,19 +52,18 @@ public:
   }
 
   // Creates a single state machine for all stream decoders.
   // Called from Load on the main thread only.
   MediaDecoderStateMachine* CreateStateMachine();
 
   // Loads the MPD from the network and subsequently loads the media streams.
   // Called from the main thread only.
-  nsresult Load(MediaResource* aResource,
-                nsIStreamListener** aListener,
-                MediaDecoder* aCloneDonor);
+  virtual nsresult Load(nsIStreamListener** aListener,
+                        MediaDecoder* aCloneDonor) MOZ_OVERRIDE;
 
   // Notifies download of MPD file has ended.
   // Called on the main thread only.
   void NotifyDownloadEnded(nsresult aStatus);
 
   // Notification from |DASHReader| that a seek has occurred in
   // |aSubsegmentIdx|. Passes notification onto subdecoder which downloaded
   // the subsegment already, if download is in the past. Otherwise, it returns.
--- a/content/media/dash/DASHRepDecoder.cpp
+++ b/content/media/dash/DASHRepDecoder.cpp
@@ -68,18 +68,17 @@ DASHRepDecoder::SetMPDRepresentation(Rep
 void
 DASHRepDecoder::SetReader(WebMReader* aReader)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   mReader = aReader;
 }
 
 nsresult
-DASHRepDecoder::Load(MediaResource* aResource,
-                     nsIStreamListener** aListener,
+DASHRepDecoder::Load(nsIStreamListener** aListener,
                      MediaDecoder* aCloneDonor)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   NS_ENSURE_TRUE(mMPDRepresentation, NS_ERROR_NOT_INITIALIZED);
 
   // Get init range and index range from MPD.
   SegmentBase const * segmentBase = mMPDRepresentation->GetSegmentBase();
   NS_ENSURE_TRUE(segmentBase, NS_ERROR_NULL_POINTER);
--- a/content/media/dash/DASHRepDecoder.h
+++ b/content/media/dash/DASHRepDecoder.h
@@ -68,19 +68,18 @@ public:
   // for this decoder's |Representation|. Called on the main thread only.
   void SetResource(MediaResource* aResource);
 
   // Sets the |Representation| object for this decoder. Called on the main
   // thread.
   void SetMPDRepresentation(Representation const * aRep);
 
   // Called from DASHDecoder on main thread; Starts media stream download.
-  nsresult Load(MediaResource* aResource = nullptr,
-                nsIStreamListener** aListener = nullptr,
-                MediaDecoder* aCloneDonor = nullptr);
+  virtual nsresult Load(nsIStreamListener** aListener = nullptr,
+                        MediaDecoder* aCloneDonor = nullptr) MOZ_OVERRIDE;
 
   // Loads the next byte range (or first one on first call). Called on the main
   // thread only.
   void LoadNextByteRange();
 
   // Returns true if the subsegment is already in the media cache.
   bool IsSubsegmentCached(int32_t aSubsegmentIdx);
 
--- a/content/media/moz.build
+++ b/content/media/moz.build
@@ -48,16 +48,17 @@ TEST_DIRS += ['test']
 MODULE = 'content'
 
 EXPORTS += [
     'AbstractMediaDecoder.h',
     'AudioAvailableEventManager.h',
     'AudioChannelFormat.h',
     'AudioEventTimeline.h',
     'AudioNodeEngine.h',
+    'AudioNodeExternalInputStream.h',
     'AudioNodeStream.h',
     'AudioSampleFormat.h',
     'AudioSegment.h',
     'AudioStream.h',
     'BufferMediaResource.h',
     'DOMMediaStream.h',
     'DecoderTraits.h',
     'EncodedBufferCache.h',
@@ -92,16 +93,17 @@ EXPORTS.mozilla.dom += [
     'VideoPlaybackQuality.h',
     'VideoStreamTrack.h',
 ]
 
 CPP_SOURCES += [
     'AudioAvailableEventManager.cpp',
     'AudioChannelFormat.cpp',
     'AudioNodeEngine.cpp',
+    'AudioNodeExternalInputStream.cpp',
     'AudioNodeStream.cpp',
     'AudioSegment.cpp',
     'AudioStream.cpp',
     'AudioStreamTrack.cpp',
     'DOMMediaStream.cpp',
     'DecoderTraits.cpp',
     'EncodedBufferCache.cpp',
     'FileBlockCache.cpp',
--- a/content/media/test/contentDuration1.sjs
+++ b/content/media/test/contentDuration1.sjs
@@ -11,13 +11,15 @@ function handleRequest(request, response
   var split = paths.split("/");
   for(var i = 0; i < split.length; ++i) {
     file.append(split[i]);
   }
   fis.init(file, -1, -1, false);
   bis.setInputStream(fis);
   var bytes = bis.readBytes(bis.available());
   response.setHeader("Content-Duration", "0.233", false);
-  response.setHeader("Content-Length", ""+bytes.length, false);
   response.setHeader("Content-Type", "video/ogg", false);
   response.write(bytes, bytes.length);
+  // Make this request async to prevent a default Content-Length from being provided.
+  response.processAsync();
+  response.finish();
   bis.close();
 }
--- a/content/media/test/contentDuration2.sjs
+++ b/content/media/test/contentDuration2.sjs
@@ -11,13 +11,15 @@ function handleRequest(request, response
   var split = paths.split("/");
   for(var i = 0; i < split.length; ++i) {
     file.append(split[i]);
   }
   fis.init(file, -1, -1, false);
   bis.setInputStream(fis);
   var bytes = bis.readBytes(bis.available());
   response.setHeader("x-amz-meta-content-duration", "0.233", false);
-  response.setHeader("Content-Length", ""+bytes.length, false);
   response.setHeader("Content-Type", "video/ogg", false);
   response.write(bytes, bytes.length);
+  // Make this request async to prevent a default Content-Length from being provided.
+  response.processAsync();
+  response.finish();
   bis.close();
 }
--- a/content/media/test/contentDuration3.sjs
+++ b/content/media/test/contentDuration3.sjs
@@ -11,13 +11,15 @@ function handleRequest(request, response
   var split = paths.split("/");
   for(var i = 0; i < split.length; ++i) {
     file.append(split[i]);
   }
   fis.init(file, -1, -1, false);
   bis.setInputStream(fis);
   var bytes = bis.readBytes(bis.available());
   response.setStatusLine(request.httpVersion, 200, "Content Follows");
-  response.setHeader("Content-Length", ""+bytes.length, false);
   response.setHeader("Content-Type", "video/ogg", false);
   response.write(bytes, bytes.length);
+  // Make this request async to prevent a default Content-Length from being provided.
+  response.processAsync();
+  response.finish();
   bis.close();
 }
--- a/content/media/test/contentDuration4.sjs
+++ b/content/media/test/contentDuration4.sjs
@@ -12,13 +12,15 @@ function handleRequest(request, response
   for(var i = 0; i < split.length; ++i) {
     file.append(split[i]);
   }
   fis.init(file, -1, -1, false);
   bis.setInputStream(fis);
   var bytes = bis.readBytes(bis.available());
   response.setStatusLine(request.httpVersion, 200, "Content Follows");
   response.setHeader("Content-Duration", "-5", false);
-  response.setHeader("Content-Length", ""+bytes.length, false);
   response.setHeader("Content-Type", "video/ogg", false);
   response.write(bytes, bytes.length);
+  // Make this request async to prevent a default Content-Length from being provided.
+  response.processAsync();
+  response.finish();
   bis.close();
 }
--- a/content/media/test/contentDuration5.sjs
+++ b/content/media/test/contentDuration5.sjs
@@ -12,12 +12,15 @@ function handleRequest(request, response
   for(var i = 0; i < split.length; ++i) {
     file.append(split[i]);
   }
   fis.init(file, -1, -1, false);
   bis.setInputStream(fis);
   var bytes = bis.readBytes(bis.available());
   response.setStatusLine(request.httpVersion, 200, "Content Follows");
   response.setHeader("Content-Duration", "-6", false);
-  response.setHeader("Content-Length", ""+bytes.length, false);
   response.setHeader("Content-Type", "video/ogg", false);
   response.write(bytes, bytes.length);
+  // Make this request async to prevent a default Content-Length from being provided.
+  response.processAsync();
+  response.finish();
+  bis.close();
 }
--- a/content/media/test/contentDuration6.sjs
+++ b/content/media/test/contentDuration6.sjs
@@ -12,13 +12,15 @@ function handleRequest(request, response
   for(var i = 0; i < split.length; ++i) {
     file.append(split[i]);
   }
   fis.init(file, -1, -1, false);
   bis.setInputStream(fis);
   var bytes = bis.readBytes(bis.available());
   response.setStatusLine(request.httpVersion, 200, "Content Follows");
   response.setHeader("Content-Duration", "Invalid Float Value", false);
-  response.setHeader("Content-Length", ""+bytes.length, false);
   response.setHeader("Content-Type", "video/ogg", false);
   response.write(bytes, bytes.length);
+  // Make this request async to prevent a default Content-Length from being provided.
+  response.processAsync();
+  response.finish();
   bis.close();
 }
--- a/content/media/test/contentDuration7.sjs
+++ b/content/media/test/contentDuration7.sjs
@@ -11,13 +11,15 @@ function handleRequest(request, response
   var split = paths.split("/");
   for(var i = 0; i < split.length; ++i) {
     file.append(split[i]);
   }
   fis.init(file, -1, -1, false);
   bis.setInputStream(fis);
   var bytes = bis.readBytes(bis.available());
   response.setHeader("X-Content-Duration", "0.233", false);
-  response.setHeader("Content-Length", ""+bytes.length, false);
   response.setHeader("Content-Type", "video/ogg", false);
   response.write(bytes, bytes.length);
+  // Make this request async to prevent a default Content-Length from being provided.
+  response.processAsync();
+  response.finish();
   bis.close();
 }
--- a/content/media/test/noContentLength.sjs
+++ b/content/media/test/noContentLength.sjs
@@ -14,10 +14,13 @@ function handleRequest(request, response
     file.append(split[i]);
   }
   fis.init(file, -1, -1, false);
   bis.setInputStream(fis);
   var bytes = bis.readBytes(bis.available());
   response.setStatusLine(request.httpVersion, 200, "Content Follows");
   response.setHeader("Content-Type", "video/ogg", false);
   response.write(bytes, bytes.length);
+  // Make this request async to prevent a default Content-Length from being provided.
+  response.processAsync();
+  response.finish();
   bis.close();
 }
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -3,24 +3,27 @@
 /* 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 "AudioContext.h"
 #include "nsContentUtils.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/ErrorResult.h"
+#include "mozilla/dom/AnalyserNode.h"
 #include "mozilla/dom/AudioContextBinding.h"
+#include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/OfflineAudioContextBinding.h"
 #include "MediaStreamGraph.h"
-#include "mozilla/dom/AnalyserNode.h"
 #include "AudioDestinationNode.h"
 #include "AudioBufferSourceNode.h"
 #include "AudioBuffer.h"
 #include "GainNode.h"
+#include "MediaElementAudioSourceNode.h"
+#include "MediaStreamAudioSourceNode.h"
 #include "DelayNode.h"
 #include "PannerNode.h"
 #include "AudioListener.h"
 #include "DynamicsCompressorNode.h"
 #include "BiquadFilterNode.h"
 #include "ScriptProcessorNode.h"
 #include "ChannelMergerNode.h"
 #include "ChannelSplitterNode.h"
@@ -248,16 +251,46 @@ AudioContext::CreateScriptProcessor(uint
 
 already_AddRefed<AnalyserNode>
 AudioContext::CreateAnalyser()
 {
   nsRefPtr<AnalyserNode> analyserNode = new AnalyserNode(this);
   return analyserNode.forget();
 }
 
+already_AddRefed<MediaElementAudioSourceNode>
+AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
+                                       ErrorResult& aRv)
+{
+  if (mIsOffline) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+  nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+  nsRefPtr<MediaElementAudioSourceNode> mediaElementAudioSourceNode =
+    new MediaElementAudioSourceNode(this, stream);
+  return mediaElementAudioSourceNode.forget();
+}
+
+already_AddRefed<MediaStreamAudioSourceNode>
+AudioContext::CreateMediaStreamSource(DOMMediaStream& aMediaStream,
+                                      ErrorResult& aRv)
+{
+  if (mIsOffline) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+  nsRefPtr<MediaStreamAudioSourceNode> mediaStreamAudioSourceNode =
+    new MediaStreamAudioSourceNode(this, &aMediaStream);
+  return mediaStreamAudioSourceNode.forget();
+}
+
 already_AddRefed<GainNode>
 AudioContext::CreateGain()
 {
   nsRefPtr<GainNode> gainNode = new GainNode(this);
   return gainNode.forget();
 }
 
 already_AddRefed<WaveShaperNode>
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -47,17 +47,20 @@ class AudioListener;
 class BiquadFilterNode;
 class ChannelMergerNode;
 class ChannelSplitterNode;
 class ConvolverNode;
 class DelayNode;
 class DynamicsCompressorNode;
 class GainNode;
 class GlobalObject;
+class HTMLMediaElement;
+class MediaElementAudioSourceNode;
 class MediaStreamAudioDestinationNode;
+class MediaStreamAudioSourceNode;
 class OfflineRenderSuccessCallback;
 class PannerNode;
 class ScriptProcessorNode;
 class WaveShaperNode;
 class PeriodicWave;
 
 class AudioContext MOZ_FINAL : public nsDOMEventTargetHelper,
                                public EnableWebAudioCheck
@@ -156,16 +159,21 @@ public:
   CreateWaveShaper();
 
   already_AddRefed<GainNode>
   CreateGainNode()
   {
     return CreateGain();
   }
 
+  already_AddRefed<MediaElementAudioSourceNode>
+  CreateMediaElementSource(HTMLMediaElement& aMediaElement, ErrorResult& aRv);
+  already_AddRefed<MediaStreamAudioSourceNode>
+  CreateMediaStreamSource(DOMMediaStream& aMediaStream, ErrorResult& aRv);
+
   already_AddRefed<DelayNode>
   CreateDelay(double aMaxDelayTime, ErrorResult& aRv);
 
   already_AddRefed<DelayNode>
   CreateDelayNode(double aMaxDelayTime, ErrorResult& aRv)
   {
     return CreateDelay(aMaxDelayTime, aRv);
   }
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/MediaElementAudioSourceNode.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "MediaElementAudioSourceNode.h"
+#include "mozilla/dom/MediaElementAudioSourceNodeBinding.h"
+#include "mozilla/dom/HTMLMediaElement.h"
+
+namespace mozilla {
+namespace dom {
+
+MediaElementAudioSourceNode::MediaElementAudioSourceNode(AudioContext* aContext,
+                                                         DOMMediaStream* aStream)
+  : MediaStreamAudioSourceNode(aContext, aStream)
+{
+}
+
+JSObject*
+MediaElementAudioSourceNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return MediaElementAudioSourceNodeBinding::Wrap(aCx, aScope, this);
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/MediaElementAudioSourceNode.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 MediaElementAudioSourceNode_h_
+#define MediaElementAudioSourceNode_h_
+
+#include "MediaStreamAudioSourceNode.h"
+
+namespace mozilla {
+namespace dom {
+
+class HTMLMediaElement;
+
+class MediaElementAudioSourceNode : public MediaStreamAudioSourceNode
+{
+public:
+  MediaElementAudioSourceNode(AudioContext* aContext,
+                              DOMMediaStream* aStream);
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+};
+
+}
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/MediaStreamAudioSourceNode.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "MediaStreamAudioSourceNode.h"
+#include "mozilla/dom/MediaStreamAudioSourceNodeBinding.h"
+#include "AudioNodeEngine.h"
+#include "AudioNodeExternalInputStream.h"
+#include "DOMMediaStream.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamAudioSourceNode)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaStreamAudioSourceNode)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStream)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamAudioSourceNode, AudioNode)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStream)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaStreamAudioSourceNode)
+NS_INTERFACE_MAP_END_INHERITING(AudioNode)
+
+NS_IMPL_ADDREF_INHERITED(MediaStreamAudioSourceNode, AudioNode)
+NS_IMPL_RELEASE_INHERITED(MediaStreamAudioSourceNode, AudioNode)
+
+MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* aContext,
+                                                       DOMMediaStream* aMediaStream)
+  : AudioNode(aContext,
+              2,
+              ChannelCountMode::Max,
+              ChannelInterpretation::Speakers),
+    mInputStream(aMediaStream)
+{
+  AudioNodeEngine* engine = new AudioNodeEngine(this);
+  mStream = aContext->Graph()->CreateAudioNodeExternalInputStream(engine);
+  ProcessedMediaStream* outputStream = static_cast<ProcessedMediaStream*>(mStream.get());
+  mInputPort = outputStream->AllocateInputPort(aMediaStream->GetStream(),
+                                               MediaInputPort::FLAG_BLOCK_INPUT);
+  mInputStream->AddConsumerToKeepAlive(this);
+}
+
+MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode()
+{
+}
+
+void
+MediaStreamAudioSourceNode::DestroyMediaStream()
+{
+  if (mInputPort) {
+    mInputPort->Destroy();
+    mInputPort = nullptr;
+  }
+  AudioNode::DestroyMediaStream();
+}
+
+JSObject*
+MediaStreamAudioSourceNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return MediaStreamAudioSourceNodeBinding::Wrap(aCx, aScope, this);
+}
+
+}
+}
+
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/MediaStreamAudioSourceNode.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 MediaStreamAudioSourceNode_h_
+#define MediaStreamAudioSourceNode_h_
+
+#include "AudioNode.h"
+
+namespace mozilla {
+
+class DOMMediaStream;
+
+namespace dom {
+
+class MediaStreamAudioSourceNode : public AudioNode
+{
+public:
+  MediaStreamAudioSourceNode(AudioContext* aContext, DOMMediaStream* aMediaStream);
+  // Define constructor out-of-line so we can forward-declare DOMMediaStream
+  virtual ~MediaStreamAudioSourceNode();
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamAudioSourceNode, AudioNode)
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  virtual void DestroyMediaStream() MOZ_OVERRIDE;
+
+  virtual uint16_t NumberOfInputs() const MOZ_OVERRIDE { return 0; }
+
+private:
+  nsRefPtr<MediaInputPort> mInputPort;
+  nsRefPtr<DOMMediaStream> mInputStream;
+};
+
+}
+}
+
+#endif
--- a/content/media/webaudio/WaveShaperNode.cpp
+++ b/content/media/webaudio/WaveShaperNode.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WaveShaperNode.h"
 #include "mozilla/dom/WaveShaperNodeBinding.h"
 #include "AudioNode.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "mozilla/PodOperations.h"
+#include "speex/speex_resampler.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(WaveShaperNode)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WaveShaperNode, AudioNode)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -31,29 +32,181 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Wav
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WaveShaperNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(WaveShaperNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(WaveShaperNode, AudioNode)
 
+static uint32_t ValueOf(OverSampleType aType)
+{
+  switch (aType) {
+  case OverSampleType::None: return 1;
+  case OverSampleType::_2x:  return 2;
+  case OverSampleType::_4x:  return 4;
+  default:
+    NS_NOTREACHED("We should never reach here");
+    return 1;
+  }
+}
+
+class Resampler
+{
+public:
+  Resampler()
+    : mType(OverSampleType::None)
+    , mUpSampler(nullptr)
+    , mDownSampler(nullptr)
+    , mChannels(0)
+    , mSampleRate(0)
+  {
+  }
+
+  ~Resampler()
+  {
+    Destroy();
+  }
+
+  void Reset(uint32_t aChannels, TrackRate aSampleRate, OverSampleType aType)
+  {
+    if (aChannels == mChannels &&
+        aSampleRate == mSampleRate &&
+        aType == mType) {
+      return;
+    }
+
+    mChannels = aChannels;
+    mSampleRate = aSampleRate;
+    mType = aType;
+
+    Destroy();
+
+    if (aType == OverSampleType::None) {
+      mBuffer.Clear();
+      return;
+    }
+
+    mUpSampler = speex_resampler_init(aChannels,
+                                      aSampleRate,
+                                      aSampleRate * ValueOf(aType),
+                                      SPEEX_RESAMPLER_QUALITY_DEFAULT,
+                                      nullptr);
+    mDownSampler = speex_resampler_init(aChannels,
+                                        aSampleRate * ValueOf(aType),
+                                        aSampleRate,
+                                        SPEEX_RESAMPLER_QUALITY_DEFAULT,
+                                        nullptr);
+    mBuffer.SetLength(WEBAUDIO_BLOCK_SIZE*ValueOf(aType));
+  }
+
+  float* UpSample(uint32_t aChannel, const float* aInputData, uint32_t aBlocks)
+  {
+    uint32_t inSamples = WEBAUDIO_BLOCK_SIZE;
+    uint32_t outSamples = WEBAUDIO_BLOCK_SIZE*aBlocks;
+    float* outputData = mBuffer.Elements();
+
+    MOZ_ASSERT(mBuffer.Length() == outSamples);
+
+    speex_resampler_process_float(mUpSampler, aChannel,
+                                  aInputData, &inSamples,
+                                  outputData, &outSamples);
+
+    MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE && outSamples == WEBAUDIO_BLOCK_SIZE*aBlocks);
+
+    return outputData;
+  }
+
+  void DownSample(uint32_t aChannel, float* aOutputData, uint32_t aBlocks)
+  {
+    uint32_t inSamples = WEBAUDIO_BLOCK_SIZE*aBlocks;
+    uint32_t outSamples = WEBAUDIO_BLOCK_SIZE;
+    const float* inputData = mBuffer.Elements();
+
+    MOZ_ASSERT(mBuffer.Length() == inSamples);
+
+    speex_resampler_process_float(mDownSampler, aChannel,
+                                  inputData, &inSamples,
+                                  aOutputData, &outSamples);
+
+    MOZ_ASSERT(inSamples == WEBAUDIO_BLOCK_SIZE*aBlocks && outSamples == WEBAUDIO_BLOCK_SIZE);
+  }
+
+private:
+  void Destroy()
+  {
+    if (mUpSampler) {
+      speex_resampler_destroy(mUpSampler);
+      mUpSampler = nullptr;
+    }
+    if (mDownSampler) {
+      speex_resampler_destroy(mDownSampler);
+      mDownSampler = nullptr;
+    }
+  }
+
+private:
+  OverSampleType mType;
+  SpeexResamplerState* mUpSampler;
+  SpeexResamplerState* mDownSampler;
+  uint32_t mChannels;
+  TrackRate mSampleRate;
+  nsTArray<float> mBuffer;
+};
+
 class WaveShaperNodeEngine : public AudioNodeEngine
 {
 public:
   explicit WaveShaperNodeEngine(AudioNode* aNode)
     : AudioNodeEngine(aNode)
+    , mType(OverSampleType::None)
   {
   }
 
+  enum Parameteres {
+    TYPE
+  };
+
   virtual void SetRawArrayData(nsTArray<float>& aCurve) MOZ_OVERRIDE
   {
     mCurve.SwapElements(aCurve);
   }
 
+  virtual void SetInt32Parameter(uint32_t aIndex, int32_t aValue) MOZ_OVERRIDE
+  {
+    switch (aIndex) {
+    case TYPE:
+      mType = static_cast<OverSampleType>(aValue);
+      break;
+    default:
+      NS_ERROR("Bad WaveShaperNode Int32Parameter");
+    }
+  }
+
+  template <uint32_t blocks>
+  void ProcessCurve(const float* aInputBuffer, float* aOutputBuffer)
+  {
+    for (uint32_t j = 0; j < WEBAUDIO_BLOCK_SIZE*blocks; ++j) {
+      // Index into the curve array based on the amplitude of the
+      // incoming signal by clamping the amplitude to [-1, 1] and
+      // performing a linear interpolation of the neighbor values.
+      float index = std::max(0.0f, std::min(float(mCurve.Length() - 1),
+                                            mCurve.Length() * (aInputBuffer[j] + 1) / 2));
+      uint32_t indexLower = uint32_t(index);
+      uint32_t indexHigher = uint32_t(index + 1.0f);
+      if (indexHigher == mCurve.Length()) {
+        aOutputBuffer[j] = mCurve[indexLower];
+      } else {
+        float interpolationFactor = index - indexLower;
+        aOutputBuffer[j] = (1.0f - interpolationFactor) * mCurve[indexLower] +
+                                   interpolationFactor  * mCurve[indexHigher];
+      }
+    }
+  }
+
   virtual void ProduceAudioBlock(AudioNodeStream* aStream,
                                  const AudioChunk& aInput,
                                  AudioChunk* aOutput,
                                  bool* aFinished)
   {
     uint32_t channelCount = aInput.mChannelData.Length();
     if (!mCurve.Length() || !channelCount) {
       // Optimize the case where we don't have a curve buffer,
@@ -61,45 +214,54 @@ public:
       *aOutput = aInput;
       return;
     }
 
     AllocateAudioBlock(channelCount, aOutput);
     for (uint32_t i = 0; i < channelCount; ++i) {
       const float* inputBuffer = static_cast<const float*>(aInput.mChannelData[i]);
       float* outputBuffer = const_cast<float*> (static_cast<const float*>(aOutput->mChannelData[i]));
-      for (uint32_t j = 0; j < WEBAUDIO_BLOCK_SIZE; ++j) {
-        // Index into the curve array based on the amplitude of the
-        // incoming signal by clamping the amplitude to [-1, 1] and
-        // performing a linear interpolation of the neighbor values.
-        float index = std::max(0.0f, std::min(float(mCurve.Length() - 1),
-                                              mCurve.Length() * (inputBuffer[j] + 1) / 2));
-        uint32_t indexLower = uint32_t(index);
-        uint32_t indexHigher = uint32_t(index + 1.0f);
-        if (indexHigher == mCurve.Length()) {
-          outputBuffer[j] = mCurve[indexLower];
-        } else {
-          float interpolationFactor = index - indexLower;
-          outputBuffer[j] = (1.0f - interpolationFactor) * mCurve[indexLower] +
-                                    interpolationFactor  * mCurve[indexHigher];
-        }
+      float* sampleBuffer;
+
+      switch (mType) {
+      case OverSampleType::None:
+        mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::None);
+        ProcessCurve<1>(inputBuffer, outputBuffer);
+        break;
+      case OverSampleType::_2x:
+        mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_2x);
+        sampleBuffer = mResampler.UpSample(i, inputBuffer, 2);
+        ProcessCurve<2>(sampleBuffer, sampleBuffer);
+        mResampler.DownSample(i, outputBuffer, 2);
+        break;
+      case OverSampleType::_4x:
+        mResampler.Reset(channelCount, aStream->SampleRate(), OverSampleType::_4x);
+        sampleBuffer = mResampler.UpSample(i, inputBuffer, 4);
+        ProcessCurve<4>(sampleBuffer, sampleBuffer);
+        mResampler.DownSample(i, outputBuffer, 4);
+        break;
+      default:
+        NS_NOTREACHED("We should never reach here");
       }
     }
   }
 
 private:
   nsTArray<float> mCurve;
+  OverSampleType mType;
+  Resampler mResampler;
 };
 
 WaveShaperNode::WaveShaperNode(AudioContext* aContext)
   : AudioNode(aContext,
               2,
               ChannelCountMode::Max,
               ChannelInterpretation::Speakers)
   , mCurve(nullptr)
+  , mType(OverSampleType::None)
 {
   NS_HOLD_JS_OBJECTS(this, WaveShaperNode);
 
   WaveShaperNodeEngine* engine = new WaveShaperNodeEngine(this);
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::INTERNAL_STREAM);
 }
 
 WaveShaperNode::~WaveShaperNode()
@@ -133,10 +295,17 @@ WaveShaperNode::SetCurve(const Nullable<
     mCurve = nullptr;
   }
 
   AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
   MOZ_ASSERT(ns, "Why don't we have a stream here?");
   ns->SetRawArrayData(curve);
 }
 
+void
+WaveShaperNode::SetOversample(OverSampleType aType)
+{
+  mType = aType;
+  SendInt32ParameterToStream(WaveShaperNodeEngine::TYPE, static_cast<int32_t>(aType));
+}
+
 }
 }
--- a/content/media/webaudio/WaveShaperNode.h
+++ b/content/media/webaudio/WaveShaperNode.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef WaveShaperNode_h_
 #define WaveShaperNode_h_
 
 #include "AudioNode.h"
 #include "AudioParam.h"
+#include "mozilla/dom/WaveShaperNodeBinding.h"
 
 namespace mozilla {
 namespace dom {
 
 class AudioContext;
 
 class WaveShaperNode : public AudioNode
 {
@@ -28,19 +29,26 @@ public:
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   JSObject* GetCurve(JSContext* aCx) const
   {
     return mCurve;
   }
   void SetCurve(const Nullable<Float32Array>& aData);
 
+  OverSampleType Oversample() const
+  {
+    return mType;
+  }
+  void SetOversample(OverSampleType aType);
+
 private:
   void ClearCurve();
 
 private:
   JS::Heap<JSObject*> mCurve;
+  OverSampleType mType;
 };
 
 }
 }
 
 #endif
--- a/content/media/webaudio/moz.build
+++ b/content/media/webaudio/moz.build
@@ -34,17 +34,19 @@ EXPORTS.mozilla.dom += [
     'BiquadFilterNode.h',
     'ChannelMergerNode.h',
     'ChannelSplitterNode.h',
     'ConvolverNode.h',
     'DelayNode.h',
     'DynamicsCompressorNode.h',
     'EnableWebAudioCheck.h',
     'GainNode.h',
+    'MediaElementAudioSourceNode.h',
     'MediaStreamAudioDestinationNode.h',
+    'MediaStreamAudioSourceNode.h',
     'OfflineAudioCompletionEvent.h',
     'PannerNode.h',
     'PeriodicWave.h',
     'ScriptProcessorNode.h',
     'WaveShaperNode.h',
 ]
 
 CPP_SOURCES += [
@@ -61,17 +63,19 @@ CPP_SOURCES += [
     'ChannelMergerNode.cpp',
     'ChannelSplitterNode.cpp',
     'ConvolverNode.cpp',
     'DelayNode.cpp',
     'DynamicsCompressorNode.cpp',
     'EnableWebAudioCheck.cpp',
     'GainNode.cpp',
     'MediaBufferDecoder.cpp',
+    'MediaElementAudioSourceNode.cpp',
     'MediaStreamAudioDestinationNode.cpp',
+    'MediaStreamAudioSourceNode.cpp',
     'OfflineAudioCompletionEvent.cpp',
     'PannerNode.cpp',
     'PeriodicWave.cpp',
     'ScriptProcessorNode.cpp',
     'ThreeDPoint.cpp',
     'WaveShaperNode.cpp',
     'WebAudioUtils.cpp',
 ]
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -62,17 +62,21 @@ MOCHITEST_FILES := \
   test_delayNode.html \
   test_delayNodeSmallMaxDelay.html \
   test_delayNodeWithGain.html \
   test_dynamicsCompressorNode.html \
   test_gainNode.html \
   test_gainNodeInLoop.html \
   test_maxChannelCount.html \
   test_mediaDecoding.html \
+  test_mediaElementAudioSourceNode.html \
   test_mediaStreamAudioDestinationNode.html \
+  test_mediaStreamAudioSourceNode.html \
+  test_mediaStreamAudioSourceNodeCrossOrigin.html \
+  test_mediaStreamAudioSourceNodeResampling.html \
   test_mixingRules.html \
   test_nodeToParamConnection.html \
   test_OfflineAudioContext.html \
   test_offlineDestinationChannelCountLess.html \
   test_offlineDestinationChannelCountMore.html \
   test_pannerNode.html \
   test_pannerNode_equalPower.html \
   test_periodicWave.html \
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_mediaElementAudioSourceNode.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset="utf-8">
+<head>
+  <title>Test MediaElementAudioSourceNode</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var audio = new Audio("small-shot.ogg");
+var context = new AudioContext();
+var node = context.createMediaElementSource(audio);
+var sp = context.createScriptProcessor(2048, 1);
+node.connect(sp);
+var expectedMinNonzeroSampleCount;
+var expectedMaxNonzeroSampleCount;
+var nonzeroSampleCount = 0;
+var complete = false;
+var iterationCount = 0;
+
+// This test ensures we receive at least expectedSampleCount nonzero samples
+function processSamples(e) {
+  if (complete) {
+    return;
+  }
+
+  if (iterationCount == 0) {
+    // Don't start playing the audio until the AudioContext stuff is connected
+    // and running.
+    audio.play();
+  }
+  ++iterationCount;
+
+  var buf = e.inputBuffer.getChannelData(0);
+  var nonzeroSamplesThisBuffer = 0;
+  for (var i = 0; i < buf.length; ++i) {
+    if (buf[i] != 0) {
+      ++nonzeroSamplesThisBuffer;
+    }
+  }
+  nonzeroSampleCount += nonzeroSamplesThisBuffer;
+  is(e.inputBuffer.numberOfChannels, 1,
+     "Checking data channel count (nonzeroSamplesThisBuffer=" +
+     nonzeroSamplesThisBuffer + ")");
+  ok(nonzeroSampleCount <= expectedMaxNonzeroSampleCount,
+     "Too many nonzero samples (got " + nonzeroSampleCount + ", expected max " + expectedMaxNonzeroSampleCount + ")");
+  if (nonzeroSampleCount >= expectedMinNonzeroSampleCount &&
+      nonzeroSamplesThisBuffer == 0) {
+    ok(true,
+     "Check received enough nonzero samples (got " + nonzeroSampleCount + ", expected min " + expectedMinNonzeroSampleCount + ")");
+    SimpleTest.finish();
+    complete = true;
+  }
+}
+
+audio.oncanplaythrough = function() {
+  // Use a fuzz factor of 100 to account for samples that just happen to be zero
+  expectedMinNonzeroSampleCount = Math.floor(audio.duration*context.sampleRate) - 100;
+  expectedMaxNonzeroSampleCount = Math.floor(audio.duration*context.sampleRate) + 500;
+  sp.onaudioprocess = processSamples;
+};
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_mediaStreamAudioSourceNode.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset="utf-8">
+<head>
+  <title>Test MediaStreamAudioSourceNode processing is correct</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="webaudio.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function createBuffer(context, delay) {
+  var buffer = context.createBuffer(2, 2048, context.sampleRate);
+  for (var i = 0; i < 2048 - delay; ++i) {
+    buffer.getChannelData(0)[i + delay] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+    buffer.getChannelData(1)[i + delay] = buffer.getChannelData(0)[i + delay];
+  }
+  return buffer;
+}
+
+var gTest = {
+  length: 2048,
+  skipOfflineContextTests: true,
+  createGraph: function(context) {
+    var sourceGraph = new AudioContext();
+    var source = sourceGraph.createBufferSource();
+    source.buffer = createBuffer(context, 0);
+    var dest = sourceGraph.createMediaStreamDestination();
+    source.connect(dest);
+    source.start(0);
+
+    var mediaStreamSource = context.createMediaStreamSource(dest.stream);
+    return mediaStreamSource;
+  },
+  createExpectedBuffers: function(context) {
+    return createBuffer(context, 1);
+  },
+};
+
+runTest();
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_mediaStreamAudioSourceNodeCrossOrigin.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset="utf-8">
+<head>
+  <title>Test MediaStreamAudioSourceNode doesn't get data from cross-origin media resources</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var audio = new Audio("http://example.org:80/tests/content/media/webaudio/test/small-shot.ogg");
+var context = new AudioContext();
+var node = context.createMediaStreamSource(audio.mozCaptureStreamUntilEnded());
+var sp = context.createScriptProcessor(2048, 1);
+node.connect(sp);
+var nonzeroSampleCount = 0;
+var complete = false;
+var iterationCount = 0;
+
+// This test ensures we receive at least expectedSampleCount nonzero samples
+function processSamples(e) {
+  if (complete) {
+    return;
+  }
+
+  if (iterationCount == 0) {
+    // Don't start playing the audio until the AudioContext stuff is connected
+    // and running.
+    audio.play();
+  }
+  ++iterationCount;
+
+  var buf = e.inputBuffer.getChannelData(0);
+  var nonzeroSamplesThisBuffer = 0;
+  for (var i = 0; i < buf.length; ++i) {
+    if (buf[i] != 0) {
+      ++nonzeroSamplesThisBuffer;
+    }
+  }
+  is(nonzeroSamplesThisBuffer, 0,
+     "Checking all samples are zero");
+  if (iterationCount >= 20) {
+    SimpleTest.finish();
+    complete = true;
+  }
+}
+
+audio.oncanplaythrough = function() {
+  sp.onaudioprocess = processSamples;
+};
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_mediaStreamAudioSourceNodeResampling.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset="utf-8">
+<head>
+  <title>Test MediaStreamAudioSourceNode processing is correct</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var audio = new Audio("small-shot.ogg");
+var context = new AudioContext();
+var node = context.createMediaStreamSource(audio.mozCaptureStreamUntilEnded());
+var sp = context.createScriptProcessor(2048, 1);
+node.connect(sp);
+var expectedMinNonzeroSampleCount;
+var expectedMaxNonzeroSampleCount;
+var nonzeroSampleCount = 0;
+var complete = false;
+var iterationCount = 0;
+
+// This test ensures we receive at least expectedSampleCount nonzero samples
+function processSamples(e) {
+  if (complete) {
+    return;
+  }
+
+  if (iterationCount == 0) {
+    // Don't start playing the audio until the AudioContext stuff is connected
+    // and running.
+    audio.play();
+  }
+  ++iterationCount;
+
+  var buf = e.inputBuffer.getChannelData(0);
+  var nonzeroSamplesThisBuffer = 0;
+  for (var i = 0; i < buf.length; ++i) {
+    if (buf[i] != 0) {
+      ++nonzeroSamplesThisBuffer;
+    }
+  }
+  nonzeroSampleCount += nonzeroSamplesThisBuffer;
+  is(e.inputBuffer.numberOfChannels, 1,
+     "Checking data channel count (nonzeroSamplesThisBuffer=" +
+     nonzeroSamplesThisBuffer + ")");
+  ok(nonzeroSampleCount <= expectedMaxNonzeroSampleCount,
+     "Too many nonzero samples (got " + nonzeroSampleCount + ", expected max " + expectedMaxNonzeroSampleCount + ")");
+  if (nonzeroSampleCount >= expectedMinNonzeroSampleCount &&
+      nonzeroSamplesThisBuffer == 0) {
+    ok(true,
+     "Check received enough nonzero samples (got " + nonzeroSampleCount + ", expected min " + expectedMinNonzeroSampleCount + ")");
+    SimpleTest.finish();
+    complete = true;
+  }
+}
+
+audio.oncanplaythrough = function() {
+  // Use a fuzz factor of 100 to account for samples that just happen to be zero
+  expectedMinNonzeroSampleCount = Math.floor(audio.duration*context.sampleRate) - 100;
+  expectedMaxNonzeroSampleCount = Math.floor(audio.duration*context.sampleRate) + 500;
+  sp.onaudioprocess = processSamples;
+};
+</script>
+</pre>
+</body>
+</html>
--- a/content/media/webaudio/test/webaudio.js
+++ b/content/media/webaudio/test/webaudio.js
@@ -51,17 +51,17 @@ function compareBuffers(buf1, buf2,
       if (firstBadIndex == -1) {
         firstBadIndex = i;
       }
     }
   };
 
   is(difference, 0, "Found " + difference + " different samples, maxDifference: " +
      maxDifference + ", first bad index: " + firstBadIndex +
-     " with source offset " + sourceOffset + " and desitnation offset " +
+     " with source offset " + sourceOffset + " and destination offset " +
      destOffset);
 }
 
 function getEmptyBuffer(context, length) {
   return context.createBuffer(gTest.numberOfChannels, length, context.sampleRate);
 }
 
 /**
@@ -82,25 +82,27 @@ function getEmptyBuffer(context, length)
  *                     or createGraph must be provided.
  * + createExpectedBuffers: optional method which takes a context object and
  *                          returns either one expected buffer or an array of
  *                          them, designating what is expected to be observed
  *                          in the output.  If omitted, the output is expected
  *                          to be silence.  The sum of the length of the expected
  *                          buffers should be equal to gTest.length.  This
  *                          function is guaranteed to be called before createGraph.
+ * + skipOfflineContextTests: optional. when true, skips running tests on an offline
+ *                            context by circumventing testOnOfflineContext.
  */
 function runTest()
 {
   function done() {
     SimpleTest.finish();
   }
 
   SimpleTest.waitForExplicitFinish();
-  addLoadEvent(function() {
+  function runTestFunction () {
     if (!gTest.numberOfChannels) {
       gTest.numberOfChannels = 2; // default
     }
 
     var testLength;
 
     function runTestOnContext(context, callback, testOutput) {
       if (!gTest.createExpectedBuffers) {
@@ -172,19 +174,30 @@ function runTest()
                              true);
             }
             samplesSeen += expectedBuffer.length;
           }
           callback();
         };
         context.startRendering();
       }
+
       var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, sampleRate);
       runTestOnContext(context, callback, testOutput);
     }
 
     testOnNormalContext(function() {
-      testOnOfflineContext(function() {
-        testOnOfflineContext(done, 44100);
-      }, 48000);
+      if (!gTest.skipOfflineContextTests) {
+        testOnOfflineContext(function() {
+          testOnOfflineContext(done, 44100);
+        }, 48000);
+      } else {
+        done();
+      }
     });
-  });
+  };
+
+  if (document.readyState !== 'complete') {
+    addLoadEvent(runTestFunction);
+  } else {
+    runTestFunction();
+  }
 }
--- a/content/media/webspeech/recognition/SpeechStreamListener.cpp
+++ b/content/media/webspeech/recognition/SpeechStreamListener.cpp
@@ -35,45 +35,56 @@ SpeechStreamListener::NotifyQueuedTrackC
                                                uint32_t aTrackEvents,
                                                const MediaSegment& aQueuedMedia)
 {
   AudioSegment* audio = const_cast<AudioSegment*>(
     static_cast<const AudioSegment*>(&aQueuedMedia));
 
   AudioSegment::ChunkIterator iterator(*audio);
   while (!iterator.IsEnded()) {
-    AudioSampleFormat format = iterator->mBufferFormat;
-
-    MOZ_ASSERT(format == AUDIO_FORMAT_S16 || format == AUDIO_FORMAT_FLOAT32);
+    // Skip over-large chunks so we don't crash!
+    if (iterator->GetDuration() > INT_MAX) {
+      continue;
+    }
+    int duration = int(iterator->GetDuration());
 
-    if (format == AUDIO_FORMAT_S16) {
-      ConvertAndDispatchAudioChunk<int16_t>(*iterator);
-    } else if (format == AUDIO_FORMAT_FLOAT32) {
-      ConvertAndDispatchAudioChunk<float>(*iterator);
+    if (iterator->IsNull()) {
+      nsTArray<int16_t> nullData;
+      PodZero(nullData.AppendElements(duration), duration);
+      ConvertAndDispatchAudioChunk(duration, iterator->mVolume, nullData.Elements());
+    } else {
+      AudioSampleFormat format = iterator->mBufferFormat;
+
+      MOZ_ASSERT(format == AUDIO_FORMAT_S16 || format == AUDIO_FORMAT_FLOAT32);
+
+      if (format == AUDIO_FORMAT_S16) {
+        ConvertAndDispatchAudioChunk(duration, iterator->mVolume,
+                                     static_cast<const int16_t*>(iterator->mChannelData[0]));
+      } else if (format == AUDIO_FORMAT_FLOAT32) {
+        ConvertAndDispatchAudioChunk(duration, iterator->mVolume,
+                                     static_cast<const float*>(iterator->mChannelData[0]));
+      }
     }
 
     iterator.Next();
   }
 }
 
 template<typename SampleFormatType> void
-SpeechStreamListener::ConvertAndDispatchAudioChunk(AudioChunk& aChunk)
+SpeechStreamListener::ConvertAndDispatchAudioChunk(int aDuration, float aVolume,
+                                                   SampleFormatType* aData)
 {
-  nsRefPtr<SharedBuffer> samples(SharedBuffer::Create(aChunk.mDuration *
+  nsRefPtr<SharedBuffer> samples(SharedBuffer::Create(aDuration *
                                                       1 * // channel
                                                       sizeof(int16_t)));
 
-  const SampleFormatType* from =
-    static_cast<const SampleFormatType*>(aChunk.mChannelData[0]);
+  int16_t* to = static_cast<int16_t*>(samples->Data());
+  ConvertAudioSamplesWithScale(aData, to, aDuration, aVolume);
 
-  int16_t* to = static_cast<int16_t*>(samples->Data());
-  ConvertAudioSamplesWithScale(from, to, aChunk.mDuration, aChunk.mVolume);
-
-  mRecognition->FeedAudioData(samples.forget(), aChunk.mDuration, this);
-  return;
+  mRecognition->FeedAudioData(samples.forget(), aDuration, this);
 }
 
 void
 SpeechStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
 {
   // TODO dispatch SpeechEnd event so services can be informed
 }
 
--- a/content/media/webspeech/recognition/SpeechStreamListener.h
+++ b/content/media/webspeech/recognition/SpeechStreamListener.h
@@ -27,14 +27,15 @@ public:
                                 TrackRate aTrackRate,
                                 TrackTicks aTrackOffset,
                                 uint32_t aTrackEvents,
                                 const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
 
   void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
 
 private:
-  template<typename SampleFormatType> void ConvertAndDispatchAudioChunk(AudioChunk& aChunk);
+  template<typename SampleFormatType>
+  void ConvertAndDispatchAudioChunk(int aDuration, float aVolume, SampleFormatType* aData);
   nsRefPtr<SpeechRecognition> mRecognition;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/content/media/webspeech/synth/ipc/test/test_ipc.html
+++ b/content/media/webspeech/synth/ipc/test/test_ipc.html
@@ -176,16 +176,16 @@
       SpecialPowers.addPermission("browser", true, document);
       SpecialPowers.pushPrefEnv({
         "set": [
           // TODO: remove this as part of bug 820712
           ["network.disable.ipc.security", true],
 
           ["dom.ipc.browser_frames.oop_by_default", true],
           ["dom.mozBrowserFramesEnabled", true],
-          ["browser.pageThumbs.enabled", false]
+          ["browser.pagethumbnails.capturing_disabled", false]
         ]
       }, runTests);
     });
 
   </script>
 </body>
 </html>
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -1203,17 +1203,17 @@ nsBindingManager::HandleChildInsertion(n
 nsIContent*
 nsBindingManager::FindNestedInsertionPoint(nsIContent* aContainer,
                                            nsIContent* aChild)
 {
   NS_PRECONDITION(aChild->GetParent() == aContainer,
                   "Wrong container");
 
   nsIContent* parent = aContainer;
-  if (aContainer->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+  if (aContainer->IsActiveChildrenElement()) {
     if (static_cast<XBLChildrenElement*>(aContainer)->
           HasInsertedChildren()) {
       return nullptr;
     }
     parent = aContainer->GetParent();
   }
 
   while (parent) {
@@ -1239,17 +1239,17 @@ nsBindingManager::FindNestedInsertionPoi
 
 nsIContent*
 nsBindingManager::FindNestedSingleInsertionPoint(nsIContent* aContainer,
                                                  bool* aMulti)
 {
   *aMulti = false;
 
   nsIContent* parent = aContainer;
-  if (aContainer->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+  if (aContainer->IsActiveChildrenElement()) {
     if (static_cast<XBLChildrenElement*>(aContainer)->
           HasInsertedChildren()) {
       return nullptr;
     }
     parent = aContainer->GetParent();
   }
 
   while(parent) {
--- a/content/xbl/src/nsXBLProtoImplField.cpp
+++ b/content/xbl/src/nsXBLProtoImplField.cpp
@@ -246,17 +246,17 @@ FieldGetterImpl(JSContext *cx, JS::CallA
   JS::Rooted<JS::Value> v(cx);
   if (!JS_GetPropertyById(cx, thisObj, id, &v)) {
     return false;
   }
   args.rval().set(v);
   return true;
 }
 
-static JSBool
+static bool
 FieldGetter(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   return JS::CallNonGenericMethod<ValueHasISupportsPrivate, FieldGetterImpl>
                                  (cx, args);
 }
 
 bool
@@ -283,17 +283,17 @@ FieldSetterImpl(JSContext *cx, JS::CallA
     if (!::JS_SetPropertyById(cx, thisObj, id, args.get(0))) {
       return false;
     }
   }
   args.rval().setUndefined();
   return true;
 }
 
-static JSBool
+static bool
 FieldSetter(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   return JS::CallNonGenericMethod<ValueHasISupportsPrivate, FieldSetterImpl>
                                  (cx, args);
 }
 
 nsresult
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2324,16 +2324,17 @@ nsXULPrototypeElement::SetAttrAt(uint32_
 }
 
 void
 nsXULPrototypeElement::Unlink()
 {
     mNumAttributes = 0;
     delete[] mAttributes;
     mAttributes = nullptr;
+    mChildren.Clear();
 }
 
 void
 nsXULPrototypeElement::TraceAllScripts(JSTracer* aTrc)
 {
     for (uint32_t i = 0; i < mChildren.Length(); ++i) {
         nsXULPrototypeNode* child = mChildren[i];
         if (child->mType == nsXULPrototypeNode::eType_Element) {
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -3777,22 +3777,22 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
     *did_resolve = true;
   }
 
   return rv;
 }
 
 // Native code for window._content getter, this simply maps
 // window._content to window.content for backwards compatibility only.
-static JSBool
+static bool
 ContentWindowGetter(JSContext *cx, unsigned argc, jsval *vp)
 {
   JSObject *obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
-    return JS_FALSE;
+    return false;
 
   JS::Rooted<JS::Value> value(cx);
   bool result = ::JS_GetProperty(cx, obj, "content", &value);
   *vp = value;
   return result;
 }
 
 template<class Interface>
@@ -4812,54 +4812,54 @@ void
 nsHTMLDocumentSH::ReleaseDocument(JSFreeOp *fop, JSObject *obj)
 {
   nsIHTMLDocument* doc = GetDocument(obj);
   if (doc) {
     nsContentUtils::DeferredFinalize(doc);
   }
 }
 
-JSBool
+bool
 nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp)
 {
   // Handle document.all("foo") style access to document.all.
 
   if (argc != 1) {
     // XXX: Should throw NS_ERROR_XPC_NOT_ENOUGH_ARGS for argc < 1,
     // and create a new NS_ERROR_XPC_TOO_MANY_ARGS for argc > 1? IE
     // accepts nothing other than one arg.
     xpc::Throw(cx, NS_ERROR_INVALID_ARG);
 
-    return JS_FALSE;
+    return false;
   }
 
   // Convert all types to string.
   JS::Rooted<JSString*> str(cx, ::JS_ValueToString(cx, JS_ARGV(cx, vp)[0]));
   if (!str) {
-    return JS_FALSE;
+    return false;
   }
 
   // If we are called via document.all(id) instead of document.all.item(i) or
   // another method, use the document.all callee object as self.
   JSObject *self;
   JS::Value callee = JS_CALLEE(cx, vp);
   if (callee.isObject() &&
       JS_GetClass(&callee.toObject()) == &sHTMLDocumentAllClass) {
     self = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
   } else {
     self = JS_THIS_OBJECT(cx, vp);
     if (!self)
-      return JS_FALSE;
+      return false;
   }
 
   size_t length;
   JS::Anchor<JSString *> anchor(str);
   const jschar *chars = ::JS_GetStringCharsAndLength(cx, str, &length);
   if (!chars) {
-    return JS_FALSE;
+    return false;
   }
 
   JS::Rooted<JS::Value> value(cx);
   if (!::JS_GetUCProperty(cx, self, chars, length, &value)) {
     return false;
   }
 
   *vp = value;
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -447,17 +447,17 @@ protected:
                                        nsDocument *doc,
                                        nsContentList **nodeList);
 public:
   static JSBool DocumentAllGetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                                        JS::MutableHandle<JS::Value> vp);
   static JSBool DocumentAllNewResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                                       unsigned flags, JS::MutableHandle<JSObject*> objp);
   static void ReleaseDocument(JSFreeOp *fop, JSObject *obj);
-  static JSBool CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp);
+  static bool CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp);
 };
 
 
 // String array helper
 
 class nsStringArraySH : public nsGenericArraySH
 {
 protected:
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1985,148 +1985,148 @@ nsJSContext::AddSupportsPrimitiveTojsval
 #ifdef XP_UNIX
 #include <unistd.h>
 #endif
 #ifdef XP_WIN32
 #include <io.h>
 #endif
 #include "nsTraceMalloc.h"
 
-static JSBool
+static bool
 CheckUniversalXPConnectForTraceMalloc(JSContext *cx)
 {
     if (nsContentUtils::IsCallerChrome())
-        return JS_TRUE;
+        return true;
     JS_ReportError(cx, "trace-malloc functions require UniversalXPConnect");
-    return JS_FALSE;
+    return false;
 }
 
-static JSBool
+static bool
 TraceMallocDisable(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     if (!CheckUniversalXPConnectForTraceMalloc(cx))
-        return JS_FALSE;
+        return false;
 
     NS_TraceMallocDisable();
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    return JS_TRUE;
+    return true;
 }
 
-static JSBool
+static bool
 TraceMallocEnable(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     if (!CheckUniversalXPConnectForTraceMalloc(cx))
-        return JS_FALSE;
+        return false;
 
     NS_TraceMallocEnable();
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    return JS_TRUE;
+    return true;
 }
 
-static JSBool
+static bool
 TraceMallocOpenLogFile(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     int fd;
     JSString *str;
 
     if (!CheckUniversalXPConnectForTraceMalloc(cx))
-        return JS_FALSE;
+        return false;
 
     if (argc == 0) {
         fd = -1;
     } else {
         str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
         if (!str)
-            return JS_FALSE;
+            return false;
         JSAutoByteString filename(cx, str);
         if (!filename)
-            return JS_FALSE;
+            return false;
         fd = open(filename.ptr(), O_CREAT | O_WRONLY | O_TRUNC, 0644);
         if (fd < 0) {
             JS_ReportError(cx, "can't open %s: %s", filename.ptr(), strerror(errno));
-            return JS_FALSE;
+            return false;
         }
     }
     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(fd));
-    return JS_TRUE;
+    return true;
 }
 
-static JSBool
+static bool
 TraceMallocChangeLogFD(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     if (!CheckUniversalXPConnectForTraceMalloc(cx))
-        return JS_FALSE;
+        return false;
 
     int32_t fd, oldfd;
     if (argc == 0) {
         oldfd = -1;
     } else {
         if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd))
-            return JS_FALSE;
+            return false;
         oldfd = NS_TraceMallocChangeLogFD(fd);
         if (oldfd == -2) {
             JS_ReportOutOfMemory(cx);
-            return JS_FALSE;
+            return false;
         }
     }
     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(oldfd));
-    return JS_TRUE;
+    return true;
 }
 
-static JSBool
+static bool
 TraceMallocCloseLogFD(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     if (!CheckUniversalXPConnectForTraceMalloc(cx))
-        return JS_FALSE;
+        return false;
 
     int32_t fd;
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     if (argc == 0)
-        return JS_TRUE;
+        return true;
     if (!JS_ValueToECMAInt32(cx, JS_ARGV(cx, vp)[0], &fd))
-        return JS_FALSE;
+        return false;
     NS_TraceMallocCloseLogFD((int) fd);
-    return JS_TRUE;
+    return true;
 }
 
-static JSBool
+static bool
 TraceMallocLogTimestamp(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     if (!CheckUniversalXPConnectForTraceMalloc(cx))
-        return JS_FALSE;
+        return false;
 
     JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
     if (!str)
-        return JS_FALSE;
+        return false;
     JSAutoByteString caption(cx, str);
     if (!caption)
-        return JS_FALSE;
+        return false;
     NS_TraceMallocLogTimestamp(caption.ptr());
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    return JS_TRUE;
+    return true;
 }
 
-static JSBool
+static bool
 TraceMallocDumpAllocations(JSContext *cx, unsigned argc, JS::Value *vp)
 {
     if (!CheckUniversalXPConnectForTraceMalloc(cx))
-        return JS_FALSE;
+        return false;
 
     JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
     if (!str)
-        return JS_FALSE;
+        return false;
     JSAutoByteString pathname(cx, str);
     if (!pathname)
-        return JS_FALSE;
+        return false;
     if (NS_TraceMallocDumpAllocations(pathname.ptr()) < 0) {
         JS_ReportError(cx, "can't dump to %s: %s", pathname.ptr(), strerror(errno));
-        return JS_FALSE;
+        return false;
     }
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    return JS_TRUE;
+    return true;
 }
 
 static const JSFunctionSpec TraceMallocFunctions[] = {
     JS_FS("TraceMallocDisable",         TraceMallocDisable,         0, 0),
     JS_FS("TraceMallocEnable",          TraceMallocEnable,          0, 0),
     JS_FS("TraceMallocOpenLogFile",     TraceMallocOpenLogFile,     1, 0),
     JS_FS("TraceMallocChangeLogFD",     TraceMallocChangeLogFD,     1, 0),
     JS_FS("TraceMallocCloseLogFD",      TraceMallocCloseLogFD,      1, 0),
@@ -2142,43 +2142,43 @@ static const JSFunctionSpec TraceMallocF
 #include <errno.h>
 
 namespace mozilla {
 namespace dmd {
 
 // See https://wiki.mozilla.org/Performance/MemShrink/DMD for instructions on
 // how to use DMD.
 
-static JSBool
+static bool
 ReportAndDump(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSString *str = JS_ValueToString(cx, argc ? JS_ARGV(cx, vp)[0] : JSVAL_VOID);
   if (!str)
-    return JS_FALSE;
+    return false;
   JSAutoByteString pathname(cx, str);
   if (!pathname)
-    return JS_FALSE;
+    return false;
 
   FILE* fp = fopen(pathname.ptr(), "w");
   if (!fp) {
     JS_ReportError(cx, "DMD can't open %s: %s",
                    pathname.ptr(), strerror(errno));
-    return JS_FALSE;
+    return false;
   }
 
   dmd::ClearReports();
   fprintf(stderr, "DMD: running reporters...\n");
   dmd::RunReporters();
   dmd::Writer writer(FpWrite, fp);
   dmd::Dump(writer);
 
   fclose(fp);
 
   JS_SET_RVAL(cx, vp, JSVAL_VOID);
-  return JS_TRUE;
+  return true;
 }
 
 } // namespace dmd
 } // namespace mozilla
 
 static const JSFunctionSpec DMDFunctions[] = {
     JS_FS("DMDReportAndDump", dmd::ReportAndDump, 1, 0),
     JS_FS_END
@@ -2196,21 +2196,21 @@ IsJProfAction(struct sigaction *action)
     return (action->sa_sigaction &&
             (action->sa_flags & (SA_RESTART | SA_SIGINFO)) == (SA_RESTART | SA_SIGINFO));
 }
 
 void NS_JProfStartProfiling();
 void NS_JProfStopProfiling();
 void NS_JProfClearCircular();
 
-static JSBool
+static bool
 JProfStartProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   NS_JProfStartProfiling();
-  return JS_TRUE;
+  return true;
 }
 
 void NS_JProfStartProfiling()
 {
     // Figure out whether we're dealing with SIGPROF, SIGALRM, or
     // SIGPOLL profiling (SIGALRM for JP_REALTIME, SIGPOLL for
     // JP_RTC_HZ)
     struct sigaction action;
@@ -2238,51 +2238,51 @@ void NS_JProfStartProfiling()
         //printf("Beginning rtc-based jprof profiling.\n");
         raise(SIGPOLL);
         return;
     }
 
     printf("Could not start jprof-profiling since JPROF_FLAGS was not set.\n");
 }
 
-static JSBool
+static bool
 JProfStopProfilingJS(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   NS_JProfStopProfiling();
-  return JS_TRUE;
+  return true;
 }
 
 void
 NS_JProfStopProfiling()
 {
     raise(SIGUSR1);
     //printf("Stopped jprof profiling.\n");
 }
 
-static JSBool
+static bool
 JProfClearCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   NS_JProfClearCircular();
-  return JS_TRUE;
+  return true;
 }
 
 void
 NS_JProfClearCircular()
 {
     raise(SIGUSR2);
     //printf("cleared jprof buffer\n");
 }
 
-static JSBool
+static bool
 JProfSaveCircularJS(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   // Not ideal...
   NS_JProfStopProfiling();
   NS_JProfStartProfiling();
-  return JS_TRUE;
+  return true;
 }
 
 static const JSFunctionSpec JProfFunctions[] = {
     JS_FS("JProfStartProfiling",        JProfStartProfilingJS,      0, 0),
     JS_FS("JProfStopProfiling",         JProfStopProfilingJS,       0, 0),
     JS_FS("JProfClearCircular",         JProfClearCircularJS,       0, 0),
     JS_FS("JProfSaveCircular",          JProfSaveCircularJS,        0, 0),
     JS_FS_END
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -272,17 +272,17 @@ DefineUnforgeableAttributes(JSContext* c
 // passed a non-Function object we also need to provide our own toString method
 // for interface objects.
 
 enum {
   TOSTRING_CLASS_RESERVED_SLOT = 0,
   TOSTRING_NAME_RESERVED_SLOT = 1
 };
 
-JSBool
+bool
 InterfaceObjectToString(JSContext* cx, unsigned argc, JS::Value *vp)
 {
   JS::Rooted<JSObject*> callee(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
 
   JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj) {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
                          "null", "object");
@@ -312,17 +312,17 @@ InterfaceObjectToString(JSContext* cx, u
   str.Append('\n');
   str.AppendLiteral("    [native code]");
   str.Append('\n');
   str.AppendLiteral("}");
 
   return xpc::NonVoidStringToJsval(cx, str, vp);
 }
 
-JSBool
+bool
 Constructor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   JSObject* callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
   const JS::Value& v =
     js::GetFunctionNativeReserved(callee,
                                   CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT);
   const JSNativeHolder* nativeHolder =
     static_cast<const JSNativeHolder*>(v.toPrivate());
@@ -712,17 +712,17 @@ VariantToJsval(JSContext* aCx, JS::Handl
       Throw<true>(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
     }
     return false;
   }
 
   return true;
 }
 
-JSBool
+bool
 QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp));
   if (thisv.isNull())
     return false;
 
   // Get the object. It might be a security wrapper, in which case we do a checked
@@ -771,17 +771,17 @@ QueryInterface(JSContext* cx, unsigned a
   if (NS_FAILED(rv)) {
     return Throw<true>(cx, rv);
   }
 
   *vp = thisv;
   return true;
 }
 
-JSBool
+bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
 }
 
 inline const NativePropertyHooks*
 GetNativePropertyHooks(JSContext *cx, JS::Handle<JSObject*> obj,
                        DOMObjectType& type)
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1381,17 +1381,17 @@ InitIds(JSContext* cx, const Prefable<Sp
     // corresponding to the list terminator for the pref.
     *ids = JSID_VOID;
     ++ids;
   } while ((++prefableSpecs)->specs);
 
   return true;
 }
 
-JSBool
+bool
 QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp);
 
 template <class T, bool isISupports=IsBaseOf<nsISupports, T>::value>
 struct
 WantsQueryInterface
 {
   static bool Enabled(JSContext* aCx, JSObject* aGlobal)
   {
@@ -1403,17 +1403,17 @@ struct
 WantsQueryInterface<T, true>
 {
   static bool Enabled(JSContext* aCx, JSObject* aGlobal)
   {
     return IsChromeOrXBL(aCx, aGlobal);
   }
 };
 
-JSBool
+bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
 GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
                        JS::Handle<jsid> id, bool* found,
                        JS::Value* vp);
 
 bool
@@ -1948,17 +1948,17 @@ extern NativePropertyHooks sWorkerNative
 // Note that some interface objects are not yet a JSFunction but a normal
 // JSObject with a DOMJSClass, those do not use these slots.
 
 enum {
   CONSTRUCTOR_NATIVE_HOLDER_RESERVED_SLOT = 0,
   CONSTRUCTOR_XRAY_EXPANDO_SLOT
 };
 
-JSBool
+bool
 Constructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 inline bool
 UseDOMXray(JSObject* obj)
 {
   const js::Class* clasp = js::GetObjectClass(obj);
   return IsDOMClass(clasp) ||
          IsDOMProxy(obj, clasp) ||
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1270,16 +1270,21 @@ DOMInterfaces = {
    'headerFile': 'WebGLFramebuffer.h'
 },
 
 'WebGLProgram': {
    'nativeType': 'mozilla::WebGLProgram',
    'headerFile': 'WebGLProgram.h'
 },
 
+'WebGLQuery': {
+   'nativeType': 'mozilla::WebGLQuery',
+   'headerFile': 'WebGLQuery.h'
+},
+
 'WebGLRenderbuffer': {
    'nativeType': 'mozilla::WebGLRenderbuffer',
    'headerFile': 'WebGLRenderbuffer.h'
 },
 
 'WebGLRenderingContext': {
   'nativeType': 'mozilla::WebGLContext',
   'headerFile': 'WebGLContext.h',
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -653,17 +653,20 @@ class CGHeaders(CGWrapper):
             def addHeaderForFunc(func):
                 # Include the right class header, which we can only do
                 # if this is a class member function.
                 if func is not None and "::" in func:
                     # Strip out the function name and convert "::" to "/"
                     bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
             for m in desc.interface.members:
                 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"))
-            addHeaderForFunc(desc.interface.getExtendedAttribute("Func"))
+            # getExtendedAttribute() returns a list, extract the entry.
+            funcList = desc.interface.getExtendedAttribute("Func")
+            if funcList is not None:
+                addHeaderForFunc(funcList[0])
 
         for d in dictionaries:
             if d.parent:
                 declareIncludes.add(self.getDeclarationFilename(d.parent))
             bindingHeaders.add(self.getDeclarationFilename(d))
 
         for c in callbacks:
             bindingHeaders.add(self.getDeclarationFilename(c))
@@ -1012,17 +1015,17 @@ class CGClassTraceHook(CGAbstractClassHo
   }""" % (self.name, self.args[0].name)
 
 class CGClassConstructor(CGAbstractStaticMethod):
     """
     JS-visible constructor for our objects
     """
     def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
-        CGAbstractStaticMethod.__init__(self, descriptor, name, 'JSBool', args)
+        CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
         self._ctor = ctor
 
     def define(self):
         if not self._ctor:
             return ""
         return CGAbstractStaticMethod.define(self)
 
     def definition_body(self):
@@ -5189,17 +5192,17 @@ class CGAbstractBindingMethod(CGAbstract
     setters.  This will generate the function declaration and unwrap the
     |this| object.  Subclasses are expected to override the generate_code
     function to do the rest of the work.  This function should return a
     CGThing which is already properly indented.
     """
     def __init__(self, descriptor, name, args, unwrapFailureCode=None,
                  getThisObj="args.computeThis(cx).toObjectOrNull()",
                  callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);",
-                 returnType="JSBool"):
+                 returnType="bool"):
         CGAbstractStaticMethod.__init__(self, descriptor, name, returnType, args)
 
         if unwrapFailureCode is None:
             self.unwrapFailureCode = 'return ThrowErrorMessage(cx, MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, "Value", "%s");' % descriptor.interface.identifier.name
         else:
             self.unwrapFailureCode = unwrapFailureCode
         self.getThisObj = getThisObj
         self.callArgs = callArgs
@@ -5233,17 +5236,17 @@ class CGAbstractStaticBindingMethod(CGAb
     and setters.  This will generate the function declaration and unwrap the
     global object.  Subclasses are expected to override the generate_code
     function to do the rest of the work.  This function should return a
     CGThing which is already properly indented.
     """
     def __init__(self, descriptor, name):
         args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
                 Argument('JS::Value*', 'vp')]
-        CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args)
+        CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
 
     def definition_body(self):
         unwrap = CGGeneric("""JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 JS::RootedObject obj(cx, args.computeThis(cx).toObjectOrNull());
 if (!obj) {
   return false;
 }""")
         return CGList([ CGIndenter(unwrap),
@@ -5357,17 +5360,17 @@ class CGNewResolveHook(CGAbstractBinding
     def __init__(self, descriptor):
         assert descriptor.interface.getExtendedAttribute("NeedNewResolve")
         args = [Argument('JSContext*', 'cx'), Argument('JS::Handle<JSObject*>', 'obj'),
                 Argument('JS::Handle<jsid>', 'id'), Argument('unsigned', 'flags'),
                 Argument('JS::MutableHandle<JSObject*>', 'objp')]
         # Our "self" is actually the "obj" argument in this case, not the thisval.
         CGAbstractBindingMethod.__init__(
             self, descriptor, NEWRESOLVE_HOOK_NAME,
-            args, getThisObj="", callArgs="")
+            args, getThisObj="", callArgs="", returnType="JSBool")
 
     def generate_code(self):
         return CGIndenter(CGGeneric(
                 "JS::Rooted<JS::Value> value(cx);\n"
                 "if (!self->DoNewResolve(cx, obj, id, &value)) {\n"
                 "  return false;\n"
                 "}\n"
                 "if (value.isUndefined()) {\n"
@@ -5385,17 +5388,17 @@ class CGEnumerateHook(CGAbstractBindingM
     """
     def __init__(self, descriptor):
         assert descriptor.interface.getExtendedAttribute("NeedNewResolve")
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'obj')]
         # Our "self" is actually the "obj" argument in this case, not the thisval.
         CGAbstractBindingMethod.__init__(
             self, descriptor, ENUMERATE_HOOK_NAME,
-            args, getThisObj="", callArgs="")
+            args, getThisObj="", callArgs="", returnType="JSBool")
 
     def generate_code(self):
         return CGIndenter(CGGeneric(
                 "nsAutoTArray<nsString, 8> names;\n"
                 "ErrorResult rv;\n"
                 "self->GetOwnPropertyNames(cx, names, rv);\n"
                 "rv.WouldReportJSException();\n"
                 "if (rv.Failed()) {\n"
@@ -6929,17 +6932,17 @@ class CGResolveOwnPropertyViaNewresolve(
                 Argument('JS::Handle<JSObject*>', 'wrapper'),
                 Argument('JS::Handle<JSObject*>', 'obj'),
                 Argument('JS::Handle<jsid>', 'id'),
                 Argument('JSPropertyDescriptor*', 'desc'), Argument('unsigned', 'flags'),
                 ]
         CGAbstractBindingMethod.__init__(self, descriptor,
                                          "ResolveOwnPropertyViaNewresolve",
                                          args, getThisObj="",
-                                         callArgs="", returnType="bool")
+                                         callArgs="")
     def generate_code(self):
         return CGIndenter(CGGeneric(
                 "JS::Rooted<JS::Value> value(cx);\n"
                 "if (!self->DoNewResolve(cx, obj, id, &value)) {\n"
                 "  return false;\n"
                 "}\n"
                 "if (!value.isUndefined()) {\n"
                 "  FillPropertyDescriptor(desc, wrapper, value, /* readonly = */ false);\n"
@@ -6966,17 +6969,17 @@ class CGEnumerateOwnPropertiesViaGetOwnP
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'wrapper'),
                 Argument('JS::Handle<JSObject*>', 'obj'),
                 Argument('JS::AutoIdVector&', 'props')]
         CGAbstractBindingMethod.__init__(self, descriptor,
                                          "EnumerateOwnPropertiesViaGetOwnPropertyNames",
                                          args, getThisObj="",
-                                         callArgs="", returnType="bool")
+                                         callArgs="")
     def generate_code(self):
         return CGIndenter(CGGeneric(
                 "nsAutoTArray<nsString, 8> names;\n"
                 "ErrorResult rv;\n"
                 "self->GetOwnPropertyNames(cx, names, rv);\n"
                 "rv.WouldReportJSException();\n"
                 "if (rv.Failed()) {\n"
                 '  return ThrowMethodFailedWithDetails<true>(cx, rv, "%s", "enumerate");\n'
@@ -9683,17 +9686,17 @@ class CGJSImplClass(CGBindingImplClass):
             [Argument("JS::Handle<JSObject*>", "aJSImplObject"),
              Argument("nsPIDOMWindow*", "aParent")],
             visibility="public",
             baseConstructors=baseConstructors,
             body=constructorBody)
 
         self.methodDecls.append(
             ClassMethod("_Create",
-                        "JSBool",
+                        "bool",
                         [Argument("JSContext*", "cx"),
                          Argument("unsigned", "argc"),
                          Argument("JS::Value*", "vp")],
                         static=True,
                         body=self.getCreateFromExistingBody()))
 
         CGClass.__init__(self, descriptor.name,
                          bases=baseClasses,
--- a/dom/browser-element/mochitest/browserElementTestHelpers.js
+++ b/dom/browser-element/mochitest/browserElementTestHelpers.js
@@ -201,17 +201,17 @@ function expectMozbrowserEvent(iframe, e
     iframe.removeEventListener('mozbrowser' + eventName, handler);
     deferred.resolve(e);
   });
   return deferred.promise;
 }
 
 // Set some prefs:
 //
-//  * browser.pageThumbs.enabled: false
+//  * browser.pagethumbnails.capturing_disabled: false
 //
 //    Disable tab view; it seriously messes us up.
 //
 //  * dom.ipc.browser_frames.oop_by_default
 //
 //    Enable or disable OOP-by-default depending on the test's filename.  You
 //    can still force OOP on or off with <iframe mozbrowser remote=true/false>,
 //    at least until bug 756376 lands.
@@ -239,17 +239,17 @@ function expectMozbrowserEvent(iframe, e
 //    Disable mixed active content blocking, so that tests can confirm that mixed
 //    content results in a broken security state.
 
 (function() {
   var oop = location.pathname.indexOf('_inproc_') == -1;
 
   browserElementTestHelpers.lockTestReady();
   SpecialPowers.setBoolPref("network.disable.ipc.security", true);
-  SpecialPowers.pushPrefEnv({set: [["browser.pageThumbs.enabled", false],
+  SpecialPowers.pushPrefEnv({set: [["browser.pagethumbnails.capturing_disabled", false],
                                    ["dom.ipc.browser_frames.oop_by_default", oop],
                                    ["dom.ipc.tabs.disabled", false],
                                    ["security.mixed_content.block_active_content", false]]},
                             browserElementTestHelpers.unlockTestReady.bind(browserElementTestHelpers));
 })();
 
 addEventListener('unload', function() {
   browserElementTestHelpers.cleanUp();
--- a/dom/devicestorage/ipc/test_ipc.html
+++ b/dom/devicestorage/ipc/test_ipc.html
@@ -153,16 +153,16 @@
           ["device.storage.testing", true],
           ["device.storage.prompt.testing", true],
 
           // TODO: remove this as part of bug 820712
           ["network.disable.ipc.security", true],
 
           ["dom.ipc.browser_frames.oop_by_default", true],
           ["dom.mozBrowserFramesEnabled", true],
-          ["browser.pageThumbs.enabled", false]
+          ["browser.pagethumbnails.capturing_disabled", false]
         ]
       }, runTests);
     });
 
   </script>
 </body>
 </html>
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -1276,17 +1276,17 @@ GetHelper::PackArgumentsForParentProcess
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(mKeyRange, "This should never be null!");
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "GetHelper::PackArgumentsForParentProcess "
                              "[IDBIndex.cpp]");
 
-  FIXME_Bug_521898_index::GetParams params;
+  GetParams params;
 
   mKeyRange->ToSerializedKeyRange(params.keyRange());
 
   aParams = params;
   return NS_OK;
 }
 
 AsyncConnectionHelper::ChildProcessSendResult
@@ -1480,17 +1480,17 @@ GetAllKeysHelper::PackArgumentsForParent
   NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "GetAllKeysHelper::PackArgumentsForParentProcess");
 
   GetAllKeysParams params;
 
   if (mKeyRange) {
-    FIXME_Bug_521898_index::KeyRange keyRange;
+    KeyRange keyRange;
     mKeyRange->ToSerializedKeyRange(keyRange);
     params.optionalKeyRange() = keyRange;
   }
   else {
     params.optionalKeyRange() = mozilla::void_t();
   }
 
   params.limit() = mLimit;
@@ -1636,20 +1636,20 @@ GetAllHelper::PackArgumentsForParentProc
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "GetAllHelper::PackArgumentsForParentProcess "
                              "[IDBIndex.cpp]");
 
-  FIXME_Bug_521898_index::GetAllParams params;
+  GetAllParams params;
 
   if (mKeyRange) {
-    FIXME_Bug_521898_index::KeyRange keyRange;
+    KeyRange keyRange;
     mKeyRange->ToSerializedKeyRange(keyRange);
     params.optionalKeyRange() = keyRange;
   }
   else {
     params.optionalKeyRange() = mozilla::void_t();
   }
 
   params.limit() = mLimit;
@@ -1981,17 +1981,17 @@ OpenKeyCursorHelper::PackArgumentsForPar
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "OpenKeyCursorHelper::"
                              "PackArgumentsForParentProcess");
 
   OpenKeyCursorParams params;
 
   if (mKeyRange) {
-    FIXME_Bug_521898_index::KeyRange keyRange;
+    KeyRange keyRange;
     mKeyRange->ToSerializedKeyRange(keyRange);
     params.optionalKeyRange() = keyRange;
   }
   else {
     params.optionalKeyRange() = mozilla::void_t();
   }
 
   params.direction() = mDirection;
@@ -2322,20 +2322,20 @@ OpenCursorHelper::PackArgumentsForParent
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "OpenCursorHelper::PackArgumentsForParentProcess "
                              "[IDBIndex.cpp]");
 
-  FIXME_Bug_521898_index::OpenCursorParams params;
+  OpenCursorParams params;
 
   if (mKeyRange) {
-    FIXME_Bug_521898_index::KeyRange keyRange;
+    KeyRange keyRange;
     mKeyRange->ToSerializedKeyRange(keyRange);
     params.optionalKeyRange() = keyRange;
   }
   else {
     params.optionalKeyRange() = mozilla::void_t();
   }
 
   params.direction() = mDirection;
@@ -2516,20 +2516,20 @@ CountHelper::PackArgumentsForParentProce
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "CountHelper::PackArgumentsForParentProcess "
                              "[IDBIndex.cpp]");
 
-  FIXME_Bug_521898_index::CountParams params;
+  CountParams params;
 
   if (mKeyRange) {
-    FIXME_Bug_521898_index::KeyRange keyRange;
+    KeyRange keyRange;
     mKeyRange->ToSerializedKeyRange(keyRange);
     params.optionalKeyRange() = keyRange;
   }
   else {
     params.optionalKeyRange() = mozilla::void_t();
   }
 
   aParams = params;
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -101,17 +101,17 @@ GetKeyFromJSValOrThrow(JSContext* aCx,
   nsresult rv = GetKeyFromJSVal(aCx, aVal, aKey);
   if (NS_FAILED(rv)) {
     ThrowException(aCx, rv);
     return false;
   }
   return true;
 }
 
-JSBool
+bool
 MakeOnlyKeyRange(JSContext* aCx,
                  unsigned aArgc,
                  jsval* aVp)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   JS::Rooted<JS::Value> val(aCx);
   if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", val.address())) {
@@ -122,17 +122,17 @@ MakeOnlyKeyRange(JSContext* aCx,
 
   if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Lower())) {
     return false;
   }
 
   return ReturnKeyRange(aCx, aVp, keyRange);
 }
 
-JSBool
+bool
 MakeLowerBoundKeyRange(JSContext* aCx,
                        unsigned aArgc,
                        jsval* aVp)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   JS::Rooted<JS::Value> val(aCx);
   JSBool open = false;
@@ -145,17 +145,17 @@ MakeLowerBoundKeyRange(JSContext* aCx,
 
   if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Lower())) {
     return false;
   }
 
   return ReturnKeyRange(aCx, aVp, keyRange);
 }
 
-JSBool
+bool
 MakeUpperBoundKeyRange(JSContext* aCx,
                        unsigned aArgc,
                        jsval* aVp)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   JS::Rooted<JS::Value> val(aCx);
   JSBool open = false;
@@ -168,17 +168,17 @@ MakeUpperBoundKeyRange(JSContext* aCx,
 
   if (!GetKeyFromJSValOrThrow(aCx, val, keyRange->Upper())) {
     return false;
   }
 
   return ReturnKeyRange(aCx, aVp, keyRange);
 }
 
-JSBool
+bool
 MakeBoundKeyRange(JSContext* aCx,
                   unsigned aArgc,
                   jsval* aVp)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   JS::Rooted<JS::Value> lowerVal(aCx), upperVal(aCx);
   JSBool lowerOpen = false, upperOpen = false;
@@ -413,22 +413,12 @@ IDBKeyRange::GetUpperOpen(bool* aUpperOp
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   *aUpperOpen = mUpperOpen;
   return NS_OK;
 }
 
 // Explicitly instantiate for all our key range types... Grumble.
 template already_AddRefed<IDBKeyRange>
-IDBKeyRange::FromSerializedKeyRange<FIXME_Bug_521898_objectstore::KeyRange>
-(const FIXME_Bug_521898_objectstore::KeyRange& aKeyRange);
-
-template already_AddRefed<IDBKeyRange>
-IDBKeyRange::FromSerializedKeyRange<FIXME_Bug_521898_index::KeyRange>
-(const FIXME_Bug_521898_index::KeyRange& aKeyRange);
+IDBKeyRange::FromSerializedKeyRange<KeyRange> (const KeyRange& aKeyRange);
 
 template void
-IDBKeyRange::ToSerializedKeyRange<FIXME_Bug_521898_objectstore::KeyRange>
-(FIXME_Bug_521898_objectstore::KeyRange& aKeyRange);
-
-template void
-IDBKeyRange::ToSerializedKeyRange<FIXME_Bug_521898_index::KeyRange>
-(FIXME_Bug_521898_index::KeyRange& aKeyRange);
+IDBKeyRange::ToSerializedKeyRange<KeyRange> (KeyRange& aKeyRange);
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -14,19 +14,17 @@
 
 #include "nsCycleCollectionParticipant.h"
 
 class mozIStorageStatement;
 
 BEGIN_INDEXEDDB_NAMESPACE
 
 namespace ipc {
-namespace FIXME_Bug_521898_objectstore {
 class KeyRange;
-} // namespace FIXME_Bug_521898_objectstore
 } // namespace ipc
 
 class IDBKeyRange MOZ_FINAL : public nsIIDBKeyRange
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIIDBKEYRANGE
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBKeyRange)
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -3254,17 +3254,17 @@ GetHelper::PackArgumentsForParentProcess
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
   NS_ASSERTION(mKeyRange, "This should never be null!");
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "GetHelper::PackArgumentsForParentProcess "
                              "[IDBObjectStore.cpp]");
 
-  FIXME_Bug_521898_objectstore::GetParams params;
+  GetParams params;
 
   mKeyRange->ToSerializedKeyRange(params.keyRange());
 
   aParams = params;
   return NS_OK;
 }
 
 AsyncConnectionHelper::ChildProcessSendResult
@@ -3709,20 +3709,20 @@ OpenCursorHelper::PackArgumentsForParent
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "OpenCursorHelper::PackArgumentsForParentProcess "
                              "[IDBObjectStore.cpp]");
 
-  FIXME_Bug_521898_objectstore::OpenCursorParams params;
+  OpenCursorParams params;
 
   if (mKeyRange) {
-    FIXME_Bug_521898_objectstore::KeyRange keyRange;
+    KeyRange keyRange;
     mKeyRange->ToSerializedKeyRange(keyRange);
     params.optionalKeyRange() = keyRange;
   }
   else {
     params.optionalKeyRange() = mozilla::void_t();
   }
 
   params.direction() = mDirection;
@@ -4171,20 +4171,20 @@ GetAllHelper::PackArgumentsForParentProc
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "GetAllHelper::PackArgumentsForParentProcess "
                              "[IDBObjectStore.cpp]");
 
-  FIXME_Bug_521898_objectstore::GetAllParams params;
+  GetAllParams params;
 
   if (mKeyRange) {
-    FIXME_Bug_521898_objectstore::KeyRange keyRange;
+    KeyRange keyRange;
     mKeyRange->ToSerializedKeyRange(keyRange);
     params.optionalKeyRange() = keyRange;
   }
   else {
     params.optionalKeyRange() = mozilla::void_t();
   }
 
   params.limit() = mLimit;
@@ -4384,20 +4384,20 @@ CountHelper::PackArgumentsForParentProce
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!IndexedDatabaseManager::IsMainProcess(), "Wrong process!");
 
   PROFILER_MAIN_THREAD_LABEL("IndexedDB",
                              "CountHelper::PackArgumentsForParentProcess "
                              "[IDBObjectStore.cpp]");
 
-  FIXME_Bug_521898_objectstore::CountParams params;
+  CountParams params;
 
   if (mKeyRange) {
-    FIXME_Bug_521898_objectstore::KeyRange keyRange;
+    KeyRange keyRange;
     mKeyRange->ToSerializedKeyRange(keyRange);
     params.optionalKeyRange() = keyRange;
   }
   else {
     params.optionalKeyRange() = mozilla::void_t();
   }
 
   aParams = params;
copy from dom/indexedDB/ipc/PIndexedDBIndex.ipdl
copy to dom/indexedDB/ipc/IndexedDBParams.ipdlh
--- a/dom/indexedDB/ipc/PIndexedDBIndex.ipdl
+++ b/dom/indexedDB/ipc/IndexedDBParams.ipdlh
@@ -1,33 +1,24 @@
 /* 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 protocol PBlob;
-include protocol PIndexedDBCursor;
-include protocol PIndexedDBObjectStore;
-include protocol PIndexedDBRequest;
-
 include "mozilla/dom/indexedDB/SerializationHelpers.h";
 
 using mozilla::dom::indexedDB::Key;
 using mozilla::dom::indexedDB::IDBCursor::Direction;
-using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
 
 using mozilla::void_t;
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
-
 namespace ipc {
 
-namespace FIXME_Bug_521898_index {
-
 struct KeyRange
 {
   Key lower;
   Key upper;
   bool lowerOpen;
   bool upperOpen;
   bool isOnly;
 };
@@ -55,75 +46,12 @@ struct CountParams
 };
 
 struct OpenCursorParams
 {
   OptionalKeyRange optionalKeyRange;
   Direction direction;
 };
 
-} // namespace FIXME_Bug_521898_index
-
-struct GetKeyParams
-{
-  KeyRange keyRange;
-};
-
-struct GetAllKeysParams
-{
-  OptionalKeyRange optionalKeyRange;
-  uint32_t limit;
-};
-
-struct OpenKeyCursorParams
-{
-  OptionalKeyRange optionalKeyRange;
-  Direction direction;
-};
-
-union IndexRequestParams
-{
-  GetParams;
-  GetKeyParams;
-  GetAllParams;
-  GetAllKeysParams;
-  CountParams;
-  OpenCursorParams;
-  OpenKeyCursorParams;
-};
-
-union OptionalStructuredCloneReadInfo
-{
-  SerializedStructuredCloneReadInfo;
-  void_t;
-};
-
-struct IndexCursorConstructorParams
-{
-  PIndexedDBRequest request;
-  Direction direction;
-  Key key;
-  Key objectKey;
-  OptionalStructuredCloneReadInfo optionalCloneInfo;
-  PBlob[] blobs;
-};
-
 } // namespace ipc
-
-protocol PIndexedDBIndex
-{
-  manager PIndexedDBObjectStore;
-
-  manages PIndexedDBCursor;
-  manages PIndexedDBRequest;
-
-parent:
-  __delete__();
-
-  PIndexedDBRequest(IndexRequestParams params);
-
-child:
-  PIndexedDBCursor(IndexCursorConstructorParams params);
-};
-
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
--- a/dom/indexedDB/ipc/IndexedDBParent.cpp
+++ b/dom/indexedDB/ipc/IndexedDBParent.cpp
@@ -1512,28 +1512,27 @@ IndexedDBObjectStoreRequestParent::Get(c
 bool
 IndexedDBObjectStoreRequestParent::GetAll(const GetAllParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams);
   MOZ_ASSERT(mObjectStore);
 
   nsRefPtr<IDBRequest> request;
 
-  const ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange keyRangeUnion =
-    aParams.optionalKeyRange();
+  const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
-    case ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange::TKeyRange:
+    case ipc::OptionalKeyRange::TKeyRange:
       keyRange =
         IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange());
       break;
 
-    case ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange::Tvoid_t:
+    case ipc::OptionalKeyRange::Tvoid_t:
       break;
 
     default:
       MOZ_CRASH("Unknown param type!");
   }
 
   {
     AutoSetCurrentTransaction asct(mObjectStore->Transaction());
@@ -1651,28 +1650,27 @@ IndexedDBObjectStoreRequestParent::Clear
 }
 
 bool
 IndexedDBObjectStoreRequestParent::Count(const CountParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams);
   MOZ_ASSERT(mObjectStore);
 
-  const ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange keyRangeUnion =
-    aParams.optionalKeyRange();
+  const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
-    case ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange::TKeyRange:
+    case ipc::OptionalKeyRange::TKeyRange:
       keyRange =
         IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange());
       break;
 
-    case ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange::Tvoid_t:
+    case ipc::OptionalKeyRange::Tvoid_t:
       break;
 
     default:
       MOZ_CRASH("Unknown param type!");
   }
 
   nsRefPtr<IDBRequest> request;
 
@@ -1690,28 +1688,27 @@ IndexedDBObjectStoreRequestParent::Count
 }
 
 bool
 IndexedDBObjectStoreRequestParent::OpenCursor(const OpenCursorParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams);
   MOZ_ASSERT(mObjectStore);
 
-  const ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange keyRangeUnion =
-    aParams.optionalKeyRange();
+  const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
-    case ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange::TKeyRange:
+    case ipc::OptionalKeyRange::TKeyRange:
       keyRange =
         IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange());
       break;
 
-    case ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange::Tvoid_t:
+    case ipc::OptionalKeyRange::Tvoid_t:
       break;
 
     default:
       MOZ_CRASH("Unknown param type!");
   }
 
   size_t direction = static_cast<size_t>(aParams.direction());
 
@@ -1811,28 +1808,27 @@ IndexedDBIndexRequestParent::GetKey(cons
 bool
 IndexedDBIndexRequestParent::GetAll(const GetAllParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllParams);
   MOZ_ASSERT(mIndex);
 
   nsRefPtr<IDBRequest> request;
 
-  const ipc::FIXME_Bug_521898_index::OptionalKeyRange keyRangeUnion =
-    aParams.optionalKeyRange();
+  const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
-    case ipc::FIXME_Bug_521898_index::OptionalKeyRange::TKeyRange:
+    case ipc::OptionalKeyRange::TKeyRange:
       keyRange =
         IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange());
       break;
 
-    case ipc::FIXME_Bug_521898_index::OptionalKeyRange::Tvoid_t:
+    case ipc::OptionalKeyRange::Tvoid_t:
       break;
 
     default:
       MOZ_CRASH("Unknown param type!");
   }
 
   {
     AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction());
@@ -1850,28 +1846,27 @@ IndexedDBIndexRequestParent::GetAll(cons
 bool
 IndexedDBIndexRequestParent::GetAllKeys(const GetAllKeysParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TGetAllKeysParams);
   MOZ_ASSERT(mIndex);
 
   nsRefPtr<IDBRequest> request;
 
-  const ipc::FIXME_Bug_521898_index::OptionalKeyRange keyRangeUnion =
-    aParams.optionalKeyRange();
+  const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
-    case ipc::FIXME_Bug_521898_index::OptionalKeyRange::TKeyRange:
+    case ipc::OptionalKeyRange::TKeyRange:
       keyRange =
         IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange());
       break;
 
-    case ipc::FIXME_Bug_521898_index::OptionalKeyRange::Tvoid_t:
+    case ipc::OptionalKeyRange::Tvoid_t:
       break;
 
     default:
       MOZ_CRASH("Unknown param type!");
   }
 
   {
     AutoSetCurrentTransaction asct(mIndex->ObjectStore()->Transaction());
@@ -1887,28 +1882,27 @@ IndexedDBIndexRequestParent::GetAllKeys(
 }
 
 bool
 IndexedDBIndexRequestParent::Count(const CountParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TCountParams);
   MOZ_ASSERT(mIndex);
 
-  const ipc::FIXME_Bug_521898_index::OptionalKeyRange keyRangeUnion =
-    aParams.optionalKeyRange();
+  const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
-    case ipc::FIXME_Bug_521898_index::OptionalKeyRange::TKeyRange:
+    case ipc::OptionalKeyRange::TKeyRange:
       keyRange =
         IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange());
       break;
 
-    case ipc::FIXME_Bug_521898_index::OptionalKeyRange::Tvoid_t:
+    case ipc::OptionalKeyRange::Tvoid_t:
       break;
 
     default:
       MOZ_CRASH("Unknown param type!");
   }
 
   nsRefPtr<IDBRequest> request;
 
@@ -1926,28 +1920,27 @@ IndexedDBIndexRequestParent::Count(const
 }
 
 bool
 IndexedDBIndexRequestParent::OpenCursor(const OpenCursorParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenCursorParams);
   MOZ_ASSERT(mIndex);
 
-  const ipc::FIXME_Bug_521898_index::OptionalKeyRange keyRangeUnion =
-    aParams.optionalKeyRange();
+  const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
-    case ipc::FIXME_Bug_521898_index::OptionalKeyRange::TKeyRange:
+    case ipc::OptionalKeyRange::TKeyRange:
       keyRange =
         IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange());
       break;
 
-    case ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange::Tvoid_t:
+    case ipc::OptionalKeyRange::Tvoid_t:
       break;
 
     default:
       MOZ_CRASH("Unknown param type!");
   }
 
   size_t direction = static_cast<size_t>(aParams.direction());
 
@@ -1967,28 +1960,27 @@ IndexedDBIndexRequestParent::OpenCursor(
 }
 
 bool
 IndexedDBIndexRequestParent::OpenKeyCursor(const OpenKeyCursorParams& aParams)
 {
   MOZ_ASSERT(mRequestType == ParamsUnionType::TOpenKeyCursorParams);
   MOZ_ASSERT(mIndex);
 
-  const ipc::FIXME_Bug_521898_index::OptionalKeyRange keyRangeUnion =
-    aParams.optionalKeyRange();
+  const ipc::OptionalKeyRange keyRangeUnion = aParams.optionalKeyRange();
 
   nsRefPtr<IDBKeyRange> keyRange;
 
   switch (keyRangeUnion.type()) {
-    case ipc::FIXME_Bug_521898_index::OptionalKeyRange::TKeyRange:
+    case ipc::OptionalKeyRange::TKeyRange:
       keyRange =
         IDBKeyRange::FromSerializedKeyRange(keyRangeUnion.get_KeyRange());
       break;
 
-    case ipc::FIXME_Bug_521898_objectstore::OptionalKeyRange::Tvoid_t:
+    case ipc::OptionalKeyRange::Tvoid_t:
       break;
 
     default:
       MOZ_CRASH("Unknown param type!");
   }
 
   size_t direction = static_cast<size_t>(aParams.direction());
 
--- a/dom/indexedDB/ipc/IndexedDBParent.h
+++ b/dom/indexedDB/ipc/IndexedDBParent.h
@@ -692,20 +692,20 @@ class IndexedDBObjectStoreRequestParent 
   typedef ipc::ObjectStoreRequestParams ParamsUnionType;
   typedef ParamsUnionType::Type RequestType;
   DebugOnly<RequestType> mRequestType;
 
   typedef ipc::AddParams AddParams;
   typedef ipc::PutParams PutParams;
   typedef ipc::ClearParams ClearParams;
   typedef ipc::DeleteParams DeleteParams;
-  typedef ipc::FIXME_Bug_521898_objectstore::GetParams GetParams;
-  typedef ipc::FIXME_Bug_521898_objectstore::GetAllParams GetAllParams;
-  typedef ipc::FIXME_Bug_521898_objectstore::CountParams CountParams;
-  typedef ipc::FIXME_Bug_521898_objectstore::OpenCursorParams OpenCursorParams;
+  typedef ipc::GetParams GetParams;
+  typedef ipc::GetAllParams GetAllParams;
+  typedef ipc::CountParams CountParams;
+  typedef ipc::OpenCursorParams OpenCursorParams;
 
 public:
   IndexedDBObjectStoreRequestParent(IDBObjectStore* aObjectStore,
                                     RequestType aRequestType);
   virtual ~IndexedDBObjectStoreRequestParent();
 
   bool
   Get(const GetParams& aParams);
@@ -753,20 +753,20 @@ class IndexedDBIndexRequestParent : publ
 
   typedef ipc::IndexRequestParams ParamsUnionType;
   typedef ParamsUnionType::Type RequestType;
   DebugOnly<RequestType> mRequestType;
 
   typedef ipc::GetKeyParams GetKeyParams;
   typedef ipc::GetAllKeysParams GetAllKeysParams;
   typedef ipc::OpenKeyCursorParams OpenKeyCursorParams;
-  typedef ipc::FIXME_Bug_521898_index::GetParams GetParams;
-  typedef ipc::FIXME_Bug_521898_index::GetAllParams GetAllParams;
-  typedef ipc::FIXME_Bug_521898_index::CountParams CountParams;
-  typedef ipc::FIXME_Bug_521898_index::OpenCursorParams OpenCursorParams;
+  typedef ipc::GetParams GetParams;
+  typedef ipc::GetAllParams GetAllParams;
+  typedef ipc::CountParams CountParams;
+  typedef ipc::OpenCursorParams OpenCursorParams;
 
 public:
   IndexedDBIndexRequestParent(IDBIndex* aIndex, RequestType aRequestType);
   virtual ~IndexedDBIndexRequestParent();
 
   bool
   Get(const GetParams& aParams);
 
--- a/dom/indexedDB/ipc/PIndexedDBIndex.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBIndex.ipdl
@@ -2,71 +2,26 @@
  * 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 protocol PBlob;
 include protocol PIndexedDBCursor;
 include protocol PIndexedDBObjectStore;
 include protocol PIndexedDBRequest;
 
-include "mozilla/dom/indexedDB/SerializationHelpers.h";
+include IndexedDBParams;
 
-using mozilla::dom::indexedDB::Key;
-using mozilla::dom::indexedDB::IDBCursor::Direction;
 using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
 
-using mozilla::void_t;
-
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 namespace ipc {
 
-namespace FIXME_Bug_521898_index {
-
-struct KeyRange
-{
-  Key lower;
-  Key upper;
-  bool lowerOpen;
-  bool upperOpen;
-  bool isOnly;
-};
-
-union OptionalKeyRange
-{
-  KeyRange;
-  void_t;
-};
-
-struct GetParams
-{
-  KeyRange keyRange;
-};
-
-struct GetAllParams
-{
-  OptionalKeyRange optionalKeyRange;
-  uint32_t limit;
-};
-
-struct CountParams
-{
-  OptionalKeyRange optionalKeyRange;
-};
-
-struct OpenCursorParams
-{
-  OptionalKeyRange optionalKeyRange;
-  Direction direction;
-};
-
-} // namespace FIXME_Bug_521898_index
-
 struct GetKeyParams
 {
   KeyRange keyRange;
 };
 
 struct GetAllKeysParams
 {
   OptionalKeyRange optionalKeyRange;
--- a/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBObjectStore.ipdl
@@ -3,74 +3,29 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBlob;
 include protocol PIndexedDBCursor;
 include protocol PIndexedDBIndex;
 include protocol PIndexedDBRequest;
 include protocol PIndexedDBTransaction;
 
-include "mozilla/dom/indexedDB/SerializationHelpers.h";
+include IndexedDBParams;
 
-using mozilla::dom::indexedDB::Key;
-using mozilla::dom::indexedDB::IDBCursor::Direction;
 using mozilla::dom::indexedDB::IndexInfo;
 using mozilla::dom::indexedDB::IndexUpdateInfo;
 using mozilla::dom::indexedDB::SerializedStructuredCloneReadInfo;
 using mozilla::dom::indexedDB::SerializedStructuredCloneWriteInfo;
 
-using mozilla::void_t;
-
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 namespace ipc {
 
-namespace FIXME_Bug_521898_objectstore {
-
-struct KeyRange
-{
-  Key lower;
-  Key upper;
-  bool lowerOpen;
-  bool upperOpen;
-  bool isOnly;
-};
-
-union OptionalKeyRange
-{
-  KeyRange;
-  void_t;
-};
-
-struct GetParams
-{
-  KeyRange keyRange;
-};
-
-struct GetAllParams
-{
-  OptionalKeyRange optionalKeyRange;
-  uint32_t limit;
-};
-
-struct CountParams
-{
-  OptionalKeyRange optionalKeyRange;
-};
-
-struct OpenCursorParams
-{
-  OptionalKeyRange optionalKeyRange;
-  Direction direction;
-};
-
-} // namespace FIXME_Bug_521898_objectstore
-
 struct AddPutParams
 {
   SerializedStructuredCloneWriteInfo cloneInfo;
   Key key;
   IndexUpdateInfo[] indexUpdateInfos;
   PBlob[] blobs;
 };
 
@@ -86,17 +41,16 @@ struct PutParams
 
 struct DeleteParams
 {
   KeyRange keyRange;
 };
 
 struct ClearParams
 {
-  void_t FIXME_Bug_753159;
 };
 
 union ObjectStoreRequestParams
 {
   GetParams;
   GetAllParams;
   AddParams;
   PutParams;
--- a/dom/indexedDB/ipc/PIndexedDBRequest.ipdl
+++ b/dom/indexedDB/ipc/PIndexedDBRequest.ipdl
@@ -53,24 +53,20 @@ struct AddResponse
 };
 
 struct PutResponse
 {
   Key key;
 };
 
 struct DeleteResponse
-{
-  void_t FIXME_Bug_753159;
-};
+{ };
 
 struct ClearResponse
-{
-  void_t FIXME_Bug_753159;
-};
+{ };
 
 struct CountResponse
 {
   uint64_t count;
 };
 
 union OpenCursorResponse
 {
--- a/dom/indexedDB/ipc/moz.build
+++ b/dom/indexedDB/ipc/moz.build
@@ -14,16 +14,17 @@ EXPORTS.mozilla.dom.indexedDB += [
 #XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 
 CPP_SOURCES += [
     'IndexedDBChild.cpp',
     'IndexedDBParent.cpp',
 ]
 
 IPDL_SOURCES += [
+    'IndexedDBParams.ipdlh',
     'PIndexedDB.ipdl',
     'PIndexedDBCursor.ipdl',
     'PIndexedDBDatabase.ipdl',
     'PIndexedDBDeleteDatabaseRequest.ipdl',
     'PIndexedDBIndex.ipdl',
     'PIndexedDBObjectStore.ipdl',
     'PIndexedDBRequest.ipdl',
     'PIndexedDBTransaction.ipdl',
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -10,18 +10,18 @@
  * to the current nsIDOMWindow.  Some of the methods may require
  * elevated privileges; the method implementations should contain the
  * necessary security checks.  Access this interface by calling
  * getInterface on a DOMWindow.
  */
 
 %{C++
 #include "nsColor.h"
-#include "nsRect.h"
-#include "gfxContext.h"
+class gfxContext;
+class nsRect;
 %}
 
 [ref] native nsConstRect(const nsRect);
 native nscolor(nscolor);
 [ptr] native gfxContext(gfxContext);
 typedef unsigned long long nsViewID;
 
 interface nsICycleCollectorListener;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -889,17 +889,18 @@ TabChild::GetTitle(PRUnichar** aTitle)
   NS_NOTREACHED("TabChild::GetTitle not supported in TabChild");
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 TabChild::SetTitle(const PRUnichar* aTitle)
 {
-  // FIXME/bug 617804: should the platform support this?
+  // JavaScript sends the "DOMTitleChanged" event to the parent
+  // via the message manager.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabChild::GetSiteWindow(void** aSiteWindow)
 {
   NS_NOTREACHED("TabChild::GetSiteWindow not supported in TabChild");
 
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -130,20 +130,20 @@ NPObjWrapper_NewResolve(JSContext *cx, J
                         JS::MutableHandle<JSObject*> objp);
 
 static JSBool
 NPObjWrapper_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp);
 
 static void
 NPObjWrapper_Finalize(JSFreeOp *fop, JSObject *obj);
 
-static JSBool
+static bool
 NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp);
 
-static JSBool
+static bool
 NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static JSBool
 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject *npobj,
                      JS::Handle<jsid> id, NPVariant* getPropertyResult, JS::Value *vp);
 
 JSClass sNPObjectJSWrapperClass =
   {
@@ -171,17 +171,17 @@ typedef struct NPObjectMemberPrivate {
 } NPObjectMemberPrivate;
 
 static JSBool
 NPObjectMember_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp);
 
 static void
 NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj);
 
-static JSBool
+static bool
 NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp);
 
 static void
 NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
 
 static JSClass sNPObjectMemberClass =
   {
     "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
@@ -1447,22 +1447,22 @@ CallNPMethodInternal(JSContext *cx, JS::
   *rval = NPVariantToJSVal(npp, cx, &v);
 
   // *rval now owns the value, release our reference.
   _releasevariantvalue(&v);
 
   return ReportExceptionIfPending(cx);
 }
 
-static JSBool
+static bool
 CallNPMethod(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
-      return JS_FALSE;
+      return false;
 
   return CallNPMethodInternal(cx, obj, argc, JS_ARGV(cx, vp), vp, false);
 }
 
 struct NPObjectEnumerateState {
   uint32_t     index;
   uint32_t     length;
   NPIdentifier *value;
@@ -1648,30 +1648,28 @@ NPObjWrapper_Finalize(JSFreeOp *fop, JSO
     }
   }
 
   if (!sDelayedReleases)
     sDelayedReleases = new nsTArray<NPObject*>;
   sDelayedReleases->AppendElement(npobj);
 }
 
-static JSBool
+static bool
 NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::Rooted<JSObject*> obj(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
-  return CallNPMethodInternal(cx, obj, argc,
-                              JS_ARGV(cx, vp), vp, false);
+  return CallNPMethodInternal(cx, obj, argc, JS_ARGV(cx, vp), vp, false);
 }
 
-static JSBool
+static bool
 NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JS::Rooted<JSObject*> obj(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
-  return CallNPMethodInternal(cx, obj, argc,
-                              JS_ARGV(cx, vp), vp, true);
+  return CallNPMethodInternal(cx, obj, argc, JS_ARGV(cx, vp), vp, true);
 }
 
 class NPObjWrapperHashEntry : public PLDHashEntryHdr
 {
 public:
   NPObject *mNPObj; // Must be the first member for the PLDHash stubs to work
   JSObject *mJSObj;
   NPP mNpp;
@@ -2063,63 +2061,63 @@ NPObjectMember_Finalize(JSFreeOp *fop, J
 
   memberPrivate = (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
   if (!memberPrivate)
     return;
 
   PR_Free(memberPrivate);
 }
 
-static JSBool
+static bool
 NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp)
 {
   JSObject *memobj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
-  NS_ENSURE_TRUE(memobj, JS_FALSE);
+  NS_ENSURE_TRUE(memobj, false);
 
   NPObjectMemberPrivate *memberPrivate =
     (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, memobj,
                                                      &sNPObjectMemberClass,
                                                      JS_ARGV(cx, vp));
   if (!memberPrivate || !memberPrivate->npobjWrapper)
-    return JS_FALSE;
+    return false;
 
   NPObject *npobj = GetNPObject(cx, memberPrivate->npobjWrapper);
   if (!npobj) {
     ThrowJSException(cx, "Call on invalid member object");
 
-    return JS_FALSE;
+    return false;
   }
 
   NPVariant npargs_buf[8];
   NPVariant *npargs = npargs_buf;
 
   if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
     // Our stack buffer isn't large enough to hold all arguments,
     // malloc a buffer.
     npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant));
 
     if (!npargs) {
       ThrowJSException(cx, "Out of memory!");
 
-      return JS_FALSE;
+      return false;
     }
   }
 
   // Convert arguments
   uint32_t i;
   JS::Value *argv = JS_ARGV(cx, vp);
   for (i = 0; i < argc; ++i) {
     if (!JSValToNPVariant(memberPrivate->npp, cx, argv[i], npargs + i)) {
       ThrowJSException(cx, "Error converting jsvals to NPVariants!");
 
       if (npargs != npargs_buf) {
         PR_Free(npargs);
       }
 
-      return JS_FALSE;
+      return false;
     }
   }
 
 
   NPVariant npv;
   JSBool ok;
   ok = npobj->_class->invoke(npobj, JSIdToNPIdentifier(memberPrivate->methodName),
                              npargs, argc, &npv);
@@ -2134,17 +2132,17 @@ NPObjectMember_Call(JSContext *cx, unsig
   }
 
   if (!ok) {
     // ReportExceptionIfPending returns a return value, which is JS_TRUE
     // if no exception was thrown. In that case, throw our own.
     if (ReportExceptionIfPending(cx))
       ThrowJSException(cx, "Error calling method on NPObject!");
 
-    return JS_FALSE;
+    return false;
   }
 
   JS_SET_RVAL(cx, vp, NPVariantToJSVal(memberPrivate->npp, cx, &npv));
 
   // *vp now owns the value, release our reference.
   _releasevariantvalue(&npv);
 
   return ReportExceptionIfPending(cx);
--- a/dom/system/gonk/SystemWorkerManager