Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 07 Aug 2013 14:40:37 -0400
changeset 141686 79b5c74ef97be205dfaf9248154632af1f806dd6
parent 141685 47bd850cb89b3cd03b180d75fba6670ed40701a4 (current diff)
parent 141626 67adb1fa99369b518d5956c75ec96aa2c38e569a (diff)
child 141706 0ace1c661c1133326547cbdab5bbeb15ecf08ca7
child 141726 ab34f6dd82d1d8f8c7cd4fc6bb9f7aff9209e869
child 155694 91d9af7d51bdbcf4ab1fb032033b4f8ee5bea26c
push id32205
push useremorley@mozilla.com
push dateWed, 07 Aug 2013 23:19:13 +0000
treeherdermozilla-inbound@56912957f3e9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone26.0a1
first release with
nightly linux32
79b5c74ef97b / 26.0a1 / 20130807161117 / files
nightly linux64
79b5c74ef97b / 26.0a1 / 20130807161117 / files
nightly mac
79b5c74ef97b / 26.0a1 / 20130807161117 / files
nightly win32
79b5c74ef97b / 26.0a1 / 20130807161117 / files
nightly win64
79b5c74ef97b / 26.0a1 / 20130807161117 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c.
browser/modules/AboutHomeUtils.jsm
mobile/android/base/resources/layout/awesomebar_actionbar.xml
widget/gtk2/compat/gtk/gtkcolorseldialog.h
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)) {