Merge m-c to birch.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 10 Jun 2013 20:29:12 -0400
changeset 146103 244eff3f194eec36488594a8e16b1f7d9d3a4bea
parent 146102 2f82ac9f123e936f663c6081a806df6f2f1d0169 (current diff)
parent 146049 d79910d9e251d19669dbc8fb0a5f31b8d8b2d6ed (diff)
child 146104 761e842067d8c5bdb5824cdada9341b4d6b411bc
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to birch.
--- a/accessible/src/jsat/AccessFu.jsm
+++ b/accessible/src/jsat/AccessFu.jsm
@@ -36,17 +36,19 @@ this.AccessFu = {
     } catch (x) {
       // Not on Android
       if (Utils.MozBuildApp === 'b2g') {
         aWindow.addEventListener('ContentStart', this, false);
       }
     }
 
     this._activatePref = new PrefCache(
-      'accessibility.accessfu.activate', this._enableOrDisable.bind(this), true);
+      'accessibility.accessfu.activate', this._enableOrDisable.bind(this));
+
+    this._enableOrDisable();
   },
 
   /**
    * Shut down chrome-layer accessibility functionality from the outside.
    */
   detach: function detach() {
     // Avoid disabling twice.
     if (this._enabled) {
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -378,54 +378,51 @@ PivotContext.prototype = {
     }
 
     return this._newAncestry;
   },
 
   /*
    * Traverse the accessible's subtree in pre or post order.
    * It only includes the accessible's visible chidren.
+   * Note: needSubtree is a function argument that can be used to determine
+   * whether aAccessible's subtree is required.
    */
-  _traverse: function _traverse(aAccessible, preorder) {
-    let list = [];
+  _traverse: function _traverse(aAccessible, aPreorder, aStop) {
+    if (aStop && aStop(aAccessible)) {
+      return;
+    }
     let child = aAccessible.firstChild;
     while (child) {
       let state = {};
       child.getState(state, {});
       if (!(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE)) {
-        let traversed = _traverse(child, preorder);
-        // Prepend or append a child, based on traverse order.
-        traversed[preorder ? "unshift" : "push"](child);
-        list.push.apply(list, traversed);
+        if (aPreorder) {
+          yield child;
+          [yield node for (node of this._traverse(child, aPreorder, aStop))];
+        } else {
+          [yield node for (node of this._traverse(child, aPreorder, aStop))];
+          yield child;
+        }
       }
       child = child.nextSibling;
     }
-    return list;
   },
 
   /*
-   * This is a flattened list of the accessible's subtree in preorder.
+   * A subtree generator function, used to generate a flattened
+   * list of the accessible's subtree in pre or post order.
    * It only includes the accessible's visible chidren.
+   * @param {boolean} aPreorder A flag for traversal order. If true, traverse
+   * in preorder; if false, traverse in postorder.
+   * @param {function} aStop An optional function, indicating whether subtree
+   * traversal should stop.
    */
-  get subtreePreorder() {
-    if (!this._subtreePreOrder)
-      this._subtreePreOrder = this._traverse(this._accessible, true);
-
-    return this._subtreePreOrder;
-  },
-
-  /*
-   * This is a flattened list of the accessible's subtree in postorder.
-   * It only includes the accessible's visible chidren.
-   */
-  get subtreePostorder() {
-    if (!this._subtreePostOrder)
-      this._subtreePostOrder = this._traverse(this._accessible, false);
-
-    return this._subtreePostOrder;
+  subtreeGenerator: function subtreeGenerator(aPreorder, aStop) {
+    return this._traverse(this._accessible, aPreorder, aStop);
   },
 
   get bounds() {
     if (!this._bounds) {
       let objX = {}, objY = {}, objW = {}, objH = {};
 
       this._accessible.getBounds(objX, objY, objW, objH);
 
--- a/accessible/src/jsat/UtteranceGenerator.jsm
+++ b/accessible/src/jsat/UtteranceGenerator.jsm
@@ -70,34 +70,34 @@ this.UtteranceGenerator = {
    *    starting from the accessible's ancestry or accessible's subtree.
    */
   genForContext: function genForContext(aContext) {
     let utterance = [];
     let addUtterance = function addUtterance(aAccessible) {
       utterance.push.apply(utterance,
         UtteranceGenerator.genForObject(aAccessible));
     };
-    let roleString = Utils.AccRetrieval.getStringRole(aContext.accessible.role);
-    let nameRule = this.roleRuleMap[roleString] || 0;
+    let ignoreSubtree = function ignoreSubtree(aAccessible) {
+      let roleString = Utils.AccRetrieval.getStringRole(aAccessible.role);
+      let nameRule = UtteranceGenerator.roleRuleMap[roleString] || 0;
+      // Ignore subtree if the name is explicit and the role's name rule is the
+      // NAME_FROM_SUBTREE_RULE.
+      return (nameRule & NAME_FROM_SUBTREE_RULE) &&
+        (Utils.getAttributes(aAccessible)['explicit-name'] === 'true');
+    };
     let utteranceOrder = gUtteranceOrder.value || UTTERANCE_DESC_FIRST;
-    // Include subtree if the name is not explicit or the role's name rule is
-    // not the NAME_FROM_SUBTREE_RULE.
-    let includeSubtree = (Utils.getAttributes(aContext.accessible)[
-      'explicit-name'] !== 'true') || !(nameRule & NAME_FROM_SUBTREE_RULE);
 
     if (utteranceOrder === UTTERANCE_DESC_FIRST) {
       aContext.newAncestry.forEach(addUtterance);
       addUtterance(aContext.accessible);
-      if (includeSubtree) {
-        aContext.subtreePreorder.forEach(addUtterance);
-      }
+      [addUtterance(node) for
+        (node of aContext.subtreeGenerator(true, ignoreSubtree))];
     } else {
-      if (includeSubtree) {
-        aContext.subtreePostorder.forEach(addUtterance);
-      }
+      [addUtterance(node) for
+        (node of aContext.subtreeGenerator(false, ignoreSubtree))];
       addUtterance(aContext.accessible);
       aContext.newAncestry.reverse().forEach(addUtterance);
     }
 
     // Clean up the white space.
     let trimmed;
     utterance = [trimmed for (word of utterance) if (trimmed = word.trim())];
 
--- a/accessible/tests/mochitest/jsat/test_explicit_names.html
+++ b/accessible/tests/mochitest/jsat/test_explicit_names.html
@@ -36,17 +36,17 @@
         accOrElmOrID: "heading1",
         expected: ["heading level 1", "Test heading", "This is the heading."]
       }, {
         accOrElmOrID: "heading2",
         expected: ["heading level 1", "This is the heading."]
       }, {
         accOrElmOrID: "list",
         expected: ["list 2 items", "Test List", "First item", "Top of the list",
-          "1.", "list one", "Last item", "2.", "list two"]
+          "Last item", "2.", "list two"]
       }, {
         accOrElmOrID: "dlist",
         expected: ["definition list 0.5 items", "Test Definition List",
           "dd one"]
       }, {
         accOrElmOrID: "li_one",
         expected: ["list 2 items", "Test List", "First item", "Top of the list"]
       }, {
@@ -61,17 +61,17 @@
         accOrElmOrID: "app.net",
         expected: ["list 2 items", "First item", "link", "star", "Last item",
           "link", "repost"]
       }, {
         // Test pivot to list from li_one.
         accOrElmOrID: "list",
         oldAccOrElmOrID: "li_one",
         expected: ["list 2 items", "Test List", "First item", "Top of the list",
-          "1.", "list one", "Last item", "2.", "list two"]
+          "Last item", "2.", "list two"]
       }, {
         // Test pivot to li_one from list.
         accOrElmOrID: "li_one",
         oldAccOrElmOrID: "list",
         expected: ["list 2 items", "Test List", "First item", "Top of the list"]
       }, {
         // Test pivot to "apples" link from the table cell.
         accOrElmOrID: "apples",
@@ -146,18 +146,18 @@
             </li>
           </ul>
         </td>
       </tr>
     </table>
     <!-- app.net -->
     <ul id="app.net" class="unstyled ul-horizontal yui3-u fixed-right ta-right" style="list-style-type: none;">
       <li class="yui3-u">
-        <a href="#star" data-starred="0" data-star-button="1" data-post-id="5098826">
-          <i aria-label="star" class="icon-star-empty"></i>
+        <a href="#star" data-starred="0" data-star-button="1" data-post-id="5098826" aria-label="star">
+          Garbage
         </a>
       </li>
       <li class="yui3-u repost">
         <a href="#repost" title="repost" data-repost-button="1" data-reposted="0" data-post-id="5098826">
           <i aria-label="repost" class="icon-repost"></i>
         </a>
       </li>
     </ul>
--- a/browser/metro/base/content/browser-ui.js
+++ b/browser/metro/base/content/browser-ui.js
@@ -1132,17 +1132,20 @@ var ContextUI = {
 
     Appbar.init();
   },
 
   /*******************************************
    * Context UI state getters & setters
    */
 
-  get isVisible() { return Elements.tray.hasAttribute("visible"); },
+  get isVisible() {
+    return (Elements.navbar.hasAttribute("visible") ||
+            Elements.navbar.hasAttribute("startpage"));
+  },
   get isExpanded() { return Elements.tray.hasAttribute("expanded"); },
   get isExpandable() { return this._expandable; },
 
   set isExpandable(aFlag) {
     this._expandable = aFlag;
     if (!this._expandable)
       this.dismiss();
   },
@@ -1168,44 +1171,38 @@ var ContextUI = {
   // returns true if any non-visible UI was shown
   show: function() {
     let shown = false;
     if (!this.isExpanded) {
       // show the tab tray
       this._setIsExpanded(true);
       shown = true;
     }
-    if (!this.isVisible) {
-      // show the navbar
-      this._setIsVisible(true);
-      shown = true;
-    }
     if (!Elements.navbar.isShowing) {
       // show the navbar
       Elements.navbar.show();
       shown = true;
     }
 
     this._clearDelayedTimeout();
     if (shown) {
       ContentAreaObserver.update(window.innerWidth, window.innerHeight);
     }
     return shown;
   },
 
   // Display the nav bar
   displayNavbar: function displayNavbar() {
     this._clearDelayedTimeout();
-    this._setIsVisible(true, true);
+    Elements.navbar.show();
   },
 
   // Display the toolbar and tabs
   displayTabs: function displayTabs() {
     this._clearDelayedTimeout();
-    this._setIsVisible(true, true);
     this._setIsExpanded(true, true);
   },
 
   /** Briefly show the tab bar and then hide it */
   peekTabs: function peekTabs() {
     if (this.isExpanded) {
       setTimeout(function () {
         ContextUI.dismissWithDelay(kNewTabAnimationDelayMsec);
@@ -1222,20 +1219,16 @@ var ContextUI = {
   // Dismiss all context UI.
   // Returns true if any visible UI was dismissed.
   dismiss: function dismiss() {
     let dismissed = false;
     if (this.isExpanded) {
       this._setIsExpanded(false);
       dismissed = true;
     }
-    if (this.isVisible && !StartUI.isStartURI()) {
-      this._setIsVisible(false);
-      dismissed = true;
-    }
     if (Elements.navbar.isShowing) {
       this.dismissAppbar();
       dismissed = true;
     }
     this._clearDelayedTimeout();
     if (dismissed) {
       ContentAreaObserver.update(window.innerWidth, window.innerHeight);
     }
@@ -1264,34 +1257,16 @@ var ContextUI = {
   dismissAppbar: function dismissAppbar() {
     this._fire("MozAppbarDismiss");
   },
 
   /*******************************************
    * Internal tray state setters
    */
 
-  // url bar state
-  _setIsVisible: function _setIsVisible(aFlag, setSilently) {
-    if (this.isVisible == aFlag)
-      return;
-
-    if (aFlag)
-      Elements.tray.setAttribute("visible", "true");
-    else
-      Elements.tray.removeAttribute("visible");
-
-    if (!aFlag) {
-      content.focus();
-    }
-
-    if (!setSilently)
-      this._fire(aFlag ? "MozContextUIShow" : "MozContextUIDismiss");
-  },
-
   // tab tray state
   _setIsExpanded: function _setIsExpanded(aFlag, setSilently) {
     // if the tray can't be expanded, don't expand it.
     if (!this.isExpandable || this.isExpanded == aFlag)
       return;
 
     if (aFlag)
       Elements.tray.setAttribute("expanded", "true");
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -175,17 +175,17 @@
     <key id="key_selectTab7" oncommand="BrowserUI.selectTabAtIndex(6);" key="7" modifiers="accel"/>
     <key id="key_selectTab8" oncommand="BrowserUI.selectTabAtIndex(7);" key="8" modifiers="accel"/>
     <key id="key_selectLastTab" oncommand="BrowserUI.selectTabAtIndex(-1);" key="9" modifiers="accel"/>
   </keyset>
 
   <stack id="stack" flex="1">
     <!-- Page Area -->
     <vbox id="page">
-      <vbox id="tray" class="tray-toolbar" visible="true" observes="bcast_windowState" >
+      <vbox id="tray" class="tray-toolbar" observes="bcast_windowState" >
         <!-- Tabs -->
         <hbox id="tabs-container" observes="bcast_windowState">
           <box id="tabs" flex="1"
                 observes="bcast_preciseInput"
                 onselect="BrowserUI.selectTabAndDismiss(this);"
                 onclosetab="BrowserUI.closeTab(this);"/>
           <vbox id="tabs-controls">
             <toolbarbutton id="newtab-button" command="cmd_newTab" label="&newtab.label;"/>
--- a/browser/metro/base/tests/mochitest/browser_context_ui.js
+++ b/browser/metro/base/tests/mochitest/browser_context_ui.js
@@ -8,61 +8,66 @@
 function test() {
   runTests();
 }
 
 gTests.push({
   desc: "Context UI on about:start",
   run: function testAboutStart() {
     yield addTab("about:start");
+
+    yield waitForCondition(function () {
+      return StartUI.isStartPageVisible;
+      });
+
     is(StartUI.isVisible, true, "Start UI is displayed on about:start");
-    is(ContextUI.isVisible, true, "Toolbar is displayed on about:start");
-    is(ContextUI.isExpanded, false, "Tab bar is not displayed initially");
+    is(ContextUI.isVisible, true, "Navbar is displayed on about:start");
+    is(ContextUI.isExpanded, false, "Tabbar is not displayed initially");
     is(Elements.navbar.isShowing, false, "Appbar is not displayed initially");
 
     // toggle on
     doEdgeUIGesture();
-    is(ContextUI.isVisible, true, "Toolbar is still visible after one swipe");
-    is(ContextUI.isExpanded, true, "Tab bar is visible after one swipe");
+    is(ContextUI.isVisible, true, "Navbar is still visible after one swipe");
+    is(ContextUI.isExpanded, true, "Tabbar is visible after one swipe");
     is(Elements.navbar.isShowing, true, "Appbar is visible after one swipe");
 
     // toggle off
     doEdgeUIGesture();
-    is(ContextUI.isVisible, true, "Toolbar is still visible after second swipe");
-    is(ContextUI.isExpanded, false, "Tab bar is hidden after second swipe");
+    is(ContextUI.isVisible, true, "Navbar is still visible after second swipe");
+    is(ContextUI.isExpanded, false, "Tabbar is hidden after second swipe");
     is(Elements.navbar.isShowing, false, "Appbar is hidden after second swipe");
 
     // sanity check - toggle on again
     doEdgeUIGesture();
-    is(ContextUI.isVisible, true, "Toolbar is still visible after third swipe");
-    is(ContextUI.isExpanded, true, "Tab bar is visible after third swipe");
+    is(ContextUI.isVisible, true, "Navbar is still visible after third swipe");
+    is(ContextUI.isExpanded, true, "Tabbar is visible after third swipe");
     is(Elements.navbar.isShowing, true, "Appbar is visible after third swipe");
 
     is(StartUI.isVisible, true, "Start UI is still visible");
   }
 });
 
 gTests.push({
   desc: "Context UI on a web page (about:)",
   run: function testAbout() {
     yield addTab("about:");
     ContextUI.dismiss();
     is(StartUI.isVisible, false, "Start UI is not visible on about:");
-    is(ContextUI.isVisible, false, "Toolbar is not initially visible on about:");
-    is(ContextUI.isExpanded, false, "Tab bar is not initially visible on about:");
+    is(ContextUI.isVisible, false, "Navbar is not initially visible on about:");
+    is(ContextUI.isExpanded, false, "Tabbar is not initially visible on about:");
     is(Elements.navbar.isShowing, false, "Appbar is not initially visible on about on about::");
 
     doEdgeUIGesture();
-    is(ContextUI.isVisible, true, "Toolbar is visible after one swipe");
-    is(ContextUI.isExpanded, true, "Tab bar is visble after one swipe");
+    is(ContextUI.isVisible, true, "Navbar is visible after one swipe");
+    is(ContextUI.isExpanded, true, "Tabbar is visble after one swipe");
     is(Elements.navbar.isShowing, true, "Appbar is visible after one swipe");
 
     doEdgeUIGesture();
-    is(ContextUI.isVisible, false, "Toolbar is not visible after second swipe");
-    is(ContextUI.isExpanded, false, "Tab bar is not visible after second swipe");
+    is(ContextUI.isVisible, false, "Navbar is not visible after second swipe");
+    is(ContextUI.isExpanded, false, "Tabbar is not visible after second swipe");
     is(Elements.navbar.isShowing, false, "Appbar is hidden after second swipe");
 
     is(StartUI.isVisible, false, "Start UI is still not visible");
   }
 });
 
 gTests.push({
   desc: "Control-L keyboard shortcut",
--- a/browser/metro/base/tests/mochitest/browser_selection_frame_content.html
+++ b/browser/metro/base/tests/mochitest/browser_selection_frame_content.html
@@ -1,15 +1,15 @@
 <!DOCTYPE html>
 <html>
   <head>
   </head>
 <body>
 <div>
-<iframe id="frame1" style="width:50em; font-size: 95%;" height="600"  src="res/textblock01.html"></iframe>
+<iframe id="frame1" style="overflow:hidden; width:50em; font-size: 95%;" height="600"  src="res/textblock01.html"></iframe>
 <br />
 <br />
 Hello there. <a id="rlink1" href="#hello">Hi!</a>
 <br />
 <br />
 <br />
 <br />
 <br />
@@ -38,11 +38,30 @@ Hello there. <a id="rlink1" href="#hello
 <br />
 <br />
 <br />
 <br />
 <br />
 <br />
 <br />
 <br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
+<br />
 </div>
 </body>
 </html>
--- a/browser/metro/theme/browser.css
+++ b/browser/metro/theme/browser.css
@@ -47,17 +47,17 @@
   transform: translateY(-@tabs_height@);
   width: 100%;
 }
 
 #tray {
   position: fixed;
 }
 
-#tray[visible][expanded]:not([viewstate="snapped"]) {
+#tray[expanded]:not([viewstate="snapped"]) {
   transform: none;
 }
 
 /* Tabs --------------------------------------------------------------------- */
 
 #tabs-container {
   background: @panel_dark_color@ @panel_dark_background@;
   padding: 0;
--- a/content/html/content/src/HTMLTrackElement.cpp
+++ b/content/html/content/src/HTMLTrackElement.cpp
@@ -3,16 +3,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/. */
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTrackElement.h"
 #include "mozilla/dom/HTMLTrackElementBinding.h"
 #include "mozilla/dom/HTMLUnknownElement.h"
+#include "WebVTTLoadListener.h"
 #include "nsAttrValueInlines.h"
 #include "nsCOMPtr.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
@@ -82,23 +83,33 @@ HTMLTrackElement::~HTMLTrackElement()
 {
 }
 
 NS_IMPL_ELEMENT_CLONE(HTMLTrackElement)
 
 NS_IMPL_ADDREF_INHERITED(HTMLTrackElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLTrackElement, Element)
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED_3(HTMLTrackElement, nsGenericHTMLElement,
-                                     mTrack, mChannel, mMediaParent)
+NS_IMPL_CYCLE_COLLECTION_INHERITED_4(HTMLTrackElement, nsGenericHTMLElement,
+                                     mTrack, mChannel, mMediaParent,
+                                     mLoadListener)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLTrackElement)
   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLElement)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
+void
+HTMLTrackElement::OnChannelRedirect(nsIChannel* aChannel,
+                                    nsIChannel* aNewChannel,
+                                    uint32_t aFlags)
+{
+  NS_ASSERTION(aChannel == mChannel, "Channels should match!");
+  mChannel = aNewChannel;
+}
+
 JSObject*
 HTMLTrackElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return HTMLTrackElementBinding::Wrap(aCx, aScope, this);
 }
 
 bool
 HTMLTrackElement::IsWebVTTEnabled()
@@ -114,22 +125,16 @@ HTMLTrackElement::Track()
     // an empty object to return if we don't already have one.
     mTrack = new TextTrack(OwnerDoc()->GetParentObject());
   }
 
   return mTrack;
 }
 
 void
-HTMLTrackElement::DisplayCueText(webvtt_node* head)
-{
-  // TODO: Bug 833382 - Propagate to the LoadListener.
-}
-
-void
 HTMLTrackElement::CreateTextTrack()
 {
   DOMString label, srcLang;
   GetSrclang(srcLang);
   GetLabel(label);
   mTrack = new TextTrack(OwnerDoc()->GetParentObject(), Kind(), label, srcLang);
 
   if (mMediaParent) {
@@ -186,16 +191,95 @@ HTMLTrackElement::ParseAttribute(int32_t
 
   // Otherwise call the generic implementation.
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID,
                                               aAttribute,
                                               aValue,
                                               aResult);
 }
 
+void
+HTMLTrackElement::LoadResource()
+{
+  // Find our 'src' url
+  nsAutoString src;
+  if (!GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
+    return;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NewURIFromString(src, getter_AddRefs(uri));
+  NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
+  LOG(PR_LOG_ALWAYS, ("%p Trying to load from src=%s", this,
+      NS_ConvertUTF16toUTF8(src).get()));
+
+  if (mChannel) {
+    mChannel->Cancel(NS_BINDING_ABORTED);
+    mChannel = nullptr;
+  }
+
+  rv = nsContentUtils::GetSecurityManager()->
+    CheckLoadURIWithPrincipal(NodePrincipal(), uri,
+                              nsIScriptSecurityManager::STANDARD);
+  NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
+
+  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+  rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_MEDIA,
+                                 uri,
+                                 NodePrincipal(),
+                                 static_cast<nsGenericHTMLElement*>(this),
+                                 NS_LITERAL_CSTRING("text/vtt"), // mime type
+                                 nullptr, // extra
+                                 &shouldLoad,
+                                 nsContentUtils::GetContentPolicy(),
+                                 nsContentUtils::GetSecurityManager());
+  NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
+  if (NS_CP_REJECTED(shouldLoad)) {
+    return;
+  }
+
+  CreateTextTrack();
+
+  // Check for a Content Security Policy to pass down to the channel
+  // created to load the media content.
+  nsCOMPtr<nsIChannelPolicy> channelPolicy;
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  rv = NodePrincipal()->GetCsp(getter_AddRefs(csp));
+  NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
+  if (csp) {
+    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
+    if (!channelPolicy) {
+      return;
+    }
+    channelPolicy->SetContentSecurityPolicy(csp);
+    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_MEDIA);
+  }
+  nsCOMPtr<nsIChannel> channel;
+  nsCOMPtr<nsILoadGroup> loadGroup = OwnerDoc()->GetDocumentLoadGroup();
+  rv = NS_NewChannel(getter_AddRefs(channel),
+                     uri,
+                     nullptr,
+                     loadGroup,
+                     nullptr,
+                     nsIRequest::LOAD_NORMAL,
+                     channelPolicy);
+  NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
+
+  mLoadListener = new WebVTTLoadListener(this);
+  rv = mLoadListener->LoadResource();
+  NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
+  channel->SetNotificationCallbacks(mLoadListener);
+
+  LOG(PR_LOG_DEBUG, ("opening webvtt channel"));
+  rv = channel->AsyncOpen(mLoadListener, nullptr);
+  NS_ENSURE_TRUE_VOID(NS_SUCCEEDED(rv));
+
+  mChannel = channel;
+}
+
 nsresult
 HTMLTrackElement::BindToTree(nsIDocument* aDocument,
                              nsIContent* aParent,
                              nsIContent* aBindingParent,
                              bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument,
                                                  aParent,
@@ -216,33 +300,18 @@ HTMLTrackElement::BindToTree(nsIDocument
   if (!mMediaParent) {
     mMediaParent = static_cast<HTMLMediaElement*>(aParent);
 
     HTMLMediaElement* media = static_cast<HTMLMediaElement*>(aParent);
     // TODO: separate notification for 'alternate' tracks?
     media->NotifyAddedSource();
     LOG(PR_LOG_DEBUG, ("Track element sent notification to parent."));
 
-    // TODO: this section needs to become async in bug 833382.
-    // See https://bugzilla.mozilla.org/show_bug.cgi?id=833385#c55.
-
-    // Find our 'src' url
-    nsAutoString src;
-
-    // TODO: we might want to instead call LoadResource() in a
-    // AfterSetAttr, like we do in media element.
-    if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
-      nsCOMPtr<nsIURI> uri;
-      nsresult rvTwo = NewURIFromString(src, getter_AddRefs(uri));
-      if (NS_SUCCEEDED(rvTwo)) {
-        LOG(PR_LOG_ALWAYS, ("%p Trying to load from src=%s", this,
-        NS_ConvertUTF16toUTF8(src).get()));
-        // TODO: bug 833382 - dispatch a load request.
-      }
-    }
+    nsContentUtils::AddScriptRunner(
+      NS_NewRunnableMethod(this, &HTMLTrackElement::LoadResource));
   }
 
   return NS_OK;
 }
 
 void
 HTMLTrackElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
--- a/content/html/content/src/HTMLTrackElement.h
+++ b/content/html/content/src/HTMLTrackElement.h
@@ -15,21 +15,22 @@
 #include "nsCycleCollectionParticipant.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIHttpChannel.h"
-#include "webvtt/node.h"
 
 namespace mozilla {
 namespace dom {
 
+class WebVTTLoadListener;
+
 class HTMLTrackElement MOZ_FINAL : public nsGenericHTMLElement
                                  , public nsIDOMHTMLElement
 {
 public:
   HTMLTrackElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   virtual ~HTMLTrackElement();
 
   // nsISupports
@@ -125,28 +126,36 @@ public:
   // Override BindToTree() so that we can trigger a load when we become
   // the child of a media element.
   virtual nsresult BindToTree(nsIDocument* aDocument,
                               nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) MOZ_OVERRIDE;
   virtual void UnbindFromTree(bool aDeep, bool aNullParent) MOZ_OVERRIDE;
 
-  void DisplayCueText(webvtt_node* head);
-
   // Check enabling preference.
   static bool IsWebVTTEnabled();
 
 protected:
   virtual JSObject* WrapNode(JSContext* aCx,
                              JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+  void OnChannelRedirect(nsIChannel* aChannel, nsIChannel* aNewChannel,
+                         uint32_t aFlags);
+  // Will open a new channel for the HTMLTrackElement's src attribute and load
+  // HTMLTrackElement's WebVTTLoadListener by calling WebVTTLoadListener's
+  // LoadResource().
+  void LoadResource();
+
+  friend class TextTrackCue;
+  friend class WebVTTLoadListener;
 
   nsRefPtr<TextTrack> mTrack;
   nsCOMPtr<nsIChannel> mChannel;
   nsRefPtr<HTMLMediaElement> mMediaParent;
+  nsRefPtr<WebVTTLoadListener> mLoadListener;
   uint16_t mReadyState;
 
   void CreateTextTrack();
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -34,13 +34,14 @@ INCLUDES	+= \
 		-I$(srcdir)/../../../../layout/generic \
 		-I$(srcdir)/../../../../dom/base \
 		-I$(srcdir)/../../../../editor/libeditor/base \
 		-I$(srcdir)/../../../../editor/libeditor/text \
 		-I$(srcdir)/../../../../editor/txmgr/src \
 		-I$(srcdir)/../../../../netwerk/base/src \
 		-I$(srcdir) \
 		-I$(topsrcdir)/xpcom/ds \
+		-I$(topsrcdir)/content/media/ \
 		$(NULL)
 
 DEFINES		+= \
 		-D_IMPL_NS_LAYOUT \
 		$(NULL)
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -411,16 +411,29 @@ private:
 AudioStream* AudioStream::AllocateStream()
 {
 #if defined(MOZ_CUBEB)
   return new BufferedAudioStream();
 #endif
   return nullptr;
 }
 
+int AudioStream::MaxNumberOfChannels()
+{
+  uint32_t maxNumberOfChannels, rv;
+
+  rv = cubeb_get_max_channel_count(GetCubebContext(), &maxNumberOfChannels);
+
+  if (rv != CUBEB_OK) {
+    return 0;
+  }
+
+  return static_cast<int>(maxNumberOfChannels);
+}
+
 static void SetUint16LE(PRUint8* aDest, PRUint16 aValue)
 {
   aDest[0] = aValue & 0xFF;
   aDest[1] = aValue >> 8;
 }
 
 static void SetUint32LE(PRUint8* aDest, PRUint32 aValue)
 {
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -100,16 +100,19 @@ public:
   // library after using it.
   static void ShutdownLibrary();
 
   // AllocateStream will return either a local stream or a remoted stream
   // depending on where you call it from.  If you call this from a child process,
   // you may receive an implementation which forwards to a compositing process.
   static AudioStream* AllocateStream();
 
+  // Returns the maximum number of channels supported by the audio hardware.
+  static int MaxNumberOfChannels();
+
   // Initialize the audio stream. aNumChannels is the number of audio
   // channels (1 for mono, 2 for stereo, etc) and aRate is the sample rate
   // (22050Hz, 44100Hz, etc).
   virtual nsresult Init(int32_t aNumChannels, int32_t aRate,
                         const dom::AudioChannelType aAudioStreamType) = 0;
 
   // Closes the stream. All future use of the stream is an error.
   virtual void Shutdown() = 0;
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -17,11 +17,17 @@ FAIL_ON_WARNINGS := 1
 endif # !_MSC_VER
 
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
+INCLUDES  += \
+  -I$(topsrcdir)/content/base/src \
+  -I$(topsrcdir)/layout/generic \
+  -I$(topsrcdir)/layout/xul/base/src \
+  $(NULL)
+
 DEFINES  += -D_IMPL_NS_LAYOUT
 CFLAGS   += $(GSTREAMER_CFLAGS)
 CXXFLAGS += $(GSTREAMER_CFLAGS)
--- a/content/media/TextTrack.cpp
+++ b/content/media/TextTrack.cpp
@@ -41,19 +41,19 @@ TextTrack::TextTrack(nsISupports* aParen
   , mMode(TextTrackMode::Disabled)
   , mCueList(new TextTrackCueList(aParent))
   , mActiveCueList(new TextTrackCueList(aParent))
 {
   SetIsDOMBinding();
 }
 
 void
-TextTrack::Update(double time)
+TextTrack::Update(double aTime)
 {
-  mCueList->Update(time);
+  mCueList->Update(aTime);
 }
 
 JSObject*
 TextTrack::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return TextTrackBinding::Wrap(aCx, aScope, this);
 }
 
--- a/content/media/TextTrack.h
+++ b/content/media/TextTrack.h
@@ -77,17 +77,18 @@ public:
   TextTrackCueList* GetActiveCues() const
   {
     if (mMode == TextTrackMode::Disabled) {
       return nullptr;
     }
     return mActiveCueList;
   }
 
-  void Update(double time);
+  // Time is in seconds.
+  void Update(double aTime);
 
   void AddCue(TextTrackCue& aCue);
   void RemoveCue(TextTrackCue& aCue);
   void CueChanged(TextTrackCue& aCue);
 
   IMPL_EVENT_HANDLER(cuechange)
 
 private:
--- a/content/media/TextTrackCue.cpp
+++ b/content/media/TextTrackCue.cpp
@@ -1,25 +1,30 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/HTMLTrackElement.h"
 #include "mozilla/dom/TextTrackCue.h"
 #include "mozilla/dom/TextTrackCueBinding.h"
+#include "mozilla/dom/ProcessingInstruction.h"
+#include "nsIFrame.h"
+#include "nsTextNode.h"
+#include "nsVideoFrame.h"
 #include "webvtt/cue.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_3(TextTrackCue,
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_4(TextTrackCue,
                                         mGlobal,
                                         mTrack,
-                                        mTrackElement)
+                                        mTrackElement,
+                                        mCueDiv)
 
 NS_IMPL_ADDREF_INHERITED(TextTrackCue, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TextTrackCue, nsDOMEventTargetHelper)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TextTrackCue)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 // Set cue setting defaults based on step 19 & seq.
 // in http://dev.w3.org/html5/webvtt/#parsing
@@ -73,21 +78,238 @@ TextTrackCue::~TextTrackCue()
 {
   if (mHead) {
     // Release our reference and null mHead.
     webvtt_release_node(&mHead);
   }
 }
 
 void
-TextTrackCue::DisplayCue()
+TextTrackCue::CreateCueOverlay()
+{
+  mTrackElement->OwnerDoc()->CreateElem(NS_LITERAL_STRING("div"), nullptr,
+                                        kNameSpaceID_XHTML,
+                                        getter_AddRefs(mCueDiv));
+  nsGenericHTMLElement* cueDiv =
+    static_cast<nsGenericHTMLElement*>(mCueDiv.get());
+  cueDiv->SetClassName(NS_LITERAL_STRING("caption-text"));
+}
+
+void
+TextTrackCue::RenderCue()
+{
+  nsRefPtr<DocumentFragment> frag = GetCueAsHTML();
+  if (!frag || !mTrackElement) {
+    return;
+  }
+
+  if (!mCueDiv) {
+    CreateCueOverlay();
+  }
+
+  HTMLMediaElement* parent = mTrackElement->mMediaParent;
+  if (!parent) {
+    return;
+  }
+
+  nsIFrame* frame = parent->GetPrimaryFrame();
+  if (!frame) {
+    return;
+  }
+
+  nsVideoFrame* videoFrame = do_QueryFrame(frame);
+  if (!videoFrame) {
+    return;
+  }
+
+  nsIContent* overlay =  videoFrame->GetCaptionOverlay();
+  if (!overlay) {
+    return;
+  }
+
+  ErrorResult rv;
+  nsContentUtils::SetNodeTextContent(overlay, EmptyString(), true);
+  nsContentUtils::SetNodeTextContent(mCueDiv, EmptyString(), true);
+
+  mCueDiv->AppendChild(*frag, rv);
+  overlay->AppendChild(*mCueDiv, rv);
+}
+
+already_AddRefed<DocumentFragment>
+TextTrackCue::GetCueAsHTML()
+{
+  nsRefPtr<DocumentFragment> frag =
+    mTrackElement->OwnerDoc()->CreateDocumentFragment();
+
+  ConvertNodeTreeToDOMTree(frag);
+
+  return frag.forget();
+}
+
+struct WebVTTNodeParentPair
+{
+  webvtt_node* mNode;
+  nsIContent* mParent;
+
+  WebVTTNodeParentPair(webvtt_node* aNode, nsIContent* aParent)
+    : mNode(aNode)
+    , mParent(aParent)
+  {}
+};
+
+static void
+PushChildren(nsTArray<WebVTTNodeParentPair> &aNodeParentPairStack,
+             webvtt_node* aNode, nsIContent* aParentContent)
+{
+  // Push on in reverse order so we process the nodes in the correct
+  // order -- left to right.
+  for (int i = aNode->data.internal_data->length; i > 0; i--) {
+    WebVTTNodeParentPair nodeParentPair(
+      aNode->data.internal_data->children[i - 1],
+      aParentContent);
+    aNodeParentPairStack.AppendElement(nodeParentPair);
+  }
+}
+
+static WebVTTNodeParentPair
+PopChild(nsTArray<WebVTTNodeParentPair> &aNodeParentPairStack) {
+  WebVTTNodeParentPair temp =
+    aNodeParentPairStack.LastElement();
+  aNodeParentPairStack.RemoveElementAt(aNodeParentPairStack.Length() - 1);
+  return temp;
+}
+
+void
+TextTrackCue::ConvertNodeTreeToDOMTree(nsIContent* aParentContent)
 {
-  if (mTrackElement) {
-    mTrackElement->DisplayCueText(mHead);
+  nsTArray<WebVTTNodeParentPair> nodeParentPairStack;
+
+  // mHead should actually be the head of a node tree.
+  if (mHead->kind != WEBVTT_HEAD_NODE) {
+    return;
+  }
+  // Seed the stack for traversal.
+  PushChildren(nodeParentPairStack, mHead, aParentContent);
+
+  while (!nodeParentPairStack.IsEmpty()) {
+    WebVTTNodeParentPair nodeParentPair = PopChild(nodeParentPairStack);
+    nsCOMPtr<nsIContent> content;
+    if (WEBVTT_IS_VALID_LEAF_NODE(nodeParentPair.mNode->kind)) {
+      content = ConvertLeafNodeToContent(nodeParentPair.mNode);
+    } else if (WEBVTT_IS_VALID_INTERNAL_NODE(nodeParentPair.mNode->kind)) {
+      content = ConvertInternalNodeToContent(nodeParentPair.mNode);
+      // Push the children of the current node onto the stack for traversal.
+      PushChildren(nodeParentPairStack, nodeParentPair.mNode, content);
+    }
+    if (content && nodeParentPair.mParent) {
+      ErrorResult rv;
+      nodeParentPair.mParent->AppendChild(*content, rv);
+    }
+  }
+}
+
+already_AddRefed<nsIContent>
+TextTrackCue::ConvertInternalNodeToContent(const webvtt_node* aWebVTTNode)
+{
+  nsIAtom* atom;
+
+  switch (aWebVTTNode->kind) {
+    case WEBVTT_BOLD:
+      atom = nsGkAtoms::b;
+      break;
+    case WEBVTT_ITALIC:
+      atom = nsGkAtoms::i;
+      break;
+    case WEBVTT_UNDERLINE:
+      atom = nsGkAtoms::u;
+      break;
+    case WEBVTT_RUBY:
+      atom = nsGkAtoms::ruby;
+      break;
+    case WEBVTT_RUBY_TEXT:
+      atom = nsGkAtoms::rt;
+      break;
+    case WEBVTT_VOICE:
+      atom = nsGkAtoms::span;
+      break;
+    case WEBVTT_CLASS:
+      atom = nsGkAtoms::span;
+      break;
+    default:
+      return nullptr;
+      break;
   }
+
+  nsCOMPtr<nsIContent> cueTextContent;
+  mTrackElement->OwnerDoc()->CreateElem(nsDependentAtomString(atom), nullptr,
+                                        kNameSpaceID_XHTML,
+                                        getter_AddRefs(cueTextContent));
+
+  if (aWebVTTNode->kind == WEBVTT_VOICE) {
+    const char* text =
+      webvtt_string_text(&aWebVTTNode->data.internal_data->annotation);
+    if (text) {
+      nsGenericHTMLElement* genericHtmlElement =
+        static_cast<nsGenericHTMLElement*>(cueTextContent.get());
+      genericHtmlElement->SetTitle(NS_ConvertUTF8toUTF16(text));
+    }
+  }
+
+  webvtt_stringlist* classes = aWebVTTNode->data.internal_data->css_classes;
+  if (classes && classes->items && classes->length > 0) {
+    nsAutoString classString;
+
+    const char *text = webvtt_string_text(classes->items);
+    if (text) {
+      AppendUTF8toUTF16(text, classString);
+      for (uint32_t i = 1; i < classes->length; i++) {
+        text = webvtt_string_text(classes->items + i);
+        if (text) {
+          classString.Append(' ');
+          AppendUTF8toUTF16(text, classString);
+        }
+      }
+    }
+
+    nsGenericHTMLElement* genericHtmlElement =
+      static_cast<nsGenericHTMLElement*>(cueTextContent.get());
+    genericHtmlElement->SetClassName(classString);
+  }
+  return cueTextContent.forget();
+}
+
+already_AddRefed<nsIContent>
+TextTrackCue::ConvertLeafNodeToContent(const webvtt_node* aWebVTTNode)
+{
+  nsCOMPtr<nsIContent> cueTextContent;
+  nsNodeInfoManager* nimgr = mTrackElement->NodeInfo()->NodeInfoManager();
+  switch (aWebVTTNode->kind) {
+    case WEBVTT_TEXT:
+    {
+      cueTextContent = new nsTextNode(nimgr);
+      const char* text = webvtt_string_text(&aWebVTTNode->data.text);
+      if (text) {
+        cueTextContent->SetText(NS_ConvertUTF8toUTF16(text), false);
+      }
+      break;
+    }
+    case WEBVTT_TIME_STAMP:
+    {
+      nsAutoString timeStamp;
+      timeStamp.AppendInt(aWebVTTNode->data.timestamp);
+      cueTextContent =
+          NS_NewXMLProcessingInstruction(nimgr, NS_LITERAL_STRING("timestamp"),
+                                         timeStamp);
+      break;
+    }
+    default:
+      return nullptr;
+      break;
+  }
+  return cueTextContent.forget();
 }
 
 JSObject*
 TextTrackCue::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return TextTrackCueBinding::Wrap(aCx, aScope, this);
 }
 
--- a/content/media/TextTrackCue.h
+++ b/content/media/TextTrackCue.h
@@ -242,21 +242,85 @@ public:
     return mId.Equals(rhs.mId);
   }
 
   const nsAString& Id() const
   {
     return mId;
   }
 
-  void DisplayCue();
+  /**
+   * Overview of WEBVTT cuetext and anonymous content setup.
+   *
+   * webvtt_nodes are the parsed version of WEBVTT cuetext. WEBVTT cuetext is
+   * the portion of a WEBVTT cue that specifies what the caption will actually
+   * show up as on screen.
+   *
+   * WEBVTT cuetext can contain markup that loosely relates to HTML markup. It
+   * can contain tags like <b>, <u>, <i>, <c>, <v>, <ruby>, <rt>, <lang>,
+   * including timestamp tags.
+   *
+   * When the caption is ready to be displayed the webvtt_nodes are converted
+   * over to anonymous DOM content. <i>, <u>, <b>, <ruby>, and <rt> all become
+   * HTMLElements of their corresponding HTML markup tags. <c> and <v> are
+   * converted to <span> tags. Timestamp tags are converted to XML processing
+   * instructions. Additionally, all cuetext tags support specifying of classes.
+   * This takes the form of <foo.class.subclass>. These classes are then parsed
+   * and set as the anonymous content's class attribute.
+   *
+   * Rules on constructing DOM objects from webvtt_nodes can be found here
+   * http://dev.w3.org/html5/webvtt/#webvtt-cue-text-dom-construction-rules.
+   * Current rules are taken from revision on April 15, 2013.
+   */
+
+  /**
+   * Converts the TextTrackCue's cuetext into a tree of DOM objects and attaches
+   * it to a div on it's owning TrackElement's MediaElement's caption overlay.
+   */
+  void RenderCue();
+
+  /**
+   * Produces a tree of anonymous content based on the tree of the processed
+   * cue text. This lives in a tree of C nodes whose head is mHead.
+   *
+   * Returns a DocumentFragment that is the head of the tree of anonymous
+   * content.
+   */
+  already_AddRefed<DocumentFragment> GetCueAsHTML();
+
+  /**
+   * Converts mHead to a list of DOM elements and attaches it to aParentContent.
+   *
+   * Processes the C node tree in a depth-first pre-order traversal and creates
+   * a mirrored DOM tree. The conversion rules come from the webvtt DOM
+   * construction rules:
+   * http://dev.w3.org/html5/webvtt/#webvtt-cue-text-dom-construction-rules
+   * Current rules taken from revision on May 13, 2013.
+   */
+  void
+  ConvertNodeTreeToDOMTree(nsIContent* aParentContent);
+
+  /**
+   * Converts an internal webvtt node, i.e. one that has children, to an
+   * anonymous HTMLElement.
+   */
+  already_AddRefed<nsIContent>
+  ConvertInternalNodeToContent(const webvtt_node* aWebVTTNode);
+
+  /**
+   * Converts a leaf webvtt node, i.e. one that does not have children, to
+   * either a text node or processing instruction.
+   */
+  already_AddRefed<nsIContent>
+  ConvertLeafNodeToContent(const webvtt_node* aWebVTTNode);
 
 private:
   void CueChanged();
   void SetDefaultCueSettings();
+  void CreateCueOverlay();
 
   nsCOMPtr<nsISupports> mGlobal;
   nsString mText;
   double mStartTime;
   double mEndTime;
 
   nsRefPtr<TextTrack> mTrack;
   nsRefPtr<HTMLTrackElement> mTrackElement;
@@ -264,14 +328,17 @@ private:
   nsString mId;
   int32_t mPosition;
   int32_t mSize;
   bool mPauseOnExit;
   bool mSnapToLines;
   nsString mVertical;
   int mLine;
   TextTrackCueAlign mAlign;
+
+  // Anonymous child which is appended to VideoFrame's caption display div.
+  nsCOMPtr<nsIContent> mCueDiv;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TextTrackCue_h
--- a/content/media/TextTrackCueList.cpp
+++ b/content/media/TextTrackCueList.cpp
@@ -20,22 +20,22 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 TextTrackCueList::TextTrackCueList(nsISupports* aParent) : mParent(aParent)
 {
   SetIsDOMBinding();
 }
 
 void
-TextTrackCueList::Update(double time)
+TextTrackCueList::Update(double aTime)
 {
   const uint32_t length = mList.Length();
   for (uint32_t i = 0; i < length; i++) {
-    if (time > mList[i]->StartTime() && time < mList[i]->EndTime()) {
-      mList[i]->DisplayCue();
+    if (aTime > mList[i]->StartTime() && aTime < mList[i]->EndTime()) {
+      mList[i]->RenderCue();
     }
   }
 }
 
 JSObject*
 TextTrackCueList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return TextTrackCueListBinding::Wrap(aCx, aScope, this);
--- a/content/media/TextTrackCueList.h
+++ b/content/media/TextTrackCueList.h
@@ -35,17 +35,18 @@ public:
     return mParent;
   }
 
   uint32_t Length() const
   {
     return mList.Length();
   }
 
-  void Update(double time);
+  // Time is in seconds.
+  void Update(double aTime);
 
   TextTrackCue* IndexedGetter(uint32_t aIndex, bool& aFound);
   TextTrackCue* GetCueById(const nsAString& aId);
 
   void AddCue(TextTrackCue& cue);
   void RemoveCue(TextTrackCue& cue);
 
 private:
--- a/content/media/TextTrackList.cpp
+++ b/content/media/TextTrackList.cpp
@@ -16,16 +16,25 @@ NS_IMPL_RELEASE_INHERITED(TextTrackList,
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TextTrackList)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 TextTrackList::TextTrackList(nsISupports* aGlobal) : mGlobal(aGlobal)
 {
   SetIsDOMBinding();
 }
 
+void
+TextTrackList::Update(double aTime)
+{
+  uint32_t length = Length(), i;
+  for (i = 0; i < length; i++) {
+    mTextTracks[i]->Update(aTime);
+  }
+}
+
 JSObject*
 TextTrackList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return TextTrackListBinding::Wrap(aCx, aScope, this);
 }
 
 TextTrack*
 TextTrackList::IndexedGetter(uint32_t aIndex, bool& aFound)
--- a/content/media/TextTrackList.h
+++ b/content/media/TextTrackList.h
@@ -33,22 +33,19 @@ public:
     return mGlobal;
   }
 
   uint32_t Length() const
   {
     return mTextTracks.Length();
   }
 
-  void Update(double time) {
-    uint32_t length = Length(), i;
-    for( i = 0; i < length; i++ ) {
-      mTextTracks[i]->Update(time);
-    }
-  }
+  // Time is in seconds.
+  void Update(double aTime);
+
   TextTrack* IndexedGetter(uint32_t aIndex, bool& aFound);
 
   already_AddRefed<TextTrack> AddTextTrack(TextTrackKind aKind,
                                            const nsAString& aLabel,
                                            const nsAString& aLanguage);
   void AddTextTrack(TextTrack* aTextTrack) {
     mTextTracks.AppendElement(aTextTrack);
   }
--- a/content/media/VideoUtils.h
+++ b/content/media/VideoUtils.h
@@ -9,16 +9,17 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/CheckedInt.h"
 
 #include "nsRect.h"
 #include "nsIThreadManager.h"
 #include "nsThreadUtils.h"
+#include "prtime.h"
 
 using mozilla::CheckedInt64;
 using mozilla::CheckedUint64;
 using mozilla::CheckedInt32;
 using mozilla::CheckedUint32;
 
 // This file contains stuff we'd rather put elsewhere, but which is
 // dependent on other changes which we don't want to wait for. We plan to
@@ -157,16 +158,19 @@ CheckedInt64 FramesToUsecs(int64_t aFram
 CheckedInt64 UsecsToFrames(int64_t aUsecs, uint32_t aRate);
 
 // Number of microseconds per second. 1e6.
 static const int64_t USECS_PER_S = 1000000;
 
 // Number of microseconds per millisecond.
 static const int64_t USECS_PER_MS = 1000;
 
+// Converts seconds to milliseconds.
+#define SECONDS_TO_MS(s) ((s) / PR_MSEC_PER_SEC)
+
 // The maximum height and width of the video. Used for
 // sanitizing the memory allocation of the RGB buffer.
 // The maximum resolution we anticipate encountering in the
 // wild is 2160p - 3840x2160 pixels.
 static const int32_t MAX_VIDEO_WIDTH = 4000;
 static const int32_t MAX_VIDEO_HEIGHT = 3000;
 
 // Scales the display rect aDisplay by aspect ratio aAspectRatio.
new file mode 100644
--- /dev/null
+++ b/content/media/WebVTTLoadListener.cpp
@@ -0,0 +1,248 @@
+/* -*- 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 "WebVTTLoadListener.h"
+#include "mozilla/dom/TextTrackCue.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "VideoUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_1(WebVTTLoadListener, mElement)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebVTTLoadListener)
+  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
+  NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
+  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebVTTLoadListener)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebVTTLoadListener)
+
+#ifdef PR_LOGGING
+PRLogModuleInfo* gTextTrackLog;
+# define LOG(...) PR_LOG(gTextTrackLog, PR_LOG_DEBUG, (__VA_ARGS__))
+#else
+# define LOG(msg)
+#endif
+
+WebVTTLoadListener::WebVTTLoadListener(HTMLTrackElement* aElement)
+  : mElement(aElement)
+{
+  MOZ_ASSERT(mElement, "Must pass an element to the callback");
+#ifdef PR_LOGGING
+  if (!gTextTrackLog) {
+    gTextTrackLog = PR_NewLogModule("TextTrack");
+  }
+#endif
+  LOG("WebVTTLoadListener created.");
+}
+
+WebVTTLoadListener::~WebVTTLoadListener()
+{
+  LOG("WebVTTLoadListener destroyed.");
+}
+
+nsresult
+WebVTTLoadListener::LoadResource()
+{
+  if (!HTMLTrackElement::IsWebVTTEnabled()) {
+    NS_WARNING("WebVTT support disabled."
+               " See media.webvtt.enabled in about:config. ");
+    return NS_ERROR_FAILURE;
+  }
+
+  LOG("Loading text track resource.");
+  webvtt_parser_t* parser = nullptr;
+  webvtt_status status = webvtt_create_parser(&OnParsedCueWebVTTCallBack,
+                                              &OnReportErrorWebVTTCallBack,
+                                              this, &parser);
+
+  if (status != WEBVTT_SUCCESS) {
+    NS_ENSURE_TRUE(status != WEBVTT_OUT_OF_MEMORY,
+                   NS_ERROR_OUT_OF_MEMORY);
+    NS_ENSURE_TRUE(status != WEBVTT_INVALID_PARAM,
+                   NS_ERROR_INVALID_ARG);
+    return NS_ERROR_FAILURE;
+  }
+
+  mParser.own(parser);
+  NS_ENSURE_TRUE(mParser != nullptr, NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WebVTTLoadListener::OnStartRequest(nsIRequest* aRequest,
+                                   nsISupports* aContext)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WebVTTLoadListener::OnStopRequest(nsIRequest* aRequest,
+                                  nsISupports* aContext,
+                                  nsresult aStatus)
+{
+  webvtt_finish_parsing(mParser);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WebVTTLoadListener::OnDataAvailable(nsIRequest* aRequest,
+                                    nsISupports* aContext,
+                                    nsIInputStream* aStream,
+                                    uint64_t aOffset,
+                                    uint32_t aCount)
+{
+  uint32_t count = aCount;
+  while (count > 0) {
+    uint32_t read;
+    nsresult rv = aStream->ReadSegments(ParseChunk, this, count, &read);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (!read) {
+      return NS_ERROR_FAILURE;
+    }
+    count -= read;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WebVTTLoadListener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
+                                           nsIChannel* aNewChannel,
+                                           uint32_t aFlags,
+                                           nsIAsyncVerifyRedirectCallback* cb)
+{
+  if (mElement) {
+    mElement->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WebVTTLoadListener::GetInterface(const nsIID &aIID,
+                                 void** aResult)
+{
+  return QueryInterface(aIID, aResult);
+}
+
+NS_METHOD
+WebVTTLoadListener::ParseChunk(nsIInputStream* aInStream, void* aClosure,
+                               const char* aFromSegment, uint32_t aToOffset,
+                               uint32_t aCount, uint32_t* aWriteCount)
+{
+  WebVTTLoadListener* loadListener = static_cast<WebVTTLoadListener*>(aClosure);
+
+  if (WEBVTT_FAILED(webvtt_parse_chunk(loadListener->mParser, aFromSegment,
+                                       aCount))) {
+    LOG("Unable to parse chunk of WEBVTT text. Aborting.");
+    *aWriteCount = 0;
+    return NS_ERROR_FAILURE;
+  }
+  *aWriteCount = aCount;
+  return NS_OK;
+}
+
+void
+WebVTTLoadListener::OnParsedCue(webvtt_cue* aCue)
+{
+  const char* text = webvtt_string_text(&aCue->body);
+
+  nsRefPtr<TextTrackCue> textTrackCue =
+    new TextTrackCue(mElement->OwnerDoc()->GetParentObject(),
+                     SECONDS_TO_MS(aCue->from), SECONDS_TO_MS(aCue->until),
+                     NS_ConvertUTF8toUTF16(text), mElement,
+                     aCue->node_head);
+
+  text = webvtt_string_text(&aCue->id);
+  textTrackCue->SetId(NS_ConvertUTF8toUTF16(text));
+
+  textTrackCue->SetSnapToLines(aCue->snap_to_lines);
+  textTrackCue->SetSize(aCue->settings.size);
+  textTrackCue->SetPosition(aCue->settings.position);
+  textTrackCue->SetLine(aCue->settings.line);
+
+  nsAutoString vertical;
+  switch (aCue->settings.vertical) {
+    case WEBVTT_VERTICAL_LR:
+      vertical = NS_LITERAL_STRING("lr");
+      break;
+    case WEBVTT_VERTICAL_RL:
+      vertical = NS_LITERAL_STRING("rl");
+      break;
+    case WEBVTT_HORIZONTAL:
+      // TODO: https://bugzilla.mozilla.org/show_bug.cgi?id=865407
+      // Will be handled in the processing model.
+      break;
+  }
+  textTrackCue->SetVertical(vertical);
+
+  TextTrackCueAlign align;
+  switch (aCue->settings.align) {
+    case WEBVTT_ALIGN_START:
+      align = TextTrackCueAlign::Start;
+      break;
+    case WEBVTT_ALIGN_MIDDLE:
+      align = TextTrackCueAlign::Middle;
+    case WEBVTT_ALIGN_END:
+      align = TextTrackCueAlign::End;
+    case WEBVTT_ALIGN_LEFT:
+      align = TextTrackCueAlign::Left;
+      break;
+    case WEBVTT_ALIGN_RIGHT:
+      align = TextTrackCueAlign::Right;
+      break;
+    default:
+      align = TextTrackCueAlign::Start;
+      break;
+  }
+  textTrackCue->SetAlign(align);
+
+  mElement->mTrack->AddCue(*textTrackCue);
+}
+
+void
+WebVTTLoadListener::OnReportError(uint32_t aLine, uint32_t aCol,
+                                  webvtt_error aError)
+{
+#ifdef PR_LOGGING
+  // Get source webvtt file to display in the log
+  DOMString wideFile;
+  mElement->GetSrc(wideFile);
+
+  NS_ConvertUTF16toUTF8 file(wideFile);
+
+  const char* error = "parser error";
+  if (aError >= 0) {
+    error = webvtt_strerror(aError);
+  }
+
+  LOG("error: %s(%d:%d) - %s\n", file.get(), aLine, aCol, error);
+#endif
+}
+
+void WEBVTT_CALLBACK
+WebVTTLoadListener::OnParsedCueWebVTTCallBack(void* aUserData, webvtt_cue* aCue)
+{
+  WebVTTLoadListener* self = static_cast<WebVTTLoadListener*>(aUserData);
+  self->OnParsedCue(aCue);
+}
+
+int WEBVTT_CALLBACK
+WebVTTLoadListener::OnReportErrorWebVTTCallBack(void* aUserData, uint32_t aLine,
+                                                uint32_t aCol,
+                                                webvtt_error aError)
+{
+  WebVTTLoadListener* self = static_cast<WebVTTLoadListener*>(aUserData);
+  self->OnReportError(aLine, aCol, aError);
+  return WEBVTT_SUCCESS;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/WebVTTLoadListener.h
@@ -0,0 +1,90 @@
+/* -*- 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_dom_WebVTTLoadListener_h
+#define mozilla_dom_WebVTTLoadListener_h
+
+#include "nsIStreamListener.h"
+#include "nsIChannelEventSink.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIObserver.h"
+#include "nsAutoPtr.h"
+#include "nsAutoRef.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/dom/HTMLTrackElement.h"
+#include "webvtt/parser.h"
+#include "webvtt/util.h"
+
+template <>
+class nsAutoRefTraits<webvtt_parser_t> : public nsPointerRefTraits<webvtt_parser_t>
+{
+public:
+  static void Release(webvtt_parser_t* aParser) { webvtt_delete_parser(aParser); }
+};
+
+namespace mozilla {
+namespace dom {
+/**
+ * Class that manages the libwebvtt parsing library and functions as an
+ * interface between Gecko and libwebvtt.
+ *
+ * Currently it's only designed to work with an HTMLTrackElement. The
+ * HTMLTrackElement controls the lifetime of the WebVTTLoadListener.
+ *
+ * The workflow of this class is as follows:
+ *  - Gets Loaded via an HTMLTrackElement class.
+ *  - As the HTMLTrackElement class gets new data WebVTTLoadListener's
+ *    OnDataAvailable() function is called and data is passed to the libwebvtt
+ *    parser.
+ *  - When the libwebvtt parser has finished a cue it will call the callbacks
+ *    that are exposed by the WebVTTLoadListener -- OnParsedCueWebVTTCallBack or
+ *    OnReportErrorWebVTTCallBack if it has encountered an error.
+ *  - When it has returned a cue successfully the WebVTTLoadListener will create
+ *    a new TextTrackCue and add it to the HTMLTrackElement's TextTrack.
+ */
+class WebVTTLoadListener MOZ_FINAL : public nsIStreamListener,
+                                     public nsIChannelEventSink,
+                                     public nsIInterfaceRequestor
+{
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSICHANNELEVENTSINK
+  NS_DECL_NSIINTERFACEREQUESTOR
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebVTTLoadListener,
+                                           nsIStreamListener)
+
+public:
+  WebVTTLoadListener(HTMLTrackElement* aElement);
+  ~WebVTTLoadListener();
+  void OnParsedCue(webvtt_cue* aCue);
+  void OnReportError(uint32_t aLine, uint32_t aCol, webvtt_error aError);
+  // Loads the libwebvtt parser. Must call this function in order to the
+  // WebVTTLoadListener to be ready to accept data.
+  nsresult LoadResource();
+
+private:
+  static NS_METHOD ParseChunk(nsIInputStream* aInStream, void* aClosure,
+                              const char* aFromSegment, uint32_t aToOffset,
+                              uint32_t aCount, uint32_t* aWriteCount);
+
+  nsRefPtr<HTMLTrackElement> mElement;
+  nsAutoRef<webvtt_parser_t> mParser;
+
+  static void WEBVTT_CALLBACK OnParsedCueWebVTTCallBack(void* aUserData,
+                                                        webvtt_cue* aCue);
+  static int WEBVTT_CALLBACK OnReportErrorWebVTTCallBack(void* aUserData,
+                                                         uint32_t aLine,
+                                                         uint32_t aCol,
+                                                         webvtt_error aError);
+};
+
+
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WebVTTLoadListener_h
--- a/content/media/moz.build
+++ b/content/media/moz.build
@@ -107,9 +107,10 @@ CPP_SOURCES += [
     'TextTrack.cpp',
     'TextTrackCue.cpp',
     'TextTrackCueList.cpp',
     'TextTrackList.cpp',
     'VideoFrameContainer.cpp',
     'VideoSegment.cpp',
     'VideoStreamTrack.cpp',
     'VideoUtils.cpp',
+    'WebVTTLoadListener.cpp',
 ]
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -45,16 +45,17 @@ AudioContext::AudioContext(nsPIDOMWindow
                            bool aIsOffline,
                            uint32_t aNumberOfChannels,
                            uint32_t aLength,
                            float aSampleRate)
   : mSampleRate(aIsOffline ? aSampleRate : IdealAudioRate())
   , mDestination(new AudioDestinationNode(this, aIsOffline,
                                           aNumberOfChannels,
                                           aLength, aSampleRate))
+  , mNumberOfChannels(aNumberOfChannels)
   , mIsOffline(aIsOffline)
 {
   // Actually play audio
   mDestination->Stream()->AddAudioOutput(&gWebAudioOutputKey);
   nsDOMEventTargetHelper::BindToOwner(aWindow);
   SetIsDOMBinding();
 
   mPannerNodes.Init();
@@ -392,16 +393,22 @@ FindConnectedSourcesOn(nsPtrHashKey<Pann
 }
 
 void
 AudioContext::UpdatePannerSource()
 {
   mPannerNodes.EnumerateEntries(FindConnectedSourcesOn, nullptr);
 }
 
+uint32_t
+AudioContext::MaxChannelCount() const
+{
+  return mIsOffline ? mNumberOfChannels : AudioStream::MaxNumberOfChannels();
+}
+
 MediaStreamGraph*
 AudioContext::Graph() const
 {
   return Destination()->Stream()->Graph();
 }
 
 MediaStream*
 AudioContext::DestinationStream() const
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -195,16 +195,18 @@ public:
 
   MediaStreamGraph* Graph() const;
   MediaStream* DestinationStream() const;
   void UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode);
   void UnregisterPannerNode(PannerNode* aNode);
   void UnregisterScriptProcessorNode(ScriptProcessorNode* aNode);
   void UpdatePannerSource();
 
+  uint32_t MaxChannelCount() const;
+
   JSContext* GetJSContext() const;
 
 private:
   void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob);
 
   friend struct ::mozilla::WebAudioDecodeJob;
 
 private:
@@ -218,16 +220,18 @@ private:
   // Two hashsets containing all the PannerNodes and AudioBufferSourceNodes,
   // to compute the doppler shift, and also to stop AudioBufferSourceNodes.
   // These are all weak pointers.
   nsTHashtable<nsPtrHashKey<PannerNode> > mPannerNodes;
   nsTHashtable<nsPtrHashKey<AudioBufferSourceNode> > mAudioBufferSourceNodes;
   // Hashset containing all ScriptProcessorNodes in order to stop them.
   // These are all weak pointers.
   nsTHashtable<nsPtrHashKey<ScriptProcessorNode> > mScriptProcessorNodes;
+  // Number of channels passed in the OfflineAudioContext ctor.
+  uint32_t mNumberOfChannels;
   bool mIsOffline;
 };
 
 }
 }
 
 #endif
 
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -196,16 +196,33 @@ AudioDestinationNode::AudioDestinationNo
                             MediaStreamGraph::GetInstance();
   AudioNodeEngine* engine = aIsOffline ?
                             new OfflineDestinationNodeEngine(this, aNumberOfChannels,
                                                              aLength, aSampleRate) :
                             new AudioNodeEngine(this);
   mStream = graph->CreateAudioNodeStream(engine, MediaStreamGraph::EXTERNAL_STREAM);
 }
 
+uint32_t
+AudioDestinationNode::MaxChannelCount() const
+{
+  return Context()->MaxChannelCount();
+}
+
+void
+AudioDestinationNode::SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv)
+{
+  if (aChannelCount > MaxChannelCount()) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  AudioNode::SetChannelCount(aChannelCount, aRv);
+}
+
 void
 AudioDestinationNode::DestroyGraph()
 {
   MOZ_ASSERT(Context() && Context()->IsOffline(),
              "Should only be called on a valid OfflineAudioContext");
   MediaStreamGraph::DestroyNonRealtimeInstance(mStream->Graph());
 }
 
--- a/content/media/webaudio/AudioDestinationNode.h
+++ b/content/media/webaudio/AudioDestinationNode.h
@@ -30,16 +30,20 @@ public:
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   virtual uint16_t NumberOfOutputs() const MOZ_FINAL MOZ_OVERRIDE
   {
     return 0;
   }
 
+  uint32_t MaxChannelCount() const;
+  virtual void SetChannelCount(uint32_t aChannelCount,
+                               ErrorResult& aRv) MOZ_OVERRIDE;
+
   void StartRendering();
 
   void DestroyGraph();
 
 private:
   uint32_t mFramesToProduce;
 };
 
--- a/content/media/webaudio/AudioNode.h
+++ b/content/media/webaudio/AudioNode.h
@@ -155,17 +155,17 @@ public:
 
   // The following two virtual methods must be implemented by each node type
   // to provide their number of input and output ports. These numbers are
   // constant for the lifetime of the node. Both default to 1.
   virtual uint16_t NumberOfInputs() const { return 1; }
   virtual uint16_t NumberOfOutputs() const { return 1; }
 
   uint32_t ChannelCount() const { return mChannelCount; }
-  void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv)
+  virtual void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv)
   {
     if (aChannelCount == 0 ||
         aChannelCount > WebAudioUtils::MaxChannelCount) {
       aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
       return;
     }
     mChannelCount = aChannelCount;
     SendChannelMixingParametersToStream();
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -53,16 +53,17 @@ MOCHITEST_FILES := \
   test_channelSplitterNodeWithVolume.html \
   test_currentTime.html \
   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_mixingRules.html \
   test_nodeToParamConnection.html \
   test_OfflineAudioContext.html \
   test_offlineDestinationChannelCountLess.html \
   test_offlineDestinationChannelCountMore.html \
   test_pannerNode.html \
   test_pannerNode_equalPower.html \
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/test/test_maxChannelCount.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test the AudioContext.destination interface</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">
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+
+  var ac = new AudioContext();
+  ok(ac.destination.maxChannelCount > 0, "We can query the maximum number of channels");
+
+  var oac = new OfflineAudioContext(2, 1024, 48000);
+  ok(oac.destination.maxChannelCount, 2, "This OfflineAudioContext should have 2 max channels.");
+
+  oac = new OfflineAudioContext(6, 1024, 48000);
+  ok(oac.destination.maxChannelCount, 6, "This OfflineAudioContext should have 6 max channels.");
+
+  expectException(function() {
+    oac.destination.channelCount = oac.destination.channelCount + 1;
+  }, DOMException.INDEX_SIZE_ERR);
+
+  SpecialPowers.clearUserPref("media.webaudio.enabled");
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/webidl/AudioDestinationNode.webidl
+++ b/dom/webidl/AudioDestinationNode.webidl
@@ -8,13 +8,12 @@
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [PrefControlled]
 interface AudioDestinationNode : AudioNode {
 
-    //readonly attribute unsigned long maxNumberOfChannels;
-    //attribute unsigned long numberOfChannels;
+    readonly attribute unsigned long maxChannelCount;
 
 };
 
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -6086,18 +6086,25 @@ IonBuilder::setStaticName(HandleObject s
     // Slots in the global object will be treated as roots during a minor GC.
     if (!staticObject->isGlobal() && NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(obj, value));
 
     // If the property has a known type, we may be able to optimize typed stores by not
     // storing the type tag. This only works if the property does not have its initial
     // |undefined| value; if |undefined| is assigned at a later point, it will be added
     // to the type set.
+    //
+    // We also need to make sure the typeset reflects the inherited types from
+    // the prototype by calling getFromPrototype. Otherwise we may specialize
+    // on a typeset that changes before compilation ends, which would mean the
+    // current script wouldn't be recompiled even when our assumption here is
+    // made false.
     MIRType slotType = MIRType_None;
     if (propertyTypes && !staticObject->getSlot(shape->slot()).isUndefined()) {
+        staticType->getFromPrototypes(cx, id, propertyTypes);
         JSValueType knownType = propertyTypes->getKnownTypeTag(cx);
         if (knownType != JSVAL_TYPE_UNKNOWN)
             slotType = MIRTypeFromValueType(knownType);
     }
 
     bool needsBarrier = !propertyTypes || propertyTypes->needsBarrier(cx);
     return storeSlot(obj, shape, value, needsBarrier, slotType);
 }
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -565,16 +565,21 @@ FilterArguments(JSContext *cx, JSString 
     static jschar arguments[] = {'a', 'r', 'g', 'u', 'm', 'e', 'n', 't', 's'};
     return !StringHasPattern(chars, str->length(), arguments, mozilla::ArrayLength(arguments));
 }
 
 #ifdef JSGC_GENERATIONAL
 void
 PostWriteBarrier(JSRuntime *rt, JSObject *obj)
 {
+#ifdef JS_GC_ZEAL
+    /* The jitcode version of IsInsideNursery does not know about the verifier. */
+    if (rt->gcVerifyPostData && rt->gcVerifierNursery.isInside(obj))
+        return;
+#endif
     JS_ASSERT(!IsInsideNursery(rt, obj));
     rt->gcStoreBuffer.putWholeObject(obj);
 }
 #endif
 
 uint32_t
 GetIndexFromString(JSString *str)
 {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-880776.js
@@ -0,0 +1,21 @@
+
+gczeal(11);
+var otherGlobal = newGlobal('new-compartment');
+function test(str, arg, result)
+{
+        var fun = new Function('x', str);
+            var code = "(function (x) { " + str + " })";
+                var c = clone(otherGlobal.evaluate(code, {compileAndGo: false}));
+                    assertEq(c.toSource(), eval(code).toSource());
+}
+test('return let (y) x;');
+test('return let (x) "" + x;', 'unicorns', 'undefined');
+test('return let (y = x) (y++, "" + y);', 'unicorns', 'NaN');
+test('return let (y = 1) (y = x, y);');
+test('return let ([] = x) x;');
+test('return let ([, ] = x) x;');
+test('return let ([, , , , ] = x) x;');
+test('return let ([[]] = x) x;');
+test('return let ([[[[[[[[[[[[[]]]]]]]]]]]]] = x) x;');
+test('return let ([[], []] = x) x;');
+test('return let ([[[[]]], [], , [], [[]]] = x) x;');
--- a/layout/generic/nsAbsoluteContainingBlock.cpp
+++ b/layout/generic/nsAbsoluteContainingBlock.cpp
@@ -151,17 +151,17 @@ nsAbsoluteContainingBlock::Reflow(nsCont
         // See bug 154892. Not sure how to do it "right" yet; probably want
         // to keep continuations within an nsAbsoluteContainingBlock eventually.
         tracker.Insert(nextFrame, kidStatus);
         NS_MergeReflowStatusInto(&reflowStatus, kidStatus);
       }
       else {
         // Delete any continuations
         if (nextFrame) {
-          tracker.Finish(kidFrame);
+          nsOverflowContinuationTracker::AutoFinish fini(&tracker, kidFrame);
           static_cast<nsContainerFrame*>(nextFrame->GetParent())
             ->DeleteNextInFlowChild(aPresContext, nextFrame, true);
         }
       }
     }
     else {
       tracker.Skip(kidFrame, reflowStatus);
       if (aOverflowAreas) {
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -3162,17 +3162,17 @@ nsBlockFrame::ReflowBlockFrame(nsBlockRe
           
           nsIFrame* nextFrame = frame->GetNextInFlow();
           NS_ASSERTION(nextFrame, "We're supposed to have a next-in-flow by now");
           
           if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
             // If nextFrame used to be an overflow container, make it a normal block
             if (!madeContinuation &&
                 (NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
-              aState.mOverflowTracker->Finish(frame);
+              nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, frame);
               nsContainerFrame* parent =
                 static_cast<nsContainerFrame*>(nextFrame->GetParent());
               rv = parent->StealFrame(aState.mPresContext, nextFrame);
               NS_ENSURE_SUCCESS(rv, rv);
               if (parent != this)
                 ReparentFrame(nextFrame, parent, this);
               mFrames.InsertFrame(nullptr, frame, nextFrame);
               madeContinuation = true; // needs to be added to mLines
--- a/layout/generic/nsBlockReflowContext.cpp
+++ b/layout/generic/nsBlockReflowContext.cpp
@@ -295,18 +295,17 @@ nsBlockReflowContext::ReflowBlock(const 
     if (NS_FRAME_IS_FULLY_COMPLETE(aFrameReflowStatus)) {
       nsIFrame* kidNextInFlow = mFrame->GetNextInFlow();
       if (nullptr != kidNextInFlow) {
         // Remove all of the childs next-in-flows. Make sure that we ask
         // the right parent to do the removal (it's possible that the
         // parent is not this because we are executing pullup code).
         // Floats will eventually be removed via nsBlockFrame::RemoveFloat
         // which detaches the placeholder from the float.
-/* XXX promote DeleteChildsNextInFlow to nsIFrame to elminate this cast */
-        aState.mOverflowTracker->Finish(mFrame);
+        nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, mFrame);
         static_cast<nsContainerFrame*>(kidNextInFlow->GetParent())
           ->DeleteNextInFlowChild(mPresContext, kidNextInFlow, true);
       }
     }
   }
 
   return rv;
 }
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -970,21 +970,21 @@ nsContainerFrame::ReflowChild(nsIFrame* 
   result = aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState,
                              aStatus);
 
   // If the reflow was successful and the child frame is complete, delete any
   // next-in-flows, but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set.
   if (NS_SUCCEEDED(result) && NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
       !(aFlags & NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD)) {
     nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
-    if (nullptr != kidNextInFlow) {
+    if (kidNextInFlow) {
       // Remove all of the childs next-in-flows. Make sure that we ask
       // the right parent to do the removal (it's possible that the
       // parent is not this because we are executing pullup code)
-      if (aTracker) aTracker->Finish(aKidFrame);
+      nsOverflowContinuationTracker::AutoFinish fini(aTracker, aKidFrame);
       static_cast<nsContainerFrame*>(kidNextInFlow->GetParent())
         ->DeleteNextInFlowChild(aPresContext, kidNextInFlow, true);
     }
   }
   return result;
 }
 
 
@@ -1566,28 +1566,37 @@ nsOverflowContinuationTracker::nsOverflo
   : mOverflowContList(nullptr),
     mPrevOverflowCont(nullptr),
     mSentry(nullptr),
     mParent(aFrame),
     mSkipOverflowContainerChildren(aSkipOverflowContainerChildren),
     mWalkOOFFrames(aWalkOOFFrames)
 {
   NS_PRECONDITION(aFrame, "null frame pointer");
-  nsContainerFrame* next = static_cast<nsContainerFrame*>
-                             (aFrame->GetNextInFlow());
-  if (next) {
-    mOverflowContList = next->GetPropTableFrames(aPresContext,
+  SetupOverflowContList();
+}
+
+void
+nsOverflowContinuationTracker::SetupOverflowContList()
+{
+  NS_PRECONDITION(mParent, "null frame pointer");
+  NS_PRECONDITION(!mOverflowContList, "already have list");
+  nsPresContext* pc = mParent->PresContext();
+  nsContainerFrame* nif =
+    static_cast<nsContainerFrame*>(mParent->GetNextInFlow());
+  if (nif) {
+    mOverflowContList = nif->GetPropTableFrames(pc,
       nsContainerFrame::OverflowContainersProperty());
     if (mOverflowContList) {
-      mParent = next;
+      mParent = nif;
       SetUpListWalker();
     }
   }
   if (!mOverflowContList) {
-    mOverflowContList = mParent->GetPropTableFrames(aPresContext,
+    mOverflowContList = mParent->GetPropTableFrames(pc,
       nsContainerFrame::ExcessOverflowContainersProperty());
     if (mOverflowContList) {
       SetUpListWalker();
     }
   }
 }
 
 /**
@@ -1757,47 +1766,69 @@ nsOverflowContinuationTracker::Insert(ns
       }
       Insert(f, aReflowStatus);
     }
   }
   return rv;
 }
 
 void
-nsOverflowContinuationTracker::Finish(nsIFrame* aChild)
+nsOverflowContinuationTracker::BeginFinish(nsIFrame* aChild)
 {
   NS_PRECONDITION(aChild, "null ptr");
   NS_PRECONDITION(aChild->GetNextInFlow(),
                   "supposed to call Finish *before* deleting next-in-flow!");
-
-  for (nsIFrame* f = aChild; f; ) {
-    // Make sure we drop all references if all the frames in the
-    // overflow containers list are about to be destroyed.
-    nsIFrame* nif = f->GetNextInFlow();
-    if (mOverflowContList &&
-        mOverflowContList->FirstChild() == nif &&
-        (!nif->GetNextSibling() ||
-         nif->GetNextSibling() == nif->GetNextInFlow())) {
-      mOverflowContList = nullptr;
+  for (nsIFrame* f = aChild; f; f = f->GetNextInFlow()) {
+    // We'll update these in EndFinish after the next-in-flows are gone.
+    if (f == mPrevOverflowCont) {
+      mSentry = nullptr;
       mPrevOverflowCont = nullptr;
-      mSentry = nullptr;
-      mParent = static_cast<nsContainerFrame*>(f->GetParent());
       break;
     }
     if (f == mSentry) {
-      // Step past aChild
-      nsIFrame* prevOverflowCont = mPrevOverflowCont;
+      mSentry = nullptr;
+      break;
+    }
+  }
+}
+
+void
+nsOverflowContinuationTracker::EndFinish(nsIFrame* aChild)
+{
+  if (!mOverflowContList) {
+    return;
+  }
+  // Forget mOverflowContList if it was deleted.
+  nsPresContext* pc = aChild->PresContext();
+  FramePropertyTable* propTable = pc->PropertyTable();
+  nsFrameList* eoc = static_cast<nsFrameList*>(propTable->Get(mParent,
+                       nsContainerFrame::ExcessOverflowContainersProperty()));
+  if (eoc != mOverflowContList) {
+    nsFrameList* oc = static_cast<nsFrameList*>(propTable->Get(mParent,
+                        nsContainerFrame::OverflowContainersProperty()));
+    if (oc != mOverflowContList) {
+      // mOverflowContList was deleted
+      mPrevOverflowCont = nullptr;
+      mSentry = nullptr;
+      mParent = static_cast<nsContainerFrame*>(aChild->GetParent());
+      mOverflowContList = nullptr;
+      SetupOverflowContList();
+      return;
+    }
+  }
+  // The list survived, update mSentry if needed.
+  if (!mSentry) {
+    if (!mPrevOverflowCont) {
+      SetUpListWalker();
+    } else {
+      mozilla::AutoRestore<nsIFrame*> saved(mPrevOverflowCont);
+      // step backward to make StepForward() use our current mPrevOverflowCont
+      mPrevOverflowCont = mPrevOverflowCont->GetPrevSibling();
       StepForward();
-      if (mPrevOverflowCont == nif) {
-        // Pull mPrevOverflowChild back to aChild's prevSibling:
-        // aChild will be removed from our list by our caller
-        mPrevOverflowCont = prevOverflowCont;
-      }
     }
-    f = nif;
   }
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Debugging
 
 #ifdef DEBUG
 void
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -595,24 +595,43 @@ public:
    *
    * The caller MUST NOT disconnect the frame from its parent's
    * child list if it is already an NS_FRAME_IS_OVERFLOW_CONTAINER.
    * (In this case we will disconnect and reconnect it ourselves.)
    */
   nsresult Insert(nsIFrame*       aOverflowCont,
                   nsReflowStatus& aReflowStatus);
   /**
-   * This function must be called for each child that is reflowed
+   * Begin/EndFinish() must be called for each child that is reflowed
    * but no longer has an overflow continuation. (It may be called for
    * other children, but in that case has no effect.) It increments our
    * walker and makes sure we drop any dangling pointers to its
    * next-in-flow. This function MUST be called before stealing or
    * deleting aChild's next-in-flow.
+   * The AutoFinish helper object does that for you. Use it like so:
+   * if (kidNextInFlow) {
+   *   nsOverflowContinuationTracker::AutoFinish fini(tracker, kid);
+   *   ... DeleteNextInFlowChild/StealFrame(kidNextInFlow) here ...
+   * }
    */
-  void Finish(nsIFrame* aChild);
+  class MOZ_STACK_CLASS AutoFinish {
+  public:
+    AutoFinish(nsOverflowContinuationTracker* aTracker, nsIFrame* aChild)
+      : mTracker(aTracker), mChild(aChild)
+    {
+      if (mTracker) mTracker->BeginFinish(mChild);
+    }
+    ~AutoFinish() 
+    {
+      if (mTracker) mTracker->EndFinish(mChild);
+    }
+  private:
+    nsOverflowContinuationTracker* mTracker;
+    nsIFrame* mChild;
+  };
 
   /**
    * This function should be called for each child that isn't reflowed.
    * It increments our walker and sets the NS_FRAME_OVERFLOW_INCOMPLETE
    * reflow flag if it encounters an overflow continuation so that our
    * next-in-flow doesn't get prematurely deleted. It MUST be called on
    * each unreflowed child that has an overflow container continuation;
    * it MAY be called on other children, but it isn't necessary (doesn't
@@ -624,16 +643,23 @@ public:
     if (aChild == mSentry) {
       StepForward();
       NS_MergeReflowStatusInto(&aReflowStatus, NS_FRAME_OVERFLOW_INCOMPLETE);
     }
   }
 
 private:
 
+  /**
+   * @see class AutoFinish
+   */
+  void BeginFinish(nsIFrame* aChild);
+  void EndFinish(nsIFrame* aChild);
+
+  void SetupOverflowContList();
   void SetUpListWalker();
   void StepForward();
 
   /* We hold a pointer to either the next-in-flow's overflow containers list
      or, if that doesn't exist, our frame's excess overflow containers list.
      We need to make sure that we drop that pointer if the list becomes
      empty and is deleted elsewhere. */
   nsFrameList* mOverflowContList;
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -53,16 +53,17 @@ nsVideoFrame::nsVideoFrame(nsStyleContex
 {
 }
 
 nsVideoFrame::~nsVideoFrame()
 {
 }
 
 NS_QUERYFRAME_HEAD(nsVideoFrame)
+  NS_QUERYFRAME_ENTRY(nsVideoFrame)
   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
 nsresult
 nsVideoFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
 {
   nsNodeInfoManager *nodeInfoManager = GetContent()->GetCurrentDoc()->NodeInfoManager();
   nsCOMPtr<nsINodeInfo> nodeInfo;
--- a/layout/generic/nsVideoFrame.h
+++ b/layout/generic/nsVideoFrame.h
@@ -36,16 +36,17 @@ class nsVideoFrame : public nsContainerF
 public:
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::FrameLayerBuilder::ContainerParameters ContainerParameters;
 
   nsVideoFrame(nsStyleContext* aContext);
 
   NS_DECL_QUERYFRAME
+  NS_DECL_QUERYFRAME_TARGET(nsVideoFrame)
   NS_DECL_FRAMEARENA_HELPERS
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
 
   NS_IMETHOD AttributeChanged(int32_t aNameSpaceID,
                               nsIAtom* aAttribute,
--- a/layout/media/symbols.def.in
+++ b/layout/media/symbols.def.in
@@ -105,16 +105,17 @@ speex_resampler_get_input_latency
 speex_resampler_get_output_latency
 speex_resampler_skip_zeros
 speex_resampler_reset_mem
 speex_resampler_strerror
 #endif
 #ifdef MOZ_CUBEB
 cubeb_destroy
 cubeb_init
+cubeb_get_max_channel_count
 cubeb_stream_destroy
 cubeb_stream_get_position
 cubeb_stream_init
 cubeb_stream_start
 cubeb_stream_stop
 #endif
 #ifdef MOZ_OGG
 th_comment_clear
@@ -570,14 +571,16 @@ hb_unicode_funcs_create
 hb_unicode_funcs_get_empty
 hb_unicode_funcs_set_combining_class_func
 hb_unicode_funcs_set_compose_func
 hb_unicode_funcs_set_decompose_func
 hb_unicode_funcs_set_eastasian_width_func
 hb_unicode_funcs_set_general_category_func
 hb_unicode_funcs_set_mirroring_func
 hb_unicode_funcs_set_script_func
-webvtt_string_text
 webvtt_create_parser
 webvtt_delete_parser
+webvtt_finish_parsing
 webvtt_parse_chunk
 webvtt_ref_node
 webvtt_release_node
+webvtt_strerror
+webvtt_string_text
--- a/layout/style/html.css
+++ b/layout/style/html.css
@@ -722,26 +722,36 @@ audio:not([controls]) {
 
 *|*::-moz-html-canvas-content {
   display: block !important;
   /* we want to be an absolute and fixed container */
   -moz-transform: translate(0) !important;
 }
 
 video > .caption-box {
-  text-align: center;
-  font-weight: bold;
-  font-size: 24px;
+  position: relative;
   pointer-events: none;
 }
 
 video > div .caption-text {
-  color: gold;
-  background-color: rgba(105,105,105,0.4);
+  position: absolute;
+  bottom: 24px;
+  padding: 5px;
+  left: 0;
+  right: 0;
+  text-align: center;
   pointer-events: auto;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 20px;
+  color: white;
+  text-shadow:
+    -2px -2px 1px #000,
+    2px -2px 1px #000,
+    -2px 2px 1px #000,
+    2px 2px 1px #000;
 }
 
 /* emulation of non-standard HTML <marquee> tag */
 marquee {
   width: -moz-available;
   display: inline-block;
   vertical-align: text-bottom;
   text-align: start;
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was 951a3c6a88aa723e222715a89bfdc65f547ddf9e.
+The git commit ID used was c21d25a9a37027e0aaf55d88303b88147761979b.
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -175,16 +175,24 @@ typedef void (* cubeb_state_callback)(cu
     @retval CUBEB_ERROR */
 int cubeb_init(cubeb ** context, char const * context_name);
 
 /** Get a read-only string identifying this context's current backend.
     @param context
     @retval Read-only string identifying current backend. */
 char const * cubeb_get_backend_id(cubeb * context);
 
+/** Get the maximum possible number of channels.
+    @param context
+    @param max_channels The maximum number of channels.
+    @retval CUBEB_OK
+    @retval CUBEB_ERROR_INVALID_PARAMETER
+    @retval CUBEB_ERROR */
+int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
+
 /** Destroy an application context.
     @param context */
 void cubeb_destroy(cubeb * context);
 
 /** Initialize a stream associated with the supplied application context.
     @param context
     @param stream
     @param stream_name
--- a/media/libcubeb/src/cubeb-internal.h
+++ b/media/libcubeb/src/cubeb-internal.h
@@ -7,16 +7,17 @@
 #if !defined(CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5)
 #define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
 
 #include "cubeb/cubeb.h"
 
 struct cubeb_ops {
   int (* init)(cubeb ** context, char const * context_name);
   char const * (* get_backend_id)(cubeb * context);
+  int (* get_max_channel_count)(cubeb * context, uint32_t * max_channels);
   void (* destroy)(cubeb * context);
   int (* stream_init)(cubeb * context, cubeb_stream ** stream, char const * stream_name,
                       cubeb_stream_params stream_params, unsigned int latency,
                       cubeb_data_callback data_callback,
                       cubeb_state_callback state_callback,
                       void * user_ptr);
   void (* stream_destroy)(cubeb_stream * stream);
   int (* stream_start)(cubeb_stream * stream);
--- a/media/libcubeb/src/cubeb.c
+++ b/media/libcubeb/src/cubeb.c
@@ -129,16 +129,26 @@ cubeb_get_backend_id(cubeb * context)
 {
   if (!context) {
     return NULL;
   }
 
   return context->ops->get_backend_id(context);
 }
 
+int
+cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
+{
+  if (!context || !max_channels) {
+    return CUBEB_ERROR_INVALID_PARAMETER;
+  }
+
+  return context->ops->get_max_channel_count(context, max_channels);
+}
+
 void
 cubeb_destroy(cubeb * context)
 {
   if (!context) {
     return;
   }
 
   context->ops->destroy(context);
--- a/media/libcubeb/src/cubeb_alsa.c
+++ b/media/libcubeb/src/cubeb_alsa.c
@@ -890,16 +890,52 @@ alsa_stream_destroy(cubeb_stream * stm)
   assert(ctx->active_streams >= 1);
   ctx->active_streams -= 1;
   pthread_mutex_unlock(&ctx->mutex);
 
   free(stm);
 }
 
 static int
+alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  int rv;
+  cubeb_stream * stm;
+  snd_pcm_hw_params_t* hw_params;
+  cubeb_stream_params params;
+  params.rate = 44100;
+  params.format = CUBEB_SAMPLE_FLOAT32NE;
+  params.channels = 2;
+
+  snd_pcm_hw_params_alloca(&hw_params);
+
+  assert(ctx);
+
+  rv = alsa_stream_init(ctx, &stm, "", params, 100, NULL, NULL, NULL);
+  if (rv != CUBEB_OK) {
+    return CUBEB_ERROR;
+  }
+
+  rv = snd_pcm_hw_params_any(stm->pcm, hw_params);
+  if (rv < 0) {
+    return CUBEB_ERROR;
+  }
+
+  rv = snd_pcm_hw_params_get_channels_max(hw_params, max_channels);
+  if (rv < 0) {
+    return CUBEB_ERROR;
+  }
+
+  alsa_stream_destroy(stm);
+
+  return CUBEB_OK;
+}
+
+
+static int
 alsa_stream_start(cubeb_stream * stm)
 {
   cubeb * ctx;
 
   assert(stm);
   ctx = stm->context;
 
   pthread_mutex_lock(&stm->mutex);
@@ -971,15 +1007,16 @@ alsa_stream_get_position(cubeb_stream * 
 
   pthread_mutex_unlock(&stm->mutex);
   return CUBEB_OK;
 }
 
 static struct cubeb_ops const alsa_ops = {
   .init = alsa_init,
   .get_backend_id = alsa_get_backend_id,
+  .get_max_channel_count = alsa_get_max_channel_count,
   .destroy = alsa_destroy,
   .stream_init = alsa_stream_init,
   .stream_destroy = alsa_stream_destroy,
   .stream_start = alsa_stream_start,
   .stream_stop = alsa_stream_stop,
   .stream_get_position = alsa_stream_get_position
 };
--- a/media/libcubeb/src/cubeb_audiotrack.c
+++ b/media/libcubeb/src/cubeb_audiotrack.c
@@ -271,16 +271,28 @@ audiotrack_init(cubeb ** context, char c
 }
 
 char const *
 audiotrack_get_backend_id(cubeb * context)
 {
   return "audiotrack";
 }
 
+static int
+audiotrack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  assert(ctx && max_channels);
+
+  /* The android mixer handles up to two channels, see
+  http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
+  *max_channels = 2;
+
+  return CUBEB_OK;
+}
+
 void
 audiotrack_destroy(cubeb * context)
 {
   assert(context);
 
   dlclose(context->library);
 
   free(context);
@@ -288,17 +300,17 @@ audiotrack_destroy(cubeb * context)
 
 int
 audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
                        cubeb_stream_params stream_params, unsigned int latency,
                        cubeb_data_callback data_callback,
                        cubeb_state_callback state_callback,
                        void * user_ptr)
 {
-  struct cubeb_stream * stm;
+  cubeb_stream * stm;
   int32_t channels;
   int32_t min_frame_count;
 
   assert(ctx && stream);
 
   if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE ||
       stream_params.format == CUBEB_SAMPLE_FLOAT32BE) {
     return CUBEB_ERROR_INVALID_FORMAT;
@@ -410,15 +422,16 @@ audiotrack_stream_get_position(cubeb_str
   *position = p;
 
   return CUBEB_OK;
 }
 
 static struct cubeb_ops const audiotrack_ops = {
   .init = audiotrack_init,
   .get_backend_id = audiotrack_get_backend_id,
+  .get_max_channel_count = audiotrack_get_max_channel_count,
   .destroy = audiotrack_destroy,
   .stream_init = audiotrack_stream_init,
   .stream_destroy = audiotrack_stream_destroy,
   .stream_start = audiotrack_stream_start,
   .stream_stop = audiotrack_stream_stop,
   .stream_get_position = audiotrack_stream_get_position
 };
--- a/media/libcubeb/src/cubeb_audiounit.c
+++ b/media/libcubeb/src/cubeb_audiounit.c
@@ -4,16 +4,17 @@
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #undef NDEBUG
 #include <assert.h>
 #include <pthread.h>
 #include <stdlib.h>
 #include <AudioUnit/AudioUnit.h>
+#include <CoreAudio/AudioHardware.h>
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
 
 #define NBUFS 4
 
 static struct cubeb_ops const audiounit_ops;
 
 struct cubeb {
@@ -105,16 +106,68 @@ audiounit_init(cubeb ** context, char co
 }
 
 static char const *
 audiounit_get_backend_id(cubeb * ctx)
 {
   return "audiounit";
 }
 
+
+int
+audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  UInt32 size;
+  OSStatus r;
+  AudioDeviceID output_device_id;
+  AudioStreamBasicDescription stream_format;
+  AudioObjectPropertyAddress output_device_address = {
+    kAudioHardwarePropertyDefaultOutputDevice,
+    kAudioObjectPropertyScopeGlobal,
+    kAudioObjectPropertyElementMaster
+  };
+  AudioObjectPropertyAddress stream_format_address = {
+    kAudioDevicePropertyStreamFormat,
+    kAudioDevicePropertyScopeOutput,
+    kAudioObjectPropertyElementMaster
+  };
+
+  assert(ctx && max_channels);
+
+  /* Get the output device. */
+  size = sizeof(output_device_id);
+
+  r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+                                 &output_device_address,
+                                 0,
+                                 0,
+                                 &size,
+                                 &output_device_id);
+
+  if (r != noErr) {
+    return CUBEB_ERROR;
+  }
+
+  size = sizeof(stream_format);
+
+  r = AudioObjectGetPropertyData(output_device_id,
+                                 &stream_format_address,
+                                 0,
+                                 NULL,
+                                 &size,
+                                 &stream_format);
+  if (r != noErr) {
+    return CUBEB_ERROR;
+  }
+
+  *max_channels = stream_format.mChannelsPerFrame;
+
+  return CUBEB_OK;
+}
+
 static void
 audiounit_destroy(cubeb * ctx)
 {
   free(ctx);
 }
 
 static void audiounit_stream_destroy(cubeb_stream * stm);
 
@@ -296,15 +349,16 @@ audiounit_stream_get_position(cubeb_stre
   *position = stm->frames_played;
   pthread_mutex_unlock(&stm->mutex);
   return CUBEB_OK;
 }
 
 static struct cubeb_ops const audiounit_ops = {
   .init = audiounit_init,
   .get_backend_id = audiounit_get_backend_id,
+  .get_max_channel_count = audiounit_get_max_channel_count,
   .destroy = audiounit_destroy,
   .stream_init = audiounit_stream_init,
   .stream_destroy = audiounit_stream_destroy,
   .stream_start = audiounit_stream_start,
   .stream_stop = audiounit_stream_stop,
   .stream_get_position = audiounit_stream_get_position
 };
--- a/media/libcubeb/src/cubeb_opensl.c
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -30,17 +30,17 @@ struct cubeb {
   SLEngineItf eng;
   SLObjectItf outmixObj;
 };
 
 #define NELEMS(A) (sizeof(A) / sizeof A[0])
 #define NBUFS 4
 
 struct cubeb_stream {
-  struct cubeb * context;
+  cubeb * context;
   SLObjectItf playerObj;
   SLPlayItf play;
   SLBufferQueueItf bufq;
   void *queuebuf[NBUFS];
   int queuebuf_idx;
   long queuebuf_len;
   long bytespersec;
   long framesize;
@@ -207,16 +207,27 @@ opensl_init(cubeb ** context, char const
 }
 
 static char const *
 opensl_get_backend_id(cubeb * ctx)
 {
   return "opensl";
 }
 
+static int
+opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  assert(ctx && max_channels);
+  /* The android mixer handles up to two channels, see
+  http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
+  *max_channels = 2;
+
+  return CUBEB_OK;
+}
+
 static void
 opensl_destroy(cubeb * ctx)
 {
   if (ctx->outmixObj)
     (*ctx->outmixObj)->Destroy(ctx->outmixObj);
   if (ctx->engObj)
     (*ctx->engObj)->Destroy(ctx->engObj);
   dlclose(ctx->lib);
@@ -400,15 +411,16 @@ opensl_stream_get_position(cubeb_stream 
     return CUBEB_ERROR;
   *position = (stm->bytespersec / (1000 * stm->framesize)) * msec;
   return CUBEB_OK;
 }
 
 static struct cubeb_ops const opensl_ops = {
   .init = opensl_init,
   .get_backend_id = opensl_get_backend_id,
+  .get_max_channel_count = opensl_get_max_channel_count,
   .destroy = opensl_destroy,
   .stream_init = opensl_stream_init,
   .stream_destroy = opensl_stream_destroy,
   .stream_start = opensl_stream_start,
   .stream_stop = opensl_stream_stop,
   .stream_get_position = opensl_stream_get_position
 };
--- a/media/libcubeb/src/cubeb_pulse.c
+++ b/media/libcubeb/src/cubeb_pulse.c
@@ -4,29 +4,32 @@
  * This program is made available under an ISC-style license.  See the
  * accompanying file LICENSE for details.
  */
 #undef NDEBUG
 #include <assert.h>
 #include <dlfcn.h>
 #include <stdlib.h>
 #include <pulse/pulseaudio.h>
+#include <string.h>
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
 
 #define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x
 MAKE_TYPEDEF(pa_channel_map_init_auto);
 MAKE_TYPEDEF(pa_context_connect);
 MAKE_TYPEDEF(pa_context_disconnect);
 MAKE_TYPEDEF(pa_context_drain);
 MAKE_TYPEDEF(pa_context_get_state);
 MAKE_TYPEDEF(pa_context_new);
 MAKE_TYPEDEF(pa_context_rttime_new);
 MAKE_TYPEDEF(pa_context_set_state_callback);
 MAKE_TYPEDEF(pa_context_unref);
+MAKE_TYPEDEF(pa_context_get_sink_info_by_name);
+MAKE_TYPEDEF(pa_context_get_server_info);
 MAKE_TYPEDEF(pa_frame_size);
 MAKE_TYPEDEF(pa_operation_get_state);
 MAKE_TYPEDEF(pa_operation_unref);
 MAKE_TYPEDEF(pa_rtclock_now);
 MAKE_TYPEDEF(pa_stream_begin_write);
 MAKE_TYPEDEF(pa_stream_cancel_write);
 MAKE_TYPEDEF(pa_stream_connect_playback);
 MAKE_TYPEDEF(pa_stream_cork);
@@ -56,37 +59,54 @@ MAKE_TYPEDEF(pa_usec_to_bytes);
 
 static struct cubeb_ops const pulse_ops;
 
 struct cubeb {
   struct cubeb_ops const * ops;
   void * libpulse;
   pa_threaded_mainloop * mainloop;
   pa_context * context;
+  pa_sink_info * default_sink_info;
   int error;
 };
 
 struct cubeb_stream {
-  struct cubeb * context;
+  cubeb * context;
   pa_stream * stream;
   cubeb_data_callback data_callback;
   cubeb_state_callback state_callback;
   void * user_ptr;
   pa_time_event * drain_timer;
   pa_sample_spec sample_spec;
   int shutdown;
 };
 
 enum cork_state {
   UNCORK = 0,
   CORK = 1 << 0,
   NOTIFY = 1 << 1
 };
 
 static void
+sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u)
+ {
+  cubeb * ctx = u;
+   if (!eol) {
+    ctx->default_sink_info = malloc(sizeof(pa_sink_info));
+    memcpy(ctx->default_sink_info, info, sizeof(pa_sink_info));
+   }
+ }
+
+static void
+server_info_callback(pa_context * context, const pa_server_info * info, void * u)
+{
+  WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name, sink_info_callback, u);
+}
+
+static void
 context_state_callback(pa_context * c, void * u)
 {
   cubeb * ctx = u;
   if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(c))) {
     ctx->error = 1;
   }
   WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
 }
@@ -270,16 +290,18 @@ pulse_init(cubeb ** context, char const 
   LOAD(pa_channel_map_init_auto);
   LOAD(pa_context_connect);
   LOAD(pa_context_disconnect);
   LOAD(pa_context_drain);
   LOAD(pa_context_get_state);
   LOAD(pa_context_new);
   LOAD(pa_context_rttime_new);
   LOAD(pa_context_set_state_callback);
+  LOAD(pa_context_get_sink_info_by_name);
+  LOAD(pa_context_get_server_info);
   LOAD(pa_context_unref);
   LOAD(pa_frame_size);
   LOAD(pa_operation_get_state);
   LOAD(pa_operation_unref);
   LOAD(pa_rtclock_now);
   LOAD(pa_stream_begin_write);
   LOAD(pa_stream_cancel_write);
   LOAD(pa_stream_connect_playback);
@@ -322,29 +344,44 @@ pulse_init(cubeb ** context, char const 
   WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
   WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL);
 
   if (wait_until_context_ready(ctx) != 0) {
     WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
     pulse_destroy(ctx);
     return CUBEB_ERROR;
   }
+  WRAP(pa_context_get_server_info)(ctx->context, server_info_callback, ctx);
   WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
 
   *context = ctx;
 
   return CUBEB_OK;
 }
 
 static char const *
 pulse_get_backend_id(cubeb * ctx)
 {
   return "pulse";
 }
 
+static int
+pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  assert(ctx && max_channels);
+
+  while (!ctx->default_sink_info) {
+    WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
+  }
+
+  *max_channels = ctx->default_sink_info->channel_map.channels;
+
+  return CUBEB_OK;
+}
+
 static void
 pulse_destroy(cubeb * ctx)
 {
   pa_operation * o;
 
   if (ctx->context) {
     WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
     o = WRAP(pa_context_drain)(ctx->context, context_notify_callback, ctx);
@@ -359,16 +396,19 @@ pulse_destroy(cubeb * ctx)
   }
 
   if (ctx->mainloop) {
     WRAP(pa_threaded_mainloop_stop)(ctx->mainloop);
     WRAP(pa_threaded_mainloop_free)(ctx->mainloop);
   }
 
   dlclose(ctx->libpulse);
+  if (ctx->default_sink_info) {
+    free(ctx->default_sink_info);
+  }
   free(ctx);
 }
 
 static void pulse_stream_destroy(cubeb_stream * stm);
 
 static int
 pulse_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
                   cubeb_stream_params stream_params, unsigned int latency,
@@ -518,15 +558,16 @@ pulse_stream_get_position(cubeb_stream *
   *position = bytes / WRAP(pa_frame_size)(&stm->sample_spec);
 
   return CUBEB_OK;
 }
 
 static struct cubeb_ops const pulse_ops = {
   .init = pulse_init,
   .get_backend_id = pulse_get_backend_id,
+  .get_max_channel_count = pulse_get_max_channel_count,
   .destroy = pulse_destroy,
   .stream_init = pulse_stream_init,
   .stream_destroy = pulse_stream_destroy,
   .stream_start = pulse_stream_start,
   .stream_stop = pulse_stream_stop,
   .stream_get_position = pulse_stream_get_position
 };
--- a/media/libcubeb/src/cubeb_sndio.c
+++ b/media/libcubeb/src/cubeb_sndio.c
@@ -50,27 +50,27 @@ float_to_s16(void *ptr, long nsamp)
 
   while (nsamp-- > 0)
     *(dst++) = *(src++) * 32767;
 }
 
 static void
 sndio_onmove(void *arg, int delta)
 {
-  struct cubeb_stream *s = (struct cubeb_stream *)arg;
+  cubeb_stream *s = (cubeb_stream *)arg;
 
   s->rdpos += delta;
 }
 
 static void *
 sndio_mainloop(void *arg)
 {
 #define MAXFDS 8
   struct pollfd pfds[MAXFDS];
-  struct cubeb_stream *s = arg;
+  cubeb_stream *s = arg;
   int n, nfds, revents, state;
   size_t start = 0, end = 0;
   long nfr;
 
   DPR("sndio_mainloop()\n");
   s->state_cb(s, s->arg, CUBEB_STATE_STARTED);
   pthread_mutex_lock(&s->mtx);
   if (!sio_start(s->hdl)) {
@@ -147,38 +147,39 @@ sndio_init(cubeb **context, char const *
 }
 
 static char const *
 sndio_get_backend_id(cubeb *context)
 {
   return "sndio";
 }
 
+
 static void
 sndio_destroy(cubeb *context)
 {
   DPR("sndio_destroy()\n");
   free(context);
 }
 
 static int
 sndio_stream_init(cubeb *context,
                   cubeb_stream **stream,
                   char const *stream_name,
                   cubeb_stream_params stream_params, unsigned int latency,
                   cubeb_data_callback data_callback,
                   cubeb_state_callback state_callback,
                   void *user_ptr)
 {
-  struct cubeb_stream *s;
+  cubeb_stream *s;
   struct sio_par wpar, rpar;
   DPR("sndio_stream_init(%s)\n", stream_name);
   size_t size;
 
-  s = malloc(sizeof(struct cubeb_stream));
+  s = malloc(sizeof(cubeb_stream));
   if (s == NULL)
     return CUBEB_ERROR;
   s->context = context;
   s->hdl = sio_open(NULL, SIO_PLAY, 0);
   if (s->hdl == NULL) {
     free(s);
     DPR("sndio_stream_init(), sio_open() failed\n");
     return CUBEB_ERROR;
@@ -242,16 +243,26 @@ sndio_stream_init(cubeb *context,
   }
   *stream = s;
   DPR("sndio_stream_init() end, ok\n");
   (void)context;
   (void)stream_name;
   return CUBEB_OK;
 }
 
+static int
+sndio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  assert(ctx && max_channels);
+
+  *max_channels = 8;
+
+  return CUBEB_OK;
+}
+
 static void
 sndio_stream_destroy(cubeb_stream *s)
 {
   DPR("sndio_stream_destroy()\n");
   sio_close(s->hdl);
   free(s);
 }
 
--- a/media/libcubeb/src/cubeb_winmm.c
+++ b/media/libcubeb/src/cubeb_winmm.c
@@ -500,16 +500,29 @@ winmm_stream_destroy(cubeb_stream * stm)
   EnterCriticalSection(&stm->context->lock);
   assert(stm->context->active_streams >= 1);
   stm->context->active_streams -= 1;
   LeaveCriticalSection(&stm->context->lock);
 
   free(stm);
 }
 
+int
+winmm_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
+{
+  MMRESULT rv;
+  LPWAVEOUTCAPS waveout_caps;
+  assert(ctx && max_channels);
+
+  /* We don't support more than two channels in this backend. */
+  *max_channels = 2;
+
+  return CUBEB_OK;
+}
+
 static int
 winmm_stream_start(cubeb_stream * stm)
 {
   MMRESULT r;
 
   EnterCriticalSection(&stm->lock);
   r = waveOutRestart(stm->waveout);
   LeaveCriticalSection(&stm->lock);
@@ -559,15 +572,16 @@ winmm_stream_get_position(cubeb_stream *
   *position = time.u.sample;
 
   return CUBEB_OK;
 }
 
 static struct cubeb_ops const winmm_ops = {
   /*.init =*/ winmm_init,
   /*.get_backend_id =*/ winmm_get_backend_id,
+  /*.get_max_channel_count=*/ winmm_get_max_channel_count,
   /*.destroy =*/ winmm_destroy,
   /*.stream_init =*/ winmm_stream_init,
   /*.stream_destroy =*/ winmm_stream_destroy,
   /*.stream_start =*/ winmm_stream_start,
   /*.stream_stop =*/ winmm_stream_stop,
   /*.stream_get_position =*/ winmm_stream_get_position
 };
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -41,21 +41,16 @@
     <uses-permission android:name="android.permission.WRITE_SMS"/>
     <uses-permission android:name="android.permission.READ_SMS"/>
 #endif
 
     <uses-feature android:name="android.hardware.location" android:required="false"/>
     <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
     <uses-feature android:name="android.hardware.touchscreen"/>
 
-    <!-- Contacts API -->
-    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
-    <uses-permission android:name="android.permission.READ_CONTACTS"/>
-    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
-
 #ifdef MOZ_ANDROID_BEAM
     <!-- Android Beam support -->
     <uses-permission android:name="android.permission.NFC"/>
     <uses-feature android:name="android.hardware.nfc" android:required="false"/>
 #endif
 
 #ifdef MOZ_WEBRTC
     <uses-permission android:name="android.permission.RECORD_AUDIO"/>
--- a/toolkit/components/social/FrameWorker.jsm
+++ b/toolkit/components/social/FrameWorker.jsm
@@ -131,17 +131,17 @@ FrameWorker.prototype = {
     let sandbox = new Cu.Sandbox(workerWindow);
 
     // copy the window apis onto the sandbox namespace only functions or
     // objects that are naturally a part of an iframe, I'm assuming they are
     // safe to import this way
     let workerAPI = ['WebSocket', 'localStorage', 'atob', 'btoa',
                      'clearInterval', 'clearTimeout', 'dump',
                      'setInterval', 'setTimeout', 'XMLHttpRequest',
-                     'FileReader', 'Blob', 'EventSource',
+                     'FileReader', 'Blob', 'EventSource', 'indexedDB',
                      'location'];
     // Bug 798660 - XHR and WebSocket have issues in a sandbox and need
     // to be unwrapped to work
     let needsWaive = ['XMLHttpRequest', 'WebSocket'];
     // Methods need to be bound with the proper |this|.
     let needsBind = ['atob', 'btoa', 'dump', 'setInterval', 'clearInterval',
                      'setTimeout', 'clearTimeout'];
     workerAPI.forEach(function(fn) {
--- a/toolkit/components/social/test/browser/browser_frameworker.js
+++ b/toolkit/components/social/test/browser/browser_frameworker.js
@@ -647,9 +647,22 @@ let tests = {
       } else if (e.data.topic == "pong") {
         worker.terminate();
         cbnext();
       }
     }
     worker.port.postMessage({topic: "ping"})
   },
 
+
+  testIndexedDB: function(cbnext) {
+    let worker = getFrameWorkerHandle("https://example.com/browser/toolkit/components/social/test/browser/worker_social.js", undefined, "testIndexedDB");
+    worker.port.onmessage = function(e) {
+      let m = e.data;
+      if (m.topic == "social.indexeddb-result") {
+        is(m.data.result, "ok", "created indexeddb");
+        worker.terminate();
+        cbnext();
+      }
+    }
+    worker.port.postMessage({topic: "test-indexeddb-create"})
+  },
 }
--- a/toolkit/components/social/test/browser/worker_social.js
+++ b/toolkit/components/social/test/browser/worker_social.js
@@ -42,16 +42,28 @@ onconnect = function(e) {
         // start
         apiPort.postMessage({topic: 'social.reload-worker'});
         break;
       case "test-notification-create":
         apiPort.postMessage({topic: 'social.notification-create',
                              data: data});
         testerPort.postMessage({topic: 'did-notification-create'});
         break;
+      case "test-indexeddb-create":
+        var request = indexedDB.open("workerdb", 1);
+        request.onerror = function(event) {
+          port.postMessage({topic: 'social.indexeddb-result', data: { result: "error" }});
+        };
+        request.onsuccess = function(event) {
+          // Do something with request.result!
+          var db = request.result;
+          db.close();
+          port.postMessage({topic: 'social.indexeddb-result', data: { result: "ok" }});
+        };
+        break;
     }
   }
   // used for "test-reload-worker"
   if (apiPort && apiPort != port) {
     port.postMessage({topic: "worker.connected"})
   }
 
 }