Merge m-c to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 14 Apr 2014 15:54:12 +0200
changeset 196853 b8081c65fd70798c77ef50d8cc9e0625acaac85b
parent 196852 008bbf75bccbdc1fbbd3e699737d84902cc57831 (current diff)
parent 196835 265d82091bce0ab6aa4ac23a3850fe8c05310f42 (diff)
child 196854 a3337bda5a22f1d5b86390a307154ea2ba7c5a50
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team
accessible/tests/mochitest/events/test_name.xul
content/mathml/content/crashtests/462929-1.html
content/mathml/content/crashtests/770710-1.html
content/mathml/content/crashtests/crashtests.list
content/mathml/content/src/moz.build
content/mathml/content/src/nsMathMLElement.cpp
content/mathml/content/src/nsMathMLElement.h
content/mathml/content/src/nsMathMLElementFactory.cpp
content/xml/content/crashtest/420429.xul
content/xml/content/crashtest/453278-frame.xml
content/xml/content/crashtest/453278.html
content/xml/content/crashtest/803586.xhtml
content/xml/content/crashtest/crashtests.list
content/xml/content/src/CDATASection.cpp
content/xml/content/src/CDATASection.h
content/xml/content/src/ProcessingInstruction.cpp
content/xml/content/src/ProcessingInstruction.h
content/xml/content/src/XMLStylesheetProcessingInstruction.cpp
content/xml/content/src/XMLStylesheetProcessingInstruction.h
content/xml/content/src/htmlmathml-f.ent
content/xml/content/src/moz.build
content/xml/content/src/nsXMLElement.cpp
content/xml/content/src/nsXMLElement.h
content/xml/document/crashtests/136896-1.xml
content/xml/document/crashtests/185285-1.xml
content/xml/document/crashtests/382636-1.xml
content/xml/document/crashtests/382636-2.svg
content/xml/document/crashtests/382636-3.xhtml
content/xml/document/crashtests/382636-4.xul
content/xml/document/crashtests/431703-1.xhtml
content/xml/document/crashtests/crashtests.list
content/xml/document/crashtests/tree.gif
content/xml/document/moz.build
content/xml/document/public/moz.build
content/xml/document/public/nsIXMLContentSink.h
content/xml/document/resources/XMLMonoPrint.css
content/xml/document/resources/XMLPrettyPrint.css
content/xml/document/resources/XMLPrettyPrint.xml
content/xml/document/resources/XMLPrettyPrint.xsl
content/xml/document/resources/jar.mn
content/xml/document/resources/moz.build
content/xml/document/src/XMLDocument.cpp
content/xml/document/src/XMLDocument.h
content/xml/document/src/moz.build
content/xml/document/src/nsXMLContentSink.cpp
content/xml/document/src/nsXMLContentSink.h
content/xml/document/src/nsXMLFragmentContentSink.cpp
content/xml/document/src/nsXMLPrettyPrinter.cpp
content/xml/document/src/nsXMLPrettyPrinter.h
content/xml/document/test/file_bug293347.xml
content/xml/document/test/file_bug293347xslt.xml
content/xml/document/test/mochitest.ini
content/xml/document/test/moz.build
content/xml/document/test/test_bug232004.xhtml
content/xml/document/test/test_bug293347.html
content/xml/document/test/test_bug343870.xhtml
content/xml/document/test/test_bug355213.xhtml
content/xml/document/test/test_bug392338.html
content/xml/document/test/test_bug399502.xhtml
content/xml/document/test/test_bug445330.html
content/xml/document/test/test_bug691215.html
content/xml/document/test/test_viewport.xhtml
content/xml/moz.build
content/xml/tests/books/bethlehem.gif
content/xml/tests/books/bill.gif
content/xml/tests/books/books.js
content/xml/tests/books/books.xml
content/xml/tests/books/charing.gif
content/xml/tests/books/classic.css
content/xml/tests/books/common.css
content/xml/tests/books/kerouac.gif
content/xml/tests/books/list.css
content/xml/tests/books/road.gif
content/xml/tests/books/welville.gif
content/xml/tests/docbook.css
content/xml/tests/docbooktest.xml
content/xml/tests/flamer.gif
content/xml/tests/load/display.css
content/xml/tests/load/display.xml
content/xml/tests/load/load.html
content/xml/tests/load/loadauth.html
content/xml/tests/load/loaddisplay.html
content/xml/tests/load/test.xml
content/xml/tests/script.xml
content/xml/tests/simple.xml
content/xml/tests/toc/book.css
content/xml/tests/toc/irslogo.gif
content/xml/tests/toc/minus.gif
content/xml/tests/toc/plus.gif
content/xml/tests/toc/rights.xml
content/xml/tests/toc/toc.css
content/xml/tests/toc/toc.js
content/xml/tests/xlink/auto.xml
content/xml/tests/xlink/link.css
content/xml/tests/xlink/manual.xml
content/xml/tests/xmlbase/xmlbase.css
content/xml/tests/xmlbase/xmlbase.xml
dom/interfaces/apps/nsIDOMMozApplicationEvent.idl
dom/interfaces/html/nsIDOMHTMLByteRanges.idl
dom/interfaces/storage/nsIDOMToString.idl
dom/telephony/test/marionette/test_conference.js
python/virtualenv/docs/_theme/nature/static/nature.css_t
python/virtualenv/docs/_theme/nature/static/pygments.css
python/virtualenv/docs/_theme/nature/theme.conf
python/virtualenv/virtualenv_support/pip-1.4.1.tar.gz
python/virtualenv/virtualenv_support/setuptools-0.9.8.tar.gz
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -965,40 +965,72 @@ DocAccessible::AttributeChangedImpl(Acce
   if (aNameSpaceID == kNameSpaceID_None) {
     // Check for hyphenated aria-foo property?
     if (StringBeginsWith(nsDependentAtomString(aAttribute),
                          NS_LITERAL_STRING("aria-"))) {
       ARIAAttributeChanged(aAccessible, aAttribute);
     }
   }
 
-  if (aAttribute == nsGkAtoms::alt ||
-      aAttribute == nsGkAtoms::title ||
-      aAttribute == nsGkAtoms::aria_label ||
-      aAttribute == nsGkAtoms::aria_labelledby) {
+  // Fire name change and description change events. XXX: it's not complete and
+  // dupes the code logic of accessible name and description calculation, we do
+  // that for performance reasons.
+  if (aAttribute == nsGkAtoms::aria_label) {
+    FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
+    return;
+  }
+
+  if (aAttribute == nsGkAtoms::aria_describedby) {
+    FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE, aAccessible);
+    return;
+  }
+
+  nsIContent* elm = aAccessible->GetContent();
+  if (aAttribute == nsGkAtoms::aria_labelledby &&
+      !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label)) {
     FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
     return;
   }
 
+  if (aAttribute == nsGkAtoms::alt &&
+      !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label) &&
+      !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_labelledby)) {
+    FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
+    return;
+  }
+
+  if (aAttribute == nsGkAtoms::title) {
+    if (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_label) &&
+        !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_labelledby) &&
+        !elm->HasAttr(kNameSpaceID_None, nsGkAtoms::alt)) {
+      FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, aAccessible);
+      return;
+    }
+
+    if (!elm->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_describedby))
+      FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE, aAccessible);
+
+    return;
+  }
+
   if (aAttribute == nsGkAtoms::aria_busy) {
-    bool isOn = aAccessible->GetContent()->
-      AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true, eCaseMatters);
+    bool isOn = elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true,
+                                 eCaseMatters);
     nsRefPtr<AccEvent> event =
       new AccStateChangeEvent(aAccessible, states::BUSY, isOn);
     FireDelayedEvent(event);
     return;
   }
 
   // ARIA or XUL selection
   if ((aAccessible->GetContent()->IsXUL() && aAttribute == nsGkAtoms::selected) ||
       aAttribute == nsGkAtoms::aria_selected) {
     Accessible* widget =
       nsAccUtils::GetSelectableContainer(aAccessible, aAccessible->State());
     if (widget) {
-      nsIContent* elm = aAccessible->GetContent();
       AccSelChangeEvent::SelChangeType selChangeType =
         elm->AttrValueIs(aNameSpaceID, aAttribute, nsGkAtoms::_true, eCaseMatters) ?
           AccSelChangeEvent::eSelectionAdd : AccSelChangeEvent::eSelectionRemove;
 
       nsRefPtr<AccEvent> event =
         new AccSelChangeEvent(widget, aAccessible, selChangeType);
       FireDelayedEvent(event);
     }
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -1,12 +1,13 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Constants
 
 const EVENT_ALERT = nsIAccessibleEvent.EVENT_ALERT;
+const EVENT_DESCRIPTION_CHANGE = nsIAccessibleEvent.EVENT_DESCRIPTION_CHANGE;
 const EVENT_DOCUMENT_LOAD_COMPLETE = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE;
 const EVENT_DOCUMENT_RELOAD = nsIAccessibleEvent.EVENT_DOCUMENT_RELOAD;
 const EVENT_DOCUMENT_LOAD_STOPPED = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_STOPPED;
 const EVENT_HIDE = nsIAccessibleEvent.EVENT_HIDE;
 const EVENT_FOCUS = nsIAccessibleEvent.EVENT_FOCUS;
 const EVENT_NAME_CHANGE = nsIAccessibleEvent.EVENT_NAME_CHANGE;
 const EVENT_MENU_START = nsIAccessibleEvent.EVENT_MENU_START;
 const EVENT_MENU_END = nsIAccessibleEvent.EVENT_MENU_END;
--- a/accessible/tests/mochitest/events/a11y.ini
+++ b/accessible/tests/mochitest/events/a11y.ini
@@ -8,16 +8,17 @@ support-files =
 [test_aria_menu.html]
 [test_aria_objattr.html]
 [test_aria_statechange.html]
 [test_attrs.html]
 [test_caretmove.html]
 [test_caretmove.xul]
 [test_coalescence.html]
 [test_contextmenu.html]
+[test_descrchange.html]
 [test_docload.html]
 [test_docload.xul]
 [test_docload_aria.html]
 [test_dragndrop.html]
 [test_flush.html]
 [test_focus_aria_activedescendant.html]
 [test_focus_autocomplete.xul]
 # Disabled on Linux and Windows due to frequent failures - bug 695019, bug 890795
@@ -36,17 +37,18 @@ skip-if = os == 'win' || os == 'linux'
 [test_focus_selects.html]
 [test_focus_tabbox.xul]
 [test_focus_tree.xul]
 [test_fromUserInput.html]
 [test_label.xul]
 [test_menu.xul]
 [test_mutation.html]
 [test_mutation.xhtml]
-[test_name.xul]
+[test_namechange.xul]
+[test_namechange.html]
 [test_scroll.xul]
 [test_selection.html]
 [test_selection.xul]
 [test_selection_aria.html]
 [test_statechange.html]
 [test_text.html]
 [test_text_alg.html]
 [test_textattrchange.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_descrchange.html
@@ -0,0 +1,85 @@
+<html>
+
+<head>
+  <title>Accessible description change event testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+
+  <script type="application/javascript">
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function setAttr(aID, aAttr, aValue, aChecker)
+    {
+      this.eventSeq = [ aChecker ];
+      this.invoke = function setAttr_invoke()
+      {
+        getNode(aID).setAttribute(aAttr, aValue);
+      }
+
+      this.getID = function setAttr_getID()
+      {
+        return "set attr '" + aAttr + "', value '" + aValue + "'";
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    //gA11yEventDumpToConsole = true; // debuggin
+
+    var gQueue = null;
+    function doTests()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new setAttr("tst1", "aria-describedby", "display",
+                              new invokerChecker(EVENT_DESCRIPTION_CHANGE, "tst1")));
+      gQueue.push(new setAttr("tst1", "title", "title",
+                              new unexpectedInvokerChecker(EVENT_DESCRIPTION_CHANGE, "tst1")));
+
+      gQueue.push(new setAttr("tst2", "title", "title",
+                              new invokerChecker(EVENT_NAME_CHANGE, "tst2")));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=991969"
+     title="Event not fired when description changes">
+    Bug 991969
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <button id="tst1">btn1</button>
+  <button id="tst2">btn2</button>
+
+  <div id="eventdump"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/events/test_namechange.html
@@ -0,0 +1,103 @@
+<html>
+
+<head>
+  <title>Accessible name change event testing</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../states.js"></script>
+
+  <script type="application/javascript">
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function setAttr(aID, aAttr, aValue, aChecker)
+    {
+      this.eventSeq = [ aChecker ];
+      this.invoke = function setAttr_invoke()
+      {
+        getNode(aID).setAttribute(aAttr, aValue);
+      }
+
+      this.getID = function setAttr_getID()
+      {
+        return "set attr '" + aAttr + "', value '" + aValue + "'";
+      }
+    }
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Do tests
+
+    //gA11yEventDumpToConsole = true; // debuggin
+
+    var gQueue = null;
+    function doTests()
+    {
+      gQueue = new eventQueue();
+
+      gQueue.push(new setAttr("tst1", "aria-label", "hi",
+                              new invokerChecker(EVENT_NAME_CHANGE, "tst1")));
+      gQueue.push(new setAttr("tst1", "aria-labelledby", "display",
+                              new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst1")));
+      gQueue.push(new setAttr("tst1", "alt", "alt",
+                              new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst1")));
+      gQueue.push(new setAttr("tst1", "title", "title",
+                              new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst1")));
+
+      gQueue.push(new setAttr("tst2", "aria-labelledby", "display",
+                              new invokerChecker(EVENT_NAME_CHANGE, "tst2")));
+      gQueue.push(new setAttr("tst2", "alt", "alt",
+                              new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst2")));
+      gQueue.push(new setAttr("tst2", "title", "title",
+                              new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst2")));
+
+      gQueue.push(new setAttr("tst3", "alt", "alt",
+                              new invokerChecker(EVENT_NAME_CHANGE, "tst3")));
+      gQueue.push(new setAttr("tst3", "title", "title",
+                              new unexpectedInvokerChecker(EVENT_NAME_CHANGE, "tst3")));
+
+      gQueue.push(new setAttr("tst4", "title", "title",
+                              new invokerChecker(EVENT_NAME_CHANGE, "tst4")));
+
+      gQueue.invoke(); // Will call SimpleTest.finish();
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTests);
+  </script>
+</head>
+
+<body>
+
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=991969"
+     title="Event not fired when description changes">
+    Bug 991969
+  </a>
+
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <img id="tst1">
+  <img id="tst2">
+  <img id="tst3">
+  <img id="tst4">
+
+  <div id="eventdump"></div>
+</body>
+</html>
rename from accessible/tests/mochitest/events/test_name.xul
rename to accessible/tests/mochitest/events/test_namechange.xul
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="55bcc2d7e44dc805c24b57d1e783fc26e8a2ee86"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="99a67a75855d8ca077018c819aedd90bf0447d9b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52c909ccead537f8f9dbf634f3e6639078a8b0bd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="55bcc2d7e44dc805c24b57d1e783fc26e8a2ee86"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="99a67a75855d8ca077018c819aedd90bf0447d9b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -13,17 +13,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "a71af1a665a6596324db3487ba73a1d83bf695f2", 
+    "revision": "baa54fb0f2c415a4521259d7fa2f14db32f608a7", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e6383e6e785cc3ea237e902beb1092f9aa88e29d">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="1ad48c4be51b279f7f63c1a13025b52fe087d231">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f3abbd2d0a60f1a1618db93f8b1957cae6de379c"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e046133c79d13d2ad26814547a163ec1732d36e7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ce95d372e6d285725b96490afdaaf489ad8f9ca9"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="8d6c36d74ba9aefbc8c3618fc93dd4907a0dbf5e"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -107,32 +107,33 @@ class EventTarget;
 class FrameRequestCallback;
 class HTMLBodyElement;
 struct LifecycleCallbackArgs;
 class Link;
 class GlobalObject;
 class NodeFilter;
 class NodeIterator;
 class ProcessingInstruction;
+class StyleSheetList;
 class Touch;
 class TouchList;
 class TreeWalker;
 class UndoManager;
 class XPathEvaluator;
 template<typename> class OwningNonNull;
 template<typename> class Sequence;
 
 template<typename, typename> class CallbackObjectHolder;
 typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0xa7679e4a, 0xa5ec, 0x45bf, \
-  { 0x8f, 0xe4, 0xad, 0x4a, 0xb8, 0xc7, 0x7f, 0xc7 } }
+{ 0x906d05e7, 0x39af, 0x4ff0, \
+  { 0xbc, 0xcd, 0x30, 0x0c, 0x7f, 0xeb, 0x86, 0x21 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
@@ -1330,16 +1331,23 @@ public:
   /**
    * @param aFireSync whether to fire onload synchronously.  If false,
    * onload will fire asynchronously after all onload blocks have been
    * removed.  It will NOT fire from inside UnblockOnload.  If true,
    * onload may fire from inside UnblockOnload.
    */
   virtual void UnblockOnload(bool aFireSync) = 0;
 
+  void BlockDOMContentLoaded()
+  {
+    ++mBlockDOMContentLoaded;
+  }
+
+  virtual void UnblockDOMContentLoaded() = 0;
+
   /**
    * Notification that the page has been shown, for documents which are loaded
    * into a DOM window.  This corresponds to the completion of document load,
    * or to the page's presentation being restored into an existing DOM window.
    * This notification fires applicable DOM events to the content window.  See
    * nsIDOMPageTransitionEvent.idl for a description of the |aPersisted|
    * parameter. If aDispatchStartTarget is null, the pageshow event is
    * dispatched on the ScriptGlobalObject for this document, otherwise it's
@@ -2164,17 +2172,17 @@ public:
   {
     return mVisibilityState;
   }
   mozilla::dom::VisibilityState MozVisibilityState()
   {
     WarnOnceAbout(ePrefixedVisibilityAPI);
     return VisibilityState();
   }
-  virtual nsIDOMStyleSheetList* StyleSheets() = 0;
+  virtual mozilla::dom::StyleSheetList* StyleSheets() = 0;
   void GetSelectedStyleSheetSet(nsAString& aSheetSet);
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) = 0;
   virtual void GetLastStyleSheetSet(nsString& aSheetSet) = 0;
   void GetPreferredStyleSheetSet(nsAString& aSheetSet);
   virtual mozilla::dom::DOMStringList* StyleSheetSets() = 0;
   virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) = 0;
   Element* ElementFromPoint(float aX, float aY);
 
@@ -2548,16 +2556,19 @@ protected:
   nsCOMPtr<nsIStructuredCloneContainer> mStateObjectContainer;
   nsCOMPtr<nsIVariant> mStateObjectCached;
 
   uint8_t mDefaultElementType;
 
   uint32_t mInSyncOperationCount;
 
   nsRefPtr<mozilla::dom::XPathEvaluator> mXPathEvaluator;
+
+  uint32_t mBlockDOMContentLoaded;
+  bool mDidFireDOMContentLoaded:1;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
 
 /**
  * mozAutoSubtreeModified batches DOM mutations so that a DOMSubtreeModified
  * event is dispatched, if necessary, when the outermost mozAutoSubtreeModified
  * object is deleted.
--- a/content/base/src/ShadowRoot.cpp
+++ b/content/base/src/ShadowRoot.cpp
@@ -479,17 +479,17 @@ ShadowRoot::SetApplyAuthorStyles(bool aA
   nsIPresShell* shell = OwnerDoc()->GetShell();
   if (shell) {
     OwnerDoc()->BeginUpdate(UPDATE_STYLE);
     shell->RestyleShadowRoot(this);
     OwnerDoc()->EndUpdate(UPDATE_STYLE);
   }
 }
 
-nsIDOMStyleSheetList*
+StyleSheetList*
 ShadowRoot::StyleSheets()
 {
   if (!mStyleSheetList) {
     mStyleSheetList = new ShadowRootStyleSheetList(this);
   }
 
   return mStyleSheetList;
 }
@@ -650,60 +650,56 @@ ShadowRoot::ContentRemoved(nsIDocument* 
 
   // Watch for node that is removed from the pool because
   // it may need to be removed from an insertion point.
   if (IsPooledNode(aChild, aContainer, mPoolHost)) {
     RemoveDistributedNode(aChild);
   }
 }
 
-NS_IMPL_CYCLE_COLLECTION_1(ShadowRootStyleSheetList, mShadowRoot)
+NS_IMPL_CYCLE_COLLECTION_INHERITED_1(ShadowRootStyleSheetList, StyleSheetList,
+                                     mShadowRoot)
 
-NS_INTERFACE_TABLE_HEAD(ShadowRootStyleSheetList)
-  NS_INTERFACE_TABLE1(ShadowRootStyleSheetList, nsIDOMStyleSheetList)
-  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(ShadowRootStyleSheetList)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StyleSheetList)
-NS_INTERFACE_MAP_END
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ShadowRootStyleSheetList)
+NS_INTERFACE_MAP_END_INHERITING(StyleSheetList)
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(ShadowRootStyleSheetList)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(ShadowRootStyleSheetList)
+NS_IMPL_ADDREF_INHERITED(ShadowRootStyleSheetList, StyleSheetList)
+NS_IMPL_RELEASE_INHERITED(ShadowRootStyleSheetList, StyleSheetList)
 
 ShadowRootStyleSheetList::ShadowRootStyleSheetList(ShadowRoot* aShadowRoot)
   : mShadowRoot(aShadowRoot)
 {
   MOZ_COUNT_CTOR(ShadowRootStyleSheetList);
 }
 
 ShadowRootStyleSheetList::~ShadowRootStyleSheetList()
 {
   MOZ_COUNT_DTOR(ShadowRootStyleSheetList);
 }
 
-NS_IMETHODIMP
-ShadowRootStyleSheetList::Item(uint32_t aIndex, nsIDOMStyleSheet** aReturn)
+nsCSSStyleSheet*
+ShadowRootStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound)
+{
+  nsTArray<nsRefPtr<nsCSSStyleSheet>>* sheets =
+    mShadowRoot->mProtoBinding->GetStyleSheets();
+
+  if (!sheets) {
+    aFound = false;
+    return nullptr;
+  }
+
+  aFound = aIndex < sheets->Length();
+  return sheets->SafeElementAt(aIndex);
+}
+
+uint32_t
+ShadowRootStyleSheetList::Length()
 {
   nsTArray<nsRefPtr<nsCSSStyleSheet> >* sheets =
     mShadowRoot->mProtoBinding->GetStyleSheets();
 
-  if (sheets) {
-    NS_IF_ADDREF(*aReturn = sheets->SafeElementAt(aIndex));
-  } else {
-    *aReturn = nullptr;
+  if (!sheets) {
+    return 0;
   }
 
-  return NS_OK;
+  return sheets->Length();
 }
 
-NS_IMETHODIMP
-ShadowRootStyleSheetList::GetLength(uint32_t* aLength)
-{
-  nsTArray<nsRefPtr<nsCSSStyleSheet> >* sheets =
-    mShadowRoot->mProtoBinding->GetStyleSheets();
-
-  if (sheets) {
-    *aLength = sheets->Length();
-  } else {
-    *aLength = 0;
-  }
-
-  return NS_OK;
-}
-
--- a/content/base/src/ShadowRoot.h
+++ b/content/base/src/ShadowRoot.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_shadowroot_h__
 #define mozilla_dom_shadowroot_h__
 
 #include "mozilla/dom/DocumentFragment.h"
+#include "mozilla/dom/StyleSheetList.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsTHashtable.h"
 #include "nsDocument.h"
 
 class nsIAtom;
 class nsIContent;
 class nsIDocument;
@@ -47,17 +48,17 @@ public:
   virtual ~ShadowRoot();
 
   void AddToIdTable(Element* aElement, nsIAtom* aId);
   void RemoveFromIdTable(Element* aElement, nsIAtom* aId);
   void InsertSheet(nsCSSStyleSheet* aSheet, nsIContent* aLinkingContent);
   void RemoveSheet(nsCSSStyleSheet* aSheet);
   bool ApplyAuthorStyles();
   void SetApplyAuthorStyles(bool aApplyAuthorStyles);
-  nsIDOMStyleSheetList* StyleSheets();
+  StyleSheetList* StyleSheets();
   HTMLShadowElement* GetShadowElement() { return mShadowElement; }
 
   /**
    * Sets the current shadow insertion point where the older
    * ShadowRoot will be projected.
    */
   void SetShadowElement(HTMLShadowElement* aShadowElement);
 
@@ -164,27 +165,32 @@ protected:
 
   // A boolean that indicates that an insertion point was added or removed
   // from this ShadowRoot and that the nodes need to be redistributed into
   // the insertion points. After this flag is set, nodes will be distributed
   // on the next mutation event.
   bool mInsertionPointChanged;
 };
 
-class ShadowRootStyleSheetList : public nsIDOMStyleSheetList
+class ShadowRootStyleSheetList : public StyleSheetList
 {
 public:
   ShadowRootStyleSheetList(ShadowRoot* aShadowRoot);
   virtual ~ShadowRootStyleSheetList();
 
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(ShadowRootStyleSheetList)
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ShadowRootStyleSheetList, StyleSheetList)
 
-  // nsIDOMStyleSheetList
-  NS_DECL_NSIDOMSTYLESHEETLIST
+  virtual nsINode* GetParentObject() const MOZ_OVERRIDE
+  {
+    return mShadowRoot;
+  }
+
+  virtual uint32_t Length() MOZ_OVERRIDE;
+  virtual nsCSSStyleSheet* IndexedGetter(uint32_t aIndex, bool& aFound) MOZ_OVERRIDE;
 
 protected:
   nsRefPtr<ShadowRoot> mShadowRoot;
 };
 
 } // namespace dom
 } // namespace mozilla
 
new file mode 100644
--- /dev/null
+++ b/content/base/src/StyleSheetList.cpp
@@ -0,0 +1,46 @@
+/* -*- 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/StyleSheetList.h"
+
+#include "mozilla/dom/StyleSheetListBinding.h"
+#include "nsCSSStyleSheet.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(StyleSheetList)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StyleSheetList)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsIDOMStyleSheetList)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(StyleSheetList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(StyleSheetList)
+
+/* virtual */ JSObject*
+StyleSheetList::WrapObject(JSContext* aCx)
+{
+  return StyleSheetListBinding::Wrap(aCx, this);
+}
+
+NS_IMETHODIMP
+StyleSheetList::GetLength(uint32_t* aLength)
+{
+  *aLength = Length();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+StyleSheetList::SlowItem(uint32_t aIndex, nsIDOMStyleSheet** aItem)
+{
+  NS_IF_ADDREF(*aItem = Item(aIndex));
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/base/src/StyleSheetList.h
@@ -0,0 +1,48 @@
+/* -*- 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_StyleSheetList_h
+#define mozilla_dom_StyleSheetList_h
+
+#include "nsIDOMStyleSheetList.h"
+#include "nsWrapperCache.h"
+
+class nsCSSStyleSheet;
+class nsINode;
+
+namespace mozilla {
+namespace dom {
+
+class StyleSheetList : public nsIDOMStyleSheetList
+                     , public nsWrapperCache
+{
+public:
+  StyleSheetList()
+  {
+    SetIsDOMBinding();
+  }
+  virtual ~StyleSheetList() {}
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(StyleSheetList)
+  NS_DECL_NSIDOMSTYLESHEETLIST
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE MOZ_FINAL;
+
+  virtual nsINode* GetParentObject() const = 0;
+
+  virtual uint32_t Length() = 0;
+  virtual nsCSSStyleSheet* IndexedGetter(uint32_t aIndex, bool& aFound) = 0;
+  nsCSSStyleSheet* Item(uint32_t aIndex)
+  {
+    bool dummy = false;
+    return IndexedGetter(aIndex, dummy);
+  }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_StyleSheetList_h
--- a/content/base/src/moz.build
+++ b/content/base/src/moz.build
@@ -67,16 +67,17 @@ EXPORTS.mozilla.dom += [
     'DOMPoint.h',
     'DOMQuad.h',
     'DOMRect.h',
     'DOMStringList.h',
     'EventSource.h',
     'Link.h',
     'NodeIterator.h',
     'ShadowRoot.h',
+    'StyleSheetList.h',
     'Text.h',
     'TreeWalker.h',
 ]
 
 UNIFIED_SOURCES += [
     'Attr.cpp',
     'ChildIterator.cpp',
     'Comment.cpp',
@@ -156,16 +157,17 @@ UNIFIED_SOURCES += [
     'nsTraversal.cpp',
     'nsTreeSanitizer.cpp',
     'nsViewportInfo.cpp',
     'nsXHTMLContentSerializer.cpp',
     'nsXMLContentSerializer.cpp',
     'nsXMLHttpRequest.cpp',
     'nsXMLNameSpaceMap.cpp',
     'ShadowRoot.cpp',
+    'StyleSheetList.cpp',
     'Text.cpp',
     'ThirdPartyUtil.cpp',
     'TreeWalker.cpp',
     'WebSocket.cpp',
 ]
 
 # These files cannot be built in unified mode because they use FORCE_PR_LOG
 SOURCES += [
--- a/content/base/src/nsAttrValue.h
+++ b/content/base/src/nsAttrValue.h
@@ -7,17 +7,17 @@
  * A struct that represents the value (type and actual data) of an
  * attribute.
  */
 
 #ifndef nsAttrValue_h___
 #define nsAttrValue_h___
 
 #include "nscore.h"
-#include "nsString.h"
+#include "nsStringGlue.h"
 #include "nsStringBuffer.h"
 #include "nsColor.h"
 #include "nsCaseTreatment.h"
 #include "nsMargin.h"
 #include "nsCOMPtr.h"
 #include "SVGAttrValueWrapper.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsIAtom.h"
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -718,86 +718,58 @@ nsDOMStyleSheetList::nsDOMStyleSheetList
 
 nsDOMStyleSheetList::~nsDOMStyleSheetList()
 {
   if (mDocument) {
     mDocument->RemoveObserver(this);
   }
 }
 
-DOMCI_DATA(StyleSheetList, nsDOMStyleSheetList)
-
-// XXX couldn't we use the GetIIDs method from CSSStyleSheetList here?
-// QueryInterface implementation for nsDOMStyleSheetList
-NS_INTERFACE_TABLE_HEAD(nsDOMStyleSheetList)
-  NS_INTERFACE_TABLE3(nsDOMStyleSheetList,
-                      nsIDOMStyleSheetList,
-                      nsIDocumentObserver,
-                      nsIMutationObserver)
-  NS_INTERFACE_TABLE_TO_MAP_SEGUE
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StyleSheetList)
-NS_INTERFACE_MAP_END
-
-
-NS_IMPL_ADDREF(nsDOMStyleSheetList)
-NS_IMPL_RELEASE(nsDOMStyleSheetList)
-
-
-NS_IMETHODIMP
-nsDOMStyleSheetList::GetLength(uint32_t* aLength)
-{
-  if (mDocument) {
-    // XXX Find the number and then cache it. We'll use the
-    // observer notification to figure out if new ones have
-    // been added or removed.
-    if (-1 == mLength) {
-      mLength = mDocument->GetNumberOfStyleSheets();
+NS_IMPL_ISUPPORTS_INHERITED2(nsDOMStyleSheetList, StyleSheetList,
+                             nsIDocumentObserver,
+                             nsIMutationObserver)
+
+uint32_t
+nsDOMStyleSheetList::Length()
+{
+  if (!mDocument) {
+    return 0;
+  }
+
+  // XXX Find the number and then cache it. We'll use the
+  // observer notification to figure out if new ones have
+  // been added or removed.
+  if (-1 == mLength) {
+    mLength = mDocument->GetNumberOfStyleSheets();
 
 #ifdef DEBUG
-      int32_t i;
-      for (i = 0; i < mLength; i++) {
-        nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i);
-        nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(sheet));
-        NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet");
-      }
+    int32_t i;
+    for (i = 0; i < mLength; i++) {
+      nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(i);
+      nsCOMPtr<nsIDOMStyleSheet> domss(do_QueryInterface(sheet));
+      NS_ASSERTION(domss, "All \"normal\" sheets implement nsIDOMStyleSheet");
+    }
 #endif
-    }
-    *aLength = mLength;
-  }
-  else {
-    *aLength = 0;
-  }
-
-  return NS_OK;
-}
-
-nsIStyleSheet*
-nsDOMStyleSheetList::GetItemAt(uint32_t aIndex)
+  }
+  return mLength;
+}
+
+nsCSSStyleSheet*
+nsDOMStyleSheetList::IndexedGetter(uint32_t aIndex, bool& aFound)
 {
   if (!mDocument || aIndex >= (uint32_t)mDocument->GetNumberOfStyleSheets()) {
+    aFound = false;
     return nullptr;
   }
 
+  aFound = true;
   nsIStyleSheet *sheet = mDocument->GetStyleSheetAt(aIndex);
   NS_ASSERTION(sheet, "Must have a sheet");
 
-  return sheet;
-}
-
-NS_IMETHODIMP
-nsDOMStyleSheetList::Item(uint32_t aIndex, nsIDOMStyleSheet** aReturn)
-{
-  nsIStyleSheet *sheet = GetItemAt(aIndex);
-  if (!sheet) {
-      *aReturn = nullptr;
-
-      return NS_OK;
-  }
-
-  return CallQueryInterface(sheet, aReturn);
+  return static_cast<nsCSSStyleSheet*>(sheet);
 }
 
 void
 nsDOMStyleSheetList::NodeWillBeDestroyed(const nsINode *aNode)
 {
   mDocument = nullptr;
 }
 
@@ -1550,17 +1522,18 @@ nsIDocument::nsIDocument()
     mRemovedFromDocShell(false),
     // mAllowDNSPrefetch starts true, so that we can always reliably && it
     // with various values that might disable it.  Since we never prefetch
     // unless we get a window, and in that case the docshell value will get
     // &&-ed in, this is safe.
     mAllowDNSPrefetch(true),
     mIsBeingUsedAsImage(false),
     mHasLinksToUpdate(false),
-    mPartID(0)
+    mPartID(0),
+    mDidFireDOMContentLoaded(true)
 {
   SetInDocument();
 }
 
 // NOTE! nsDocument::operator new() zeroes out all members, so don't
 // bother initializing members to 0.
 
 nsDocument::nsDocument(const char* aContentType)
@@ -4702,16 +4675,18 @@ nsDocument::EndUpdate(nsUpdateType aUpda
 }
 
 void
 nsDocument::BeginLoad()
 {
   // Block onload here to prevent having to deal with blocking and
   // unblocking it while we know the document is loading.
   BlockOnload();
+  mDidFireDOMContentLoaded = false;
+  BlockDOMContentLoaded();
 
   if (mScriptLoader) {
     mScriptLoader->BeginDeferringScripts();
   }
 
   NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
 }
 
@@ -4943,16 +4918,29 @@ nsDocument::EndLoad()
   // do that to get a StartLayout() to happen.
   if (mParser) {
     mWeakSink = do_GetWeakReference(mParser->GetContentSink());
     mParser = nullptr;
   }
 
   NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
 
+  UnblockDOMContentLoaded();
+}
+
+void
+nsDocument::UnblockDOMContentLoaded()
+{
+  MOZ_ASSERT(mBlockDOMContentLoaded);
+  if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
+    return;
+  }
+  mDidFireDOMContentLoaded = true;
+
+  MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
   if (!mSynchronousDOMContentLoaded) {
     nsRefPtr<nsIRunnable> ev =
       NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents);
     NS_DispatchToCurrentThread(ev);
   } else {
     DispatchContentLoadedEvents();
   }
 }
@@ -6087,17 +6075,17 @@ nsDocument::Load(const nsAString& aUrl, 
 
 NS_IMETHODIMP
 nsDocument::GetStyleSheets(nsIDOMStyleSheetList** aStyleSheets)
 {
   NS_ADDREF(*aStyleSheets = StyleSheets());
   return NS_OK;
 }
 
-nsIDOMStyleSheetList*
+StyleSheetList*
 nsDocument::StyleSheets()
 {
   if (!mDOMStyleSheets) {
     mDOMStyleSheets = new nsDOMStyleSheetList(this);
   }
   return mDOMStyleSheets;
 }
 
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -18,17 +18,16 @@
 #include "nsCRT.h"
 #include "nsWeakReference.h"
 #include "nsWeakPtr.h"
 #include "nsVoidArray.h"
 #include "nsTArray.h"
 #include "nsIDOMXMLDocument.h"
 #include "nsIDOMDocumentXBL.h"
 #include "nsStubDocumentObserver.h"
-#include "nsIDOMStyleSheetList.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIContent.h"
 #include "nsIPrincipal.h"
 #include "nsIParser.h"
 #include "nsBindingManager.h"
 #include "nsINodeInfo.h"
 #include "nsInterfaceHashtable.h"
 #include "nsJSThingHashtable.h"
@@ -60,30 +59,30 @@
 #include "nsIProgressEventSink.h"
 #include "nsISecurityEventSink.h"
 #include "nsIChannelEventSink.h"
 #include "imgIRequest.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/DOMImplementation.h"
+#include "mozilla/dom/StyleSheetList.h"
 #include "nsIDOMTouchEvent.h"
 #include "nsDataHashtable.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Attributes.h"
 #include "nsIDOMXPathEvaluator.h"
 #include "jsfriendapi.h"
 
 #define XML_DECLARATION_BITS_DECLARATION_EXISTS   (1 << 0)
 #define XML_DECLARATION_BITS_ENCODING_EXISTS      (1 << 1)
 #define XML_DECLARATION_BITS_STANDALONE_EXISTS    (1 << 2)
 #define XML_DECLARATION_BITS_STANDALONE_YES       (1 << 3)
 
 
-class nsDOMStyleSheetList;
 class nsDOMStyleSheetSetList;
 class nsIOutputStream;
 class nsDocument;
 class nsIDTD;
 class nsIRadioVisitor;
 class nsIFormControl;
 struct nsRadioGroupStruct;
 class nsOnloadBlocker;
@@ -431,35 +430,39 @@ public:
     delete mNext;
   }
 
   nsCOMPtr<nsIAtom> mField;
   nsString          mData;
   nsDocHeaderData*  mNext;
 };
 
-class nsDOMStyleSheetList : public nsIDOMStyleSheetList,
+class nsDOMStyleSheetList : public mozilla::dom::StyleSheetList,
                             public nsStubDocumentObserver
 {
 public:
   nsDOMStyleSheetList(nsIDocument *aDocument);
   virtual ~nsDOMStyleSheetList();
 
-  NS_DECL_ISUPPORTS
-
-  NS_DECL_NSIDOMSTYLESHEETLIST
+  NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDocumentObserver
   NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED
   NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED
 
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
 
-  nsIStyleSheet* GetItemAt(uint32_t aIndex);
+  virtual nsINode* GetParentObject() const MOZ_OVERRIDE
+  {
+    return mDocument;
+  }
+
+  virtual uint32_t Length() MOZ_OVERRIDE;
+  virtual nsCSSStyleSheet* IndexedGetter(uint32_t aIndex, bool& aFound) MOZ_OVERRIDE;
 
 protected:
   int32_t       mLength;
   nsIDocument*  mDocument;
 };
 
 class nsOnloadBlocker MOZ_FINAL : public nsIRequest
 {
@@ -1220,32 +1223,34 @@ public:
 
   // WebIDL bits
   virtual mozilla::dom::DOMImplementation*
     GetImplementation(mozilla::ErrorResult& rv) MOZ_OVERRIDE;
   virtual JSObject*
     RegisterElement(JSContext* aCx, const nsAString& aName,
                     const mozilla::dom::ElementRegistrationOptions& aOptions,
                     mozilla::ErrorResult& rv) MOZ_OVERRIDE;
-  virtual nsIDOMStyleSheetList* StyleSheets() MOZ_OVERRIDE;
+  virtual mozilla::dom::StyleSheetList* StyleSheets() MOZ_OVERRIDE;
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) MOZ_OVERRIDE;
   virtual void GetLastStyleSheetSet(nsString& aSheetSet) MOZ_OVERRIDE;
   virtual mozilla::dom::DOMStringList* StyleSheetSets() MOZ_OVERRIDE;
   virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) MOZ_OVERRIDE;
   using nsIDocument::CreateElement;
   using nsIDocument::CreateElementNS;
   virtual already_AddRefed<Element> CreateElement(const nsAString& aTagName,
                                                   const nsAString& aTypeExtension,
                                                   mozilla::ErrorResult& rv) MOZ_OVERRIDE;
   virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
                                                     const nsAString& aQualifiedName,
                                                     const nsAString& aTypeExtension,
                                                     mozilla::ErrorResult& rv) MOZ_OVERRIDE;
   virtual void UseRegistryFromDocument(nsIDocument* aDocument) MOZ_OVERRIDE;
 
+  virtual void UnblockDOMContentLoaded() MOZ_OVERRIDE;
+
 protected:
   friend class nsNodeUtils;
   friend class nsDocumentOnStack;
 
   void IncreaseStackRefCnt()
   {
     ++mStackRefCnt;
   }
@@ -1425,17 +1430,17 @@ public:
                                     mozilla::ErrorResult& rv);
 
   static bool IsRegisterElementEnabled(JSContext* aCx, JSObject* aObject);
 
   // The "registry" from the web components spec.
   nsRefPtr<mozilla::dom::Registry> mRegistry;
 
   nsRefPtr<mozilla::EventListenerManager> mListenerManager;
-  nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
+  nsRefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets;
   nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   nsRefPtr<nsScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
    *    entries as needed).
    * 2) Removals from the DOM affect the table immediately
    * 3) Additions to the DOM always update existing entries for names, and add
--- a/content/base/src/nsHostObjectProtocolHandler.cpp
+++ b/content/base/src/nsHostObjectProtocolHandler.cpp
@@ -89,23 +89,24 @@ class BlobURLsReporter MOZ_FINAL : publi
     nsAutoCString origin;
     nsCOMPtr<nsIURI> principalURI;
     if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI)))
         && principalURI) {
       principalURI->GetPrePath(origin);
     }
 
     for (uint32_t i = 0; i < maxFrames && frame; ++i) {
-      nsCString fileName;
+      nsString fileNameUTF16;
       int32_t lineNumber = 0;
 
-      frame->GetFilename(fileName);
+      frame->GetFilename(fileNameUTF16);
       frame->GetLineNumber(&lineNumber);
 
-      if (!fileName.IsEmpty()) {
+      if (!fileNameUTF16.IsEmpty()) {
+        NS_ConvertUTF16toUTF8 fileName(fileNameUTF16);
         stack += "js(";
         if (!origin.IsEmpty()) {
           // Make the file name root-relative for conciseness if possible.
           const char* originData;
           uint32_t originLen;
 
           originLen = origin.GetData(&originData);
           // If fileName starts with origin + "/", cut up to that "/".
--- a/content/base/src/nsImageLoadingContent.h
+++ b/content/base/src/nsImageLoadingContent.h
@@ -229,31 +229,16 @@ private:
   /**
    * UpdateImageState recomputes the current state of this image loading
    * content and updates what ImageState() returns accordingly.  It will also
    * fire a ContentStatesChanged() notification as needed if aNotify is true.
    */
   void UpdateImageState(bool aNotify);
 
   /**
-   * CancelImageRequests can be called when we want to cancel the
-   * image requests, generally due to our src changing and us wanting
-   * to start a new load.  The "current" request will be canceled only
-   * if it has not progressed far enough to know the image size yet
-   * unless aEvenIfSizeAvailable is true.
-   *
-   * @param aReason the reason the requests are being canceled
-   * @param aEvenIfSizeAvailable cancels the current load even if its size is
-   *                             available
-   * @param aNewImageStatus the nsIContentPolicy status of the new image load
-   */
-  void CancelImageRequests(nsresult aReason, bool aEvenIfSizeAvailable,
-                           int16_t aNewImageStatus);
-
-  /**
    * Method to fire an event once we know what's going on with the image load.
    *
    * @param aEventType "load" or "error" depending on how things went
    */
   nsresult FireEvent(const nsAString& aEventType);
 
 protected:
   /**
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -119,17 +119,18 @@ NS_IMPL_ISUPPORTS0(nsScriptLoadRequest)
 //
 //////////////////////////////////////////////////////////////
 
 nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
   : mDocument(aDocument),
     mBlockerCount(0),
     mEnabled(true),
     mDeferEnabled(false),
-    mDocumentParsingDone(false)
+    mDocumentParsingDone(false),
+    mBlockingDOMContentLoaded(false)
 {
   // enable logging for CSP
 #ifdef PR_LOGGING
   if (!gCspPRLog)
     gCspPRLog = PR_NewLogModule("CSP");
 #endif
 }
 
@@ -651,17 +652,17 @@ nsScriptLoader::ProcessScriptElement(nsI
       // If we come here, the script is a parser-created script and it has
       // the defer attribute but not the async attribute. Since a
       // a parser-inserted script is being run, we came here by the parser
       // running the script, which means the parser is still alive and the
       // parse is ongoing.
       NS_ASSERTION(mDocument->GetCurrentContentSink() ||
                    aElement->GetParserCreated() == FROM_PARSER_XSLT,
           "Non-XSLT Defer script on a document without an active parser; bug 592366.");
-      mDeferRequests.AppendElement(request);
+      AddDeferRequest(request);
       return false;
     }
 
     if (aElement->GetParserCreated() == FROM_PARSER_XSLT) {
       // Need to maintain order for XSLT-inserted scripts
       NS_ASSERTION(!mParserBlockingRequest,
           "Parser-blocking scripts and XSLT scripts in the same doc!");
       mXSLTRequests.AppendElement(request);
@@ -1166,16 +1167,19 @@ nsScriptLoader::ProcessPendingRequests()
     mPendingChildLoaders.RemoveElementAt(0);
     child->RemoveExecuteBlocker();
   }
 
   if (mDocumentParsingDone && mDocument &&
       !mParserBlockingRequest && mAsyncRequests.IsEmpty() &&
       mNonAsyncExternalScriptInsertedRequests.IsEmpty() &&
       mXSLTRequests.IsEmpty() && mDeferRequests.IsEmpty()) {
+    if (MaybeRemovedDeferRequests()) {
+      return ProcessPendingRequests();
+    }
     // No more pending scripts; time to unblock onload.
     // OK to unblock onload synchronously here, since callers must be
     // prepared for the world changing anyway.
     mDocumentParsingDone = false;
     mDocument->UnblockOnload(true);
   }
 }
 
@@ -1483,8 +1487,32 @@ nsScriptLoader::PreloadURI(nsIURI *aURI,
   if (NS_FAILED(rv)) {
     return;
   }
 
   PreloadInfo *pi = mPreloads.AppendElement();
   pi->mRequest = request;
   pi->mCharset = aCharset;
 }
+
+void
+nsScriptLoader::AddDeferRequest(nsScriptLoadRequest* aRequest)
+{
+  mDeferRequests.AppendElement(aRequest);
+  if (mDeferEnabled && mDeferRequests.Length() == 1 && mDocument &&
+      !mBlockingDOMContentLoaded) {
+    MOZ_ASSERT(mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING);
+    mBlockingDOMContentLoaded = true;
+    mDocument->BlockDOMContentLoaded();
+  }
+}
+
+bool
+nsScriptLoader::MaybeRemovedDeferRequests()
+{
+  if (mDeferRequests.Length() == 0 && mDocument &&
+      mBlockingDOMContentLoaded) {
+    mBlockingDOMContentLoaded = false;
+    mDocument->UnblockDOMContentLoaded();
+    return true;
+  }
+  return false;
+}
--- a/content/base/src/nsScriptLoader.h
+++ b/content/base/src/nsScriptLoader.h
@@ -286,16 +286,19 @@ private:
                                     JS::CompileOptions *aOptions);
 
   nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
                                 nsIStreamLoader* aLoader,
                                 nsresult aStatus,
                                 uint32_t aStringLen,
                                 const uint8_t* aString);
 
+  void AddDeferRequest(nsScriptLoadRequest* aRequest);
+  bool MaybeRemovedDeferRequests();
+
   nsIDocument* mDocument;                   // [WEAK]
   nsCOMArray<nsIScriptLoaderObserver> mObservers;
   nsTArray<nsRefPtr<nsScriptLoadRequest> > mNonAsyncExternalScriptInsertedRequests;
   nsTArray<nsRefPtr<nsScriptLoadRequest> > mAsyncRequests;
   nsTArray<nsRefPtr<nsScriptLoadRequest> > mDeferRequests;
   nsTArray<nsRefPtr<nsScriptLoadRequest> > mXSLTRequests;
   nsRefPtr<nsScriptLoadRequest> mParserBlockingRequest;
 
@@ -320,16 +323,17 @@ private:
   nsCOMPtr<nsIScriptElement> mCurrentScript;
   nsCOMPtr<nsIScriptElement> mCurrentParserInsertedScript;
   // XXXbz do we want to cycle-collect these or something?  Not sure.
   nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders;
   uint32_t mBlockerCount;
   bool mEnabled;
   bool mDeferEnabled;
   bool mDocumentParsingDone;
+  bool mBlockingDOMContentLoaded;
 };
 
 class nsAutoScriptLoaderDisabler
 {
 public:
   nsAutoScriptLoaderDisabler(nsIDocument* aDoc)
   {
     mLoader = aDoc->ScriptLoader();
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -491,17 +491,21 @@ public:
   {
     return mAudioCaptured;
   }
 
   JSObject* MozGetMetadata(JSContext* aCx, ErrorResult& aRv);
 
   double MozFragmentEnd();
 
-  AudioChannel MozAudioChannelType() const;
+  AudioChannel MozAudioChannelType() const
+  {
+    return mAudioChannel;
+  }
+
   void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
 
   TextTrackList* TextTracks();
 
   already_AddRefed<TextTrack> AddTextTrack(TextTrackKind aKind,
                                            const nsAString& aLabel,
                                            const nsAString& aLanguage);
 
@@ -1117,18 +1121,18 @@ protected:
   CORSMode mCORSMode;
 
   // True if the media has an audio track
   bool mHasAudio;
 
   // True if the media's channel's download has been suspended.
   bool mDownloadSuspendedByCache;
 
-  // Audio Channel Type.
-  AudioChannelType mAudioChannelType;
+  // Audio Channel.
+  AudioChannel mAudioChannel;
 
   // The audio channel has been faded.
   bool mAudioChannelFaded;
 
   // Is this media element playing?
   bool mPlayingThroughTheAudioChannel;
 
   // An agent used to join audio channel service.
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -3914,17 +3914,17 @@ HTMLInputElement::PostHandleEvent(EventC
       // control and as a result aVisitor.mEventStatus will already have been
       // set to nsEventStatus_eConsumeNoDefault. However, we know that
       // whenever the up/down arrow keys cause the value of the number
       // control to change the string in the text control will change, and
       // the cursor will be moved to the end of the text control, overwriting
       // the editor's handling of up/down keypress events. For that reason we
       // just ignore aVisitor.mEventStatus here and go ahead and handle the
       // event to increase/decrease the value of the number control.
-      if (!aVisitor.mEvent->mFlags.mDefaultPreventedByContent) {
+      if (!aVisitor.mEvent->mFlags.mDefaultPreventedByContent && IsMutable()) {
         StepNumberControlForUserEvent(keyEvent->keyCode == NS_VK_UP ? 1 : -1);
         aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
       }
     } else if (nsEventStatus_eIgnore == aVisitor.mEventStatus) {
       switch (aVisitor.mEvent->message) {
 
         case NS_FOCUS_CONTENT:
         {
@@ -4140,17 +4140,18 @@ HTMLInputElement::PostHandleEvent(EventC
             if (mouseEvent->button == WidgetMouseEvent::eLeftButton &&
                 !(mouseEvent->IsShift() || mouseEvent->IsControl() ||
                   mouseEvent->IsAlt() || mouseEvent->IsMeta() ||
                   mouseEvent->IsAltGraph() || mouseEvent->IsFn() ||
                   mouseEvent->IsOS())) {
               nsNumberControlFrame* numberControlFrame =
                 do_QueryFrame(GetPrimaryFrame());
               if (numberControlFrame) {
-                if (aVisitor.mEvent->message == NS_MOUSE_BUTTON_DOWN) {
+                if (aVisitor.mEvent->message == NS_MOUSE_BUTTON_DOWN && 
+                    IsMutable()) {
                   switch (numberControlFrame->GetSpinButtonForPointerEvent(
                             aVisitor.mEvent->AsMouseEvent())) {
                   case nsNumberControlFrame::eSpinButtonUp:
                     StepNumberControlForUserEvent(1);
                     mNumberControlSpinnerSpinsUp = true;
                     StartNumberControlSpinnerSpin();
                     aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
                     break;
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -1999,29 +1999,30 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mHasPlayedOrSeeked(false),
     mHasSelfReference(false),
     mShuttingDown(false),
     mSuspendedForPreloadNone(false),
     mMediaSecurityVerified(false),
     mCORSMode(CORS_NONE),
     mHasAudio(false),
     mDownloadSuspendedByCache(false),
-    mAudioChannelType(AUDIO_CHANNEL_NORMAL),
     mAudioChannelFaded(false),
     mPlayingThroughTheAudioChannel(false)
 {
 #ifdef PR_LOGGING
   if (!gMediaElementLog) {
     gMediaElementLog = PR_NewLogModule("nsMediaElement");
   }
   if (!gMediaElementEventsLog) {
     gMediaElementEventsLog = PR_NewLogModule("nsMediaElementEvents");
   }
 #endif
 
+  mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
+
   mPaused.SetOuter(this);
 
   RegisterFreezableElement();
   NotifyOwnerDocumentActivityChanged();
 }
 
 HTMLMediaElement::~HTMLMediaElement()
 {
@@ -2277,53 +2278,44 @@ bool HTMLMediaElement::ParseAttribute(in
   static const nsAttrValue::EnumTable kPreloadTable[] = {
     { "",         HTMLMediaElement::PRELOAD_ATTR_EMPTY },
     { "none",     HTMLMediaElement::PRELOAD_ATTR_NONE },
     { "metadata", HTMLMediaElement::PRELOAD_ATTR_METADATA },
     { "auto",     HTMLMediaElement::PRELOAD_ATTR_AUTO },
     { 0 }
   };
 
-  // Mappings from 'mozaudiochannel' attribute strings to an enumeration.
-  static const nsAttrValue::EnumTable kMozAudioChannelAttributeTable[] = {
-    { "normal",             AUDIO_CHANNEL_NORMAL },
-    { "content",            AUDIO_CHANNEL_CONTENT },
-    { "notification",       AUDIO_CHANNEL_NOTIFICATION },
-    { "alarm",              AUDIO_CHANNEL_ALARM },
-    { "telephony",          AUDIO_CHANNEL_TELEPHONY },
-    { "ringer",             AUDIO_CHANNEL_RINGER },
-    { "publicnotification", AUDIO_CHANNEL_PUBLICNOTIFICATION },
-    { 0 }
-  };
-
   if (aNamespaceID == kNameSpaceID_None) {
     if (ParseImageAttribute(aAttribute, aValue, aResult)) {
       return true;
     }
     if (aAttribute == nsGkAtoms::crossorigin) {
       ParseCORSValue(aValue, aResult);
       return true;
     }
     if (aAttribute == nsGkAtoms::preload) {
       return aResult.ParseEnumValue(aValue, kPreloadTable, false);
     }
 
     if (aAttribute == nsGkAtoms::mozaudiochannel) {
-      bool parsed = aResult.ParseEnumValue(aValue, kMozAudioChannelAttributeTable, false,
-                                           &kMozAudioChannelAttributeTable[0]);
+      const nsAttrValue::EnumTable* table =
+        AudioChannelService::GetAudioChannelTable();
+      MOZ_ASSERT(table);
+
+      bool parsed = aResult.ParseEnumValue(aValue, table, false, &table[0]);
       if (!parsed) {
         return false;
       }
 
-      AudioChannelType audioChannelType = static_cast<AudioChannelType>(aResult.GetEnumValue());
-
-      if (audioChannelType != mAudioChannelType &&
+      AudioChannel audioChannel = static_cast<AudioChannel>(aResult.GetEnumValue());
+
+      if (audioChannel != mAudioChannel &&
           !mDecoder &&
           CheckAudioChannelPermissions(aValue)) {
-        mAudioChannelType = audioChannelType;
+        mAudioChannel = audioChannel;
       }
 
       return true;
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                               aResult);
@@ -2599,17 +2591,17 @@ nsresult HTMLMediaElement::FinishDecoder
   mPendingEvents.Clear();
   // Set mDecoder now so if methods like GetCurrentSrc get called between
   // here and Load(), they work.
   mDecoder = aDecoder;
 
   // Tell the decoder about its MediaResource now so things like principals are
   // available immediately.
   mDecoder->SetResource(aStream);
-  mDecoder->SetAudioChannelType(mAudioChannelType);
+  aDecoder->SetAudioChannel(mAudioChannel);
   mDecoder->SetAudioCaptured(mAudioCaptured);
   mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
   mDecoder->SetPreservesPitch(mPreservesPitch);
   mDecoder->SetPlaybackRate(mPlaybackRate);
 
   if (mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
     mDecoder->SetMinimizePrerollUntilPlaybackStarts();
   }
@@ -3841,22 +3833,24 @@ void HTMLMediaElement::UpdateAudioChanne
     if (!mAudioChannelAgent) {
       nsresult rv;
       mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
       if (!mAudioChannelAgent) {
         return;
       }
       nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
       // Use a weak ref so the audio channel agent can't leak |this|.
-      if (AUDIO_CHANNEL_NORMAL == mAudioChannelType && video) {
+      if (AudioChannel::Normal == mAudioChannel && video) {
         mAudioChannelAgent->InitWithVideo(OwnerDoc()->GetWindow(),
-                                          mAudioChannelType, this, true);
+                                          static_cast<int32_t>(mAudioChannel),
+                                          this, true);
       } else {
         mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetWindow(),
-                                                 mAudioChannelType, this);
+                                                 static_cast<int32_t>(mAudioChannel),
+                                                 this);
       }
       mAudioChannelAgent->SetVisibilityState(!OwnerDoc()->Hidden());
     }
 
     if (mPlayingThroughTheAudioChannel) {
       int32_t canPlay;
       mAudioChannelAgent->StartPlaying(&canPlay);
       CanPlayChanged(canPlay);
@@ -3922,47 +3916,21 @@ HTMLMediaElement::PopulatePendingTextTra
   }
 }
 
 TextTrackManager*
 HTMLMediaElement::GetOrCreateTextTrackManager()
 {
   if (!mTextTrackManager) {
     mTextTrackManager = new TextTrackManager(this);
+    mTextTrackManager->AddListeners();
   }
   return mTextTrackManager;
 }
 
-AudioChannel
-HTMLMediaElement::MozAudioChannelType() const
-{
-  switch (mAudioChannelType) {
-    case AUDIO_CHANNEL_CONTENT:
-      return AudioChannel::Content;
-
-    case AUDIO_CHANNEL_NOTIFICATION:
-      return AudioChannel::Notification;
-
-    case AUDIO_CHANNEL_ALARM:
-      return AudioChannel::Alarm;
-
-    case AUDIO_CHANNEL_TELEPHONY:
-      return AudioChannel::Telephony;
-
-    case AUDIO_CHANNEL_RINGER:
-      return AudioChannel::Ringer;
-
-    case AUDIO_CHANNEL_PUBLICNOTIFICATION:
-      return AudioChannel::Publicnotification;
-
-    default:
-      return AudioChannelService::GetDefaultAudioChannel();
-  }
-}
-
 void
 HTMLMediaElement::SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv)
 {
   nsString channel;
   channel.AssignASCII(AudioChannelValues::strings[uint32_t(aValue)].value,
                       AudioChannelValues::strings[uint32_t(aValue)].length);
   SetHTMLAttr(nsGkAtoms::mozaudiochannel, channel, aRv);
 }
--- a/content/html/content/src/TextTrackManager.cpp
+++ b/content/html/content/src/TextTrackManager.cpp
@@ -3,18 +3,20 @@
 
 /* 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/TextTrackManager.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTrackElement.h"
+#include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/TextTrack.h"
 #include "mozilla/dom/TextTrackCue.h"
+#include "mozilla/dom/Event.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "nsComponentManagerUtils.h"
 #include "nsVideoFrame.h"
 #include "nsIFrame.h"
 #include "nsTArrayHelpers.h"
 #include "nsIWebVTTParserWrapper.h"
 
 namespace mozilla {
@@ -70,27 +72,30 @@ CompareTextTracks::LessThan(TextTrack* a
       // No rules for Media Resource Specific tracks yet.
       break;
   }
   return true;
 }
 
 NS_IMPL_CYCLE_COLLECTION_4(TextTrackManager, mMediaElement, mTextTracks,
                            mPendingTextTracks, mNewCues)
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(TextTrackManager, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(TextTrackManager, Release)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextTrackManager)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TextTrackManager)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TextTrackManager)
 
 StaticRefPtr<nsIWebVTTParserWrapper> TextTrackManager::sParserWrapper;
 
 TextTrackManager::TextTrackManager(HTMLMediaElement *aMediaElement)
   : mMediaElement(aMediaElement)
   , performedTrackSelection(false)
 {
-  MOZ_COUNT_CTOR(TextTrackManager);
-
   bool hasHadScriptObject = true;
   nsIScriptGlobalObject* scriptObject =
     mMediaElement->OwnerDoc()->GetScriptHandlingObject(hasHadScriptObject);
 
   NS_ENSURE_TRUE_VOID(scriptObject || !hasHadScriptObject);
 
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(scriptObject);
   mNewCues = new TextTrackCueList(window);
@@ -100,21 +105,16 @@ TextTrackManager::TextTrackManager(HTMLM
   if (!sParserWrapper) {
     nsCOMPtr<nsIWebVTTParserWrapper> parserWrapper =
       do_CreateInstance(NS_WEBVTTPARSERWRAPPER_CONTRACTID);
     sParserWrapper = parserWrapper;
     ClearOnShutdown(&sParserWrapper);
   }
 }
 
-TextTrackManager::~TextTrackManager()
-{
-  MOZ_COUNT_DTOR(TextTrackManager);
-}
-
 TextTrackList*
 TextTrackManager::TextTracks() const
 {
   return mTextTracks;
 }
 
 already_AddRefed<TextTrack>
 TextTrackManager::AddTextTrack(TextTrackKind aKind, const nsAString& aLabel,
@@ -251,16 +251,25 @@ TextTrackManager::PopulatePendingList()
         ttrack->ReadyState() == TextTrackReadyState::Loading) {
       mPendingTextTracks->AddTextTrack(ttrack,
                                        CompareTextTracks(mMediaElement));
     }
   }
 }
 
 void
+TextTrackManager::AddListeners()
+{
+  if (mMediaElement) {
+    mMediaElement->AddEventListener(NS_LITERAL_STRING("resizevideocontrols"),
+                                    this, false, false);
+  }
+}
+
+void
 TextTrackManager::HonorUserPreferencesForTrackSelection()
 {
   if (performedTrackSelection || !mTextTracks) {
     return;
   }
 
   TextTrackKind ttKinds[] = { TextTrackKind::Captions,
                               TextTrackKind::Subtitles };
@@ -347,10 +356,27 @@ TextTrackManager::GetTextTracksOfKind(Te
   for (uint32_t i = 0; i < mTextTracks->Length(); i++) {
     TextTrack* textTrack = (*mTextTracks)[i];
     if (textTrack->Kind() == aTextTrackKind) {
       aTextTracks.AppendElement(textTrack);
     }
   }
 }
 
+NS_IMETHODIMP
+TextTrackManager::HandleEvent(nsIDOMEvent* aEvent)
+{
+  if (!mTextTracks) {
+    return NS_OK;
+  }
+
+  nsAutoString type;
+  aEvent->GetType(type);
+  if (type.EqualsLiteral("resizevideocontrols")) {
+    for (uint32_t i = 0; i< mTextTracks->Length(); i++) {
+      ((*mTextTracks)[i])->SetCuesDirty();
+    }
+  }
+  return NS_OK;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/TextTrackManager.h
+++ b/content/html/content/src/TextTrackManager.h
@@ -28,24 +28,25 @@ public:
   int32_t TrackChildPosition(TextTrack* aTrack) const;
   bool Equals(TextTrack* aOne, TextTrack* aTwo) const;
   bool LessThan(TextTrack* aOne, TextTrack* aTwo) const;
 };
 
 class TextTrack;
 class TextTrackCue;
 
-class TextTrackManager
+class TextTrackManager MOZ_FINAL : public nsIDOMEventListener
 {
 public:
-  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(TextTrackManager)
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(TextTrackManager);
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(TextTrackManager)
+
+  NS_DECL_NSIDOMEVENTLISTENER
 
   TextTrackManager(HTMLMediaElement *aMediaElement);
-  ~TextTrackManager();
 
   TextTrackList* TextTracks() const;
   already_AddRefed<TextTrack> AddTextTrack(TextTrackKind aKind,
                                            const nsAString& aLabel,
                                            const nsAString& aLanguage,
                                            TextTrackMode aMode,
                                            TextTrackReadyState aReadyState,
                                            TextTrackSource aTextTrackSource);
@@ -83,16 +84,18 @@ public:
   /**
    * 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 UpdateCueDisplay();
 
   void PopulatePendingList();
 
+  void AddListeners();
+
   // The HTMLMediaElement that this TextTrackManager manages the TextTracks of.
   nsRefPtr<HTMLMediaElement> mMediaElement;
 private:
   // List of the TextTrackManager's owning HTMLMediaElement's TextTracks.
   nsRefPtr<TextTrackList> mTextTracks;
   // List of text track objects awaiting loading.
   nsRefPtr<TextTrackList> mPendingTextTracks;
   // List of newly introduced Text Track cues.
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHTMLDocument.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/HTMLAllCollection.h"
 #include "nsCOMPtr.h"
+#include "nsGlobalWindow.h"
 #include "nsXPIDLString.h"
 #include "nsPrintfCString.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsIHTMLContentSink.h"
 #include "nsIXMLContentSink.h"
 #include "nsHTMLParts.h"
 #include "nsHTMLStyleSheet.h"
@@ -2149,38 +2150,34 @@ nsHTMLDocument::Embeds()
   }
   return mEmbeds;
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::GetSelection(nsISelection** aReturn)
 {
   ErrorResult rv;
-  *aReturn = GetSelection(rv).take();
+  NS_IF_ADDREF(*aReturn = GetSelection(rv));
   return rv.ErrorCode();
 }
 
-already_AddRefed<Selection>
-nsHTMLDocument::GetSelection(ErrorResult& rv)
+Selection*
+nsHTMLDocument::GetSelection(ErrorResult& aRv)
 {
-  nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetScopeObject());
-  nsCOMPtr<nsPIDOMWindow> pwin = do_QueryInterface(window);
-  if (!pwin) {
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetScopeObject());
+  if (!window) {
     return nullptr;
   }
-  NS_ASSERTION(pwin->IsInnerWindow(), "Should have inner window here!");
-  if (!pwin->GetOuterWindow() ||
-      pwin->GetOuterWindow()->GetCurrentInnerWindow() != pwin) {
+
+  NS_ASSERTION(window->IsInnerWindow(), "Should have inner window here!");
+  if (!window->IsCurrentInnerWindow()) {
     return nullptr;
   }
 
-  nsCOMPtr<nsISelection> sel;
-  rv = window->GetSelection(getter_AddRefs(sel));
-  nsRefPtr<Selection> selection = static_cast<Selection*>(sel.get());
-  return selection.forget();
+  return static_cast<nsGlobalWindow*>(window.get())->GetSelection(aRv);
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::CaptureEvents()
 {
   WarnOnceAbout(nsIDocument::eUseOfCaptureEvents);
   return NS_OK;
 }
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -228,17 +228,17 @@ public:
   // The XPCOM Get/SetALinkColor work OK for us, since they never throw.
   // The XPCOM Get/SetBgColor work OK for us, since they never throw.
   nsIHTMLCollection* Anchors();
   nsIHTMLCollection* Applets();
   void Clear() const
   {
     // Deprecated
   }
-  already_AddRefed<mozilla::Selection> GetSelection(mozilla::ErrorResult& rv);
+  mozilla::Selection* GetSelection(mozilla::ErrorResult& aRv);
   // The XPCOM CaptureEvents works fine for us.
   // The XPCOM ReleaseEvents works fine for us.
   // We're picking up GetLocation from Document
   already_AddRefed<nsIDOMLocation> GetLocation() const {
     return nsIDocument::GetLocation();
   }
 
   virtual nsHTMLDocument* AsHTMLDocument() MOZ_OVERRIDE { return this; }
--- a/content/media/AudioSampleFormat.h
+++ b/content/media/AudioSampleFormat.h
@@ -19,16 +19,18 @@ namespace mozilla {
  * produce that format only; queued AudioData always uses that format.
  */
 enum AudioSampleFormat
 {
   // Native-endian signed 16-bit audio samples
   AUDIO_FORMAT_S16,
   // Signed 32-bit float samples
   AUDIO_FORMAT_FLOAT32,
+  // Silence: format will be chosen later
+  AUDIO_FORMAT_SILENCE,
   // The format used for output by AudioStream.
 #ifdef MOZ_SAMPLE_TYPE_S16
   AUDIO_OUTPUT_FORMAT = AUDIO_FORMAT_S16
 #else
   AUDIO_OUTPUT_FORMAT = AUDIO_FORMAT_FLOAT32
 #endif
 };
 
--- a/content/media/AudioSegment.cpp
+++ b/content/media/AudioSegment.cpp
@@ -47,16 +47,19 @@ InterleaveAndConvertBuffer(const void** 
     break;
   case AUDIO_FORMAT_S16:
     InterleaveAndConvertBuffer(reinterpret_cast<const int16_t**>(aSourceChannels),
                                aLength,
                                aVolume,
                                aChannels,
                                aOutput);
     break;
+   case AUDIO_FORMAT_SILENCE:
+    // nothing to do here.
+    break;
   }
 }
 
 void
 AudioSegment::ApplyVolume(float aVolume)
 {
   for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
     ci->mVolume *= aVolume;
@@ -116,17 +119,28 @@ void AudioSegment::ResampleChunks(SpeexR
   uint32_t inRate, outRate;
 
   if (mChunks.IsEmpty()) {
     return;
   }
 
   speex_resampler_get_rate(aResampler, &inRate, &outRate);
 
-  switch (mChunks[0].mBufferFormat) {
+  AudioSampleFormat format = AUDIO_FORMAT_SILENCE;
+  for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
+    if (ci->mBufferFormat != AUDIO_FORMAT_SILENCE) {
+      format = ci->mBufferFormat;
+    }
+  }
+
+  switch (format) {
+    // If the format is silence at this point, all the chunks are silent. The
+    // actual function we use does not matter, it's just a matter of changing
+    // the chunks duration.
+    case AUDIO_FORMAT_SILENCE:
     case AUDIO_FORMAT_FLOAT32:
       Resample<float>(aResampler, inRate, outRate);
     break;
     case AUDIO_FORMAT_S16:
       Resample<int16_t>(aResampler, inRate, outRate);
     break;
     default:
       MOZ_ASSERT(false);
--- a/content/media/AudioSegment.h
+++ b/content/media/AudioSegment.h
@@ -105,16 +105,17 @@ struct AudioChunk {
   }
   bool IsNull() const { return mBuffer == nullptr; }
   void SetNull(TrackTicks aDuration)
   {
     mBuffer = nullptr;
     mChannelData.Clear();
     mDuration = aDuration;
     mVolume = 1.0f;
+    mBufferFormat = AUDIO_FORMAT_SILENCE;
   }
   int ChannelCount() const { return mChannelData.Length(); }
 
   TrackTicks mDuration; // in frames within the buffer
   nsRefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes
   nsTArray<const void*> mChannelData; // one pointer per channel; empty if and only if mBuffer is null
   float mVolume; // volume multiplier to apply (1.0f if mBuffer is nonnull)
   SampleFormat mBufferFormat; // format of frames in mBuffer (only meaningful if mBuffer is nonnull)
@@ -139,16 +140,21 @@ public:
   void Resample(SpeexResamplerState* aResampler, uint32_t aInRate, uint32_t aOutRate)
   {
     mDuration = 0;
 
     for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
       nsAutoTArray<nsTArray<T>, GUESS_AUDIO_CHANNELS> output;
       nsAutoTArray<const T*, GUESS_AUDIO_CHANNELS> bufferPtrs;
       AudioChunk& c = *ci;
+      // If this chunk is null, don't bother resampling, just alter its duration
+      if (c.IsNull()) {
+        c.mDuration *= aOutRate / aInRate;
+        mDuration += c.mDuration;
+      }
       uint32_t channels = c.mChannelData.Length();
       output.SetLength(channels);
       bufferPtrs.SetLength(channels);
       uint32_t inFrames = c.mDuration,
       outFrames = c.mDuration * aOutRate / aInRate;
       for (uint32_t i = 0; i < channels; i++) {
         const T* in = static_cast<const T*>(c.mChannelData[i]);
         T* out = output[i].AppendElements(outFrames);
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -105,35 +105,35 @@ bool AudioStream::sCubebLatencyPrefSet;
 
 /*static*/ bool AudioStream::CubebLatencyPrefSet()
 {
   StaticMutexAutoLock lock(sMutex);
   return sCubebLatencyPrefSet;
 }
 
 #if defined(__ANDROID__) && defined(MOZ_B2G)
-static cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannelType aType)
+static cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel)
 {
-  switch(aType) {
-    case dom::AUDIO_CHANNEL_NORMAL:
+  switch(aChannel) {
+    case dom::AudioChannel::Normal:
       return CUBEB_STREAM_TYPE_SYSTEM;
-    case dom::AUDIO_CHANNEL_CONTENT:
+    case dom::AudioChannel::Content:
       return CUBEB_STREAM_TYPE_MUSIC;
-    case dom::AUDIO_CHANNEL_NOTIFICATION:
+    case dom::AudioChannel::Notification:
       return CUBEB_STREAM_TYPE_NOTIFICATION;
-    case dom::AUDIO_CHANNEL_ALARM:
+    case dom::AudioChannel::Alarm:
       return CUBEB_STREAM_TYPE_ALARM;
-    case dom::AUDIO_CHANNEL_TELEPHONY:
+    case dom::AudioChannel::Telephony:
       return CUBEB_STREAM_TYPE_VOICE_CALL;
-    case dom::AUDIO_CHANNEL_RINGER:
+    case dom::AudioChannel::Ringer:
       return CUBEB_STREAM_TYPE_RING;
     // Currently Android openSLES library doesn't support FORCE_AUDIBLE yet.
-    case dom::AUDIO_CHANNEL_PUBLICNOTIFICATION:
+    case dom::AudioChannel::Publicnotification:
     default:
-      NS_ERROR("The value of AudioChannelType is invalid");
+      NS_ERROR("The value of AudioChannel is invalid");
       return CUBEB_STREAM_TYPE_MAX;
   }
 }
 #endif
 
 AudioStream::AudioStream()
   : mMonitor("AudioStream")
   , mInRate(0)
@@ -344,17 +344,17 @@ WriteDumpFile(FILE* aDumpFile, AudioStre
     SetUint16LE(output + i*2, int16_t(input[i]*32767.0f));
   }
   fwrite(output, 2, samples, aDumpFile);
   fflush(aDumpFile);
 }
 
 nsresult
 AudioStream::Init(int32_t aNumChannels, int32_t aRate,
-                  const dom::AudioChannelType aAudioChannelType,
+                  const dom::AudioChannel aAudioChannel,
                   LatencyRequest aLatencyRequest)
 {
   cubeb* cubebContext = GetCubebContext();
 
   if (!cubebContext || aNumChannels < 0 || aRate < 0) {
     return NS_ERROR_FAILURE;
   }
 
@@ -367,17 +367,17 @@ AudioStream::Init(int32_t aNumChannels, 
 
   mDumpFile = OpenDumpFile(this);
 
   cubeb_stream_params params;
   params.rate = aRate;
   params.channels = mOutChannels;
 #if defined(__ANDROID__)
 #if defined(MOZ_B2G)
-  params.stream_type = ConvertChannelToCubebType(aAudioChannelType);
+  params.stream_type = ConvertChannelToCubebType(aAudioChannel);
 #else
   params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
 #endif
 
   if (params.stream_type == CUBEB_STREAM_TYPE_MAX) {
     return NS_ERROR_INVALID_ARG;
   }
 #endif
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -2,21 +2,21 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(AudioStream_h_)
 #define AudioStream_h_
 
 #include "AudioSampleFormat.h"
-#include "AudioChannelCommon.h"
 #include "nsAutoPtr.h"
 #include "nsAutoRef.h"
 #include "nsCOMPtr.h"
 #include "Latency.h"
+#include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/StaticMutex.h"
 
 #include "cubeb/cubeb.h"
 
 template <>
 class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
 {
 public:
@@ -193,17 +193,17 @@ public:
     HighLatency,
     LowLatency
   };
 
   // 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).
   nsresult Init(int32_t aNumChannels, int32_t aRate,
-                const dom::AudioChannelType aAudioStreamType,
+                const dom::AudioChannel aAudioStreamChannel,
                 LatencyRequest aLatencyRequest);
 
   // Closes the stream. All future use of the stream is an error.
   void Shutdown();
 
   // Write audio data to the audio hardware.  aBuf is an array of AudioDataValues
   // AudioDataValue of length aFrames*mChannels.  If aFrames is larger
   // than the result of Available(), the write will block until sufficient
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -18,16 +18,17 @@
 #include "nsError.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIMemoryReporter.h"
 #include "nsComponentManagerUtils.h"
 #include "nsITimer.h"
 #include <algorithm>
 #include "MediaShutdownManager.h"
+#include "AudioChannelService.h"
 
 #ifdef MOZ_WMF
 #include "WMFDecoder.h"
 #endif
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 
@@ -422,27 +423,28 @@ MediaDecoder::MediaDecoder() :
   mNextState(PLAY_STATE_PAUSED),
   mCalledResourceLoaded(false),
   mIgnoreProgressData(false),
   mInfiniteStream(false),
   mOwner(nullptr),
   mPinnedForSeek(false),
   mShuttingDown(false),
   mPausedForPlaybackRateNull(false),
-  mAudioChannelType(AUDIO_CHANNEL_NORMAL),
   mMinimizePreroll(false)
 {
   MOZ_COUNT_CTOR(MediaDecoder);
   MOZ_ASSERT(NS_IsMainThread());
   MediaMemoryTracker::AddMediaDecoder(this);
 #ifdef PR_LOGGING
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 #endif
+
+  mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
 }
 
 bool MediaDecoder::Init(MediaDecoderOwner* aOwner)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mOwner = aOwner;
   mVideoFrameContainer = aOwner->GetVideoFrameContainer();
   MediaShutdownManager::Instance().Register(this);
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -178,21 +178,21 @@ destroying the MediaDecoder object.
 #if !defined(MediaDecoder_h_)
 #define MediaDecoder_h_
 
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
 #include "nsIObserver.h"
 #include "nsAutoPtr.h"
 #include "MediaResource.h"
+#include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/TimeStamp.h"
 #include "MediaStreamGraph.h"
-#include "AudioChannelCommon.h"
 #include "AbstractMediaDecoder.h"
 #include "necko-config.h"
 
 class nsIStreamListener;
 class nsIPrincipal;
 class nsITimer;
 
 namespace mozilla {
@@ -734,18 +734,18 @@ public:
   bool CanPlayThrough();
 
   // Make the decoder state machine update the playback position. Called by
   // the reader on the decoder thread (Assertions for this checked by
   // mDecoderStateMachine). This must be called with the decode monitor
   // held.
   void UpdatePlaybackPosition(int64_t aTime) MOZ_FINAL MOZ_OVERRIDE;
 
-  void SetAudioChannelType(dom::AudioChannelType aType) { mAudioChannelType = aType; }
-  dom::AudioChannelType GetAudioChannelType() { return mAudioChannelType; }
+  void SetAudioChannel(dom::AudioChannel aChannel) { mAudioChannel = aChannel; }
+  dom::AudioChannel GetAudioChannel() { return mAudioChannel; }
 
   // Send a new set of metadata to the state machine, to be dispatched to the
   // main thread to be presented when the |currentTime| of the media is greater
   // or equal to aPublishTime.
   void QueueMetadata(int64_t aPublishTime,
                      int aChannels,
                      int aRate,
                      bool aHasAudio,
@@ -1200,17 +1200,17 @@ protected:
   // Read/Write from the main thread only.
   bool mShuttingDown;
 
   // True if the playback is paused because the playback rate member is 0.0.
   bool mPausedForPlaybackRateNull;
 
   // Be assigned from media element during the initialization and pass to
   // AudioStream Class.
-  dom::AudioChannelType mAudioChannelType;
+  dom::AudioChannel mAudioChannel;
 
   // True if the decoder has been directed to minimize its preroll before
   // playback starts. After the first time playback starts, we don't attempt
   // to minimize preroll, as we assume the user is likely to keep playing,
   // or play the media again.
   bool mMinimizePreroll;
 };
 
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -758,38 +758,38 @@ void MediaDecoderStateMachine::AudioLoop
   int64_t audioStartTime = -1;
   uint32_t channels, rate;
   double volume = -1;
   bool setVolume;
   double playbackRate = -1;
   bool setPlaybackRate;
   bool preservesPitch;
   bool setPreservesPitch;
-  AudioChannelType audioChannelType;
+  AudioChannel audioChannel;
 
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     mAudioCompleted = false;
     audioStartTime = mAudioStartTime;
     NS_ASSERTION(audioStartTime != -1, "Should have audio start time by now");
     channels = mInfo.mAudio.mChannels;
     rate = mInfo.mAudio.mRate;
 
-    audioChannelType = mDecoder->GetAudioChannelType();
+    audioChannel = mDecoder->GetAudioChannel();
     volume = mVolume;
     preservesPitch = mPreservesPitch;
     playbackRate = mPlaybackRate;
   }
 
   {
     // AudioStream initialization can block for extended periods in unusual
     // circumstances, so we take care to drop the decoder monitor while
     // initializing.
     nsAutoPtr<AudioStream> audioStream(new AudioStream());
-    audioStream->Init(channels, rate, audioChannelType, AudioStream::HighLatency);
+    audioStream->Init(channels, rate, audioChannel, AudioStream::HighLatency);
     audioStream->SetVolume(volume);
     if (audioStream->SetPreservesPitch(preservesPitch) != NS_OK) {
       NS_WARNING("Setting the pitch preservation failed at AudioLoop start.");
     }
     if (playbackRate != 1.0) {
       NS_ASSERTION(playbackRate != 0,
                    "Don't set the playbackRate to 0 on an AudioStream.");
       if (audioStream->SetPlaybackRate(playbackRate) != NS_OK) {
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -1,29 +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 "MediaStreamGraphImpl.h"
 #include "mozilla/LinkedList.h"
+#include "mozilla/MathAlgorithms.h"
 #include "mozilla/unused.h"
 
 #include "AudioSegment.h"
 #include "VideoSegment.h"
 #include "nsContentUtils.h"
 #include "nsIAppShell.h"
 #include "nsIObserver.h"
 #include "nsServiceManagerUtils.h"
 #include "nsWidgetsCID.h"
 #include "prlog.h"
 #include "mozilla/Attributes.h"
 #include "TrackUnionStream.h"
 #include "ImageContainer.h"
-#include "AudioChannelCommon.h"
+#include "AudioChannelService.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "AudioNodeExternalInputStream.h"
 #include <algorithm>
 #include "DOMMediaStream.h"
 #include "GeckoProfiler.h"
 #include "mozilla/unused.h"
 #include "speex/speex_resampler.h"
@@ -836,17 +837,19 @@ MediaStreamGraphImpl::CreateOrDestroyAud
         MediaStream::AudioOutputStream* audioOutputStream =
           aStream->mAudioOutputStreams.AppendElement();
         audioOutputStream->mAudioPlaybackStartTime = aAudioOutputStartTime;
         audioOutputStream->mBlockedAudioTime = 0;
         audioOutputStream->mLastTickWritten = 0;
         audioOutputStream->mStream = new AudioStream();
         // XXX for now, allocate stereo output. But we need to fix this to
         // match the system's ideal channel configuration.
-        audioOutputStream->mStream->Init(2, IdealAudioRate(), AUDIO_CHANNEL_NORMAL, AudioStream::LowLatency);
+        audioOutputStream->mStream->Init(2, IdealAudioRate(),
+                                         AudioChannel::Normal,
+                                         AudioStream::LowLatency);
         audioOutputStream->mTrackID = tracks->GetID();
 
         LogLatency(AsyncLatencyLogger::AudioStreamCreate,
                    reinterpret_cast<uint64_t>(aStream),
                    reinterpret_cast<int64_t>(audioOutputStream->mStream.get()));
       }
     }
   }
@@ -896,17 +899,17 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
     // sample, or play a sample twice.
     TrackTicks offset = track->TimeToTicksRoundDown(GraphTimeToStreamTime(aStream, aFrom));
     if (!audioOutput.mLastTickWritten) {
         audioOutput.mLastTickWritten = offset;
     }
     if (audioOutput.mLastTickWritten != offset) {
       // If there is a global underrun of the MSG, this property won't hold, and
       // we reset the sample count tracking.
-      if (std::abs(audioOutput.mLastTickWritten - offset) != 1) {
+      if (mozilla::Abs(audioOutput.mLastTickWritten - offset) != 1) {
         audioOutput.mLastTickWritten = offset;
       } else {
         offset = audioOutput.mLastTickWritten;
       }
     }
 
     // We don't update aStream->mBufferStartTime here to account for
     // time spent blocked. Instead, we'll update it in UpdateCurrentTime after the
--- a/content/media/TextTrack.cpp
+++ b/content/media/TextTrack.cpp
@@ -120,16 +120,24 @@ TextTrack::AddCue(TextTrackCue& aCue)
 void
 TextTrack::RemoveCue(TextTrackCue& aCue, ErrorResult& aRv)
 {
   mCueList->RemoveCue(aCue, aRv);
   SetDirty();
 }
 
 void
+TextTrack::SetCuesDirty()
+{
+  for (uint32_t i = 0; i < mCueList->Length(); i++) {
+    ((*mCueList)[i])->Reset();
+  }
+}
+
+void
 TextTrack::UpdateActiveCueList()
 {
   if (!mTextTrackList) {
     return;
   }
 
   HTMLMediaElement* mediaElement = mTextTrackList->GetMediaElement();
   if (!mediaElement) {
--- a/content/media/TextTrack.h
+++ b/content/media/TextTrack.h
@@ -100,16 +100,17 @@ public:
 
   TextTrackReadyState ReadyState() const;
   void SetReadyState(TextTrackReadyState aState);
   void SetReadyState(uint32_t aReadyState);
 
   void AddCue(TextTrackCue& aCue);
   void RemoveCue(TextTrackCue& aCue, ErrorResult& aRv);
   void SetDirty() { mDirty = true; }
+  void SetCuesDirty();
 
   TextTrackList* GetTextTrackList();
   void SetTextTrackList(TextTrackList* aTextTrackList);
 
   IMPL_EVENT_HANDLER(cuechange)
 
   HTMLTrackElement* GetTrackElement();
   void SetTrackElement(HTMLTrackElement* aTrackElement);
--- a/content/media/TextTrackCue.h
+++ b/content/media/TextTrackCue.h
@@ -290,16 +290,21 @@ public:
   }
 
   void SetDisplayState(HTMLDivElement* aDisplayState)
   {
     mDisplayState = aDisplayState;
     mReset = false;
   }
 
+  void Reset()
+  {
+    mReset = true;
+  }
+
   bool HasBeenReset()
   {
     return mReset;
   }
 
   // Helper functions for implementation.
   bool
   operator==(const TextTrackCue& rhs) const
--- a/content/media/encoder/VorbisTrackEncoder.cpp
+++ b/content/media/encoder/VorbisTrackEncoder.cpp
@@ -75,17 +75,17 @@ VorbisTrackEncoder::Init(int aChannels, 
 
   mon.NotifyAll();
 
   return ret == 0 ? NS_OK : NS_ERROR_FAILURE;
 }
 
 void VorbisTrackEncoder::WriteLacing(nsTArray<uint8_t> *aOutput, int32_t aLacing)
 {
-  while (aLacing > 255) {
+  while (aLacing >= 255) {
     aLacing -= 255;
     aOutput->AppendElement(255);
   }
   aOutput->AppendElement((uint8_t)aLacing);
 }
 
 already_AddRefed<TrackMetadataBase>
 VorbisTrackEncoder::GetMetadata()
--- a/content/media/omx/MediaOmxDecoder.cpp
+++ b/content/media/omx/MediaOmxDecoder.cpp
@@ -40,17 +40,17 @@ MediaOmxDecoder::MediaOmxDecoder() :
 MediaDecoder* MediaOmxDecoder::Clone()
 {
   return new MediaOmxDecoder();
 }
 
 MediaDecoderStateMachine* MediaOmxDecoder::CreateStateMachine()
 {
   mReader = new MediaOmxReader(this);
-  mReader->SetAudioChannelType(GetAudioChannelType());
+  mReader->SetAudioChannel(GetAudioChannel());
   return new MediaDecoderStateMachine(this, mReader);
 }
 
 void MediaOmxDecoder::SetCanOffloadAudio(bool aCanOffloadAudio)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mCanOffloadAudio = aCanOffloadAudio;
 }
--- a/content/media/omx/MediaOmxReader.cpp
+++ b/content/media/omx/MediaOmxReader.cpp
@@ -8,16 +8,17 @@
 
 #include "MediaDecoderStateMachine.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "MediaResource.h"
 #include "VideoUtils.h"
 #include "MediaOmxDecoder.h"
 #include "AbstractMediaDecoder.h"
+#include "AudioChannelService.h"
 #include "OmxDecoder.h"
 #include "MPAPI.h"
 #include "gfx2DGlue.h"
 
 #ifdef MOZ_AUDIO_OFFLOAD
 #include <stagefright/Utils.h>
 #include <cutils/properties.h>
 #include <stagefright/MetaData.h>
@@ -40,24 +41,25 @@ extern PRLogModuleInfo* gMediaDecoderLog
 #endif
 
 MediaOmxReader::MediaOmxReader(AbstractMediaDecoder *aDecoder) :
   MediaDecoderReader(aDecoder),
   mHasVideo(false),
   mHasAudio(false),
   mVideoSeekTimeUs(-1),
   mAudioSeekTimeUs(-1),
-  mSkipCount(0),
-  mAudioChannelType(dom::AUDIO_CHANNEL_DEFAULT)
+  mSkipCount(0)
 {
 #ifdef PR_LOGGING
   if (!gMediaDecoderLog) {
     gMediaDecoderLog = PR_NewLogModule("MediaDecoder");
   }
 #endif
+
+  mAudioChannel = dom::AudioChannelService::GetDefaultAudioChannel();
 }
 
 MediaOmxReader::~MediaOmxReader()
 {
   ReleaseMediaResources();
   ReleaseDecoder();
   mOmxDecoder.clear();
 }
@@ -432,21 +434,21 @@ void MediaOmxReader::CheckAudioOffload()
 
   // Supporting audio offload only when there is no video, no streaming
   bool hasNoVideo = !mOmxDecoder->HasVideo();
   bool isNotStreaming
       = mDecoder->GetResource()->IsDataCachedToEndOfResource(0);
 
   // Not much benefit in trying to offload other channel types. Most of them
   // aren't supported and also duration would be less than a minute
-  bool isTypeMusic = mAudioChannelType == dom::AUDIO_CHANNEL_CONTENT;
+  bool isTypeMusic = mAudioChannel == dom::AudioChannel::Content;
 
   DECODER_LOG(PR_LOG_DEBUG, ("%s meta %p, no video %d, no streaming %d,"
       " channel type %d", __FUNCTION__, meta.get(), hasNoVideo,
-      isNotStreaming, mAudioChannelType));
+      isNotStreaming, mAudioChannel));
 
   if ((meta.get()) && hasNoVideo && isNotStreaming && isTypeMusic &&
       canOffloadStream(meta, false, false, AUDIO_STREAM_MUSIC)) {
     DECODER_LOG(PR_LOG_DEBUG, ("Can offload this audio stream"));
     mDecoder->SetCanOffloadAudio(true);
   }
 }
 #endif
--- a/content/media/omx/MediaOmxReader.h
+++ b/content/media/omx/MediaOmxReader.h
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 #if !defined(MediaOmxReader_h_)
 #define MediaOmxReader_h_
 
 #include "MediaResource.h"
 #include "MediaDecoderReader.h"
 #include "nsRect.h"
-#include "AudioChannelCommon.h"
+#include "mozilla/dom/AudioChannelBinding.h"
 #include <ui/GraphicBuffer.h>
 #include <stagefright/MediaSource.h>
 
 namespace android {
 class OmxDecoder;
 class MediaExtractor;
 }
 
@@ -31,17 +31,17 @@ class MediaOmxReader : public MediaDecod
   nsCString mType;
   bool mHasVideo;
   bool mHasAudio;
   nsIntRect mPicture;
   nsIntSize mInitialFrame;
   int64_t mVideoSeekTimeUs;
   int64_t mAudioSeekTimeUs;
   int32_t mSkipCount;
-  dom::AudioChannelType mAudioChannelType;
+  dom::AudioChannel mAudioChannel;
   android::sp<android::MediaSource> mAudioOffloadTrack;
 
 protected:
   android::sp<android::OmxDecoder> mOmxDecoder;
   android::sp<android::MediaExtractor> mExtractor;
 
   // Called by ReadMetadata() during MediaDecoderStateMachine::DecodeMetadata()
   // on decode thread. It create and initialize the OMX decoder including
@@ -80,18 +80,18 @@ public:
 
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
                                 MetadataTags** aTags);
   virtual nsresult Seek(int64_t aTime, int64_t aStartTime, int64_t aEndTime, int64_t aCurrentTime);
 
   virtual void SetIdle() MOZ_OVERRIDE;
   virtual void SetActive() MOZ_OVERRIDE;
 
-  void SetAudioChannelType(dom::AudioChannelType aAudioChannelType) {
-    mAudioChannelType = aAudioChannelType;
+  void SetAudioChannel(dom::AudioChannel aAudioChannel) {
+    mAudioChannel = aAudioChannel;
   }
 
   android::sp<android::MediaSource> GetAudioOffloadTrack() {
     return mAudioOffloadTrack;
   }
 
 #ifdef MOZ_AUDIO_OFFLOAD
   // Check whether it is possible to offload current audio track. This access
--- a/content/media/webaudio/AudioDestinationNode.cpp
+++ b/content/media/webaudio/AudioDestinationNode.cpp
@@ -472,50 +472,20 @@ AudioDestinationNode::CheckAudioChannelP
 
 void
 AudioDestinationNode::CreateAudioChannelAgent()
 {
   if (mAudioChannelAgent) {
     mAudioChannelAgent->StopPlaying();
   }
 
-  AudioChannelType type = AUDIO_CHANNEL_NORMAL;
-  switch(mAudioChannel) {
-    case AudioChannel::Normal:
-      type = AUDIO_CHANNEL_NORMAL;
-      break;
-
-    case AudioChannel::Content:
-      type = AUDIO_CHANNEL_CONTENT;
-      break;
-
-    case AudioChannel::Notification:
-      type = AUDIO_CHANNEL_NOTIFICATION;
-      break;
-
-    case AudioChannel::Alarm:
-      type = AUDIO_CHANNEL_ALARM;
-      break;
-
-    case AudioChannel::Telephony:
-      type = AUDIO_CHANNEL_TELEPHONY;
-      break;
-
-    case AudioChannel::Ringer:
-      type = AUDIO_CHANNEL_RINGER;
-      break;
-
-    case AudioChannel::Publicnotification:
-      type = AUDIO_CHANNEL_PUBLICNOTIFICATION;
-      break;
-
-  }
-
   mAudioChannelAgent = new AudioChannelAgent();
-  mAudioChannelAgent->InitWithWeakCallback(GetOwner(), type, this);
+  mAudioChannelAgent->InitWithWeakCallback(GetOwner(),
+                                           static_cast<int32_t>(mAudioChannel),
+                                           this);
 
   nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
   if (docshell) {
     bool isActive = false;
     docshell->GetIsActive(&isActive);
     mAudioChannelAgent->SetVisibilityState(isActive);
   }
 
--- a/content/media/webrtc/LoadManager.cpp
+++ b/content/media/webrtc/LoadManager.cpp
@@ -29,18 +29,17 @@ PRLogModuleInfo *gLoadManagerLog = nullp
 #endif
 
 namespace mozilla {
 
 LoadManager::LoadManager(int aLoadMeasurementInterval,
                          int aAveragingMeasurements,
                          float aHighLoadThreshold,
                          float aLowLoadThreshold)
-  : mLastSystemLoad(0),
-    mLoadSum(0.0f),
+  : mLoadSum(0.0f),
     mLoadSumMeasurements(0),
     mOveruseActive(false),
     mLoadMeasurementInterval(aLoadMeasurementInterval),
     mAveragingMeasurements(aAveragingMeasurements),
     mHighLoadThreshold(aHighLoadThreshold),
     mLowLoadThreshold(aLowLoadThreshold),
     mCurrentState(webrtc::kLoadNormal)
 {
--- a/content/media/webrtc/LoadManager.h
+++ b/content/media/webrtc/LoadManager.h
@@ -37,17 +37,16 @@ public:
     // CPULoadStateCallbackInvoker interface
     virtual void AddObserver(webrtc::CPULoadStateObserver * aObserver) MOZ_OVERRIDE;
     virtual void RemoveObserver(webrtc::CPULoadStateObserver * aObserver) MOZ_OVERRIDE;
 
 private:
     void LoadHasChanged();
 
     nsRefPtr<LoadMonitor> mLoadMonitor;
-    float mLastSystemLoad;
     float mLoadSum;
     int   mLoadSumMeasurements;
     // Set when overuse was signaled to us, and hasn't been un-signaled yet.
     bool  mOveruseActive;
     // Load measurement settings
     int mLoadMeasurementInterval;
     int mAveragingMeasurements;
     float mHighLoadThreshold;
--- a/content/media/webrtc/LoadMonitor.h
+++ b/content/media/webrtc/LoadMonitor.h
@@ -48,17 +48,16 @@ private:
     void SetSystemLoad(float load);
     void FireCallbacks();
 
     int                  mLoadUpdateInterval;
     mozilla::Mutex       mLock;
     mozilla::CondVar     mCondVar;
     bool                 mShutdownPending;
     nsCOMPtr<nsIThread>  mLoadInfoThread;
-    uint64_t             mTicksPerInterval;
     float                mSystemLoad;
     float                mProcessLoad;
     LoadNotificationCallback* mLoadNotificationCallback;
 };
 
 } //namespace
 
 #endif /* _LOADMONITOR_H_ */
--- a/content/svg/content/src/DOMSVGLength.cpp
+++ b/content/svg/content/src/DOMSVGLength.cpp
@@ -4,53 +4,65 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMSVGLength.h"
 #include "DOMSVGLengthList.h"
 #include "DOMSVGAnimatedLengthList.h"
 #include "SVGLength.h"
 #include "SVGAnimatedLengthList.h"
 #include "nsSVGElement.h"
+#include "nsSVGLength2.h"
 #include "nsIDOMSVGLength.h"
 #include "nsError.h"
 #include "nsMathUtils.h"
+#include "mozilla/dom/SVGLengthBinding.h"
+#include "nsSVGAttrTearoffTable.h"
 
 // See the architecture comment in DOMSVGAnimatedLengthList.h.
 
 namespace mozilla {
 
+static nsSVGAttrTearoffTable<nsSVGLength2, DOMSVGLength>
+  sBaseSVGLengthTearOffTable,
+  sAnimSVGLengthTearOffTable;
+
 // We could use NS_IMPL_CYCLE_COLLECTION_1, except that in Unlink() we need to
 // clear our list's weak ref to us to be safe. (The other option would be to
 // not unlink and rely on the breaking of the other edges in the cycle, as
 // NS_SVG_VAL_IMPL_CYCLE_COLLECTION does.)
 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMSVGLength)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMSVGLength)
   // We may not belong to a list, so we must null check tmp->mList.
   if (tmp->mList) {
     tmp->mList->mItems[tmp->mListIndex] = nullptr;
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK(mList)
+NS_IMPL_CYCLE_COLLECTION_UNLINK(mSVGElement)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMSVGLength)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mList)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSVGElement)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMSVGLength)
+NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMSVGLength)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMSVGLength)
 
-}
-DOMCI_DATA(SVGLength, mozilla::DOMSVGLength)
-namespace mozilla {
-
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMSVGLength)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(mozilla::DOMSVGLength) // pseudo-interface
   NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLength)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLength)
 NS_INTERFACE_MAP_END
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeLengthNotifier
 // Stack-based helper class to pair calls to WillChangeLengthList and
 // DidChangeLengthList.
 class MOZ_STACK_CLASS AutoChangeLengthNotifier
 {
@@ -86,238 +98,417 @@ DOMSVGLength::DOMSVGLength(DOMSVGLengthL
                            uint32_t aListIndex,
                            bool aIsAnimValItem)
   : mList(aList)
   , mListIndex(aListIndex)
   , mAttrEnum(aAttrEnum)
   , mIsAnimValItem(aIsAnimValItem)
   , mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)
   , mValue(0.0f)
+  , mVal(nullptr)
 {
   // These shifts are in sync with the members in the header.
   NS_ABORT_IF_FALSE(aList &&
                     aAttrEnum < (1 << 4) &&
                     aListIndex <= MaxListIndex(), "bad arg");
 
   NS_ABORT_IF_FALSE(IndexIsValid(), "Bad index for DOMSVGNumber!");
+
+  SetIsDOMBinding();
 }
 
 DOMSVGLength::DOMSVGLength()
   : mList(nullptr)
   , mListIndex(0)
   , mAttrEnum(0)
   , mIsAnimValItem(false)
   , mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)
   , mValue(0.0f)
+  , mVal(nullptr)
 {
+  SetIsDOMBinding();
+}
+
+DOMSVGLength::DOMSVGLength(nsSVGLength2* aVal, nsSVGElement* aSVGElement,
+                           bool aAnimVal)
+  : mList(nullptr)
+  , mListIndex(0)
+  , mAttrEnum(0)
+  , mIsAnimValItem(aAnimVal)
+  , mUnit(nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER)
+  , mValue(0.0f)
+  , mVal(aVal)
+  , mSVGElement(aSVGElement)
+{
+  SetIsDOMBinding();
+}
+
+DOMSVGLength::~DOMSVGLength()
+{
+  // Our mList's weak ref to us must be nulled out when we die. If GC has
+  // unlinked us using the cycle collector code, then that has already
+  // happened, and mList is null.
+  if (mList) {
+    mList->mItems[mListIndex] = nullptr;
+  }
+
+  if (mVal) {
+    auto& table = mIsAnimValItem ? sAnimSVGLengthTearOffTable : sBaseSVGLengthTearOffTable;
+    table.RemoveTearoff(mVal);
+  }
+}
+
+already_AddRefed<DOMSVGLength>
+DOMSVGLength::GetTearOff(nsSVGLength2* aVal, nsSVGElement* aSVGElement,
+                         bool aAnimVal)
+{
+  auto& table = aAnimVal ? sAnimSVGLengthTearOffTable : sBaseSVGLengthTearOffTable;
+  nsRefPtr<DOMSVGLength> domLength = table.GetTearoff(aVal);
+  if (!domLength) {
+    domLength = new DOMSVGLength(aVal, aSVGElement, aAnimVal);
+    table.AddTearoff(aVal, domLength);
+  }
+
+  return domLength.forget();
+}
+
+uint16_t
+DOMSVGLength::UnitType()
+{
+  if (mVal) {
+    if (mIsAnimValItem) {
+      mSVGElement->FlushAnimations();
+    }
+    return mVal->mSpecifiedUnitType;
+  }
+
+  if (mIsAnimValItem && HasOwner()) {
+    Element()->FlushAnimations(); // May make HasOwner() == false
+  }
+  return HasOwner() ? InternalItem().GetUnit() : mUnit;
 }
 
 NS_IMETHODIMP
 DOMSVGLength::GetUnitType(uint16_t* aUnit)
 {
+  *aUnit = UnitType();
+  return NS_OK;
+}
+
+float
+DOMSVGLength::GetValue(ErrorResult& aRv)
+{
+  if (mVal) {
+    if (mIsAnimValItem) {
+      mSVGElement->FlushAnimations();
+      return mVal->GetAnimValue(mSVGElement);
+    }
+    return mVal->GetBaseValue(mSVGElement);
+  }
+
   if (mIsAnimValItem && HasOwner()) {
     Element()->FlushAnimations(); // May make HasOwner() == false
   }
-  *aUnit = HasOwner() ? InternalItem().GetUnit() : mUnit;
-  return NS_OK;
+  if (HasOwner()) {
+    float value = InternalItem().GetValueInUserUnits(Element(), Axis());
+    if (!NS_finite(value)) {
+      aRv.Throw(NS_ERROR_FAILURE);
+    }
+    return value;
+  } else if (mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER ||
+             mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) {
+    return mValue;
+  }
+  // else [SVGWG issue] Can't convert this length's value to user units
+  // ReportToConsole
+  aRv.Throw(NS_ERROR_FAILURE);
+  return 0.0f;
 }
 
 NS_IMETHODIMP
 DOMSVGLength::GetValue(float* aValue)
 {
-  if (mIsAnimValItem && HasOwner()) {
-    Element()->FlushAnimations(); // May make HasOwner() == false
-  }
-  if (HasOwner()) {
-    *aValue = InternalItem().GetValueInUserUnits(Element(), Axis());
-    if (NS_finite(*aValue)) {
-      return NS_OK;
-    }
-  } else if (mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER ||
-             mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) {
-    *aValue = mValue;
-    return NS_OK;
-  }
-  // else [SVGWG issue] Can't convert this length's value to user units
-  // ReportToConsole
-  return NS_ERROR_FAILURE;
+  ErrorResult rv;
+  *aValue = GetValue(rv);
+  return rv.ErrorCode();
 }
 
-NS_IMETHODIMP
-DOMSVGLength::SetValue(float aUserUnitValue)
+void
+DOMSVGLength::SetValue(float aUserUnitValue, ErrorResult& aRv)
 {
   if (mIsAnimValItem) {
-    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
+    return;
   }
 
-  if (!NS_finite(aUserUnitValue)) {
-    return NS_ERROR_ILLEGAL_VALUE;
+  if (mVal) {
+    mVal->SetBaseValue(aUserUnitValue, mSVGElement, true);
+    return;
   }
 
   // Although the value passed in is in user units, this method does not turn
   // this length into a user unit length. Instead it converts the user unit
   // value to this length's current unit and sets that, leaving this length's
   // unit as it is.
 
   if (HasOwner()) {
     if (InternalItem().GetValueInUserUnits(Element(), Axis()) ==
         aUserUnitValue) {
-      return NS_OK;
+      return;
     }
     float uuPerUnit = InternalItem().GetUserUnitsPerUnit(Element(), Axis());
     if (uuPerUnit > 0) {
       float newValue = aUserUnitValue / uuPerUnit;
       if (NS_finite(newValue)) {
         AutoChangeLengthNotifier notifier(this);
         InternalItem().SetValueAndUnit(newValue, InternalItem().GetUnit());
-        return NS_OK;
+        return;
       }
     }
   } else if (mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER ||
              mUnit == nsIDOMSVGLength::SVG_LENGTHTYPE_PX) {
     mValue = aUserUnitValue;
-    return NS_OK;
+    return;
   }
   // else [SVGWG issue] Can't convert user unit value to this length's unit
   // ReportToConsole
-  return NS_ERROR_FAILURE;
+  aRv.Throw(NS_ERROR_FAILURE);
+}
+
+NS_IMETHODIMP
+DOMSVGLength::SetValue(float aUserUnitValue)
+{
+  if (!NS_finite(aUserUnitValue)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  ErrorResult rv;
+  SetValue(aUserUnitValue, rv);
+  return rv.ErrorCode();
+}
+
+float
+DOMSVGLength::ValueInSpecifiedUnits()
+{
+  if (mVal) {
+    if (mIsAnimValItem) {
+      mSVGElement->FlushAnimations();
+      return mVal->mAnimVal;
+    }
+    return mVal->mBaseVal;
+  }
+
+  if (mIsAnimValItem && HasOwner()) {
+    Element()->FlushAnimations(); // May make HasOwner() == false
+  }
+  return HasOwner() ? InternalItem().GetValueInCurrentUnits() : mValue;
 }
 
 NS_IMETHODIMP
 DOMSVGLength::GetValueInSpecifiedUnits(float* aValue)
 {
-  if (mIsAnimValItem && HasOwner()) {
-    Element()->FlushAnimations(); // May make HasOwner() == false
+  *aValue = ValueInSpecifiedUnits();
+  return NS_OK;
+}
+
+void
+DOMSVGLength::SetValueInSpecifiedUnits(float aValue, ErrorResult& aRv)
+{
+  if (mIsAnimValItem) {
+    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
+    return;
   }
-  *aValue = HasOwner() ? InternalItem().GetValueInCurrentUnits() : mValue;
-  return NS_OK;
+
+  if (mVal) {
+    mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement, true);
+    return;
+  }
+
+  if (HasOwner()) {
+    if (InternalItem().GetValueInCurrentUnits() == aValue) {
+      return;
+    }
+    AutoChangeLengthNotifier notifier(this);
+    InternalItem().SetValueInCurrentUnits(aValue);
+    return;
+  }
+  mValue = aValue;
 }
 
 NS_IMETHODIMP
 DOMSVGLength::SetValueInSpecifiedUnits(float aValue)
 {
-  if (mIsAnimValItem) {
-    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
-  }
-
   if (!NS_finite(aValue)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
 
+  ErrorResult rv;
+  SetValueInSpecifiedUnits(aValue, rv);
+  return rv.ErrorCode();
+}
+
+void
+DOMSVGLength::SetValueAsString(const nsAString& aValue, ErrorResult& aRv)
+{
+  if (mIsAnimValItem) {
+    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
+    return;
+  }
+
+  if (mVal) {
+    mVal->SetBaseValueString(aValue, mSVGElement, true);
+    return;
+  }
+
+  SVGLength value;
+  if (!value.SetValueFromString(aValue)) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return;
+  }
   if (HasOwner()) {
-    if (InternalItem().GetValueInCurrentUnits() == aValue) {
-      return NS_OK;
+    if (InternalItem() == value) {
+      return;
     }
     AutoChangeLengthNotifier notifier(this);
-    InternalItem().SetValueInCurrentUnits(aValue);
-    return NS_OK;
+    InternalItem() = value;
+    return;
   }
-  mValue = aValue;
-  return NS_OK;
+  mValue = value.GetValueInCurrentUnits();
+  mUnit = value.GetUnit();
 }
 
 NS_IMETHODIMP
 DOMSVGLength::SetValueAsString(const nsAString& aValue)
 {
-  if (mIsAnimValItem) {
-    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
-  }
-
-  SVGLength value;
-  if (!value.SetValueFromString(aValue)) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-  if (HasOwner()) {
-    if (InternalItem() == value) {
-      return NS_OK;
-    }
-    AutoChangeLengthNotifier notifier(this);
-    InternalItem() = value;
-    return NS_OK;
-  }
-  mValue = value.GetValueInCurrentUnits();
-  mUnit = value.GetUnit();
-  return NS_OK;
+  ErrorResult rv;
+  SetValueAsString(aValue, rv);
+  return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
 DOMSVGLength::GetValueAsString(nsAString& aValue)
 {
+  if (mVal) {
+    if (mIsAnimValItem) {
+      mSVGElement->FlushAnimations();
+      mVal->GetAnimValueString(aValue);
+    } else {
+      mVal->GetBaseValueString(aValue);
+    }
+    return NS_OK;
+  }
+
   if (mIsAnimValItem && HasOwner()) {
     Element()->FlushAnimations(); // May make HasOwner() == false
   }
   if (HasOwner()) {
     InternalItem().GetValueAsString(aValue);
     return NS_OK;
   }
   SVGLength(mValue, mUnit).GetValueAsString(aValue);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit, float aValue)
+void
+DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit, float aValue,
+                                     ErrorResult& aRv)
 {
   if (mIsAnimValItem) {
-    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
+    return;
   }
 
-  if (!NS_finite(aValue)) {
-    return NS_ERROR_ILLEGAL_VALUE;
+  if (mVal) {
+    mVal->NewValueSpecifiedUnits(aUnit, aValue, mSVGElement);
+    return;
   }
 
   if (!SVGLength::IsValidUnitType(aUnit)) {
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
   }
   if (HasOwner()) {
     if (InternalItem().GetUnit() == aUnit &&
         InternalItem().GetValueInCurrentUnits() == aValue) {
-      return NS_OK;
+      return;
     }
     AutoChangeLengthNotifier notifier(this);
     InternalItem().SetValueAndUnit(aValue, uint8_t(aUnit));
-    return NS_OK;
+    return;
   }
   mUnit = uint8_t(aUnit);
   mValue = aValue;
-  return NS_OK;
 }
 
 NS_IMETHODIMP
-DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit)
+DOMSVGLength::NewValueSpecifiedUnits(uint16_t aUnit, float aValue)
+{
+  if (!NS_finite(aValue)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  ErrorResult rv;
+  NewValueSpecifiedUnits(aUnit, aValue, rv);
+  return rv.ErrorCode();
+}
+
+void
+DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit, ErrorResult& aRv)
 {
   if (mIsAnimValItem) {
-    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
+    return;
+  }
+
+  if (mVal) {
+    mVal->ConvertToSpecifiedUnits(aUnit, mSVGElement);
+    return;
   }
 
   if (!SVGLength::IsValidUnitType(aUnit)) {
-    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return;
   }
   if (HasOwner()) {
     if (InternalItem().GetUnit() == aUnit) {
-      return NS_OK;
+      return;
     }
     float val = InternalItem().GetValueInSpecifiedUnit(
                                  aUnit, Element(), Axis());
     if (NS_finite(val)) {
       AutoChangeLengthNotifier notifier(this);
       InternalItem().SetValueAndUnit(val, aUnit);
-      return NS_OK;
+      return;
     }
   } else {
     SVGLength len(mValue, mUnit);
     float val = len.GetValueInSpecifiedUnit(aUnit, nullptr, 0);
     if (NS_finite(val)) {
       mValue = val;
       mUnit = aUnit;
-      return NS_OK;
+      return;
     }
   }
   // else [SVGWG issue] Can't convert unit
   // ReportToConsole
-  return NS_ERROR_FAILURE;
+  aRv.Throw(NS_ERROR_FAILURE);
+}
+
+NS_IMETHODIMP
+DOMSVGLength::ConvertToSpecifiedUnits(uint16_t aUnit)
+{
+  ErrorResult rv;
+  ConvertToSpecifiedUnits(aUnit, rv);
+  return rv.ErrorCode();
+}
+
+JSObject*
+DOMSVGLength::WrapObject(JSContext* aCx)
+{
+  return dom::SVGLengthBinding::Wrap(aCx, this);
 }
 
 void
 DOMSVGLength::InsertingIntoList(DOMSVGLengthList *aList,
                                 uint8_t aAttrEnum,
                                 uint32_t aListIndex,
                                 bool aIsAnimValItem)
 {
--- a/content/svg/content/src/DOMSVGLength.h
+++ b/content/svg/content/src/DOMSVGLength.h
@@ -9,31 +9,34 @@
 #include "DOMSVGLengthList.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "nsIDOMSVGLength.h"
 #include "nsTArray.h"
 #include "SVGLength.h"
 #include "mozilla/Attributes.h"
+#include "nsWrapperCache.h"
 
 class nsSVGElement;
 
 // We make DOMSVGLength a pseudo-interface to allow us to QI to it in order to
 // check that the objects that scripts pass to DOMSVGLengthList methods are our
 // *native* length objects.
 //
 // {A8468350-7F7B-4976-9A7E-3765A1DADF9A}
 #define MOZILLA_DOMSVGLENGTH_IID \
   { 0xA8468350, 0x7F7B, 0x4976, { 0x9A, 0x7E, 0x37, 0x65, 0xA1, 0xDA, 0xDF, 0x9A } }
 
 #define MOZ_SVG_LIST_INDEX_BIT_COUNT 22 // supports > 4 million list items
 
 namespace mozilla {
 
+class ErrorResult;
+
 /**
  * Class DOMSVGLength
  *
  * This class creates the DOM objects that wrap internal SVGLength objects that
  * are in an SVGLengthList. It is also used to create the objects returned by
  * SVGSVGElement.createSVGLength().
  *
  * For the DOM wrapper classes for non-list SVGLength, see nsSVGLength2.h.
@@ -61,48 +64,51 @@ namespace mozilla {
  * type can find their corresponding internal SVGLength.
  *
  * To use these classes for <length> attributes as well as <list-of-length>
  * attributes, we would need to take a bit from mListIndex and use that to
  * indicate whether the object belongs to a list or non-list attribute, then
  * if-else as appropriate. The bug for doing that work is:
  * https://bugzilla.mozilla.org/show_bug.cgi?id=571734
  */
-class DOMSVGLength MOZ_FINAL : public nsIDOMSVGLength
+class DOMSVGLength MOZ_FINAL : public nsIDOMSVGLength,
+                               public nsWrapperCache
 {
   friend class AutoChangeLengthNotifier;
 
+  /**
+   * Ctor for creating the object returned by nsSVGLength2::ToDOMBaseVal/ToDOMAnimVal
+   */
+  DOMSVGLength(nsSVGLength2* aVal, nsSVGElement* aSVGElement, bool aAnimVal);
+
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGLENGTH_IID)
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGLength)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DOMSVGLength)
   NS_DECL_NSIDOMSVGLENGTH
 
   /**
    * Generic ctor for DOMSVGLength objects that are created for an attribute.
    */
   DOMSVGLength(DOMSVGLengthList *aList,
                uint8_t aAttrEnum,
                uint32_t aListIndex,
                bool aIsAnimValItem);
 
   /**
    * Ctor for creating the objects returned by SVGSVGElement.createSVGLength(),
    * which do not initially belong to an attribute.
    */
   DOMSVGLength();
 
-  ~DOMSVGLength() {
-    // Our mList's weak ref to us must be nulled out when we die. If GC has
-    // unlinked us using the cycle collector code, then that has already
-    // happened, and mList is null.
-    if (mList) {
-      mList->mItems[mListIndex] = nullptr;
-    }
-  }
+  ~DOMSVGLength();
+
+  static already_AddRefed<DOMSVGLength> GetTearOff(nsSVGLength2* aVal,
+                                                   nsSVGElement* aSVGElement,
+                                                   bool aAnimVal);
 
   /**
    * Create an unowned copy of an owned length. The caller is responsible for
    * the first AddRef().
    */
   DOMSVGLength* Copy() {
     NS_ASSERTION(mList, "unexpected caller");
     DOMSVGLength *copy = new DOMSVGLength();
@@ -151,19 +157,38 @@ public:
    * removed from its current DOM list so that it can first make a copy of its
    * internal counterpart's values. (If it didn't do this, then it would
    * "lose" its value on being removed.)
    */
   void RemovingFromList();
 
   SVGLength ToSVGLength();
 
+  // WebIDL
+  uint16_t UnitType();
+  float GetValue(ErrorResult& aRv);
+  void SetValue(float aValue, ErrorResult& aRv);
+  float ValueInSpecifiedUnits();
+  void SetValueInSpecifiedUnits(float aValue, ErrorResult& aRv);
+  // The XPCOM GetValueAsString is good
+  void SetValueAsString(const nsAString& aValue, ErrorResult& aRv);
+  void NewValueSpecifiedUnits(uint16_t aUnit, float aValue,
+                              ErrorResult& aRv);
+  void ConvertToSpecifiedUnits(uint16_t aUnit, ErrorResult& aRv);
+
+  nsISupports* GetParentObject() const {
+    auto svgElement = mList ? Element() : mSVGElement.get();
+    return static_cast<nsIDOMSVGElement*> (svgElement);
+  }
+
+  JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
 private:
 
-  nsSVGElement* Element() {
+  nsSVGElement* Element() const {
     return mList->Element();
   }
 
   uint8_t AttrEnum() const {
     return mAttrEnum;
   }
 
   /**
@@ -196,16 +221,20 @@ private:
 
   uint32_t mListIndex:MOZ_SVG_LIST_INDEX_BIT_COUNT;
   uint32_t mAttrEnum:4; // supports up to 16 attributes
   uint32_t mIsAnimValItem:1;
 
   // The following members are only used when we're not in a list:
   uint32_t mUnit:5; // can handle 31 units (the 10 SVG 1.1 units + rem, vw, vh, wm, calc + future additions)
   float mValue;
+
+  // The following members are only used when we have an nsSVGLength2
+  nsSVGLength2* mVal; // kept alive because it belongs to mSVGElement
+  nsRefPtr<nsSVGElement> mSVGElement;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(DOMSVGLength, MOZILLA_DOMSVGLENGTH_IID)
 
 } // namespace mozilla
 
 #undef MOZ_SVG_LIST_INDEX_BIT_COUNT
 
--- a/content/svg/content/src/DOMSVGLengthList.cpp
+++ b/content/svg/content/src/DOMSVGLengthList.cpp
@@ -167,89 +167,89 @@ DOMSVGLengthList::Clear(ErrorResult& aEr
     // their values. This also notifies the animVal list:
     mAList->InternalBaseValListWillChangeTo(SVGLengthList());
 
     mItems.Clear();
     InternalList().Clear();
   }
 }
 
-already_AddRefed<nsIDOMSVGLength>
-DOMSVGLengthList::Initialize(nsIDOMSVGLength *newItem,
+already_AddRefed<DOMSVGLength>
+DOMSVGLengthList::Initialize(DOMSVGLength& newItem,
                              ErrorResult& error)
 {
   if (IsAnimValList()) {
     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return nullptr;
   }
 
   // If newItem is already in a list we should insert a clone of newItem, and
   // for consistency, this should happen even if *this* is the list that
   // newItem is currently in. Note that in the case of newItem being in this
   // list, the Clear() call before the InsertItemBefore() call would remove it
   // from this list, and so the InsertItemBefore() call would not insert a
   // clone of newItem, it would actually insert newItem. To prevent that from
   // happening we have to do the clone here, if necessary.
 
-  nsCOMPtr<DOMSVGLength> domItem = do_QueryInterface(newItem);
+  nsRefPtr<DOMSVGLength> domItem = &newItem;
   if (!domItem) {
     error.Throw(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR);
     return nullptr;
   }
   if (domItem->HasOwner()) {
-    newItem = domItem->Copy();
+    domItem = domItem->Copy();
   }
 
   ErrorResult rv;
   Clear(rv);
   MOZ_ASSERT(!rv.Failed());
-  return InsertItemBefore(newItem, 0, error);
+  return InsertItemBefore(*domItem, 0, error);
 }
 
-already_AddRefed<nsIDOMSVGLength>
+already_AddRefed<DOMSVGLength>
 DOMSVGLengthList::GetItem(uint32_t index, ErrorResult& error)
 {
   bool found;
-  nsRefPtr<nsIDOMSVGLength> item = IndexedGetter(index, found, error);
+  nsRefPtr<DOMSVGLength> item = IndexedGetter(index, found, error);
   if (!found) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
   }
   return item.forget();
 }
 
-already_AddRefed<nsIDOMSVGLength>
+already_AddRefed<DOMSVGLength>
 DOMSVGLengthList::IndexedGetter(uint32_t index, bool& found, ErrorResult& error)
 {
   if (IsAnimValList()) {
     Element()->FlushAnimations();
   }
   found = index < LengthNoFlush();
   if (found) {
     return GetItemAt(index);
   }
   return nullptr;
 }
 
-already_AddRefed<nsIDOMSVGLength>
-DOMSVGLengthList::InsertItemBefore(nsIDOMSVGLength *newItem,
+already_AddRefed<DOMSVGLength>
+DOMSVGLengthList::InsertItemBefore(DOMSVGLength& newItem,
                                    uint32_t index,
                                    ErrorResult& error)
 {
   if (IsAnimValList()) {
     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return nullptr;
   }
 
   index = std::min(index, LengthNoFlush());
   if (index >= DOMSVGLength::MaxListIndex()) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
-  nsCOMPtr<DOMSVGLength> domItem = do_QueryInterface(newItem);
+  nsRefPtr<DOMSVGLength> domItem = &newItem;
   if (!domItem) {
     error.Throw(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR);
     return nullptr;
   }
   if (domItem->HasOwner()) {
     domItem = domItem->Copy(); // must do this before changing anything!
   }
 
@@ -272,27 +272,27 @@ DOMSVGLengthList::InsertItemBefore(nsIDO
   // data from InternalList() itself!:
   domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
 
   UpdateListIndicesFromIndex(mItems, index + 1);
 
   return domItem.forget();
 }
 
-already_AddRefed<nsIDOMSVGLength>
-DOMSVGLengthList::ReplaceItem(nsIDOMSVGLength *newItem,
+already_AddRefed<DOMSVGLength>
+DOMSVGLengthList::ReplaceItem(DOMSVGLength& newItem,
                               uint32_t index,
                               ErrorResult& error)
 {
   if (IsAnimValList()) {
     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return nullptr;
   }
 
-  nsCOMPtr<DOMSVGLength> domItem = do_QueryInterface(newItem);
+  nsRefPtr<DOMSVGLength> domItem = &newItem;
   if (!domItem) {
     error.Throw(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR);
     return nullptr;
   }
   if (index >= LengthNoFlush()) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
@@ -312,17 +312,17 @@ DOMSVGLengthList::ReplaceItem(nsIDOMSVGL
 
   // This MUST come after the ToSVGPoint() call, otherwise that call
   // would end up reading bad data from InternalList()!
   domItem->InsertingIntoList(this, AttrEnum(), index, IsAnimValList());
 
   return domItem.forget();
 }
 
-already_AddRefed<nsIDOMSVGLength>
+already_AddRefed<DOMSVGLength>
 DOMSVGLengthList::RemoveItem(uint32_t index,
                              ErrorResult& error)
 {
   if (IsAnimValList()) {
     error.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return nullptr;
   }
 
@@ -333,39 +333,39 @@ DOMSVGLengthList::RemoveItem(uint32_t in
 
   AutoChangeLengthListNotifier notifier(this);
   // Now that we know we're removing, keep animVal list in sync as necessary.
   // Do this *before* touching InternalList() so the removed item can get its
   // internal value.
   MaybeRemoveItemFromAnimValListAt(index);
 
   // We have to return the removed item, so get it, creating it if necessary:
-  nsCOMPtr<nsIDOMSVGLength> result = GetItemAt(index);
+  nsCOMPtr<DOMSVGLength> result = GetItemAt(index);
 
   // Notify the DOM item of removal *before* modifying the lists so that the
   // DOM item can copy its *old* value:
   mItems[index]->RemovingFromList();
 
   InternalList().RemoveItem(index);
   mItems.RemoveElementAt(index);
 
   UpdateListIndicesFromIndex(mItems, index);
 
   return result.forget();
 }
 
-already_AddRefed<nsIDOMSVGLength>
+already_AddRefed<DOMSVGLength>
 DOMSVGLengthList::GetItemAt(uint32_t aIndex)
 {
   MOZ_ASSERT(aIndex < mItems.Length());
 
   if (!mItems[aIndex]) {
     mItems[aIndex] = new DOMSVGLength(this, AttrEnum(), aIndex, IsAnimValList());
   }
-  nsRefPtr<nsIDOMSVGLength> result = mItems[aIndex];
+  nsRefPtr<DOMSVGLength> result = mItems[aIndex];
   return result.forget();
 }
 
 void
 DOMSVGLengthList::MaybeInsertNullInAnimValListAt(uint32_t aIndex)
 {
   NS_ABORT_IF_FALSE(!IsAnimValList(), "call from baseVal to animVal");
 
--- a/content/svg/content/src/DOMSVGLengthList.h
+++ b/content/svg/content/src/DOMSVGLengthList.h
@@ -10,17 +10,16 @@
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDebug.h"
 #include "nsTArray.h"
 #include "SVGLengthList.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 
-class nsIDOMSVGLength;
 class nsSVGElement;
 
 namespace mozilla {
 
 class DOMSVGLength;
 
 /**
  * Class DOMSVGLengthList
@@ -104,32 +103,32 @@ public:
   uint32_t NumberOfItems() const
   {
     if (IsAnimValList()) {
       Element()->FlushAnimations();
     }
     return LengthNoFlush();
   }
   void Clear(ErrorResult& aError);
-  already_AddRefed<nsIDOMSVGLength> Initialize(nsIDOMSVGLength *newItem,
+  already_AddRefed<DOMSVGLength> Initialize(DOMSVGLength& newItem,
+                                            ErrorResult& error);
+  already_AddRefed<DOMSVGLength> GetItem(uint32_t index,
+                                         ErrorResult& error);
+  already_AddRefed<DOMSVGLength> IndexedGetter(uint32_t index, bool& found,
                                                ErrorResult& error);
-  already_AddRefed<nsIDOMSVGLength> GetItem(uint32_t index,
-                                            ErrorResult& error);
-  already_AddRefed<nsIDOMSVGLength> IndexedGetter(uint32_t index, bool& found,
+  already_AddRefed<DOMSVGLength> InsertItemBefore(DOMSVGLength& newItem,
+                                                  uint32_t index,
                                                   ErrorResult& error);
-  already_AddRefed<nsIDOMSVGLength> InsertItemBefore(nsIDOMSVGLength *newItem,
-                                                     uint32_t index,
-                                                     ErrorResult& error);
-  already_AddRefed<nsIDOMSVGLength> ReplaceItem(nsIDOMSVGLength *newItem,
-                                                uint32_t index,
-                                                ErrorResult& error);
-  already_AddRefed<nsIDOMSVGLength> RemoveItem(uint32_t index,
-                                               ErrorResult& error);
-  already_AddRefed<nsIDOMSVGLength> AppendItem(nsIDOMSVGLength *newItem,
-                                               ErrorResult& error)
+  already_AddRefed<DOMSVGLength> ReplaceItem(DOMSVGLength& newItem,
+                                             uint32_t index,
+                                             ErrorResult& error);
+  already_AddRefed<DOMSVGLength> RemoveItem(uint32_t index,
+                                            ErrorResult& error);
+  already_AddRefed<DOMSVGLength> AppendItem(DOMSVGLength& newItem,
+                                            ErrorResult& error)
   {
     return InsertItemBefore(newItem, LengthNoFlush(), error);
   }
   uint32_t Length() const
   {
     return NumberOfItems();
   }
 
@@ -159,18 +158,18 @@ private:
    *
    * To simplify the code we just have this one method for obtaining both
    * baseVal and animVal internal lists. This means that animVal lists don't
    * get const protection, but our setter methods guard against changing
    * animVal lists.
    */
   SVGLengthList& InternalList() const;
 
-  /// Returns the nsIDOMSVGLength at aIndex, creating it if necessary.
-  already_AddRefed<nsIDOMSVGLength> GetItemAt(uint32_t aIndex);
+  /// Returns the DOMSVGLength at aIndex, creating it if necessary.
+  already_AddRefed<DOMSVGLength> GetItemAt(uint32_t aIndex);
 
   void MaybeInsertNullInAnimValListAt(uint32_t aIndex);
   void MaybeRemoveItemFromAnimValListAt(uint32_t aIndex);
 
   // Weak refs to our DOMSVGLength items. The items are friends and take care
   // of clearing our pointer to them when they die.
   FallibleTArray<DOMSVGLength*> mItems;
 
--- a/content/svg/content/src/SVGAnimatedLength.cpp
+++ b/content/svg/content/src/SVGAnimatedLength.cpp
@@ -1,41 +1,42 @@
 /* -*- 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/SVGAnimatedLength.h"
 #include "mozilla/dom/SVGAnimatedLengthBinding.h"
 #include "nsSVGLength2.h"
+#include "DOMSVGLength.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(SVGAnimatedLength, mSVGElement)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(SVGAnimatedLength, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(SVGAnimatedLength, Release)
 
 JSObject*
 SVGAnimatedLength::WrapObject(JSContext* aCx)
 {
   return SVGAnimatedLengthBinding::Wrap(aCx, this);
 }
 
-already_AddRefed<nsIDOMSVGLength>
+already_AddRefed<DOMSVGLength>
 SVGAnimatedLength::BaseVal()
 {
-  nsRefPtr<nsIDOMSVGLength> angle;
+  nsRefPtr<DOMSVGLength> angle;
   mVal->ToDOMBaseVal(getter_AddRefs(angle), mSVGElement);
   return angle.forget();
 }
 
-already_AddRefed<nsIDOMSVGLength>
+already_AddRefed<DOMSVGLength>
 SVGAnimatedLength::AnimVal()
 {
-  nsRefPtr<nsIDOMSVGLength> angle;
+  nsRefPtr<DOMSVGLength> angle;
   mVal->ToDOMAnimVal(getter_AddRefs(angle), mSVGElement);
   return angle.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/svg/content/src/SVGAnimatedLength.h
+++ b/content/svg/content/src/SVGAnimatedLength.h
@@ -5,19 +5,21 @@
 
 #ifndef mozilla_dom_SVGAnimatedLength_h
 #define mozilla_dom_SVGAnimatedLength_h
 
 #include "mozilla/Attributes.h"
 #include "nsSVGElement.h"
 
 class nsSVGLength2;
-class nsIDOMSVGLength;
 
 namespace mozilla {
+
+class DOMSVGLength;
+
 namespace dom {
 
 class SVGAnimatedLength MOZ_FINAL : public nsWrapperCache
 {
 public:
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SVGAnimatedLength)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(SVGAnimatedLength)
 
@@ -25,18 +27,18 @@ public:
     : mVal(aVal), mSVGElement(aSVGElement)
   { SetIsDOMBinding(); }
 
   ~SVGAnimatedLength();
 
   // WebIDL
   nsSVGElement* GetParentObject() { return mSVGElement; }
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
-  already_AddRefed<nsIDOMSVGLength> BaseVal();
-  already_AddRefed<nsIDOMSVGLength> AnimVal();
+  already_AddRefed<DOMSVGLength> BaseVal();
+  already_AddRefed<DOMSVGLength> AnimVal();
 
 protected:
   nsSVGLength2* mVal; // kept alive because it belongs to content
   nsRefPtr<nsSVGElement> mSVGElement;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/content/svg/content/src/SVGAttrValueWrapper.h
+++ b/content/svg/content/src/SVGAttrValueWrapper.h
@@ -7,17 +7,17 @@
 #ifndef MOZILLA_SVGATTRVALUEWRAPPER_H__
 #define MOZILLA_SVGATTRVALUEWRAPPER_H__
 
 /**
  * Utility wrapper for handling SVG types used inside nsAttrValue so that these
  * types don't need to be exported outside the SVG module.
  */
 
-#include "nsString.h"
+#include "nsStringGlue.h"
 
 class nsSVGAngle;
 class nsSVGIntegerPair;
 class nsSVGLength2;
 class nsSVGNumberPair;
 class nsSVGViewBox;
 
 namespace mozilla {
--- a/content/svg/content/src/SVGEllipseElement.cpp
+++ b/content/svg/content/src/SVGEllipseElement.cpp
@@ -93,18 +93,18 @@ SVGEllipseElement::GetLengthInfo()
 // nsSVGPathGeometryElement methods
 
 void
 SVGEllipseElement::ConstructPath(gfxContext *aCtx)
 {
   if (!aCtx->IsCairo()) {
     RefPtr<Path> path = BuildPath();
     if (path) {
-      gfxPath gfxpath(path);
-      aCtx->SetPath(&gfxpath);
+      nsRefPtr<gfxPath> gfxpath = new gfxPath(path);
+      aCtx->SetPath(gfxpath);
     }
     return;
   }
 
   float x, y, rx, ry;
 
   GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
 
--- a/content/svg/content/src/SVGSVGElement.cpp
+++ b/content/svg/content/src/SVGSVGElement.cpp
@@ -382,20 +382,20 @@ SVGSVGElement::DeselectAll()
 
 already_AddRefed<nsIDOMSVGNumber>
 SVGSVGElement::CreateSVGNumber()
 {
   nsCOMPtr<nsIDOMSVGNumber> number = new DOMSVGNumber();
   return number.forget();
 }
 
-already_AddRefed<nsIDOMSVGLength>
+already_AddRefed<DOMSVGLength>
 SVGSVGElement::CreateSVGLength()
 {
-  nsCOMPtr<nsIDOMSVGLength> length = new DOMSVGLength();
+  nsCOMPtr<DOMSVGLength> length = new DOMSVGLength();
   return length.forget();
 }
 
 already_AddRefed<SVGAngle>
 SVGSVGElement::CreateSVGAngle()
 {
   nsSVGAngle* angle = new nsSVGAngle();
   angle->Init();
--- a/content/svg/content/src/SVGSVGElement.h
+++ b/content/svg/content/src/SVGSVGElement.h
@@ -25,16 +25,17 @@ class nsIDOMSVGNumber;
 class nsSMILTimeContainer;
 class nsSVGOuterSVGFrame;
 class nsSVGInnerSVGFrame;
 class nsSVGImageFrame;
 
 namespace mozilla {
 class AutoSVGRenderingState;
 class DOMSVGAnimatedPreserveAspectRatio;
+class DOMSVGLength;
 class EventChainPreVisitor;
 class SVGFragmentIdentifier;
 
 namespace dom {
 class SVGAngle;
 class SVGAnimatedRect;
 class SVGMatrix;
 class SVGTransform;
@@ -234,17 +235,17 @@ public:
   void ForceRedraw(ErrorResult& rv);
   void PauseAnimations();
   void UnpauseAnimations();
   bool AnimationsPaused();
   float GetCurrentTime();
   void SetCurrentTime(float seconds);
   void DeselectAll();
   already_AddRefed<nsIDOMSVGNumber> CreateSVGNumber();
-  already_AddRefed<nsIDOMSVGLength> CreateSVGLength();
+  already_AddRefed<DOMSVGLength> CreateSVGLength();
   already_AddRefed<SVGAngle> CreateSVGAngle();
   already_AddRefed<nsISVGPoint> CreateSVGPoint();
   already_AddRefed<SVGMatrix> CreateSVGMatrix();
   already_AddRefed<SVGIRect> CreateSVGRect();
   already_AddRefed<SVGTransform> CreateSVGTransform();
   already_AddRefed<SVGTransform> CreateSVGTransformFromMatrix(SVGMatrix& matrix);
   using nsINode::GetElementById; // This does what we want
   already_AddRefed<SVGAnimatedRect> ViewBox();
--- a/content/svg/content/src/nsSVGLength2.cpp
+++ b/content/svg/content/src/nsSVGLength2.cpp
@@ -10,42 +10,21 @@
 #include "mozilla/dom/SVGSVGElement.h"
 #include "nsContentUtils.h" // NS_ENSURE_FINITE
 #include "nsIFrame.h"
 #include "nsSMILFloatType.h"
 #include "nsSMILValue.h"
 #include "nsSVGAttrTearoffTable.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsTextFormatter.h"
+#include "DOMSVGLength.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
-NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGLength2::DOMBaseVal, mSVGElement)
-
-NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGLength2::DOMAnimVal, mSVGElement)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGLength2::DOMBaseVal)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGLength2::DOMBaseVal)
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGLength2::DOMAnimVal)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGLength2::DOMAnimVal)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGLength2::DOMBaseVal)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLength)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLength)
-NS_INTERFACE_MAP_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGLength2::DOMAnimVal)
-  NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLength)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGLength)
-NS_INTERFACE_MAP_END
-
 static nsIAtom** const unitMap[] =
 {
   nullptr, /* SVG_LENGTHTYPE_UNKNOWN */
   nullptr, /* SVG_LENGTHTYPE_NUMBER */
   &nsGkAtoms::percentage,
   &nsGkAtoms::em,
   &nsGkAtoms::ex,
   &nsGkAtoms::px,
@@ -53,20 +32,16 @@ static nsIAtom** const unitMap[] =
   &nsGkAtoms::mm,
   &nsGkAtoms::in,
   &nsGkAtoms::pt,
   &nsGkAtoms::pc
 };
 
 static nsSVGAttrTearoffTable<nsSVGLength2, SVGAnimatedLength>
   sSVGAnimatedLengthTearoffTable;
-static nsSVGAttrTearoffTable<nsSVGLength2, nsSVGLength2::DOMBaseVal>
-  sBaseSVGLengthTearoffTable;
-static nsSVGAttrTearoffTable<nsSVGLength2, nsSVGLength2::DOMAnimVal>
-  sAnimSVGLengthTearoffTable;
 
 /* Helper functions */
 
 static bool
 IsValidUnitType(uint16_t unit)
 {
   if (unit > nsIDOMSVGLength::SVG_LENGTHTYPE_UNKNOWN &&
       unit <= nsIDOMSVGLength::SVG_LENGTHTYPE_PC)
@@ -340,53 +315,35 @@ nsSVGLength2::NewValueSpecifiedUnits(uin
   else {
     aSVGElement->AnimationNeedsResample();
   }
   aSVGElement->DidChangeLength(mAttrEnum, emptyOrOldValue);
   return NS_OK;
 }
 
 nsresult
-nsSVGLength2::ToDOMBaseVal(nsIDOMSVGLength **aResult, nsSVGElement *aSVGElement)
+nsSVGLength2::ToDOMBaseVal(DOMSVGLength **aResult, nsSVGElement *aSVGElement)
 {
-  nsRefPtr<DOMBaseVal> domBaseVal =
-    sBaseSVGLengthTearoffTable.GetTearoff(this);
-  if (!domBaseVal) {
-    domBaseVal = new DOMBaseVal(this, aSVGElement);
-    sBaseSVGLengthTearoffTable.AddTearoff(this, domBaseVal);
-  }
+  nsRefPtr<DOMSVGLength> domBaseVal =
+    DOMSVGLength::GetTearOff(this, aSVGElement, false);
 
   domBaseVal.forget(aResult);
   return NS_OK;
 }
 
-nsSVGLength2::DOMBaseVal::~DOMBaseVal()
+nsresult
+nsSVGLength2::ToDOMAnimVal(DOMSVGLength **aResult, nsSVGElement *aSVGElement)
 {
-  sBaseSVGLengthTearoffTable.RemoveTearoff(mVal);
-}
-
-nsresult
-nsSVGLength2::ToDOMAnimVal(nsIDOMSVGLength **aResult, nsSVGElement *aSVGElement)
-{
-  nsRefPtr<DOMAnimVal> domAnimVal =
-    sAnimSVGLengthTearoffTable.GetTearoff(this);
-  if (!domAnimVal) {
-    domAnimVal = new DOMAnimVal(this, aSVGElement);
-    sAnimSVGLengthTearoffTable.AddTearoff(this, domAnimVal);
-  }
+  nsRefPtr<DOMSVGLength> domAnimVal =
+    DOMSVGLength::GetTearOff(this, aSVGElement, true);
 
   domAnimVal.forget(aResult);
   return NS_OK;
 }
 
-nsSVGLength2::DOMAnimVal::~DOMAnimVal()
-{
-  sAnimSVGLengthTearoffTable.RemoveTearoff(mVal);
-}
-
 /* Implementation */
 
 nsresult
 nsSVGLength2::SetBaseValueString(const nsAString &aValueAsString,
                                  nsSVGElement *aSVGElement,
                                  bool aDoSetAttr)
 {
   float value;
--- a/content/svg/content/src/nsSVGLength2.h
+++ b/content/svg/content/src/nsSVGLength2.h
@@ -16,26 +16,28 @@
 #include "nsMathUtils.h"
 #include "nsSVGElement.h"
 #include "SVGContentUtils.h"
 
 class nsIFrame;
 class nsSMILValue;
 
 namespace mozilla {
+class DOMSVGLength;
 namespace dom {
 class SVGAnimatedLength;
 class SVGAnimationElement;
 class SVGSVGElement;
 }
 }
 
 class nsSVGLength2
 {
   friend class mozilla::dom::SVGAnimatedLength;
+  friend class mozilla::DOMSVGLength;
 public:
   void Init(uint8_t aCtxType = SVGContentUtils::XY,
             uint8_t aAttrEnum = 0xff,
             float aValue = 0,
             uint8_t aUnitType = nsIDOMSVGLength::SVG_LENGTHTYPE_NUMBER) {
     mAnimVal = mBaseVal = aValue;
     mSpecifiedUnitType = aUnitType;
     mAttrEnum = aAttrEnum;
@@ -126,128 +128,20 @@ private:
   void SetBaseValue(float aValue, nsSVGElement *aSVGElement, bool aDoSetAttr);
   void SetBaseValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement,
                                     bool aDoSetAttr);
   void SetAnimValue(float aValue, nsSVGElement *aSVGElement);
   void SetAnimValueInSpecifiedUnits(float aValue, nsSVGElement *aSVGElement);
   nsresult NewValueSpecifiedUnits(uint16_t aUnitType, float aValue,
                                   nsSVGElement *aSVGElement);
   nsresult ConvertToSpecifiedUnits(uint16_t aUnitType, nsSVGElement *aSVGElement);
-  nsresult ToDOMBaseVal(nsIDOMSVGLength **aResult, nsSVGElement* aSVGElement);
-  nsresult ToDOMAnimVal(nsIDOMSVGLength **aResult, nsSVGElement* aSVGElement);
+  nsresult ToDOMBaseVal(mozilla::DOMSVGLength **aResult, nsSVGElement* aSVGElement);
+  nsresult ToDOMAnimVal(mozilla::DOMSVGLength **aResult, nsSVGElement* aSVGElement);
 
 public:
-  struct DOMBaseVal : public nsIDOMSVGLength
-  {
-    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-    NS_DECL_CYCLE_COLLECTION_CLASS(DOMBaseVal)
-
-    DOMBaseVal(nsSVGLength2* aVal, nsSVGElement *aSVGElement)
-      : mVal(aVal), mSVGElement(aSVGElement) {}
-    virtual ~DOMBaseVal();
-    
-    nsSVGLength2* mVal; // kept alive because it belongs to mSVGElement
-    nsRefPtr<nsSVGElement> mSVGElement;
-    
-    NS_IMETHOD GetUnitType(uint16_t* aResult) MOZ_OVERRIDE
-      { *aResult = mVal->mSpecifiedUnitType; return NS_OK; }
-
-    NS_IMETHOD GetValue(float* aResult) MOZ_OVERRIDE
-      { *aResult = mVal->GetBaseValue(mSVGElement); return NS_OK; }
-    NS_IMETHOD SetValue(float aValue) MOZ_OVERRIDE
-      {
-        if (!NS_finite(aValue)) {
-          return NS_ERROR_ILLEGAL_VALUE;
-        }
-        mVal->SetBaseValue(aValue, mSVGElement, true);
-        return NS_OK;
-      }
-
-    NS_IMETHOD GetValueInSpecifiedUnits(float* aResult) MOZ_OVERRIDE
-      { *aResult = mVal->mBaseVal; return NS_OK; }
-    NS_IMETHOD SetValueInSpecifiedUnits(float aValue) MOZ_OVERRIDE
-      {
-        if (!NS_finite(aValue)) {
-          return NS_ERROR_ILLEGAL_VALUE;
-        }
-        mVal->SetBaseValueInSpecifiedUnits(aValue, mSVGElement, true);
-        return NS_OK;
-      }
-
-    NS_IMETHOD SetValueAsString(const nsAString& aValue) MOZ_OVERRIDE
-      { return mVal->SetBaseValueString(aValue, mSVGElement, true); }
-    NS_IMETHOD GetValueAsString(nsAString& aValue) MOZ_OVERRIDE
-      { mVal->GetBaseValueString(aValue); return NS_OK; }
-
-    NS_IMETHOD NewValueSpecifiedUnits(uint16_t unitType,
-                                      float valueInSpecifiedUnits) MOZ_OVERRIDE
-      {
-        return mVal->NewValueSpecifiedUnits(unitType, valueInSpecifiedUnits,
-                                            mSVGElement); }
-
-    NS_IMETHOD ConvertToSpecifiedUnits(uint16_t unitType) MOZ_OVERRIDE
-      { return mVal->ConvertToSpecifiedUnits(unitType, mSVGElement); }
-  };
-
-  struct DOMAnimVal : public nsIDOMSVGLength
-  {
-    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-    NS_DECL_CYCLE_COLLECTION_CLASS(DOMAnimVal)
-
-    DOMAnimVal(nsSVGLength2* aVal, nsSVGElement *aSVGElement)
-      : mVal(aVal), mSVGElement(aSVGElement) {}
-    virtual ~DOMAnimVal();
-    
-    nsSVGLength2* mVal; // kept alive because it belongs to mSVGElement
-    nsRefPtr<nsSVGElement> mSVGElement;
-    
-    // Script may have modified animation parameters or timeline -- DOM getters
-    // need to flush any resample requests to reflect these modifications.
-    NS_IMETHOD GetUnitType(uint16_t* aResult) MOZ_OVERRIDE
-    {
-      mSVGElement->FlushAnimations();
-      *aResult = mVal->mSpecifiedUnitType;
-      return NS_OK;
-    }
-
-    NS_IMETHOD GetValue(float* aResult) MOZ_OVERRIDE
-    {
-      mSVGElement->FlushAnimations();
-      *aResult = mVal->GetAnimValue(mSVGElement);
-      return NS_OK;
-    }
-    NS_IMETHOD SetValue(float aValue) MOZ_OVERRIDE
-      { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
-
-    NS_IMETHOD GetValueInSpecifiedUnits(float* aResult) MOZ_OVERRIDE
-    {
-      mSVGElement->FlushAnimations();
-      *aResult = mVal->mAnimVal;
-      return NS_OK;
-    }
-    NS_IMETHOD SetValueInSpecifiedUnits(float aValue) MOZ_OVERRIDE
-      { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
-
-    NS_IMETHOD SetValueAsString(const nsAString& aValue) MOZ_OVERRIDE
-      { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
-    NS_IMETHOD GetValueAsString(nsAString& aValue) MOZ_OVERRIDE
-    {
-      mSVGElement->FlushAnimations();
-      mVal->GetAnimValueString(aValue);
-      return NS_OK;
-    }
-
-    NS_IMETHOD NewValueSpecifiedUnits(uint16_t unitType,
-                                      float valueInSpecifiedUnits) MOZ_OVERRIDE
-      { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
-
-    NS_IMETHOD ConvertToSpecifiedUnits(uint16_t unitType) MOZ_OVERRIDE
-      { return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; }
-  };
-
   struct SMILLength : public nsISMILAttr
   {
   public:
     SMILLength(nsSVGLength2* aVal, nsSVGElement *aSVGElement)
       : mVal(aVal), mSVGElement(aSVGElement) {}
 
     // These will stay alive because a nsISMILAttr only lives as long
     // as the Compositing step, and DOM elements don't get a chance to
new file mode 100644
--- /dev/null
+++ b/content/xml/document/crashtests/994740-1.xhtml
@@ -0,0 +1,15 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+<![CDATA[
+
+function boom()
+{
+    document.createElement("head").innerHTML = "<";
+}
+
+]]>
+</script></head>
+
+<body onload="boom();"></body>
+</html>
--- a/content/xml/document/crashtests/crashtests.list
+++ b/content/xml/document/crashtests/crashtests.list
@@ -1,7 +1,8 @@
 load 136896-1.xml
 load 185285-1.xml
 load 382636-1.xml
 load 382636-2.svg
 load 382636-3.xhtml
 load 382636-4.xul # Throws (bug 455856)
 load 431703-1.xhtml
+load 994740-1.xhtml
--- a/content/xml/document/src/nsXMLFragmentContentSink.cpp
+++ b/content/xml/document/src/nsXMLFragmentContentSink.cpp
@@ -357,16 +357,18 @@ nsXMLFragmentContentSink::FinishFragment
   *aFragment = nullptr;
   mTargetDocument = nullptr;
   mNodeInfoManager = nullptr;
   mScriptLoader = nullptr;
   mCSSLoader = nullptr;
   mContentStack.Clear();
   mDocumentURI = nullptr;
   mDocShell = nullptr;
+  mDocElement = nullptr;
+  mCurrentHead = nullptr;
   if (mParseError) {
     //XXX PARSE_ERR from DOM3 Load and Save would be more appropriate
     mRoot = nullptr;
     mParseError = false;
     return NS_ERROR_DOM_SYNTAX_ERR;
   } else if (mRoot) {
     nsresult rv = CallQueryInterface(mRoot, aFragment);
     mRoot = nullptr;
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -2331,16 +2331,86 @@ onInstallSuccessAck: function onInstallS
     // For packaged apps, keep the update manifest distinct from the app manifest.
     let manifestName = aIsPackage ? "update.webapp" : "manifest.webapp";
 
     let dir = this._getAppDir(aId).path;
     let manFile = OS.Path.join(dir, manifestName);
     this._writeFile(manFile, JSON.stringify(aJsonManifest));
   },
 
+  // Add an app that is already installed to the registry.
+  addInstalledApp: Task.async(function*(aApp, aManifest, aUpdateManifest) {
+    if (this.getAppLocalIdByManifestURL(aApp.manifestURL) !=
+        Ci.nsIScriptSecurityManager.NO_APP_ID) {
+      return;
+    }
+
+    let app = AppsUtils.cloneAppObject(aApp);
+
+    if (!AppsUtils.checkManifest(aManifest, app) ||
+        (aUpdateManifest && !AppsUtils.checkManifest(aUpdateManifest, app))) {
+      return;
+    }
+
+    app.name = aManifest.name;
+
+    app.csp = aManifest.csp || "";
+
+    app.appStatus = AppsUtils.getAppManifestStatus(aManifest);
+
+    app.removable = true;
+
+    // Reuse the app ID if the scheme is "app".
+    let uri = Services.io.newURI(app.origin, null, null);
+    if (uri.scheme == "app") {
+      app.id = uri.host;
+    } else {
+      app.id = this.makeAppId();
+    }
+
+    app.localId = this._nextLocalId();
+
+    app.basePath = OS.Path.dirname(this.appsFile);
+
+    app.progress = 0.0;
+    app.installState = "installed";
+    app.downloadAvailable = false;
+    app.downloading = false;
+    app.readyToApplyDownload = false;
+
+    if (aUpdateManifest && aUpdateManifest.size) {
+      app.downloadSize = aUpdateManifest.size;
+    }
+
+    app.manifestHash = AppsUtils.computeHash(JSON.stringify(aUpdateManifest ||
+                                                            aManifest));
+
+    let zipFile = WebappOSUtils.getPackagePath(app);
+    app.packageHash = yield this._computeFileHash(zipFile);
+
+    app.role = aManifest.role || "";
+
+    app.redirects = this.sanitizeRedirects(aManifest.redirects);
+
+    this.webapps[app.id] = app;
+
+    // Store the manifest in the manifest cache, so we don't need to re-read it
+    this._manifestCache[app.id] = app.manifest;
+
+    // Store the manifest and the updateManifest.
+    this._writeManifestFile(app.id, false, aManifest);
+    if (aUpdateManifest) {
+      this._writeManifestFile(app.id, true, aUpdateManifest);
+    }
+
+    this._saveApps().then(() => {
+      this.broadcastMessage("Webapps:AddApp", { id: app.id, app: app });
+    });
+  }),
+
   confirmInstall: function(aData, aProfileDir, aInstallSuccessCallback) {
     debug("confirmInstall");
 
     let origin = Services.io.newURI(aData.app.origin, null, null);
     let id = this._appIdForManifestURL(aData.app.manifestURL);
     let manifestURL = origin.resolve(aData.app.manifestURL);
     let localId = this.getAppLocalIdByManifestURL(manifestURL);
 
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -79,32 +79,32 @@ nsresult
 AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType,
                                 nsIAudioChannelAgentCallback *aCallback,
                                 bool aUseWeakRef, bool aWithVideo)
 {
   // We need the window only for IPC.
   MOZ_ASSERT(aWindow || XRE_GetProcessType() == GeckoProcessType_Default);
 
   // We syncd the enum of channel type between nsIAudioChannelAgent.idl and
-  // AudioChannelCommon.h the same.
-  static_assert(static_cast<AudioChannelType>(AUDIO_AGENT_CHANNEL_NORMAL) ==
-                AUDIO_CHANNEL_NORMAL &&
-                static_cast<AudioChannelType>(AUDIO_AGENT_CHANNEL_CONTENT) ==
-                AUDIO_CHANNEL_CONTENT &&
-                static_cast<AudioChannelType>(AUDIO_AGENT_CHANNEL_NOTIFICATION) ==
-                AUDIO_CHANNEL_NOTIFICATION &&
-                static_cast<AudioChannelType>(AUDIO_AGENT_CHANNEL_ALARM) ==
-                AUDIO_CHANNEL_ALARM &&
-                static_cast<AudioChannelType>(AUDIO_AGENT_CHANNEL_TELEPHONY) ==
-                AUDIO_CHANNEL_TELEPHONY &&
-                static_cast<AudioChannelType>(AUDIO_AGENT_CHANNEL_RINGER) ==
-                AUDIO_CHANNEL_RINGER &&
-                static_cast<AudioChannelType>(AUDIO_AGENT_CHANNEL_PUBLICNOTIFICATION) ==
-                AUDIO_CHANNEL_PUBLICNOTIFICATION,
-                "Enum of channel on nsIAudioChannelAgent.idl should be the same with AudioChannelCommon.h");
+  // AudioChannelBinding.h the same.
+  MOZ_ASSERT(static_cast<AudioChannel>(AUDIO_AGENT_CHANNEL_NORMAL) ==
+             AudioChannel::Normal &&
+             static_cast<AudioChannel>(AUDIO_AGENT_CHANNEL_CONTENT) ==
+             AudioChannel::Content &&
+             static_cast<AudioChannel>(AUDIO_AGENT_CHANNEL_NOTIFICATION) ==
+             AudioChannel::Notification &&
+             static_cast<AudioChannel>(AUDIO_AGENT_CHANNEL_ALARM) ==
+             AudioChannel::Alarm &&
+             static_cast<AudioChannel>(AUDIO_AGENT_CHANNEL_TELEPHONY) ==
+             AudioChannel::Telephony &&
+             static_cast<AudioChannel>(AUDIO_AGENT_CHANNEL_RINGER) ==
+             AudioChannel::Ringer &&
+             static_cast<AudioChannel>(AUDIO_AGENT_CHANNEL_PUBLICNOTIFICATION) ==
+             AudioChannel::Publicnotification,
+             "Enum of channel on nsIAudioChannelAgent.idl should be the same with AudioChannelBinding.h");
 
   if (mAudioChannelType != AUDIO_AGENT_CHANNEL_ERROR ||
       aChannelType > AUDIO_AGENT_CHANNEL_PUBLICNOTIFICATION ||
       aChannelType < AUDIO_AGENT_CHANNEL_NORMAL) {
     return NS_ERROR_FAILURE;
   }
 
   mWindow = aWindow;
@@ -126,17 +126,17 @@ NS_IMETHODIMP AudioChannelAgent::StartPl
 {
   AudioChannelService *service = AudioChannelService::GetAudioChannelService();
   if (mAudioChannelType == AUDIO_AGENT_CHANNEL_ERROR ||
       service == nullptr || mIsRegToService) {
     return NS_ERROR_FAILURE;
   }
 
   service->RegisterAudioChannelAgent(this,
-    static_cast<AudioChannelType>(mAudioChannelType), mWithVideo);
+    static_cast<AudioChannel>(mAudioChannelType), mWithVideo);
   *_retval = service->GetState(this, !mVisible);
   mIsRegToService = true;
   return NS_OK;
 }
 
 /* void stopPlaying (); */
 NS_IMETHODIMP AudioChannelAgent::StopPlaying(void)
 {
--- a/dom/audiochannel/AudioChannelCommon.h
+++ b/dom/audiochannel/AudioChannelCommon.h
@@ -5,30 +5,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_audiochannelcommon_h__
 #define mozilla_dom_audiochannelcommon_h__
 
 namespace mozilla {
 namespace dom {
 
-// The audio channel. Read the nsIHTMLMediaElement.idl for a description
-// about this attribute.
-enum AudioChannelType {
-  AUDIO_CHANNEL_DEFAULT = -1,
-  AUDIO_CHANNEL_NORMAL = 0,
-  AUDIO_CHANNEL_CONTENT,
-  AUDIO_CHANNEL_NOTIFICATION,
-  AUDIO_CHANNEL_ALARM,
-  AUDIO_CHANNEL_TELEPHONY,
-  AUDIO_CHANNEL_RINGER,
-  AUDIO_CHANNEL_PUBLICNOTIFICATION,
-  AUDIO_CHANNEL_LAST
-};
-
 enum AudioChannelState {
   AUDIO_CHANNEL_STATE_NORMAL = 0,
   AUDIO_CHANNEL_STATE_MUTED,
   AUDIO_CHANNEL_STATE_FADED,
   AUDIO_CHANNEL_STATE_LAST
 };
 
 } // namespace dom
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -32,16 +32,28 @@
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::hal;
 
 StaticRefPtr<AudioChannelService> gAudioChannelService;
 
+// Mappings from 'mozaudiochannel' attribute strings to an enumeration.
+static const nsAttrValue::EnumTable kMozAudioChannelAttributeTable[] = {
+  { "normal",             (int16_t)AudioChannel::Normal },
+  { "content",            (int16_t)AudioChannel::Content },
+  { "notification",       (int16_t)AudioChannel::Notification },
+  { "alarm",              (int16_t)AudioChannel::Alarm },
+  { "telephony",          (int16_t)AudioChannel::Telephony },
+  { "ringer",             (int16_t)AudioChannel::Ringer },
+  { "publicnotification", (int16_t)AudioChannel::Publicnotification },
+  { nullptr }
+};
+
 // static
 AudioChannelService*
 AudioChannelService::GetAudioChannelService()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     return AudioChannelServiceChild::GetAudioChannelService();
@@ -70,18 +82,18 @@ AudioChannelService::Shutdown()
   if (gAudioChannelService) {
     gAudioChannelService = nullptr;
   }
 }
 
 NS_IMPL_ISUPPORTS2(AudioChannelService, nsIObserver, nsITimerCallback)
 
 AudioChannelService::AudioChannelService()
-: mCurrentHigherChannel(AUDIO_CHANNEL_LAST)
-, mCurrentVisibleHigherChannel(AUDIO_CHANNEL_LAST)
+: mCurrentHigherChannel(INT32_MAX)
+, mCurrentVisibleHigherChannel(INT32_MAX)
 , mPlayableHiddenContentChildID(CONTENT_PROCESS_ID_UNKNOWN)
 , mDisabled(false)
 , mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN)
 {
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, "ipc:content-shutdown", false);
@@ -95,62 +107,62 @@ AudioChannelService::AudioChannelService
 }
 
 AudioChannelService::~AudioChannelService()
 {
 }
 
 void
 AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                               AudioChannelType aType,
+                                               AudioChannel aChannel,
                                                bool aWithVideo)
 {
   if (mDisabled) {
     return;
   }
 
-  MOZ_ASSERT(aType != AUDIO_CHANNEL_DEFAULT);
-
-  AudioChannelAgentData* data = new AudioChannelAgentData(aType,
+  AudioChannelAgentData* data = new AudioChannelAgentData(aChannel,
                                 true /* aElementHidden */,
                                 AUDIO_CHANNEL_STATE_MUTED /* aState */,
                                 aWithVideo);
   mAgents.Put(aAgent, data);
-  RegisterType(aType, CONTENT_PROCESS_ID_MAIN, aWithVideo);
+  RegisterType(aChannel, CONTENT_PROCESS_ID_MAIN, aWithVideo);
 
   // If this is the first agent for this window, we must notify the observers.
   uint32_t count = CountWindow(aAgent->Window());
   if (count == 1) {
     nsCOMPtr<nsIObserverService> observerService =
       services::GetObserverService();
     if (observerService) {
       observerService->NotifyObservers(ToSupports(aAgent->Window()),
                                        "media-playback",
                                        NS_LITERAL_STRING("active").get());
     }
   }
 }
 
 void
-AudioChannelService::RegisterType(AudioChannelType aType, uint64_t aChildID, bool aWithVideo)
+AudioChannelService::RegisterType(AudioChannel aChannel, uint64_t aChildID,
+                                  bool aWithVideo)
 {
   if (mDisabled) {
     return;
   }
 
-  AudioChannelInternalType type = GetInternalType(aType, true);
+  AudioChannelInternalType type = GetInternalType(aChannel, true);
   mChannelCounters[type].AppendElement(aChildID);
 
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     // Since there is another telephony registered, we can unregister old one
     // immediately.
-    if (mDeferTelChannelTimer && aType == AUDIO_CHANNEL_TELEPHONY) {
+    if (mDeferTelChannelTimer && aChannel == AudioChannel::Telephony) {
       mDeferTelChannelTimer->Cancel();
       mDeferTelChannelTimer = nullptr;
-      UnregisterTypeInternal(aType, mTimerElementHidden, mTimerChildID, false);
+      UnregisterTypeInternal(aChannel, mTimerElementHidden, mTimerChildID,
+                             false);
     }
 
     if (aWithVideo) {
       mWithVideoChildIDs.AppendElement(aChildID);
     }
 
     // No hidden content channel can be playable if there is a content channel
     // in foreground (bug 855208), nor if there is a normal channel with video
@@ -181,17 +193,17 @@ AudioChannelService::UnregisterAudioChan
   if (mDisabled) {
     return;
   }
 
   nsAutoPtr<AudioChannelAgentData> data;
   mAgents.RemoveAndForget(aAgent, data);
 
   if (data) {
-    UnregisterType(data->mType, data->mElementHidden,
+    UnregisterType(data->mChannel, data->mElementHidden,
                    CONTENT_PROCESS_ID_MAIN, data->mWithVideo);
   }
 #ifdef MOZ_WIDGET_GONK
   bool active = AnyAudioChannelIsActive();
   for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
     mSpeakerManager[i]->SetAudioChannelActive(active);
   }
 #endif
@@ -205,84 +217,84 @@ AudioChannelService::UnregisterAudioChan
       observerService->NotifyObservers(ToSupports(aAgent->Window()),
                                        "media-playback",
                                        NS_LITERAL_STRING("inactive").get());
     }
   }
 }
 
 void
-AudioChannelService::UnregisterType(AudioChannelType aType,
+AudioChannelService::UnregisterType(AudioChannel aChannel,
                                     bool aElementHidden,
                                     uint64_t aChildID,
                                     bool aWithVideo)
 {
   if (mDisabled) {
     return;
   }
 
   // There are two reasons to defer the decrease of telephony channel.
   // 1. User can have time to remove device from his ear before music resuming.
   // 2. Give BT SCO to be disconnected before starting to connect A2DP.
   if (XRE_GetProcessType() == GeckoProcessType_Default &&
-      aType == AUDIO_CHANNEL_TELEPHONY &&
+      aChannel == AudioChannel::Telephony &&
       (mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN].Length() +
        mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].Length()) == 1) {
     mTimerElementHidden = aElementHidden;
     mTimerChildID = aChildID;
     mDeferTelChannelTimer = do_CreateInstance("@mozilla.org/timer;1");
     mDeferTelChannelTimer->InitWithCallback(this, 1500, nsITimer::TYPE_ONE_SHOT);
     return;
   }
 
-  UnregisterTypeInternal(aType, aElementHidden, aChildID, aWithVideo);
+  UnregisterTypeInternal(aChannel, aElementHidden, aChildID, aWithVideo);
 }
 
 void
-AudioChannelService::UnregisterTypeInternal(AudioChannelType aType,
+AudioChannelService::UnregisterTypeInternal(AudioChannel aChannel,
                                             bool aElementHidden,
                                             uint64_t aChildID,
                                             bool aWithVideo)
 {
   // The array may contain multiple occurrence of this appId but
   // this should remove only the first one.
-  AudioChannelInternalType type = GetInternalType(aType, aElementHidden);
+  AudioChannelInternalType type = GetInternalType(aChannel, aElementHidden);
   MOZ_ASSERT(mChannelCounters[type].Contains(aChildID));
   mChannelCounters[type].RemoveElement(aChildID);
 
   // In order to avoid race conditions, it's safer to notify any existing
   // agent any time a new one is registered.
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     // No hidden content channel is playable if the original playable hidden
     // process does not need to play audio from background anymore.
-    if (aType == AUDIO_CHANNEL_CONTENT &&
+    if (aChannel == AudioChannel::Content &&
         mPlayableHiddenContentChildID == aChildID &&
         !mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID)) {
       mPlayableHiddenContentChildID = CONTENT_PROCESS_ID_UNKNOWN;
     }
 
     if (aWithVideo) {
       MOZ_ASSERT(mWithVideoChildIDs.Contains(aChildID));
       mWithVideoChildIDs.RemoveElement(aChildID);
     }
 
     SendAudioChannelChangedNotification(aChildID);
     Notify();
   }
 }
 
 void
-AudioChannelService::UpdateChannelType(AudioChannelType aType,
+AudioChannelService::UpdateChannelType(AudioChannel aChannel,
                                        uint64_t aChildID,
                                        bool aElementHidden,
                                        bool aElementWasHidden)
 {
   // Calculate the new and old internal type and update the hashtable if needed.
-  AudioChannelInternalType newType = GetInternalType(aType, aElementHidden);
-  AudioChannelInternalType oldType = GetInternalType(aType, aElementWasHidden);
+  AudioChannelInternalType newType = GetInternalType(aChannel, aElementHidden);
+  AudioChannelInternalType oldType = GetInternalType(aChannel, aElementWasHidden);
 
   if (newType != oldType) {
     mChannelCounters[newType].AppendElement(aChildID);
     MOZ_ASSERT(mChannelCounters[oldType].Contains(aChildID));
     mChannelCounters[oldType].RemoveElement(aChildID);
   }
 
   // No hidden content channel can be playable if there is a content channel
@@ -310,34 +322,36 @@ AudioChannelService::GetState(AudioChann
   if (!mAgents.Get(aAgent, &data)) {
     return AUDIO_CHANNEL_STATE_MUTED;
   }
 
   bool oldElementHidden = data->mElementHidden;
   // Update visibility.
   data->mElementHidden = aElementHidden;
 
-  data->mState = GetStateInternal(data->mType, CONTENT_PROCESS_ID_MAIN,
+  data->mState = GetStateInternal(data->mChannel, CONTENT_PROCESS_ID_MAIN,
                                 aElementHidden, oldElementHidden);
   return data->mState;
 }
 
 AudioChannelState
-AudioChannelService::GetStateInternal(AudioChannelType aType, uint64_t aChildID,
-                                      bool aElementHidden, bool aElementWasHidden)
+AudioChannelService::GetStateInternal(AudioChannel aChannel, uint64_t aChildID,
+                                      bool aElementHidden,
+                                      bool aElementWasHidden)
 {
-  UpdateChannelType(aType, aChildID, aElementHidden, aElementWasHidden);
+  UpdateChannelType(aChannel, aChildID, aElementHidden, aElementWasHidden);
 
   // Calculating the new and old type and update the hashtable if needed.
-  AudioChannelInternalType newType = GetInternalType(aType, aElementHidden);
-  AudioChannelInternalType oldType = GetInternalType(aType, aElementWasHidden);
+  AudioChannelInternalType newType = GetInternalType(aChannel, aElementHidden);
+  AudioChannelInternalType oldType = GetInternalType(aChannel,
+                                                     aElementWasHidden);
 
   if (newType != oldType &&
-      (aType == AUDIO_CHANNEL_CONTENT ||
-       (aType == AUDIO_CHANNEL_NORMAL &&
+      (aChannel == AudioChannel::Content ||
+       (aChannel == AudioChannel::Normal &&
         mWithVideoChildIDs.Contains(aChildID)))) {
     Notify();
   }
 
   SendAudioChannelChangedNotification(aChildID);
 
   // Let play any visible audio channel.
   if (!aElementHidden) {
@@ -422,40 +436,48 @@ bool
 AudioChannelService::ProcessContentOrNormalChannelIsActive(uint64_t aChildID)
 {
   return mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].Contains(aChildID) ||
          mChannelCounters[AUDIO_CHANNEL_INT_CONTENT_HIDDEN].Contains(aChildID) ||
          mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].Contains(aChildID);
 }
 
 void
-AudioChannelService::SetDefaultVolumeControlChannel(AudioChannelType aType,
+AudioChannelService::SetDefaultVolumeControlChannel(int32_t aChannel,
                                                     bool aHidden)
 {
-  SetDefaultVolumeControlChannelInternal(aType, aHidden, CONTENT_PROCESS_ID_MAIN);
+  SetDefaultVolumeControlChannelInternal(aChannel, aHidden,
+                                         CONTENT_PROCESS_ID_MAIN);
 }
 
 void
-AudioChannelService::SetDefaultVolumeControlChannelInternal(
-  AudioChannelType aType, bool aHidden, uint64_t aChildID)
+AudioChannelService::SetDefaultVolumeControlChannelInternal(int32_t aChannel,
+                                                            bool aHidden,
+                                                            uint64_t aChildID)
 {
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     return;
   }
 
   // If this child is in the background and mDefChannelChildID is set to
   // others then it means other child in the foreground already set it's
   // own default channel already.
   if (!aHidden && mDefChannelChildID != aChildID) {
     return;
   }
 
   mDefChannelChildID = aChildID;
   nsString channelName;
-  channelName.AssignASCII(ChannelName(aType));
+
+  if (aChannel == -1) {
+    channelName.AssignASCII("unknown");
+  } else {
+    GetAudioChannelString(static_cast<AudioChannel>(aChannel), channelName);
+  }
+
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(nullptr, "default-volume-channel-changed",
                          channelName.get());
   }
 }
 
 void
@@ -470,87 +492,94 @@ AudioChannelService::SendAudioChannelCha
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(static_cast<nsIWritablePropertyBag*>(props),
                          "audio-channel-process-changed", nullptr);
   }
 
   // Calculating the most important active channel.
-  AudioChannelType higher = AUDIO_CHANNEL_DEFAULT;
+  int32_t higher = -1;
 
   // Top-Down in the hierarchy for visible elements
   if (!mChannelCounters[AUDIO_CHANNEL_INT_PUBLICNOTIFICATION].IsEmpty()) {
-    higher = AUDIO_CHANNEL_PUBLICNOTIFICATION;
+    higher = static_cast<int32_t>(AudioChannel::Publicnotification);
   }
 
   else if (!mChannelCounters[AUDIO_CHANNEL_INT_RINGER].IsEmpty()) {
-    higher = AUDIO_CHANNEL_RINGER;
+    higher = static_cast<int32_t>(AudioChannel::Ringer);
   }
 
   else if (!mChannelCounters[AUDIO_CHANNEL_INT_TELEPHONY].IsEmpty()) {
-    higher = AUDIO_CHANNEL_TELEPHONY;
+    higher = static_cast<int32_t>(AudioChannel::Telephony);
   }
 
   else if (!mChannelCounters[AUDIO_CHANNEL_INT_ALARM].IsEmpty()) {
-    higher = AUDIO_CHANNEL_ALARM;
+    higher = static_cast<int32_t>(AudioChannel::Alarm);
   }
 
   else if (!mChannelCounters[AUDIO_CHANNEL_INT_NOTIFICATION].IsEmpty()) {
-    higher = AUDIO_CHANNEL_NOTIFICATION;
+    higher = static_cast<int32_t>(AudioChannel::Notification);
   }
 
   else if (!mChannelCounters[AUDIO_CHANNEL_INT_CONTENT].IsEmpty()) {
-    higher = AUDIO_CHANNEL_CONTENT;
+    higher = static_cast<int32_t>(AudioChannel::Content);
   }
 
   else if (!mChannelCounters[AUDIO_CHANNEL_INT_NORMAL].IsEmpty()) {
-    higher = AUDIO_CHANNEL_NORMAL;
+    higher = static_cast<int32_t>(AudioChannel::Normal);
   }
 
-  AudioChannelType visibleHigher = higher;
+  int32_t visibleHigher = higher;
 
   // Top-Down in the hierarchy for non-visible elements
   // And we can ignore normal channel because it can't play in the background.
-  for (int i = AUDIO_CHANNEL_LAST - 1;
-       i > higher && i > AUDIO_CHANNEL_NORMAL; i--) {
-    if (i == AUDIO_CHANNEL_CONTENT &&
+  int32_t index;
+  for (index = 0; kMozAudioChannelAttributeTable[index].tag; ++index);
+
+  for (--index;
+       kMozAudioChannelAttributeTable[index].value > higher &&
+       kMozAudioChannelAttributeTable[index].value > (int16_t)AudioChannel::Normal;
+       --index) {
+    if (kMozAudioChannelAttributeTable[index].value == (int16_t)AudioChannel::Content &&
       mPlayableHiddenContentChildID != CONTENT_PROCESS_ID_UNKNOWN) {
-      higher = static_cast<AudioChannelType>(i);
+      higher = kMozAudioChannelAttributeTable[index].value;
     }
 
     // Each channel type will be split to fg and bg for recording the state,
     // so here need to do a translation.
-    if (!mChannelCounters[i * 2 + 1].IsEmpty()) {
-      higher = static_cast<AudioChannelType>(i);
+    if (!mChannelCounters[index * 2 + 1].IsEmpty()) {
+      higher = kMozAudioChannelAttributeTable[index].value;
       break;
     }
   }
 
   if (higher != mCurrentHigherChannel) {
     mCurrentHigherChannel = higher;
 
     nsString channelName;
-    if (mCurrentHigherChannel != AUDIO_CHANNEL_DEFAULT) {
-      channelName.AssignASCII(ChannelName(mCurrentHigherChannel));
+    if (mCurrentHigherChannel != -1) {
+      GetAudioChannelString(static_cast<AudioChannel>(mCurrentHigherChannel),
+                            channelName);
     } else {
       channelName.AssignLiteral("none");
     }
 
     if (obs) {
       obs->NotifyObservers(nullptr, "audio-channel-changed", channelName.get());
     }
   }
 
   if (visibleHigher != mCurrentVisibleHigherChannel) {
     mCurrentVisibleHigherChannel = visibleHigher;
 
     nsString channelName;
-    if (mCurrentVisibleHigherChannel != AUDIO_CHANNEL_DEFAULT) {
-      channelName.AssignASCII(ChannelName(mCurrentVisibleHigherChannel));
+    if (mCurrentVisibleHigherChannel != -1) {
+      GetAudioChannelString(static_cast<AudioChannel>(mCurrentVisibleHigherChannel),
+                            channelName);
     } else {
       channelName.AssignLiteral("none");
     }
 
     if (obs) {
       obs->NotifyObservers(nullptr, "visible-audio-channel-changed", channelName.get());
     }
   }
@@ -579,17 +608,18 @@ AudioChannelService::Notify()
   for (uint32_t i = 0; i < children.Length(); i++) {
     unused << children[i]->SendAudioChannelNotify();
   }
 }
 
 NS_IMETHODIMP
 AudioChannelService::Notify(nsITimer* aTimer)
 {
-  UnregisterTypeInternal(AUDIO_CHANNEL_TELEPHONY, mTimerElementHidden, mTimerChildID, false);
+  UnregisterTypeInternal(AudioChannel::Telephony, mTimerElementHidden,
+                         mTimerChildID, false);
   mDeferTelChannelTimer = nullptr;
   return NS_OK;
 }
 
 bool
 AudioChannelService::AnyAudioChannelIsActive()
 {
   for (int i = AUDIO_CHANNEL_INT_LAST - 1;
@@ -615,44 +645,16 @@ AudioChannelService::ChannelsActiveWithH
     if (!mChannelCounters[i].IsEmpty()) {
       return true;
     }
   }
 
   return false;
 }
 
-const char*
-AudioChannelService::ChannelName(AudioChannelType aType)
-{
-  static struct {
-    int32_t type;
-    const char* value;
-  } ChannelNameTable[] = {
-    { AUDIO_CHANNEL_NORMAL,             "normal" },
-    { AUDIO_CHANNEL_CONTENT,            "content" },
-    { AUDIO_CHANNEL_NOTIFICATION,       "notification" },
-    { AUDIO_CHANNEL_ALARM,              "alarm" },
-    { AUDIO_CHANNEL_TELEPHONY,          "telephony" },
-    { AUDIO_CHANNEL_RINGER,             "ringer" },
-    { AUDIO_CHANNEL_PUBLICNOTIFICATION, "publicnotification" },
-    { -1,                               "unknown" }
-  };
-
-  for (int i = AUDIO_CHANNEL_NORMAL; ; ++i) {
-    if (ChannelNameTable[i].type == aType ||
-        ChannelNameTable[i].type == -1) {
-      return ChannelNameTable[i].value;
-    }
-  }
-
-  NS_NOTREACHED("Execution should not reach here!");
-  return nullptr;
-}
-
 NS_IMETHODIMP
 AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
 {
   if (!strcmp(aTopic, "xpcom-shutdown")) {
     mDisabled = true;
   }
 
   if (!strcmp(aTopic, "ipc:content-shutdown")) {
@@ -688,18 +690,17 @@ AudioChannelService::Observe(nsISupports
 
       // We don't have to remove the agents from the mAgents hashtable because if
       // that table contains only agents running on the same process.
 
       SendAudioChannelChangedNotification(childID);
       Notify();
 
       if (mDefChannelChildID == childID) {
-        SetDefaultVolumeControlChannelInternal(AUDIO_CHANNEL_DEFAULT,
-                                               false, childID);
+        SetDefaultVolumeControlChannelInternal(-1, false, childID);
         mDefChannelChildID = CONTENT_PROCESS_ID_UNKNOWN;
       }
     } else {
       NS_WARNING("ipc:content-shutdown message without childID property");
     }
   }
 #ifdef MOZ_WIDGET_GONK
   // To process the volume control on each audio channel according to
@@ -734,83 +735,82 @@ AudioChannelService::Observe(nsISupports
       return NS_OK;
     }
 
     nsCOMPtr<nsIAudioManager> audioManager = do_GetService(NS_AUDIOMANAGER_CONTRACTID);
     NS_ENSURE_TRUE(audioManager, NS_OK);
 
     int32_t index = value.toInt32();
     if (keyStr.EqualsLiteral("audio.volume.content")) {
-      audioManager->SetAudioChannelVolume(AUDIO_CHANNEL_CONTENT, index);
+      audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Content, index);
     } else if (keyStr.EqualsLiteral("audio.volume.notification")) {
-      audioManager->SetAudioChannelVolume(AUDIO_CHANNEL_NOTIFICATION, index);
+      audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Notification, index);
     } else if (keyStr.EqualsLiteral("audio.volume.alarm")) {
-      audioManager->SetAudioChannelVolume(AUDIO_CHANNEL_ALARM, index);
+      audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Alarm, index);
     } else if (keyStr.EqualsLiteral("audio.volume.telephony")) {
-      audioManager->SetAudioChannelVolume(AUDIO_CHANNEL_TELEPHONY, index);
+      audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Telephony, index);
     } else if (!keyStr.EqualsLiteral("audio.volume.bt_sco")) {
       // bt_sco is not a valid audio channel so we manipulate it in
       // AudioManager.cpp. And the others should not be used.
       // We didn't use MOZ_ASSUME_UNREACHABLE here because any web content who
       // has permission of mozSettings can set any names then it can be easy to
       // crash the B2G.
       NS_WARNING("unexpected audio channel for volume control");
     }
   }
 #endif
 
   return NS_OK;
 }
 
 AudioChannelService::AudioChannelInternalType
-AudioChannelService::GetInternalType(AudioChannelType aType,
+AudioChannelService::GetInternalType(AudioChannel aChannel,
                                      bool aElementHidden)
 {
-  switch (aType) {
-    case AUDIO_CHANNEL_NORMAL:
+  switch (aChannel) {
+    case AudioChannel::Normal:
       return aElementHidden
                ? AUDIO_CHANNEL_INT_NORMAL_HIDDEN
                : AUDIO_CHANNEL_INT_NORMAL;
 
-    case AUDIO_CHANNEL_CONTENT:
+    case AudioChannel::Content:
       return aElementHidden
                ? AUDIO_CHANNEL_INT_CONTENT_HIDDEN
                : AUDIO_CHANNEL_INT_CONTENT;
 
-    case AUDIO_CHANNEL_NOTIFICATION:
+    case AudioChannel::Notification:
       return aElementHidden
                ? AUDIO_CHANNEL_INT_NOTIFICATION_HIDDEN
                : AUDIO_CHANNEL_INT_NOTIFICATION;
 
-    case AUDIO_CHANNEL_ALARM:
+    case AudioChannel::Alarm:
       return aElementHidden
                ? AUDIO_CHANNEL_INT_ALARM_HIDDEN
                : AUDIO_CHANNEL_INT_ALARM;
 
-    case AUDIO_CHANNEL_TELEPHONY:
+    case AudioChannel::Telephony:
       return aElementHidden
                ? AUDIO_CHANNEL_INT_TELEPHONY_HIDDEN
                : AUDIO_CHANNEL_INT_TELEPHONY;
 
-    case AUDIO_CHANNEL_RINGER:
+    case AudioChannel::Ringer:
       return aElementHidden
                ? AUDIO_CHANNEL_INT_RINGER_HIDDEN
                : AUDIO_CHANNEL_INT_RINGER;
 
-    case AUDIO_CHANNEL_PUBLICNOTIFICATION:
+    case AudioChannel::Publicnotification:
       return aElementHidden
                ? AUDIO_CHANNEL_INT_PUBLICNOTIFICATION_HIDDEN
                : AUDIO_CHANNEL_INT_PUBLICNOTIFICATION;
 
-    case AUDIO_CHANNEL_LAST:
     default:
       break;
   }
 
-  MOZ_CRASH("unexpected audio channel type");
+  MOZ_CRASH("unexpected audio channel");
 }
 
 struct RefreshAgentsVolumeData
 {
   RefreshAgentsVolumeData(nsPIDOMWindow* aWindow)
     : mWindow(aWindow)
   {}
 
@@ -878,56 +878,73 @@ AudioChannelService::CountWindowEnumerat
 uint32_t
 AudioChannelService::CountWindow(nsIDOMWindow* aWindow)
 {
   CountWindowData data(aWindow);
   mAgents.EnumerateRead(CountWindowEnumerator, &data);
   return data.mCount;
 }
 
-// Mappings from 'mozaudiochannel' attribute strings to an enumeration.
-static const struct AudioChannelTable
+/* static */ const nsAttrValue::EnumTable*
+AudioChannelService::GetAudioChannelTable()
+{
+  return kMozAudioChannelAttributeTable;
+}
+
+/* static */ AudioChannel
+AudioChannelService::GetAudioChannel(const nsAString& aChannel)
 {
-  const char* string;
-  AudioChannel value;
-} kMozAudioChannelAttributeTable[] = {
-  { "normal",             AudioChannel::Normal },
-  { "content",            AudioChannel::Content },
-  { "notification",       AudioChannel::Notification },
-  { "alarm",              AudioChannel::Alarm },
-  { "telephony",          AudioChannel::Telephony },
-  { "ringer",             AudioChannel::Ringer },
-  { "publicnotification", AudioChannel::Publicnotification },
-  { nullptr }
-};
+  for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
+    if (aChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) {
+      return static_cast<AudioChannel>(kMozAudioChannelAttributeTable[i].value);
+    }
+  }
+
+  return AudioChannel::Normal;
+}
 
 /* static */ AudioChannel
 AudioChannelService::GetDefaultAudioChannel()
 {
   nsString audioChannel = Preferences::GetString("media.defaultAudioChannel");
   if (audioChannel.IsEmpty()) {
     return AudioChannel::Normal;
   }
 
-  for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].string; ++i) {
-    if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].string)) {
-      return kMozAudioChannelAttributeTable[i].value;
+  for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
+    if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) {
+      return static_cast<AudioChannel>(kMozAudioChannelAttributeTable[i].value);
     }
   }
 
   return AudioChannel::Normal;
 }
 
 /* static */ void
-AudioChannelService::GetDefaultAudioChannelString(nsString& aString)
+AudioChannelService::GetAudioChannelString(AudioChannel aChannel,
+                                           nsAString& aString)
+{
+  aString.AssignASCII("normal");
+
+  for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
+    if (aChannel ==
+        static_cast<AudioChannel>(kMozAudioChannelAttributeTable[i].value)) {
+      aString.AssignASCII(kMozAudioChannelAttributeTable[i].tag);
+      break;
+    }
+  }
+}
+
+/* static */ void
+AudioChannelService::GetDefaultAudioChannelString(nsAString& aString)
 {
   aString.AssignASCII("normal");
 
   nsString audioChannel = Preferences::GetString("media.defaultAudioChannel");
   if (!audioChannel.IsEmpty()) {
-    for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].string; ++i) {
-      if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].string)) {
+    for (uint32_t i = 0; kMozAudioChannelAttributeTable[i].tag; ++i) {
+      if (audioChannel.EqualsASCII(kMozAudioChannelAttributeTable[i].tag)) {
         aString = audioChannel;
         break;
       }
     }
   }
 }
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -9,16 +9,17 @@
 
 #include "nsAutoPtr.h"
 #include "nsIObserver.h"
 #include "nsTArray.h"
 #include "nsITimer.h"
 
 #include "AudioChannelCommon.h"
 #include "AudioChannelAgent.h"
+#include "nsAttrValue.h"
 #include "nsClassHashtable.h"
 #include "mozilla/dom/AudioChannelBinding.h"
 
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 #ifdef MOZ_WIDGET_GONK
@@ -43,20 +44,20 @@ public:
 
   /**
    * Shutdown the singleton.
    */
   static void Shutdown();
 
   /**
    * Any audio channel agent that starts playing should register itself to
-   * this service, sharing the AudioChannelType.
+   * this service, sharing the AudioChannel.
    */
   virtual void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                         AudioChannelType aType,
+                                         AudioChannel aChannel,
                                          bool aWithVideo);
 
   /**
    * Any audio channel agent that stops playing should unregister itself to
    * this service.
    */
   virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
 
@@ -76,19 +77,21 @@ public:
   /**
    * Return true if a normal or content channel is active for the given
    * process ID.
    */
   virtual bool ProcessContentOrNormalChannelIsActive(uint64_t aChildID);
 
   /***
    * AudioChannelManager calls this function to notify the default channel used
-   * to adjust volume when there is no any active channel.
+   * to adjust volume when there is no any active channel. if aChannel is -1,
+   * the default audio channel will be used. Otherwise aChannel is casted to
+   * AudioChannel enum.
    */
-  virtual void SetDefaultVolumeControlChannel(AudioChannelType aType,
+  virtual void SetDefaultVolumeControlChannel(int32_t aChannel,
                                               bool aHidden);
 
   bool AnyAudioChannelIsActive();
 
   void RefreshAgentsVolume(nsPIDOMWindow* aWindow);
 
 #ifdef MOZ_WIDGET_GONK
   void RegisterSpeakerManager(SpeakerManagerService* aSpeakerManager)
@@ -99,45 +102,48 @@ public:
   }
 
   void UnregisterSpeakerManager(SpeakerManagerService* aSpeakerManager)
   {
     mSpeakerManager.RemoveElement(aSpeakerManager);
   }
 #endif
 
+  static const nsAttrValue::EnumTable* GetAudioChannelTable();
+  static AudioChannel GetAudioChannel(const nsAString& aString);
   static AudioChannel GetDefaultAudioChannel();
-  static void GetDefaultAudioChannelString(nsString& aString);
+  static void GetAudioChannelString(AudioChannel aChannel, nsAString& aString);
+  static void GetDefaultAudioChannelString(nsAString& aString);
 
 protected:
   void Notify();
 
   /**
    * Send the audio-channel-changed notification for the given process ID if
    * needed.
    */
   void SendAudioChannelChangedNotification(uint64_t aChildID);
 
   /* Register/Unregister IPC types: */
-  void RegisterType(AudioChannelType aType, uint64_t aChildID, bool aWithVideo);
-  void UnregisterType(AudioChannelType aType, bool aElementHidden,
+  void RegisterType(AudioChannel aChannel, uint64_t aChildID, bool aWithVideo);
+  void UnregisterType(AudioChannel aChannel, bool aElementHidden,
                       uint64_t aChildID, bool aWithVideo);
-  void UnregisterTypeInternal(AudioChannelType aType, bool aElementHidden,
+  void UnregisterTypeInternal(AudioChannel aChannel, bool aElementHidden,
                               uint64_t aChildID, bool aWithVideo);
 
-  AudioChannelState GetStateInternal(AudioChannelType aType, uint64_t aChildID,
+  AudioChannelState GetStateInternal(AudioChannel aChannel, uint64_t aChildID,
                                      bool aElementHidden,
                                      bool aElementWasHidden);
 
   /* Update the internal type value following the visibility changes */
-  void UpdateChannelType(AudioChannelType aType, uint64_t aChildID,
+  void UpdateChannelType(AudioChannel aChannel, uint64_t aChildID,
                          bool aElementHidden, bool aElementWasHidden);
 
   /* Send the default-volume-channel-changed notification */
-  void SetDefaultVolumeControlChannelInternal(AudioChannelType aType,
+  void SetDefaultVolumeControlChannelInternal(int32_t aChannel,
                                               bool aHidden, uint64_t aChildID);
 
   AudioChannelService();
   virtual ~AudioChannelService();
 
   enum AudioChannelInternalType {
     AUDIO_CHANNEL_INT_NORMAL = 0,
     AUDIO_CHANNEL_INT_NORMAL_HIDDEN,
@@ -156,34 +162,32 @@ protected:
     AUDIO_CHANNEL_INT_LAST
   };
 
   bool ChannelsActiveWithHigherPriorityThan(AudioChannelInternalType aType);
 
   bool CheckVolumeFadedCondition(AudioChannelInternalType aType,
                                  bool aElementHidden);
 
-  const char* ChannelName(AudioChannelType aType);
-
-  AudioChannelInternalType GetInternalType(AudioChannelType aType,
+  AudioChannelInternalType GetInternalType(AudioChannel aChannel,
                                            bool aElementHidden);
 
   class AudioChannelAgentData {
   public:
-    AudioChannelAgentData(AudioChannelType aType,
+    AudioChannelAgentData(AudioChannel aChannel,
                           bool aElementHidden,
                           AudioChannelState aState,
                           bool aWithVideo)
-    : mType(aType)
+    : mChannel(aChannel)
     , mElementHidden(aElementHidden)
     , mState(aState)
     , mWithVideo(aWithVideo)
     {}
 
-    AudioChannelType mType;
+    AudioChannel mChannel;
     bool mElementHidden;
     AudioChannelState mState;
     const bool mWithVideo;
   };
 
   static PLDHashOperator
   NotifyEnumerator(AudioChannelAgent* aAgent,
                    AudioChannelAgentData* aData, void *aUnused);
@@ -202,18 +206,18 @@ protected:
   uint32_t CountWindow(nsIDOMWindow* aWindow);
 
   nsClassHashtable< nsPtrHashKey<AudioChannelAgent>, AudioChannelAgentData > mAgents;
 #ifdef MOZ_WIDGET_GONK
   nsTArray<SpeakerManagerService*>  mSpeakerManager;
 #endif
   nsTArray<uint64_t> mChannelCounters[AUDIO_CHANNEL_INT_LAST];
 
-  AudioChannelType mCurrentHigherChannel;
-  AudioChannelType mCurrentVisibleHigherChannel;
+  int32_t mCurrentHigherChannel;
+  int32_t mCurrentVisibleHigherChannel;
 
   nsTArray<uint64_t> mWithVideoChildIDs;
 
   // mPlayableHiddenContentChildID stores the ChildID of the process which can
   // play content channel(s) in the background.
   // A background process contained content channel(s) will become playable:
   //   1. When this background process registers its content channel(s) in
   //   AudioChannelService and there is no foreground process with registered
--- a/dom/audiochannel/AudioChannelServiceChild.cpp
+++ b/dom/audiochannel/AudioChannelServiceChild.cpp
@@ -67,39 +67,39 @@ AudioChannelServiceChild::GetState(Audio
   AudioChannelAgentData* data;
   if (!mAgents.Get(aAgent, &data)) {
     return AUDIO_CHANNEL_STATE_MUTED;
   }
 
   AudioChannelState state = AUDIO_CHANNEL_STATE_MUTED;
   bool oldElementHidden = data->mElementHidden;
 
-  UpdateChannelType(data->mType, CONTENT_PROCESS_ID_MAIN, aElementHidden, oldElementHidden);
+  UpdateChannelType(data->mChannel, CONTENT_PROCESS_ID_MAIN, aElementHidden,
+                    oldElementHidden);
 
   // Update visibility.
   data->mElementHidden = aElementHidden;
 
   ContentChild* cc = ContentChild::GetSingleton();
-  cc->SendAudioChannelGetState(data->mType, aElementHidden, oldElementHidden, &state);
+  cc->SendAudioChannelGetState(data->mChannel, aElementHidden, oldElementHidden,
+                               &state);
   data->mState = state;
   cc->SendAudioChannelChangedNotification();
 
   return state;
 }
 
 void
 AudioChannelServiceChild::RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                                    AudioChannelType aType,
+                                                    AudioChannel aChannel,
                                                     bool aWithVideo)
 {
-  MOZ_ASSERT(aType != AUDIO_CHANNEL_DEFAULT);
+  AudioChannelService::RegisterAudioChannelAgent(aAgent, aChannel, aWithVideo);
 
-  AudioChannelService::RegisterAudioChannelAgent(aAgent, aType, aWithVideo);
-
-  ContentChild::GetSingleton()->SendAudioChannelRegisterType(aType, aWithVideo);
+  ContentChild::GetSingleton()->SendAudioChannelRegisterType(aChannel, aWithVideo);
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr);
   }
 }
 
 void
@@ -112,31 +112,31 @@ AudioChannelServiceChild::UnregisterAudi
 
   // We need to keep a copy because unregister will remove the
   // AudioChannelAgentData object from the hashtable.
   AudioChannelAgentData data(*pData);
 
   AudioChannelService::UnregisterAudioChannelAgent(aAgent);
 
   ContentChild::GetSingleton()->SendAudioChannelUnregisterType(
-      data.mType, data.mElementHidden, data.mWithVideo);
+      data.mChannel, data.mElementHidden, data.mWithVideo);
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->NotifyObservers(nullptr, "audio-channel-agent-changed", nullptr);
   }
 #ifdef MOZ_WIDGET_GONK
   bool active = AnyAudioChannelIsActive();
   for (uint32_t i = 0; i < mSpeakerManager.Length(); i++) {
     mSpeakerManager[i]->SetAudioChannelActive(active);
   }
 #endif
 }
 
 void
-AudioChannelServiceChild::SetDefaultVolumeControlChannel(
-  AudioChannelType aType, bool aHidden)
+AudioChannelServiceChild::SetDefaultVolumeControlChannel(int32_t aChannel,
+                                                         bool aHidden)
 {
   ContentChild *cc = ContentChild::GetSingleton();
   if (cc) {
-    cc->SendAudioChannelChangeDefVolChannel(aType, aHidden);
+    cc->SendAudioChannelChangeDefVolChannel(aChannel, aHidden);
   }
 }
--- a/dom/audiochannel/AudioChannelServiceChild.h
+++ b/dom/audiochannel/AudioChannelServiceChild.h
@@ -26,28 +26,29 @@ public:
    *
    * @return NS_OK on proper assignment, NS_ERROR_FAILURE otherwise.
    */
   static AudioChannelService* GetAudioChannelService();
 
   static void Shutdown();
 
   virtual void RegisterAudioChannelAgent(AudioChannelAgent* aAgent,
-                                         AudioChannelType aType,
+                                         AudioChannel aChannel,
                                          bool aWithVideo);
   virtual void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent);
 
   /**
    * Return the state to indicate this agent should keep playing/
    * fading volume/muted.
    */
   virtual AudioChannelState GetState(AudioChannelAgent* aAgent,
                                      bool aElementHidden);
 
-  virtual void SetDefaultVolumeControlChannel(AudioChannelType aType, bool aHidden);
+  virtual void SetDefaultVolumeControlChannel(int32_t aChannel,
+                                              bool aHidden);
 
 protected:
   AudioChannelServiceChild();
   virtual ~AudioChannelServiceChild();
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/audiochannel/tests/TestAudioChannelService.cpp
+++ b/dom/audiochannel/tests/TestAudioChannelService.cpp
@@ -26,18 +26,18 @@
 using namespace mozilla::dom;
 
 class Agent : public nsIAudioChannelAgentCallback,
               public nsSupportsWeakReference
 {
 public:
   NS_DECL_ISUPPORTS
 
-  Agent(AudioChannelType aType)
-  : mType(aType)
+  Agent(AudioChannel aChannel)
+  : mChannel(aChannel)
   , mWaitCallback(false)
   , mRegistered(false)
   , mCanPlay(AUDIO_CHANNEL_STATE_MUTED)
   {
     mAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
   }
 
   virtual ~Agent()
@@ -46,20 +46,22 @@ public:
       StopPlaying();
     }
   }
 
   nsresult Init(bool video=false)
   {
     nsresult rv = NS_OK;
     if (video) {
-      rv = mAgent->InitWithVideo(nullptr, mType, this, true);
+      rv = mAgent->InitWithVideo(nullptr, static_cast<int32_t>(mChannel),
+                                 this, true);
     }
     else {
-      rv = mAgent->InitWithWeakCallback(nullptr, mType, this);
+      rv = mAgent->InitWithWeakCallback(nullptr, static_cast<int32_t>(mChannel),
+                                        this);
     }
     NS_ENSURE_SUCCESS(rv, rv);
 
     return mAgent->SetVisibilityState(false);
   }
 
   nsresult StartPlaying(AudioChannelState *_ret)
   {
@@ -122,29 +124,29 @@ public:
         TEST_ENSURE_BASE(false, "GetCanPlay timeout");
       }
     }
     *_ret = mCanPlay;
     return NS_OK;
   }
 
   nsCOMPtr<nsIAudioChannelAgent> mAgent;
-  AudioChannelType mType;
+  AudioChannel mChannel;
   bool mWaitCallback;
   bool mRegistered;
   AudioChannelState mCanPlay;
 };
 
 NS_IMPL_ISUPPORTS2(Agent, nsIAudioChannelAgentCallback,
                    nsISupportsWeakReference)
 
 nsresult
 TestDoubleStartPlaying()
 {
-  nsRefPtr<Agent> agent = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> agent = new Agent(AudioChannel::Normal);
 
   nsresult rv = agent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   AudioChannelState playable;
   rv = agent->mAgent->StartPlaying((int32_t *)&playable);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -153,17 +155,17 @@ TestDoubleStartPlaying()
     "Test0: StartPlaying calling twice must return error");
 
   return NS_OK;
 }
 
 nsresult
 TestOneNormalChannel()
 {
-  nsRefPtr<Agent> agent = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> agent = new Agent(AudioChannel::Normal);
   nsresult rv = agent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   AudioChannelState playable;
   rv = agent->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
     "Test1: A normal channel unvisible agent must be muted");
@@ -177,21 +179,21 @@ TestOneNormalChannel()
     "Test1: A normal channel visible agent must be playable");
 
   return rv;
 }
 
 nsresult
 TestTwoNormalChannels()
 {
-  nsRefPtr<Agent> agent1 = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> agent1 = new Agent(AudioChannel::Normal);
   nsresult rv = agent1->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<Agent> agent2 = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> agent2 = new Agent(AudioChannel::Normal);
   rv = agent2->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   AudioChannelState playable;
   rv = agent1->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
     "Test2: A normal channel unvisible agent1 must be muted");
@@ -218,21 +220,21 @@ TestTwoNormalChannels()
     "Test2: A normal channel visible agent2 must be playable");
 
   return rv;
 }
 
 nsresult
 TestContentChannels()
 {
-  nsRefPtr<Agent> agent1 = new Agent(AUDIO_CHANNEL_CONTENT);
+  nsRefPtr<Agent> agent1 = new Agent(AudioChannel::Content);
   nsresult rv = agent1->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<Agent> agent2 = new Agent(AUDIO_CHANNEL_CONTENT);
+  nsRefPtr<Agent> agent2 = new Agent(AudioChannel::Content);
   rv = agent2->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // All content channels in the foreground can be allowed to play
   rv = agent1->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = agent2->SetVisibilityState(true);
@@ -303,25 +305,25 @@ TestContentChannels()
     "from background state");
 
   return rv;
 }
 
 nsresult
 TestFadedState()
 {
-  nsRefPtr<Agent> normalAgent = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> normalAgent = new Agent(AudioChannel::Normal);
   nsresult rv = normalAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<Agent> contentAgent = new Agent(AUDIO_CHANNEL_CONTENT);
+  nsRefPtr<Agent> contentAgent = new Agent(AudioChannel::Content);
   rv = contentAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<Agent> notificationAgent = new Agent(AUDIO_CHANNEL_NOTIFICATION);
+  nsRefPtr<Agent> notificationAgent = new Agent(AudioChannel::Notification);
   rv = notificationAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = normalAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = contentAgent->SetVisibilityState(true);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -382,42 +384,42 @@ TestFadedState()
   NS_ENSURE_SUCCESS(rv, rv);
 
   return rv;
 }
 
 nsresult
 TestPriorities()
 {
-  nsRefPtr<Agent> normalAgent = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> normalAgent = new Agent(AudioChannel::Normal);
   nsresult rv = normalAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<Agent> contentAgent = new Agent(AUDIO_CHANNEL_CONTENT);
+  nsRefPtr<Agent> contentAgent = new Agent(AudioChannel::Content);
   rv = contentAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<Agent> notificationAgent = new Agent(AUDIO_CHANNEL_NOTIFICATION);
+  nsRefPtr<Agent> notificationAgent = new Agent(AudioChannel::Notification);
   rv = notificationAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<Agent> alarmAgent = new Agent(AUDIO_CHANNEL_ALARM);
+  nsRefPtr<Agent> alarmAgent = new Agent(AudioChannel::Alarm);
   rv = alarmAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<Agent> telephonyAgent = new Agent(AUDIO_CHANNEL_TELEPHONY);
+  nsRefPtr<Agent> telephonyAgent = new Agent(AudioChannel::Telephony);
   rv = telephonyAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<Agent> ringerAgent = new Agent(AUDIO_CHANNEL_RINGER);
+  nsRefPtr<Agent> ringerAgent = new Agent(AudioChannel::Ringer);
   rv = ringerAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<Agent> pNotificationAgent =
-    new Agent(AUDIO_CHANNEL_PUBLICNOTIFICATION);
+    new Agent(AudioChannel::Publicnotification);
   rv = pNotificationAgent->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   AudioChannelState playable;
 
   rv = normalAgent->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
@@ -539,21 +541,21 @@ TestPriorities()
     "Test5: A pNotification channel visible agent must be playable");
 
   return rv;
 }
 
 nsresult
 TestOneVideoNormalChannel()
 {
-  nsRefPtr<Agent> agent1 = new Agent(AUDIO_CHANNEL_NORMAL);
+  nsRefPtr<Agent> agent1 = new Agent(AudioChannel::Normal);
   nsresult rv = agent1->Init(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsRefPtr<Agent> agent2 = new Agent(AUDIO_CHANNEL_CONTENT);
+  nsRefPtr<Agent> agent2 = new Agent(AudioChannel::Content);
   rv = agent2->Init(false);
   NS_ENSURE_SUCCESS(rv, rv);
 
   AudioChannelState playable;
   rv = agent1->StartPlaying(&playable);
   NS_ENSURE_SUCCESS(rv, rv);
   TEST_ENSURE_BASE(playable == AUDIO_CHANNEL_STATE_MUTED,
     "Test6: A video normal channel invisible agent1 must be muted");
@@ -644,21 +646,21 @@ int main(int argc, char** argv)
   if (NS_FAILED(TestContentChannels())) {
     return 1;
   }
 
   if (NS_FAILED(TestFadedState())) {
     return 1;
   }
 
-  // Channel type with AUDIO_CHANNEL_TELEPHONY cannot be unregistered until the
+  // Channel type with AudioChannel::Telephony cannot be unregistered until the
   // main thread has chances to process 1500 millisecond timer. In order to
   // skip ambiguous return value of ChannelsActiveWithHigherPriorityThan(), new
   // test cases are added before any test case that registers the channel type
-  // with AUDIO_CHANNEL_TELEPHONY channel.
+  // with AudioChannel::Telephony channel.
   if (NS_FAILED(TestOneVideoNormalChannel())) {
     return 1;
   }
 
   if (NS_FAILED(TestPriorities())) {
     return 1;
   }
 
--- a/dom/audiochannel/tests/moz.build
+++ b/dom/audiochannel/tests/moz.build
@@ -3,10 +3,13 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 CPP_UNIT_TESTS += [
     'TestAudioChannelService.cpp',
 ]
 
+if CONFIG['OS_ARCH'] == 'WINNT':
+    DEFINES['NOMINMAX'] = True
+
 FAIL_ON_WARNINGS = True
 
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -809,42 +809,37 @@ Console::Method(JSContext* aCx, MethodNa
       Throw(aCx, rv);
       return;
     }
 
     if (language == nsIProgrammingLanguage::JAVASCRIPT ||
         language == nsIProgrammingLanguage::JAVASCRIPT2) {
       ConsoleStackEntry& data = *callData->mStack.AppendElement();
 
-      nsCString string;
-      rv = stack->GetFilename(string);
+      rv = stack->GetFilename(data.mFilename);
       if (NS_FAILED(rv)) {
         Throw(aCx, rv);
         return;
       }
 
-      CopyUTF8toUTF16(string, data.mFilename);
-
       int32_t lineNumber;
       rv = stack->GetLineNumber(&lineNumber);
       if (NS_FAILED(rv)) {
         Throw(aCx, rv);
         return;
       }
 
       data.mLineNumber = lineNumber;
 
-      rv = stack->GetName(string);
+      rv = stack->GetName(data.mFunctionName);
       if (NS_FAILED(rv)) {
         Throw(aCx, rv);
         return;
       }
 
-      CopyUTF8toUTF16(string, data.mFunctionName);
-
       data.mLanguage = language;
     }
 
     nsCOMPtr<nsIStackFrame> caller;
     rv = stack->GetCaller(getter_AddRefs(caller));
     if (NS_FAILED(rv)) {
       Throw(aCx, rv);
       return;
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -315,19 +315,19 @@ Exception::GetName(nsACString& aName)
     if (name) {
       aName.Assign(name);
     }
   }
 
   return NS_OK;
 }
 
-/* readonly attribute AUTF8String filename; */
+/* readonly attribute AString filename; */
 NS_IMETHODIMP
-Exception::GetFilename(nsACString& aFilename)
+Exception::GetFilename(nsAString& aFilename)
 {
   NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
 
   if (mLocation) {
     return mLocation->GetFilename(aFilename);
   }
 
   aFilename.Assign(mFilename);
@@ -504,28 +504,16 @@ Exception::GetName(nsString& retval)
 #ifdef DEBUG
   DebugOnly<nsresult> rv =
 #endif
   GetName(str);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
   CopyUTF8toUTF16(str, retval);
 }
 
-void
-Exception::GetFilename(nsString& retval)
-{
-  nsCString str;
-#ifdef DEBUG
-  DebugOnly<nsresult> rv =
-#endif
-  GetFilename(str);
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
-  CopyUTF8toUTF16(str, retval);
-}
-
 uint32_t
 Exception::LineNumber() const
 {
   if (mLocation) {
     int32_t lineno;
     if (NS_SUCCEEDED(mLocation->GetLineNumber(&lineno))) {
       return lineno;
     }
@@ -617,25 +605,27 @@ DOMException::ToString(nsACString& aRetu
   static const char defaultLocation[] = "<unknown>";
   static const char defaultName[] = "<unknown>";
   static const char format[] =
     "[Exception... \"%s\"  code: \"%d\" nsresult: \"0x%x (%s)\"  location: \"%s\"]";
 
   nsAutoCString location;
 
   if (mInner) {
-    nsCString filename;
+    nsString filename;
     mInner->GetFilename(filename);
 
     if (!filename.IsEmpty()) {
       uint32_t line_nr = 0;
 
       mInner->GetLineNumber(&line_nr);
 
-      char *temp = PR_smprintf("%s Line: %d", filename.get(), line_nr);
+      char *temp = PR_smprintf("%s Line: %d",
+                               NS_ConvertUTF16toUTF8(filename).get(),
+                               line_nr);
       if (temp) {
         location.Assign(temp);
         PR_smprintf_free(temp);
       }
     }
   }
 
   if (location.IsEmpty()) {
--- a/dom/base/DOMException.h
+++ b/dom/base/DOMException.h
@@ -64,17 +64,17 @@ public:
   nsISupports* GetParentObject() const { return nullptr; }
 
   void GetMessageMoz(nsString& retval);
 
   uint32_t Result() const;
 
   void GetName(nsString& retval);
 
-  void GetFilename(nsString& retval);
+  // The XPCOM GetFilename does the right thing.
 
   uint32_t LineNumber() const;
 
   uint32_t ColumnNumber() const;
 
   already_AddRefed<nsIStackFrame> GetLocation() const;
 
   already_AddRefed<nsISupports> GetInner() const;
@@ -95,17 +95,17 @@ public:
 protected:
   virtual ~Exception();
 
   nsCString       mMessage;
   nsresult        mResult;
   nsCString       mName;
   nsCOMPtr<nsIStackFrame> mLocation;
   nsCOMPtr<nsISupports> mData;
-  nsCString       mFilename;
+  nsString        mFilename;
   int             mLineNumber;
   nsCOMPtr<nsIException> mInner;
   bool            mInitialized;
 
   bool mHoldingJSVal;
   JS::Heap<JS::Value> mThrownJSVal;
 
 private:
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -16,17 +16,17 @@ load 375399-1.html
 load 404869-1.xul
 load 417852-1.html
 load 462947.html
 load 439206-1.html
 load 473284.xul
 load 499006-1.html
 load 499006-2.html
 load 502617.html
-asserts(1-2) load 504224.html # bug 564098
+load 504224.html
 load 603531.html
 load 601247.html
 load 609560-1.xhtml
 load 612018-1.html
 load 637116.html
 load 666869.html
 load 675621-1.html
 load 677194.html
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -80,17 +80,16 @@
 #include "nsIDOMXULCheckboxElement.h"
 #include "nsIDOMXULPopupElement.h"
 
 // Event related includes
 #include "nsIDOMEventTarget.h"
 
 // CSS related includes
 #include "nsCSSRules.h"
-#include "nsIDOMStyleSheetList.h"
 #include "nsIDOMCSSRule.h"
 #include "nsICSSRuleList.h"
 #include "nsAutoPtr.h"
 #include "nsMemory.h"
 
 // Tranformiix
 #include "nsIXSLTProcessor.h"
 #include "nsIXSLTProcessorPrivate.h"
@@ -117,17 +116,16 @@
 #include "nsIXULTemplateBuilder.h"
 #include "nsITreeColumns.h"
 #endif
 #include "nsIDOMXPathExpression.h"
 #include "nsIDOMNSXPathExpression.h"
 #include "nsIDOMXPathNSResolver.h"
 #include "nsIDOMXPathResult.h"
 
-#include "nsIDOMSVGLength.h"
 #include "nsIDOMSVGNumber.h"
 
 // Storage includes
 #include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 
 // Drag and drop
 #include "nsIDOMFile.h"
@@ -314,18 +312,16 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(CSSImportRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CSSMediaRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CSSNameSpaceRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CSSRuleList, nsCSSRuleListSH,
                            ARRAY_SCRIPTABLE_FLAGS)
-  NS_DEFINE_CLASSINFO_DATA(StyleSheetList, nsStyleSheetListSH,
-                           ARRAY_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CSSStyleSheet, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   // XUL classes
 #ifdef MOZ_XUL
   NS_DEFINE_CHROME_XBL_CLASSINFO_DATA(XULCommandDispatcher, nsDOMGenericSH,
                                       DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
@@ -360,18 +356,16 @@ static nsDOMClassInfoData sClassInfoData
 
   NS_DEFINE_CLASSINFO_DATA(CSSMozDocumentRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(CSSSupportsRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   // other SVG classes
-  NS_DEFINE_CLASSINFO_DATA(SVGLength, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(SVGNumber, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(WindowUtils, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XSLTProcessor, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -943,20 +937,16 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(CSSNameSpaceRule, nsIDOMCSSRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSRule)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(CSSRuleList, nsIDOMCSSRuleList)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSRuleList)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(StyleSheetList, nsIDOMStyleSheetList)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMStyleSheetList)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(CSSStyleSheet, nsIDOMCSSStyleSheet)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSStyleSheet)
   DOM_CLASSINFO_MAP_END
 
 #ifdef MOZ_XUL
   DOM_CLASSINFO_MAP_BEGIN(XULCommandDispatcher, nsIDOMXULCommandDispatcher)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMXULCommandDispatcher)
   DOM_CLASSINFO_MAP_END
@@ -1013,20 +1003,16 @@ nsDOMClassInfo::Init()
 
   DOM_CLASSINFO_MAP_BEGIN(CSSSupportsRule, nsIDOMCSSSupportsRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSSupportsRule)
   DOM_CLASSINFO_MAP_END
 
   // The SVG document
 
   // other SVG classes
-  DOM_CLASSINFO_MAP_BEGIN(SVGLength, nsIDOMSVGLength)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGLength)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(SVGNumber, nsIDOMSVGNumber)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMSVGNumber)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(XSLTProcessor, nsIXSLTProcessor)
     DOM_CLASSINFO_MAP_ENTRY(nsIXSLTProcessor)
     DOM_CLASSINFO_MAP_ENTRY(nsIXSLTProcessorPrivate)
   DOM_CLASSINFO_MAP_END
@@ -3675,29 +3661,16 @@ nsArraySH::GetProperty(nsIXPConnectWrapp
       rv = NS_SUCCESS_I_DID_SOMETHING;
     }
   }
 
   return rv;
 }
 
 
-// StyleSheetList helper
-
-nsISupports*
-nsStyleSheetListSH::GetItemAt(nsISupports *aNative, uint32_t aIndex,
-                              nsWrapperCache **aCache, nsresult *rv)
-{
-  nsIDOMStyleSheetList* list = static_cast<nsIDOMStyleSheetList*>(aNative);
-  nsCOMPtr<nsIDOMStyleSheet> sheet;
-  list->Item(aIndex, getter_AddRefs(sheet));
-  return sheet;
-}
-
-
 // CSSRuleList scriptable helper
 
 nsISupports*
 nsCSSRuleListSH::GetItemAt(nsISupports *aNative, uint32_t aIndex,
                            nsWrapperCache **aCache, nsresult *aResult)
 {
   nsICSSRuleList* list = static_cast<nsICSSRuleList*>(aNative);
 #ifdef DEBUG
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -363,40 +363,16 @@ public:
                          JSObject *obj, jsid id, JS::Value *vp, bool *_retval) MOZ_OVERRIDE;
 
 private:
   // Not implemented, nothing should create an instance of this class.
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData);
 };
 
 
-// StyleSheetList helper
-
-class nsStyleSheetListSH : public nsArraySH
-{
-protected:
-  nsStyleSheetListSH(nsDOMClassInfoData* aData) : nsArraySH(aData)
-  {
-  }
-
-  virtual ~nsStyleSheetListSH()
-  {
-  }
-
-  virtual nsISupports* GetItemAt(nsISupports *aNative, uint32_t aIndex,
-                                 nsWrapperCache **aCache, nsresult *aResult) MOZ_OVERRIDE;
-
-public:
-  static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
-  {
-    return new nsStyleSheetListSH(aData);
-  }
-};
-
-
 // CSSRuleList helper
 
 class nsCSSRuleListSH : public nsArraySH
 {
 protected:
   nsCSSRuleListSH(nsDOMClassInfoData* aData) : nsArraySH(aData)
   {
   }
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -11,17 +11,16 @@ DOMCI_CLASS(DOMConstructor)
 
 // CSS classes
 DOMCI_CLASS(CSSStyleRule)
 DOMCI_CLASS(CSSCharsetRule)
 DOMCI_CLASS(CSSImportRule)
 DOMCI_CLASS(CSSMediaRule)
 DOMCI_CLASS(CSSNameSpaceRule)
 DOMCI_CLASS(CSSRuleList)
-DOMCI_CLASS(StyleSheetList)
 DOMCI_CLASS(CSSStyleSheet)
 
 // XUL classes
 #ifdef MOZ_XUL
 DOMCI_CLASS(XULCommandDispatcher)
 #endif
 DOMCI_CLASS(XULControllers)
 DOMCI_CLASS(BoxObject)
@@ -41,17 +40,16 @@ DOMCI_CLASS(XULTreeBuilder)
 #ifdef MOZ_XUL
 DOMCI_CLASS(TreeColumn)
 #endif
 
 DOMCI_CLASS(CSSMozDocumentRule)
 DOMCI_CLASS(CSSSupportsRule)
 
 // other SVG classes
-DOMCI_CLASS(SVGLength)
 DOMCI_CLASS(SVGNumber)
 
 // WindowUtils
 DOMCI_CLASS(WindowUtils)
 
 // XSLTProcessor
 DOMCI_CLASS(XSLTProcessor)
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -429,44 +429,25 @@ nsDOMWindowUtils::SetDisplayPortMarginsF
   if (!content) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (content->GetCurrentDoc() != presShell->GetDocument()) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  DisplayPortMarginsPropertyData* currentData =
-    static_cast<DisplayPortMarginsPropertyData*>(content->GetProperty(nsGkAtoms::DisplayPortMargins));
-  if (currentData && currentData->mPriority > aPriority) {
-    return NS_OK;
-  }
-
   // Note order change of arguments between our function signature and
   // LayerMargin constructor.
   LayerMargin displayportMargins(aTopMargin,
                                  aRightMargin,
                                  aBottomMargin,
                                  aLeftMargin);
 
-  content->SetProperty(nsGkAtoms::DisplayPortMargins,
-                       new DisplayPortMarginsPropertyData(displayportMargins, aAlignmentX, aAlignmentY, aPriority),
-                       nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
-
-  nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
-  if (rootScrollFrame && content == rootScrollFrame->GetContent()) {
-    // We are setting a root displayport for a document.
-    // The pres shell needs a special flag set.
-    presShell->SetIgnoreViewportScrolling(true);
-  }
-
-  nsIFrame* rootFrame = presShell->FrameManager()->GetRootFrame();
-  if (rootFrame) {
-    rootFrame->SchedulePaint();
-  }
+  nsLayoutUtils::SetDisplayPortMargins(content, presShell, displayportMargins,
+                                       aAlignmentX, aAlignmentY, aPriority);
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SetDisplayPortBaseForElement(int32_t aX,
                                                int32_t aY,
@@ -492,18 +473,17 @@ nsDOMWindowUtils::SetDisplayPortBaseForE
   if (!content) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (content->GetCurrentDoc() != presShell->GetDocument()) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  content->SetProperty(nsGkAtoms::DisplayPortBase, new nsRect(aX, aY, aWidth, aHeight),
-                       nsINode::DeleteProperty<nsRect>);
+  nsLayoutUtils::SetDisplayPortBase(content, nsRect(aX, aY, aWidth, aHeight));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SetCriticalDisplayPortForElement(float aXPx, float aYPx,
                                                    float aWidthPx, float aHeightPx,
                                                    nsIDOMElement* aElement)
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -106,31 +106,35 @@ public:
     mImpl.construct(aValue1, aValue2);
   }
 
   bool WasPassed() const
   {
     return !mImpl.empty();
   }
 
-  void Construct()
+  // Return InternalType here so we can work with it usefully.
+  InternalType& Construct()
   {
     mImpl.construct();
+    return mImpl.ref();
   }
 
   template <class T1>
-  void Construct(const T1 &t1)
+  InternalType& Construct(const T1 &t1)
   {
     mImpl.construct(t1);
+    return mImpl.ref();
   }
 
   template <class T1, class T2>
-  void Construct(const T1 &t1, const T2 &t2)
+  InternalType& Construct(const T1 &t1, const T2 &t2)
   {
     mImpl.construct(t1, t2);
+    return mImpl.ref();
   }
 
   void Reset()
   {
     if (WasPassed()) {
       mImpl.destroy();
     }
   }
@@ -223,28 +227,28 @@ public:
     Optional_base<JSObject*, JSObject*>()
   {}
 
   explicit Optional(JSObject* aValue) :
     Optional_base<JSObject*, JSObject*>(aValue)
   {}
 
   // Don't allow us to have an uninitialized JSObject*
-  void Construct()
+  JSObject*& Construct()
   {
     // The Android compiler sucks and thinks we're trying to construct
     // a JSObject* from an int if we don't cast here.  :(
-    Optional_base<JSObject*, JSObject*>::Construct(
+    return Optional_base<JSObject*, JSObject*>::Construct(
       static_cast<JSObject*>(nullptr));
   }
 
   template <class T1>
-  void Construct(const T1& t1)
+  JSObject*& Construct(const T1& t1)
   {
-    Optional_base<JSObject*, JSObject*>::Construct(t1);
+    return Optional_base<JSObject*, JSObject*>::Construct(t1);
   }
 };
 
 // A specialization of Optional for JS::Value to make sure no one ever uses it.
 template<>
 class Optional<JS::Value>
 {
 private:
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1050,16 +1050,21 @@ DOMInterfaces = {
     'concrete': False,
     'resultNotAddRefed': ['nearestViewportElement', 'farthestViewportElement']
 },
 
 'SVGGradientElement': {
     'concrete': False,
 },
 
+'SVGLength': {
+    'nativeType': 'mozilla::DOMSVGLength',
+    'headerFile': 'DOMSVGLength.h'
+},
+
 'SVGLengthList': {
     'nativeType': 'mozilla::DOMSVGLengthList',
     'headerFile': 'DOMSVGLengthList.h'
 },
 
 'SVGLinearGradientElement': {
     'headerFile': 'mozilla/dom/SVGGradientElement.h',
 },
@@ -1910,18 +1915,16 @@ addExternalIface('nsIDocShell', nativeTy
 addExternalIface('nsIEditor', nativeType='nsIEditor', notflattened=True)
 addExternalIface('nsIVariant', nativeType='nsIVariant', notflattened=True)
 addExternalIface('OutputStream', nativeType='nsIOutputStream',
                  notflattened=True)
 addExternalIface('Principal', nativeType='nsIPrincipal',
                  headerFile='nsIPrincipal.h', notflattened=True)
 addExternalIface('StackFrame', nativeType='nsIStackFrame',
                  headerFile='nsIException.h', notflattened=True)
-addExternalIface('StyleSheetList')
-addExternalIface('SVGLength')
 addExternalIface('SVGNumber')
 addExternalIface('URI', nativeType='nsIURI', headerFile='nsIURI.h',
                  notflattened=True)
 addExternalIface('UserDataHandler')
 addExternalIface('XPathResult', nativeType='nsISupports')
 addExternalIface('XPathExpression')
 addExternalIface('XPathNSResolver')
 addExternalIface('XULCommandDispatcher')
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -297,18 +297,18 @@ private:
   }
 
   int32_t GetLineno();
 
   nsRefPtr<StackDescriptionOwner> mStackDescription;
   nsCOMPtr<nsIStackFrame> mCaller;
 
   // Cached values
-  nsCString mFilename;
-  nsCString mFunname;
+  nsString mFilename;
+  nsString mFunname;
   int32_t mLineno;
   uint32_t mLanguage;
 
   size_t mIndex;
 
   bool mFilenameInitialized;
   bool mFunnameInitialized;
   bool mLinenoInitialized;
@@ -369,44 +369,46 @@ NS_IMETHODIMP JSStackFrame::GetLanguageN
     aLanguageName.AssignASCII(js);
   } else {
     aLanguageName.AssignASCII(cpp);
   }
 
   return NS_OK;
 }
 
-/* readonly attribute string filename; */
-NS_IMETHODIMP JSStackFrame::GetFilename(nsACString& aFilename)
+/* readonly attribute AString filename; */
+NS_IMETHODIMP JSStackFrame::GetFilename(nsAString& aFilename)
 {
   if (!mFilenameInitialized) {
     JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
     if (const char *filename = desc.filename()) {
-      mFilename.Assign(filename);
+      CopyUTF8toUTF16(filename, mFilename);
     }
     mFilenameInitialized = true;
   }
 
   // The filename must be set to null if empty.
   if (mFilename.IsEmpty()) {
     aFilename.SetIsVoid(true);
   } else {
     aFilename.Assign(mFilename);
   }
 
   return NS_OK;
 }
 
-/* readonly attribute string name; */
-NS_IMETHODIMP JSStackFrame::GetName(nsACString& aFunction)
+/* readonly attribute AString name; */
+NS_IMETHODIMP JSStackFrame::GetName(nsAString& aFunction)
 {
   if (!mFunnameInitialized) {
     JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
     if (JSFlatString *name = desc.funDisplayName()) {
-      CopyUTF16toUTF8(JS_GetFlatStringChars(name), mFunname);
+      mFunname.Assign(JS_GetFlatStringChars(name),
+                      // XXXbz Can't JS_GetStringLength on JSFlatString!
+                      JS_GetStringLength(JS_FORGET_STRING_FLATNESS(name)));
     }
     mFunnameInitialized = true;
   }
 
   // The function name must be set to null if empty.
   if (mFunname.IsEmpty()) {
     aFunction.SetIsVoid(true);
   } else {
@@ -455,34 +457,36 @@ NS_IMETHODIMP JSStackFrame::GetCaller(ns
 
 /* AUTF8String toString (); */
 NS_IMETHODIMP JSStackFrame::ToString(nsACString& _retval)
 {
   _retval.Truncate();
 
   const char* frametype = IsJSFrame() ? "JS" : "native";
 
-  nsCString filename;
+  nsString filename;
   nsresult rv = GetFilename(filename);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (filename.IsEmpty()) {
-    filename.AssignASCII("<unknown filename>");
+    filename.AssignLiteral("<unknown filename>");
   }
 
-  nsCString funname;
+  nsString funname;
   rv = GetName(funname);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (funname.IsEmpty()) {
-    funname.AssignASCII("<TOP_LEVEL>");
+    funname.AssignLiteral("<TOP_LEVEL>");
   }
   static const char format[] = "%s frame :: %s :: %s :: line %d";
-  _retval.AppendPrintf(format, frametype, filename.get(),
-                       funname.get(), GetLineno());
+  _retval.AppendPrintf(format, frametype,
+                       NS_ConvertUTF16toUTF8(filename).get(),
+                       NS_ConvertUTF16toUTF8(funname).get(),
+                       GetLineno());
   return NS_OK;
 }
 
 /* static */ already_AddRefed<nsIStackFrame>
 JSStackFrame::CreateStack(JSContext* aCx, int32_t aMaxDepth)
 {
   static const unsigned MAX_FRAMES = 100;
   if (aMaxDepth < 0) {
@@ -506,18 +510,18 @@ JSStackFrame::CreateStackFrameLocation(u
                                        const char* aFunctionName,
                                        int32_t aLineNumber,
                                        nsIStackFrame* aCaller)
 {
   nsRefPtr<JSStackFrame> self = new JSStackFrame(nullptr, 0);
 
   self->mLanguage = aLanguage;
   self->mLineno = aLineNumber;
-  self->mFilename = aFilename;
-  self->mFunname = aFunctionName;
+  CopyUTF8toUTF16(aFilename, self->mFilename);
+  CopyUTF8toUTF16(aFunctionName, self->mFunname);
 
   self->mCaller = aCaller;
 
   return self.forget();
 }
 
 already_AddRefed<nsIStackFrame>
 CreateStack(JSContext* aCx, int32_t aMaxDepth)
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -188,16 +188,26 @@ template <typename T>
 bool
 ToJSValue(JSContext* aCx,
           const nsRefPtr<T>& aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   return ToJSValue(aCx, *aArgument.get(), aValue);
 }
 
+// Accept WebIDL dictionaries
+template <class T>
+typename EnableIf<IsBaseOf<DictionaryBase, T>::value, bool>::Type
+ToJSValue(JSContext* aCx,
+          const T& aArgument,
+          JS::MutableHandle<JS::Value> aValue)
+{
+  return aArgument.ToObject(aCx, aValue);
+}
+
 // Accept arrays of other things we accept
 template <typename T>
 bool
 ToJSValue(JSContext* aCx,
           T* aArguments,
           size_t aLength,
           JS::MutableHandle<JS::Value> aValue)
 {
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -200,17 +200,32 @@ ClassToIcon(uint32_t aClass, nsAString& 
         break;
       }
       if (aClass & 0x20) {
         aRetIcon.AssignLiteral("camera-photo");
         break;
       }
       break;
   }
+
+  if (aRetIcon.IsEmpty()) {
+    if (HAS_AUDIO(aClass)) {
+      /**
+       * Property 'Icon' may be missed due to CoD of major class is TOY(0x08).
+       * But we need to assign Icon as audio-card if service class is 'Audio'.
+       * This is for PTS test case TC_AG_COD_BV_02_I. As HFP specification
+       * defines that service class is 'Audio' can be considered as HFP HF.
+       */
+      aRetIcon.AssignLiteral("audio-card");
+    } else {
+      BT_LOGR("No icon to match class: %x", aClass);
+    }
+  }
 }
+
 static ControlPlayStatus
 PlayStatusStringToControlPlayStatus(const nsAString& aPlayStatus)
 {
   ControlPlayStatus playStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN;
   if (aPlayStatus.EqualsLiteral("STOPPED")) {
     playStatus = ControlPlayStatus::PLAYSTATUS_STOPPED;
   } else if (aPlayStatus.EqualsLiteral("PLAYING")) {
     playStatus = ControlPlayStatus::PLAYSTATUS_PLAYING;
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -638,29 +638,27 @@ void BluetoothHfpManager::ProcessDialCal
   if (!mDialingRequestProcessed) {
     BT_HF_DISPATCH_MAIN(MainThreadTaskCmd::POST_TASK_RESPOND_TO_BLDN);
   }
 }
 
 void
 BluetoothHfpManager::ProcessAtCnum()
 {
-  NS_ENSURE_TRUE_VOID(!mMsisdn.IsEmpty());
-  NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
+  if (!mMsisdn.IsEmpty()) {
+    nsAutoCString message("+CNUM: ,\"");
+    message.Append(NS_ConvertUTF16toUTF8(mMsisdn).get());
+    message.AppendLiteral("\",");
+    message.AppendInt(BTHF_CALL_ADDRTYPE_UNKNOWN);
+    message.AppendLiteral(",,4");
 
-  nsAutoCString message("+CNUM: ,\"");
-  message.Append(NS_ConvertUTF16toUTF8(mMsisdn).get());
-  message.AppendLiteral("\",");
-  message.AppendInt(BTHF_CALL_ADDRTYPE_UNKNOWN);
-  message.AppendLiteral(",,4");
+    SendLine(message.get());
+  }
 
-  NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
-    sBluetoothHfpInterface->formatted_at_response(message.get()));
-  NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
-    sBluetoothHfpInterface->at_response(BTHF_AT_RESPONSE_OK, 0));
+  SendResponse(BTHF_AT_RESPONSE_OK);
 }
 
 void
 BluetoothHfpManager::ProcessAtCind()
 {
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
 
   int numActive = GetNumberOfCalls(nsITelephonyProvider::CALL_STATE_CONNECTED);
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -722,17 +722,17 @@ nsDOMCameraControl::StartRecording(const
 
   NotifyRecordingStatusChange(NS_LITERAL_STRING("starting"));
 
 #ifdef MOZ_B2G
   if (!mAudioChannelAgent) {
     mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
     if (mAudioChannelAgent) {
       // Camera app will stop recording when it falls to the background, so no callback is necessary.
-      mAudioChannelAgent->Init(mWindow, AUDIO_CHANNEL_CONTENT, nullptr);
+      mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
       // Video recording doesn't output any sound, so it's not necessary to check canPlay.
       int32_t canPlay;
       mAudioChannelAgent->StartPlaying(&canPlay);
     }
   }
 #endif
 
   nsCOMPtr<nsIDOMDOMRequest> request;
--- a/dom/interfaces/html/moz.build
+++ b/dom/interfaces/html/moz.build
@@ -8,17 +8,16 @@ XPIDL_SOURCES += [
     'nsIDOMHTMLAnchorElement.idl',
     'nsIDOMHTMLAppletElement.idl',
     'nsIDOMHTMLAreaElement.idl',
     'nsIDOMHTMLAudioElement.idl',
     'nsIDOMHTMLBaseElement.idl',
     'nsIDOMHTMLBodyElement.idl',
     'nsIDOMHTMLBRElement.idl',
     'nsIDOMHTMLButtonElement.idl',
-    'nsIDOMHTMLByteRanges.idl',
     'nsIDOMHTMLCanvasElement.idl',
     'nsIDOMHTMLCollection.idl',
     'nsIDOMHTMLDirectoryElement.idl',
     'nsIDOMHTMLDivElement.idl',
     'nsIDOMHTMLDocument.idl',
     'nsIDOMHTMLElement.idl',
     'nsIDOMHTMLEmbedElement.idl',
     'nsIDOMHTMLFieldSetElement.idl',
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMHTMLByteRanges.idl
+++ /dev/null
@@ -1,27 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include "domstubs.idl"
-
-[scriptable, uuid(992c540c-4d81-42df-80a6-f71ede2b59d8)]
-interface nsIDOMHTMLByteRanges : nsISupports
-{
-  /* The number of ranges represented by the object */
-  readonly attribute unsigned long length;
-
-  /* The start(index) method must return the position of the first
-     byte of the indexth range represented by the object. */
-  unsigned long start(in unsigned long index);
-
-  /* The end(index) method must return the position of the byte
-     immediately after the last byte of the indexth range represented
-     by the object. (The byte position returned by this method is not
-     in the range itself. If the first byte of the range is the byte
-     at position 0, and the entire stream of bytes is in the range,
-     then the value of the position of the byte returned by this
-     method for that range will be the same as the number of bytes in
-     the stream.) */
-  unsigned long end(in unsigned long index);
-};
--- a/dom/interfaces/storage/moz.build
+++ b/dom/interfaces/storage/moz.build
@@ -3,17 +3,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
     'nsIDOMStorage.idl',
     'nsIDOMStorageEvent.idl',
     'nsIDOMStorageManager.idl',
-    'nsIDOMToString.idl',
 ]
 
 XPIDL_MODULE = 'dom_storage'
 
 EXPORTS += [
     'nsPIDOMStorage.h',
 ]
 
deleted file mode 100644
--- a/dom/interfaces/storage/nsIDOMToString.idl
+++ /dev/null
@@ -1,23 +0,0 @@
-/* -*- Mode: IDL; 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 "domstubs.idl"
-
-/**
- * The sole purpose of this interface is to make it easy for XPCOM
- * object's to hook into JS' toString() when interacting with
- * XPConnect. If you implement this interface and advertize it in the
- * object's classinfo, you'll get automatic mapping from JS'
- * toString() to the toString() method in this interface.
- *
- * XXXjst: This doesn't really belong in dom/public/idl/storage, but
- * it gets to live here until I find a more suitable place.
- */
-
-[scriptable, uuid(2a72e20f-e337-4822-8994-2e35b5550d03)]
-interface nsIDOMToString : nsISupports
-{
-  DOMString toString();
-};
--- a/dom/interfaces/stylesheets/nsIDOMStyleSheetList.idl
+++ b/dom/interfaces/stylesheets/nsIDOMStyleSheetList.idl
@@ -8,14 +8,15 @@
 /**
  * The nsIDOMStyleSheetList interface is a datatype for a style sheet
  * list in the Document Object Model.
  *
  * For more information on this interface please see
  * http://www.w3.org/TR/DOM-Level-2-Style
  */
 
-[scriptable, uuid(a6cf9081-15b3-11d2-932e-00805f8add32)]
+[scriptable, uuid(0e424250-ac2a-4fe2-bccd-a45824af090e)]
 interface nsIDOMStyleSheetList : nsISupports
 {
   readonly attribute unsigned long    length;
+  [binaryname(SlowItem)]
   nsIDOMStyleSheet         item(in unsigned long index);
 };
--- a/dom/ipc/AppProcessChecker.cpp
+++ b/dom/ipc/AppProcessChecker.cpp
@@ -9,16 +9,17 @@
 #include "nsIPermissionManager.h"
 #ifdef MOZ_CHILD_PERMISSIONS
 #include "ContentParent.h"
 #include "mozIApplication.h"
 #include "mozilla/hal_sandbox/PHalParent.h"
 #include "nsIAppsService.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsPrintfCString.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "TabParent.h"
 
 #include <algorithm>
 
 using namespace mozilla::dom;
@@ -110,17 +111,21 @@ AssertAppProcess(PContentParent* aActor,
   const InfallibleTArray<PBrowserParent*>& browsers =
     aActor->ManagedPBrowserParent();
   for (uint32_t i = 0; i < browsers.Length(); ++i) {
     if (AssertAppProcess(browsers[i], aType, aCapability)) {
       return true;
     }
   }
 
-  printf_stderr("Security problem: Content process does not have `%s'.  It will be killed.\n", aCapability);
+  NS_ERROR(
+    nsPrintfCString(
+      "Security problem: Content process does not have `%s'.  It will be killed.\n",
+      aCapability).get());
+
   static_cast<ContentParent*>(aActor)->KillHard();
 
   return false;
 }
 
 bool
 AssertAppStatus(PContentParent* aActor,
                 unsigned short aStatus)
@@ -128,17 +133,21 @@ AssertAppStatus(PContentParent* aActor,
   const InfallibleTArray<PBrowserParent*>& browsers =
     aActor->ManagedPBrowserParent();
   for (uint32_t i = 0; i < browsers.Length(); ++i) {
     if (AssertAppStatus(browsers[i], aStatus)) {
       return true;
     }
   }
 
-  printf_stderr("Security problem: Content process does not have `%d' status.  It will be killed.\n", aStatus);
+  NS_ERROR(
+    nsPrintfCString(
+      "Security problem: Content process does not have `%d' status.  It will be killed.",
+      aStatus).get());
+
   static_cast<ContentParent*>(aActor)->KillHard();
 
   return false;
 }
 
 bool
 AssertAppProcess(PHalParent* aActor,
                  AssertAppProcessType aType,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1894,75 +1894,75 @@ ContentParent::RecvFirstIdle()
     // which we use as a good time to prelaunch another process. If we
     // prelaunch any sooner than this, then we'll be competing with the
     // child process and slowing it down.
     PreallocatedProcessManager::AllocateAfterDelay();
     return true;
 }
 
 bool
-ContentParent::RecvAudioChannelGetState(const AudioChannelType& aType,
+ContentParent::RecvAudioChannelGetState(const AudioChannel& aChannel,
                                         const bool& aElementHidden,
                                         const bool& aElementWasHidden,
                                         AudioChannelState* aState)
 {
     nsRefPtr<AudioChannelService> service =
         AudioChannelService::GetAudioChannelService();
     *aState = AUDIO_CHANNEL_STATE_NORMAL;
     if (service) {
-        *aState = service->GetStateInternal(aType, mChildID,
+        *aState = service->GetStateInternal(aChannel, mChildID,
                                             aElementHidden, aElementWasHidden);
     }
     return true;
 }
 
 bool
-ContentParent::RecvAudioChannelRegisterType(const AudioChannelType& aType,
+ContentParent::RecvAudioChannelRegisterType(const AudioChannel& aChannel,
                                             const bool& aWithVideo)
 {
     nsRefPtr<AudioChannelService> service =
         AudioChannelService::GetAudioChannelService();
     if (service) {
-        service->RegisterType(aType, mChildID, aWithVideo);
+        service->RegisterType(aChannel, mChildID, aWithVideo);
     }
     return true;
 }
 
 bool
-ContentParent::RecvAudioChannelUnregisterType(const AudioChannelType& aType,
+ContentParent::RecvAudioChannelUnregisterType(const AudioChannel& aChannel,
                                               const bool& aElementHidden,
                                               const bool& aWithVideo)
 {
     nsRefPtr<AudioChannelService> service =
         AudioChannelService::GetAudioChannelService();
     if (service) {
-        service->UnregisterType(aType, aElementHidden, mChildID, aWithVideo);
+        service->UnregisterType(aChannel, aElementHidden, mChildID, aWithVideo);
     }
     return true;
 }
 
 bool
 ContentParent::RecvAudioChannelChangedNotification()
 {
     nsRefPtr<AudioChannelService> service =
         AudioChannelService::GetAudioChannelService();
     if (service) {
        service->SendAudioChannelChangedNotification(ChildID());
     }
     return true;
 }
 
 bool
-ContentParent::RecvAudioChannelChangeDefVolChannel(
-  const AudioChannelType& aType, const bool& aHidden)
+ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
+                                                   const bool& aHidden)
 {
     nsRefPtr<AudioChannelService> service =
         AudioChannelService::GetAudioChannelService();
     if (service) {
-       service->SetDefaultVolumeControlChannelInternal(aType,
+       service->SetDefaultVolumeControlChannelInternal(aChannel,
                                                        aHidden, mChildID);
     }
     return true;
 }
 
 bool
 ContentParent::RecvBroadcastVolume(const nsString& aVolumeName)
 {
@@ -1979,18 +1979,21 @@ ContentParent::RecvBroadcastVolume(const
 #endif
 }
 
 bool
 ContentParent::RecvNuwaReady()
 {
 #ifdef MOZ_NUWA_PROCESS
     if (!IsNuwaProcess()) {
-        printf_stderr("Terminating child process %d for unauthorized IPC message: "
-                      "NuwaReady", Pid());
+        NS_ERROR(
+            nsPrintfCString(
+                "Terminating child process %d for unauthorized IPC message: NuwaReady",
+                Pid()).get());
+
         KillHard();
         return false;
     }
     PreallocatedProcessManager::OnNuwaReady();
     return true;
 #else
     NS_ERROR("ContentParent::RecvNuwaReady() not implemented!");
     return false;
@@ -1998,18 +2001,21 @@ ContentParent::RecvNuwaReady()
 }
 
 bool
 ContentParent::RecvAddNewProcess(const uint32_t& aPid,
                                  const InfallibleTArray<ProtocolFdMapping>& aFds)
 {
 #ifdef MOZ_NUWA_PROCESS
     if (!IsNuwaProcess()) {
-        printf_stderr("Terminating child process %d for unauthorized IPC message: "
-                      "AddNewProcess(%d)", Pid(), aPid);
+        NS_ERROR(
+            nsPrintfCString(
+                "Terminating child process %d for unauthorized IPC message: "
+                "AddNewProcess(%d)", Pid(), aPid).get());
+
         KillHard();
         return false;
     }
     nsRefPtr<ContentParent> content;
     content = new ContentParent(this,
                                 MAGIC_PREALLOCATED_APP_MANIFEST_URL,
                                 aPid,
                                 aFds);
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -485,30 +485,30 @@ private:
                                  const uint32_t& aColNumber,
                                  const uint32_t& aFlags,
                                  const nsCString& aCategory) MOZ_OVERRIDE;
 
     virtual bool RecvPrivateDocShellsExist(const bool& aExist) MOZ_OVERRIDE;
 
     virtual bool RecvFirstIdle() MOZ_OVERRIDE;
 
-    virtual bool RecvAudioChannelGetState(const AudioChannelType& aType,
+    virtual bool RecvAudioChannelGetState(const AudioChannel& aChannel,
                                           const bool& aElementHidden,
                                           const bool& aElementWasHidden,
                                           AudioChannelState* aValue) MOZ_OVERRIDE;
 
-    virtual bool RecvAudioChannelRegisterType(const AudioChannelType& aType,
+    virtual bool RecvAudioChannelRegisterType(const AudioChannel& aChannel,
                                               const bool& aWithVideo) MOZ_OVERRIDE;
-    virtual bool RecvAudioChannelUnregisterType(const AudioChannelType& aType,
+    virtual bool RecvAudioChannelUnregisterType(const AudioChannel& aChannel,
                                                 const bool& aElementHidden,
                                                 const bool& aWithVideo) MOZ_OVERRIDE;
 
     virtual bool RecvAudioChannelChangedNotification() MOZ_OVERRIDE;
 
-    virtual bool RecvAudioChannelChangeDefVolChannel(const AudioChannelType& aType,
+    virtual bool RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
                                                      const bool& aHidden) MOZ_OVERRIDE;
 
     virtual bool RecvBroadcastVolume(const nsString& aVolumeName) MOZ_OVERRIDE;
 
     virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue) MOZ_OVERRIDE;
 
     virtual bool RecvSpeakerManagerForceSpeaker(const bool& aEnable) MOZ_OVERRIDE;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -41,17 +41,17 @@ using struct ResourceMapping from "mozil
 using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
 using base::ChildPrivileges from "base/process_util.h";
 using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using mozilla::dom::asmjscache::OpenMode from "mozilla/dom/asmjscache/AsmJSCache.h";
 using mozilla::dom::asmjscache::WriteParams from "mozilla/dom/asmjscache/AsmJSCache.h";
-using mozilla::dom::AudioChannelType from "AudioChannelCommon.h";
+using mozilla::dom::AudioChannel from "mozilla/dom/AudioChannelBinding.h";
 using mozilla::dom::AudioChannelState from "AudioChannelCommon.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h";
 using gfxIntSize from "nsSize.h";
 
 namespace mozilla {
 namespace dom {
 
@@ -510,28 +510,27 @@ parent:
 
     // Notify the parent of the presence or absence of private docshells
     PrivateDocShellsExist(bool aExist);
 
     // Tell the parent that the child has gone idle for the first time
     async FirstIdle();
 
     // Get Muted from the main AudioChannelService.
-    sync AudioChannelGetState(AudioChannelType aType, bool aElementHidden,
+    sync AudioChannelGetState(AudioChannel aChannel, bool aElementHidden,
                               bool aElementWasHidden)
         returns (AudioChannelState value);
 
-    sync AudioChannelRegisterType(AudioChannelType aType, bool aWithVideo);
-    sync AudioChannelUnregisterType(AudioChannelType aType,
+    sync AudioChannelRegisterType(AudioChannel aChannel, bool aWithVideo);
+    sync AudioChannelUnregisterType(AudioChannel aChannel,
                                     bool aElementHidden,
                                     bool aWithVideo);
 
     async AudioChannelChangedNotification();
-    async AudioChannelChangeDefVolChannel(AudioChannelType aType,
-                                          bool aHidden);
+    async AudioChannelChangeDefVolChannel(int32_t aChannel, bool aHidden);
 
     async FilePathUpdateNotify(nsString aType,
                                nsString aStorageName,
                                nsString aFilepath,
                                nsCString aReason);
     // get nsIVolumeService to broadcast volume information
     async BroadcastVolume(nsString volumeName);
 
--- a/dom/ipc/TabMessageUtils.h
+++ b/dom/ipc/TabMessageUtils.h
@@ -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/. */
 
 #ifndef TABMESSAGE_UTILS_H
 #define TABMESSAGE_UTILS_H
 
 #include "AudioChannelCommon.h"
 #include "ipc/IPCMessageUtils.h"
+#include "mozilla/dom/AudioChannelBinding.h"
 #include "nsIDOMEvent.h"
 #include "nsCOMPtr.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 namespace mozilla {
@@ -53,26 +54,48 @@ struct ParamTraits<mozilla::dom::RemoteD
     return mozilla::dom::ReadRemoteEvent(aMsg, aIter, aResult);
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
   }
 };
 
-template <>
-struct ParamTraits<mozilla::dom::AudioChannelType>
-  : public EnumSerializer<mozilla::dom::AudioChannelType,
-                          mozilla::dom::AUDIO_CHANNEL_DEFAULT,
-                          mozilla::dom::AUDIO_CHANNEL_LAST>
-{ };
+template<>
+struct ParamTraits<mozilla::dom::AudioChannel>
+{
+  typedef mozilla::dom::AudioChannel paramType;
+
+  static bool IsLegalValue(const paramType &aValue) {
+    return aValue <= mozilla::dom::AudioChannel::Publicnotification;
+  }
+
+  static void Write(Message* aMsg, const paramType& aValue) {
+    MOZ_ASSERT(IsLegalValue(aValue));
+    WriteParam(aMsg, (uint32_t)aValue);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult) {
+    uint32_t value;
+    if(!ReadParam(aMsg, aIter, &value) ||
+       !IsLegalValue(paramType(value))) {
+      return false;
+    }
+    *aResult = paramType(value);
+    return true;
+  }
+
+  static void Log(const paramType& aParam, std::wstring* aLog)
+  {
+  }
+};
 
 template <>
 struct ParamTraits<mozilla::dom::AudioChannelState>
   : public EnumSerializer<mozilla::dom::AudioChannelState,
-                           mozilla::dom::AUDIO_CHANNEL_STATE_NORMAL,
-                           mozilla::dom::AUDIO_CHANNEL_STATE_LAST>
+                          mozilla::dom::AUDIO_CHANNEL_STATE_NORMAL,
+                          mozilla::dom::AUDIO_CHANNEL_STATE_LAST>
 { };
 
 }
 
 
 #endif
--- a/dom/mobilemessage/src/MmsMessage.cpp
+++ b/dom/mobilemessage/src/MmsMessage.cpp
@@ -9,16 +9,17 @@
 #include "jsfriendapi.h" // For js_DateGetMsecSinceEpoch
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "nsIDOMFile.h"
 #include "nsTArrayHelpers.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/mobilemessage/Constants.h" // For MessageType
 #include "mozilla/dom/mobilemessage/SmsTypes.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "nsDOMFile.h"
 #include "nsCxPusher.h"
 
 using namespace mozilla::dom::mobilemessage;
 
 DOMCI_DATA(MozMmsMessage, mozilla::dom::MmsMessage)
 
 namespace mozilla {
@@ -462,86 +463,20 @@ MmsMessage::GetDeliveryInfo(JSContext* a
   // to return a more correct value. Ex, if .delivery = 'received', we should
   // also make .deliveryInfo = null, since the .deliveryInfo is useless.
   uint32_t length = mDeliveryInfo.Length();
   if (length == 0) {
     aDeliveryInfo.setNull();
     return NS_OK;
   }
 
-  JS::Rooted<JSObject*> deliveryInfo(
-    aCx, JS_NewArrayObject(aCx, length));
-  NS_ENSURE_TRUE(deliveryInfo, NS_ERROR_OUT_OF_MEMORY);
-
-  for (uint32_t i = 0; i < length; ++i) {
-    const MmsDeliveryInfo &info = mDeliveryInfo[i];
-
-    JS::Rooted<JSObject*> infoJsObj(
-      aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
-    NS_ENSURE_TRUE(infoJsObj, NS_ERROR_OUT_OF_MEMORY);
-
-    JS::Rooted<JS::Value> tmpJsVal(aCx);
-    JSString* tmpJsStr;
-
-    // Get |info.mReceiver|.
-    tmpJsStr = JS_NewUCStringCopyN(aCx,
-                                   info.mReceiver.get(),
-                                   info.mReceiver.Length());
-    NS_ENSURE_TRUE(tmpJsStr, NS_ERROR_OUT_OF_MEMORY);
-
-    tmpJsVal.setString(tmpJsStr);
-    if (!JS_DefineProperty(aCx, infoJsObj, "receiver", tmpJsVal,
-                           nullptr, nullptr, JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    // Get |info.mDeliveryStatus|.
-    tmpJsStr = JS_NewUCStringCopyN(aCx,
-                                   info.mDeliveryStatus.get(),
-                                   info.mDeliveryStatus.Length());
-    NS_ENSURE_TRUE(tmpJsStr, NS_ERROR_OUT_OF_MEMORY);
-
-    tmpJsVal.setString(tmpJsStr);
-    if (!JS_DefineProperty(aCx, infoJsObj, "deliveryStatus", tmpJsVal,
-                           nullptr, nullptr, JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    // Get |info.mDeliveryTimestamp|.
-    tmpJsVal.setNumber(static_cast<double>(info.mDeliveryTimestamp));
-    if (!JS_DefineProperty(aCx, infoJsObj, "deliveryTimestamp", tmpJsVal,
-                           nullptr, nullptr, JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    // Get |info.mReadStatus|.
-    tmpJsStr = JS_NewUCStringCopyN(aCx,
-                                   info.mReadStatus.get(),
-                                   info.mReadStatus.Length());
-    NS_ENSURE_TRUE(tmpJsStr, NS_ERROR_OUT_OF_MEMORY);
-
-    tmpJsVal.setString(tmpJsStr);
-    if (!JS_DefineProperty(aCx, infoJsObj, "readStatus", tmpJsVal,
-                           nullptr, nullptr, JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    // Get |info.mReadTimestamp|.
-    tmpJsVal.setNumber(static_cast<double>(info.mReadTimestamp));
-    if (!JS_DefineProperty(aCx, infoJsObj, "readTimestamp", tmpJsVal,
-                           nullptr, nullptr, JSPROP_ENUMERATE)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    if (!JS_SetElement(aCx, deliveryInfo, i, infoJsObj)) {
-      return NS_ERROR_FAILURE;
-    }
+  if (!ToJSValue(aCx, mDeliveryInfo, aDeliveryInfo)) {
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  aDeliveryInfo.setObject(*deliveryInfo);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MmsMessage::GetSender(nsAString& aSender)
 {
   aSender = mSender;
   return NS_OK;
--- a/dom/permission/tests/test_wifi-manage.html
+++ b/dom/permission/tests/test_wifi-manage.html
@@ -25,17 +25,17 @@ function verifier(success, failure) {
   }
 }
 
 var gData = [
   {
     perm: ["wifi-manage"],
     needParentPerm: true,
     obj: "mozWifiManager",
-    idl: "nsIDOMWifiManager",
+    webidl: "MozWifiManager",
     verifier: verifier.toSource(),
   },
 ]
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/system/gonk/AudioChannelManager.cpp
+++ b/dom/system/gonk/AudioChannelManager.cpp
@@ -20,17 +20,17 @@ namespace system {
 
 NS_IMPL_QUERY_INTERFACE_INHERITED1(AudioChannelManager, DOMEventTargetHelper,
                                    nsIDOMEventListener)
 NS_IMPL_ADDREF_INHERITED(AudioChannelManager, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(AudioChannelManager, DOMEventTargetHelper)
 
 AudioChannelManager::AudioChannelManager()
   : mState(SWITCH_STATE_UNKNOWN)
-  , mVolumeChannel(AUDIO_CHANNEL_DEFAULT)
+  , mVolumeChannel(-1)
 {
   RegisterSwitchObserver(SWITCH_HEADPHONES, this);
   mState = GetCurrentSwitchState(SWITCH_HEADPHONES);
   SetIsDOMBinding();
 }
 
 AudioChannelManager::~AudioChannelManager()
 {
@@ -75,69 +75,51 @@ AudioChannelManager::Notify(const Switch
 
 bool
 AudioChannelManager::SetVolumeControlChannel(const nsAString& aChannel)
 {
   if (aChannel.EqualsASCII("publicnotification")) {
     return false;
   }
 
-  AudioChannelType oldVolumeChannel = mVolumeChannel;
+  AudioChannel newChannel = AudioChannelService::GetAudioChannel(aChannel);
+
   // Only normal channel doesn't need permission.
-  if (aChannel.EqualsASCII("normal")) {
-    mVolumeChannel = AUDIO_CHANNEL_NORMAL;
-  } else {
+  if (newChannel != AudioChannel::Normal) {
     nsCOMPtr<nsIPermissionManager> permissionManager =
       do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
     if (!permissionManager) {
       return false;
     }
     uint32_t perm = nsIPermissionManager::UNKNOWN_ACTION;
     permissionManager->TestPermissionFromWindow(GetOwner(),
       nsCString(NS_LITERAL_CSTRING("audio-channel-") +
       NS_ConvertUTF16toUTF8(aChannel)).get(), &perm);
     if (perm != nsIPermissionManager::ALLOW_ACTION) {
       return false;
     }
-
-    if (aChannel.EqualsASCII("content")) {
-      mVolumeChannel = AUDIO_CHANNEL_CONTENT;
-    } else if (aChannel.EqualsASCII("notification")) {
-      mVolumeChannel = AUDIO_CHANNEL_NOTIFICATION;
-    } else if (aChannel.EqualsASCII("alarm")) {
-      mVolumeChannel = AUDIO_CHANNEL_ALARM;
-    } else if (aChannel.EqualsASCII("telephony")) {
-      mVolumeChannel = AUDIO_CHANNEL_TELEPHONY;
-    } else if (aChannel.EqualsASCII("ringer")) {
-      mVolumeChannel = AUDIO_CHANNEL_RINGER;
-    }
   }
 
-  if (oldVolumeChannel == mVolumeChannel) {
+  if (mVolumeChannel == (int32_t)newChannel) {
     return true;
   }
+
+  mVolumeChannel = (int32_t)newChannel;
+
   NotifyVolumeControlChannelChanged();
   return true;
 }
 
 bool
 AudioChannelManager::GetVolumeControlChannel(nsAString & aChannel)
 {
-  if (mVolumeChannel == AUDIO_CHANNEL_NORMAL) {
-    aChannel.AssignASCII("normal");
-  } else if (mVolumeChannel == AUDIO_CHANNEL_CONTENT) {
-    aChannel.AssignASCII("content");
-  } else if (mVolumeChannel == AUDIO_CHANNEL_NOTIFICATION) {
-    aChannel.AssignASCII("notification");
-  } else if (mVolumeChannel == AUDIO_CHANNEL_ALARM) {
-    aChannel.AssignASCII("alarm");
-  } else if (mVolumeChannel == AUDIO_CHANNEL_TELEPHONY) {
-    aChannel.AssignASCII("telephony");
-  } else if (mVolumeChannel == AUDIO_CHANNEL_RINGER) {
-    aChannel.AssignASCII("ringer");
+  if (mVolumeChannel >= 0) {
+    AudioChannelService::GetAudioChannelString(
+                                      static_cast<AudioChannel>(mVolumeChannel),
+                                      aChannel);
   } else {
     aChannel.AssignASCII("");
   }
 
   return true;
 }
 
 void
@@ -148,17 +130,17 @@ AudioChannelManager::NotifyVolumeControl
 
   bool isActive = false;
   docshell->GetIsActive(&isActive);
 
   AudioChannelService* service = AudioChannelService::GetAudioChannelService();
   if (isActive) {
     service->SetDefaultVolumeControlChannel(mVolumeChannel, isActive);
   } else {
-    service->SetDefaultVolumeControlChannel(AUDIO_CHANNEL_DEFAULT, isActive);
+    service->SetDefaultVolumeControlChannel(-1, isActive);
   }
 }
 
 NS_IMETHODIMP
 AudioChannelManager::HandleEvent(nsIDOMEvent* aEvent)
 {
   nsAutoString type;
   aEvent->GetType(type);
--- a/dom/system/gonk/AudioChannelManager.h
+++ b/dom/system/gonk/AudioChannelManager.h
@@ -61,16 +61,16 @@ public:
   bool GetVolumeControlChannel(nsAString& aChannel);
 
   IMPL_EVENT_HANDLER(headphoneschange)
 
 private:
   void NotifyVolumeControlChannelChanged();
 
   hal::SwitchState mState;
-  AudioChannelType mVolumeChannel;
+  int32_t mVolumeChannel;
 };
 
 } // namespace system
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_system_AudioChannelManager_h
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -124,24 +124,27 @@ public:
   NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
   {
     nsCOMPtr<nsIAudioManager> audioManager =
       do_GetService(NS_AUDIOMANAGER_CONTRACTID);
     NS_ENSURE_TRUE(JSVAL_IS_INT(aResult), NS_OK);
 
     int32_t volIndex = JSVAL_TO_INT(aResult);
     if (aName.EqualsLiteral("audio.volume.content")) {
-      audioManager->SetAudioChannelVolume(AUDIO_CHANNEL_CONTENT, volIndex);
+      audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Content,
+                                          volIndex);
     } else if (aName.EqualsLiteral("audio.volume.notification")) {
-      audioManager->SetAudioChannelVolume(AUDIO_CHANNEL_NOTIFICATION,
+      audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Notification,
                                           volIndex);
     } else if (aName.EqualsLiteral("audio.volume.alarm")) {
-      audioManager->SetAudioChannelVolume(AUDIO_CHANNEL_ALARM, volIndex);
+      audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Alarm,
+                                          volIndex);
     } else if (aName.EqualsLiteral("audio.volume.telephony")) {
-      audioManager->SetAudioChannelVolume(AUDIO_CHANNEL_TELEPHONY, volIndex);
+      audioManager->SetAudioChannelVolume((int32_t)AudioChannel::Telephony,
+                                          volIndex);
     } else if (aName.EqualsLiteral("audio.volume.bt_sco")) {
       static_cast<AudioManager *>(audioManager.get())->SetStreamVolumeIndex(
         AUDIO_STREAM_BLUETOOTH_SCO, volIndex);
     } else {
       MOZ_ASSUME_UNREACHABLE("unexpected audio channel for initializing "
                              "volume control");
     }
 
@@ -526,19 +529,19 @@ AudioManager::SetPhoneState(int32_t aSta
     mPhoneAudioAgent = nullptr;
   }
 
   if (aState == PHONE_STATE_IN_CALL || aState == PHONE_STATE_RINGTONE) {
     mPhoneAudioAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
     MOZ_ASSERT(mPhoneAudioAgent);
     if (aState == PHONE_STATE_IN_CALL) {
       // Telephony doesn't be paused by any other channels.
-      mPhoneAudioAgent->Init(nullptr, AUDIO_CHANNEL_TELEPHONY, nullptr);
+      mPhoneAudioAgent->Init(nullptr, (int32_t)AudioChannel::Telephony, nullptr);
     } else {
-      mPhoneAudioAgent->Init(nullptr, AUDIO_CHANNEL_RINGER, nullptr);
+      mPhoneAudioAgent->Init(nullptr, (int32_t)AudioChannel::Ringer, nullptr);
     }
 
     // Telephony can always play.
     int32_t canPlay;
     mPhoneAudioAgent->StartPlaying(&canPlay);
   }
 
   return NS_OK;
@@ -603,97 +606,97 @@ AudioManager::SetFmRadioAudioEnabled(boo
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 }
 
 NS_IMETHODIMP
 AudioManager::SetAudioChannelVolume(int32_t aChannel, int32_t aIndex) {
   nsresult status;
 
-  switch (aChannel) {
-    case AUDIO_CHANNEL_CONTENT:
+  switch (static_cast<AudioChannel>(aChannel)) {
+    case AudioChannel::Content:
       // sync FMRadio's volume with content channel.
       if (IsDeviceOn(AUDIO_DEVICE_OUT_FM)) {
         status = SetStreamVolumeIndex(AUDIO_STREAM_FM, aIndex);
         NS_ENSURE_SUCCESS(status, status);
       }
       status = SetStreamVolumeIndex(AUDIO_STREAM_MUSIC, aIndex);
       NS_ENSURE_SUCCESS(status, status);
       status = SetStreamVolumeIndex(AUDIO_STREAM_SYSTEM, aIndex);
       break;
-    case AUDIO_CHANNEL_NOTIFICATION:
+    case AudioChannel::Notification:
       status = SetStreamVolumeIndex(AUDIO_STREAM_NOTIFICATION, aIndex);
       NS_ENSURE_SUCCESS(status, status);
       status = SetStreamVolumeIndex(AUDIO_STREAM_RING, aIndex);
       break;
-    case AUDIO_CHANNEL_ALARM:
+    case AudioChannel::Alarm:
       status = SetStreamVolumeIndex(AUDIO_STREAM_ALARM, aIndex);
       break;
-    case AUDIO_CHANNEL_TELEPHONY:
+    case AudioChannel::Telephony:
       status = SetStreamVolumeIndex(AUDIO_STREAM_VOICE_CALL, aIndex);
       break;
     default:
       return NS_ERROR_INVALID_ARG;
   }
 
   return status;
 }
 
 NS_IMETHODIMP
 AudioManager::GetAudioChannelVolume(int32_t aChannel, int32_t* aIndex) {
   if (!aIndex) {
     return NS_ERROR_NULL_POINTER;
   }
 
-  switch (aChannel) {
-    case AUDIO_CHANNEL_CONTENT:
+  switch (static_cast<AudioChannel>(aChannel)) {
+    case AudioChannel::Content:
       MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC] ==
                  mCurrentStreamVolumeTbl[AUDIO_STREAM_SYSTEM]);
       *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_MUSIC];
       break;
-    case AUDIO_CHANNEL_NOTIFICATION:
+    case AudioChannel::Notification:
       MOZ_ASSERT(mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
                  mCurrentStreamVolumeTbl[AUDIO_STREAM_RING]);
       *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION];
       break;
-    case AUDIO_CHANNEL_ALARM:
+    case AudioChannel::Alarm:
       *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_ALARM];
       break;
-    case AUDIO_CHANNEL_TELEPHONY:
+    case AudioChannel::Telephony:
       *aIndex = mCurrentStreamVolumeTbl[AUDIO_STREAM_VOICE_CALL];
       break;
     default:
       return NS_ERROR_INVALID_ARG;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioManager::GetMaxAudioChannelVolume(int32_t aChannel, int32_t* aMaxIndex) {
   if (!aMaxIndex) {
     return NS_ERROR_NULL_POINTER;
   }
 
   int32_t stream;
-  switch (aChannel) {
-    case AUDIO_CHANNEL_CONTENT:
+  switch (static_cast<AudioChannel>(aChannel)) {
+    case AudioChannel::Content:
       MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_MUSIC] ==
                  sMaxStreamVolumeTbl[AUDIO_STREAM_SYSTEM]);
       stream = AUDIO_STREAM_MUSIC;
       break;
-    case AUDIO_CHANNEL_NOTIFICATION:
+    case AudioChannel::Notification:
       MOZ_ASSERT(sMaxStreamVolumeTbl[AUDIO_STREAM_NOTIFICATION] ==
                  sMaxStreamVolumeTbl[AUDIO_STREAM_RING]);
       stream = AUDIO_STREAM_NOTIFICATION;
       break;
-    case AUDIO_CHANNEL_ALARM:
+    case AudioChannel::Alarm:
       stream = AUDIO_STREAM_ALARM;
       break;
-    case AUDIO_CHANNEL_TELEPHONY:
+    case AudioChannel::Telephony:
       stream = AUDIO_STREAM_VOICE_CALL;
       break;
     default:
       return NS_ERROR_INVALID_ARG;
   }
 
   *aMaxIndex = sMaxStreamVolumeTbl[stream];
    return NS_OK;
--- a/dom/telephony/test/marionette/head.js
+++ b/dom/telephony/test/marionette/head.js
@@ -1,13 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let Promise = SpecialPowers.Cu.import("resource://gre/modules/Promise.jsm").Promise;
 let telephony;
+let conference;
 
 /**
  * Emulator helper.
  */
 let emulator = (function() {
   let pendingCmdCount = 0;
   let originalRunEmulatorCmd = runEmulatorCmd;
 
@@ -46,24 +47,16 @@ let emulator = (function() {
     waitFinish: waitFinish
   };
 }());
 
 /**
  * Telephony related helper functions.
  */
 (function() {
-  function checkInitialState() {
-    log("Verify initial state.");
-    ok(telephony, "telephony");
-    is(telephony.active, null, "telephony.active");
-    ok(telephony.calls, "telephony.calls");
-    is(telephony.calls.length, 0, "telephony.calls.length");
-  }
-
   /**
    * @return Promise
    */
   function clearCalls() {
     let deferred = Promise.defer();
 
     log("Clear existing calls.");
     emulator.run("gsm clear", function(result) {
@@ -77,18 +70,992 @@ let emulator = (function() {
         log("Failed to clear existing calls.");
         deferred.reject();
       }
     });
 
     return deferred.promise;
   }
 
-  this.checkInitialState = checkInitialState;
-  this.clearCalls = clearCalls;
+  /**
+   * Provide a string with format of the emulator call list result.
+   *
+   * @param prefix
+   *        Possible values are "outbound" and "inbound".
+   * @param number
+   *        Call number.
+   * @return A string with format of the emulator call list result.
+   */
+  function callStrPool(prefix, number) {
+    let padding = "           : ";
+    let numberInfo = prefix + number + padding.substr(number.length);
+
+    let info = {};
+    let states = ['ringing', 'incoming', 'active', 'held'];
+    for (let state of states) {
+      info[state] = numberInfo + state;
+    }
+
+    return info;
+  }
+
+  /**
+   * Provide a corresponding string of an outgoing call. The string is with
+   * format of the emulator call list result.
+   *
+   * @param number
+   *        Number of an outgoing call.
+   * @return A string with format of the emulator call list result.
+   */
+  function outCallStrPool(number) {
+    return callStrPool("outbound to  ", number);
+  }
+
+  /**
+   * Provide a corresponding string of an incoming call. The string is with
+   * format of the emulator call list result.
+   *
+   * @param number
+   *        Number of an incoming call.
+   * @return A string with format of the emulator call list result.
+   */
+  function inCallStrPool(number) {
+    return callStrPool("inbound from ", number);
+  }
+
+  /**
+   * Check utility functions.
+   */
+
+  function checkInitialState() {
+    log("Verify initial state.");
+    ok(telephony.calls, 'telephony.call');
+    checkTelephonyActiveAndCalls(null, []);
+    ok(conference.calls, 'conference.calls');
+    checkConferenceStateAndCalls('', []);
+  }
+
+  /**
+   * Convenient helper to compare a TelephonyCall and a received call event.
+   */
+  function checkEventCallState(event, call, state) {
+    is(call, event.call, "event.call");
+    is(call.state, state, "call state");
+  }
+
+  /**
+   * Convenient helper to check mozTelephony.active and mozTelephony.calls.
+   */
+  function checkTelephonyActiveAndCalls(active, calls) {
+    is(telephony.active, active, "telephony.active");
+    is(telephony.calls.length, calls.length, "telephony.calls");
+    for (let i = 0; i < calls.length; ++i) {
+      is(telephony.calls[i], calls[i]);
+    }
+  }
+
+  /**
+   * Convenient helper to check mozTelephony.conferenceGroup.state and
+   * .conferenceGroup.calls.
+   */
+  function checkConferenceStateAndCalls(state, calls) {
+    is(conference.state, state, "conference.state");
+    is(conference.calls.length, calls.length, "conference.calls");
+    for (let i = 0; i < calls.length; i++) {
+      is(conference.calls[i], calls[i]);
+    }
+  }
+
+  /**
+   * Convenient helper to handle *.oncallschanged event.
+   *
+   * @param container
+   *        Representation of "mozTelephony" or "mozTelephony.conferenceGroup."
+   * @param containerName
+   *        Name of container. Could be an arbitrary string, used for debug
+   *        messages only.
+   * @param expectedCalls
+   *        An array of calls.
+   * @param callback
+   *        A callback function.
+   */
+  function check_oncallschanged(container, containerName, expectedCalls,
+                                callback) {
+    container.oncallschanged = function(event) {
+      log("Received 'callschanged' event for the " + containerName);
+      if (event.call) {
+        let index = expectedCalls.indexOf(event.call);
+        ok(index != -1);
+        expectedCalls.splice(index, 1);
+
+        if (expectedCalls.length === 0) {
+          container.oncallschanged = null;
+          callback();
+        }
+      }
+    };
+  }
+
+  /**
+   * Convenient helper to handle *.ongroupchange event.
+   *
+   * @param call
+   *        A TelephonyCall object.
+   * @param callName
+   *        Name of a call. Could be an arbitrary string, used for debug messages
+   *        only.
+   * @param group
+   *        Representation of mozTelephony.conferenceGroup.
+   * @param callback
+   *        A callback function.
+   */
+  function check_ongroupchange(call, callName, group, callback) {
+    call.ongroupchange = function(event) {
+      log("Received 'groupchange' event for the " + callName);
+      call.ongroupchange = null;
+
+      is(call.group, group);
+      callback();
+    };
+  }
+
+  /**
+   * Convenient helper to handle *.onstatechange event.
+   *
+   * @param container
+   *        Representation of a TelephonyCall or mozTelephony.conferenceGroup.
+   * @param containerName
+   *        Name of container. Could be an arbitrary string, used for debug messages
+   *        only.
+   * @param state
+   *        A string.
+   * @param callback
+   *        A callback function.
+   */
+  function check_onstatechange(container, containerName, state, callback) {
+    container.onstatechange = function(event) {
+      log("Received 'statechange' event for the " + containerName);
+      container.onstatechange = null;
+
+      is(container.state, state);
+      callback();
+    };
+  }
+
+  /**
+   * Convenient helper to check the sequence of call state and event handlers.
+   *
+   * @param state
+   *        A string of the expected call state.
+   * @param previousEvent
+   *        A string of the event that should come before the expected state.
+   */
+  function StateEventChecker(state, previousEvent) {
+    let event = 'on' + state;
+
+    return function(call, callName, callback) {
+      call[event] = function() {
+        log("Received '" + state + "' event for the " + callName);
+        call[event] = null;
+
+        if (previousEvent) {
+          // We always clear the event handler when the event is received.
+          // Therefore, if the corresponding handler is not existed, the expected
+          // previous event has been already received.
+          ok(!call[previousEvent]);
+        }
+        is(call.state, state);
+        callback();
+      };
+    };
+  }
+
+  /**
+   * Convenient helper to check the call list existing in the emulator.
+   *
+   * @param expectedCallList
+   *        An array of call info with the format of "callStrPool()[state]".
+   * @return A deferred promise.
+   */
+  function checkEmulatorCallList(expectedCallList) {
+    let deferred = Promise.defer();
+
+    emulator.run("gsm list", function(result) {
+        log("Call list is now: " + result);
+        for (let i = 0; i < expectedCallList.length; ++i) {
+          is(result[i], expectedCallList[i], "emulator calllist");
+        }
+        is(result[expectedCallList.length], "OK", "emulator calllist");
+        deferred.resolve();
+        });
+
+    return deferred.promise;
+  }
+
+  /**
+   * Super convenient helper to check calls and state of mozTelephony and
+   * mozTelephony.conferenceGroup.
+   *
+   * @param active
+   *        A TelephonyCall object. Should be the expected active call.
+   * @param calls
+   *        An array of TelephonyCall objects. Should be the expected list of
+   *        mozTelephony.calls.
+   * @param conferenceState
+   *        A string. Should be the expected conference state.
+   * @param conferenceCalls
+   *        An array of TelephonyCall objects. Should be the expected list of
+   *        mozTelephony.conferenceGroup.calls.
+   */
+  function checkState(active, calls, conferenceState, conferenceCalls) {
+    checkTelephonyActiveAndCalls(active, calls);
+    checkConferenceStateAndCalls(conferenceState, conferenceCalls);
+  }
+
+  /**
+   * Super convenient helper to check calls and state of mozTelephony and
+   * mozTelephony.conferenceGroup as well as the calls existing in the emulator.
+   *
+   * @param active
+   *        A TelephonyCall object. Should be the expected active call.
+   * @param calls
+   *        An array of TelephonyCall objects. Should be the expected list of
+   *        mozTelephony.calls.
+   * @param conferenceState
+   *        A string. Should be the expected conference state.
+   * @param conferenceCalls
+   *        An array of TelephonyCall objects. Should be the expected list of
+   *        mozTelephony.conferenceGroup.calls.
+   * @param callList
+   *        An array of call info with the format of "callStrPool()[state]".
+   * @return A deferred promise.
+   */
+  function checkAll(active, calls, conferenceState, conferenceCalls, callList) {
+    checkState(active, calls, conferenceState, conferenceCalls);
+    return checkEmulatorCallList(callList);
+  }
+
+  /**
+   * Request utility functions.
+   */
+
+  /**
+   * Make sure there's no pending event before we jump to the next action.
+   *
+   * @param received
+   *        A string of the received event.
+   * @param pending
+   *        An array of the pending events.
+   * @param nextAction
+   *        A callback function that is called when there's no pending event.
+   */
+  function receivedPending(received, pending, nextAction) {
+    let index = pending.indexOf(received);
+    if (index != -1) {
+      pending.splice(index, 1);
+    }
+    if (pending.length === 0) {
+      nextAction();
+    }
+  }
+
+  /**
+   * Make an outgoing call.
+   *
+   * @param number
+   *        A string.
+   * @param serviceId [optional]
+   *        Identification of a service. 0 is set as default.
+   * @return A deferred promise.
+   */
+  function dial(number, serviceId) {
+    serviceId = typeof serviceId !== "undefined" ? serviceId : 0;
+    log("Make an outgoing call: " + number + ", serviceId: " + serviceId);
+
+    let deferred = Promise.defer();
+
+    telephony.dial(number, serviceId).then(call => {
+      ok(call);
+      is(call.number, number);
+      is(call.state, "dialing");
+      is(call.serviceId, serviceId);
+
+      call.onalerting = function onalerting(event) {
+        call.onalerting = null;
+        log("Received 'onalerting' call event.");
+        checkEventCallState(event, call, "alerting");
+        deferred.resolve(call);
+      };
+    });
+
+    return deferred.promise;
+  }
+
+  /**
+   * Answer an incoming call.
+   *
+   * @param call
+   *        An incoming TelephonyCall object.
+   * @param conferenceStateChangeCallback [optional]
+   *        A callback function which is called if answering an incoming call
+   *        triggers conference state change.
+   * @return A deferred promise.
+   */
+  function answer(call, conferenceStateChangeCallback) {
+    log("Answering the incoming call.");
+
+    let deferred = Promise.defer();
+    let done = function() {
+      deferred.resolve(call);
+    };
+
+    let pending = ["call.onconnected"];
+    let receive = function(name) {
+      receivedPending(name, pending, done);
+    };
+
+    // When there's already a connected conference call, answering a new incoming
+    // call triggers conference state change. We should wait for
+    // |conference.onstatechange| before checking the state of the conference call.
+    if (conference.state === "connected") {
+      pending.push("conference.onstatechange");
+      check_onstatechange(conference, "conference", "held", function() {
+        if (typeof conferenceStateChangeCallback === "function") {
+          conferenceStateChangeCallback();
+        }
+        receive("conference.onstatechange");
+      });
+    }
+
+    call.onconnecting = function onconnectingIn(event) {
+      log("Received 'connecting' call event for incoming call.");
+      call.onconnecting = null;
+      checkEventCallState(event, call, "connecting");
+    };
+
+    call.onconnected = function onconnectedIn(event) {
+      log("Received 'connected' call event for incoming call.");
+      call.onconnected = null;
+      checkEventCallState(event, call, "connected");
+      ok(!call.onconnecting);
+      receive("call.onconnected");
+    };
+    call.answer();
+
+    return deferred.promise;
+  }
+
+  /**
+   * Simulate an incoming call.
+   *
+   * @param number
+   *        A string.
+   * @return A deferred promise.
+   */
+  function remoteDial(number) {
+    log("Simulating an incoming call.");
+
+    let deferred = Promise.defer();
+
+    telephony.onincoming = function onincoming(event) {
+      log("Received 'incoming' call event.");
+      telephony.onincoming = null;
+
+      let call = event.call;
+
+      ok(call);
+      is(call.number, number);
+      is(call.state, "incoming");
+
+      deferred.resolve(call);
+    };
+    emulator.run("gsm call " + number);
+
+    return deferred.promise;
+  }
+
+  /**
+   * Remote party answers the call.
+   *
+   * @param call
+   *        A TelephonyCall object.
+   * @return A deferred promise.
+   */
+  function remoteAnswer(call) {
+    log("Remote answering the call.");
+
+    let deferred = Promise.defer();
+
+    call.onconnected = function onconnected(event) {
+      log("Received 'connected' call event.");
+      call.onconnected = null;
+      checkEventCallState(event, call, "connected");
+      deferred.resolve(call);
+    };
+    emulator.run("gsm accept " + call.number);
+
+    return deferred.promise;
+  }
+
+  /**
+   * Remote party hangs up the call.
+   *
+   * @param call
+   *        A TelephonyCall object.
+   * @return A deferred promise.
+   */
+  function remoteHangUp(call) {
+    log("Remote hanging up the call.");
+
+    let deferred = Promise.defer();
+
+    call.ondisconnected = function ondisconnected(event) {
+      log("Received 'disconnected' call event.");
+      call.ondisconnected = null;
+      checkEventCallState(event, call, "disconnected");
+      deferred.resolve(call);
+    };
+    emulator.run("gsm cancel " + call.number);
+
+    return deferred.promise;
+  }
+
+  /**
+   * Remote party hangs up all the calls.
+   *
+   * @param calls
+   *        An array of TelephonyCall objects.
+   * @return A deferred promise.
+   */
+  function remoteHangUpCalls(calls) {
+    let promise = Promise.resolve();
+
+    for (let call of calls) {
+      promise = promise.then(remoteHangUp.bind(null, call));
+    }
+
+    return promise;
+  }
+
+  /**
+   * Add calls to conference.
+   *
+   * @param callsToAdd
+   *        An array of TelephonyCall objects to be added into conference. The
+   *        length of the array should be 1 or 2.
+   * @param connectedCallback [optional]
+   *        A callback function which is called when conference state becomes
+   *        connected.
+   * @return A deferred promise.
+   */
+  function addCallsToConference(callsToAdd, connectedCallback) {
+    log("Add " + callsToAdd.length + " calls into conference.");
+
+    let deferred = Promise.defer();
+    let done = function() {
+      deferred.resolve();
+    };
+
+    let pending = ["conference.oncallschanged", "conference.onconnected"];
+    let receive = function(name) {
+      receivedPending(name, pending, done);
+    };
+
+    let check_onconnected  = StateEventChecker('connected', 'onresuming');
+
+    for (let call of callsToAdd) {
+      let callName = "callToAdd (" + call.number + ')';
+
+      let ongroupchange = callName + ".ongroupchange";
+      pending.push(ongroupchange);
+      check_ongroupchange(call, callName, conference,
+                          receive.bind(null, ongroupchange));
+
+      let onstatechange = callName + ".onstatechange";
+      pending.push(onstatechange);
+      check_onstatechange(call, callName, 'connected',
+                          receive.bind(null, onstatechange));
+    }
+
+    check_oncallschanged(conference, 'conference', callsToAdd,
+                         receive.bind(null, "conference.oncallschanged"));
+
+    check_onconnected(conference, "conference", function() {
+      ok(!conference.oncallschanged);
+      if (typeof connectedCallback === 'function') {
+        connectedCallback();
+      }
+      receive("conference.onconnected");
+    });
+
+    // Cannot use apply() through webidl, so just separate the cases to handle.
+    if (callsToAdd.length == 2) {
+      conference.add(callsToAdd[0], callsToAdd[1]);
+    } else {
+      conference.add(callsToAdd[0]);
+    }
+
+    return deferred.promise;
+  }
+
+  /**
+   * Hold the conference.
+   *
+   * @param calls
+   *        An array of TelephonyCall objects existing in conference.
+   * @param heldCallback [optional]
+   *        A callback function which is called when conference state becomes
+   *        held.
+   * @return A deferred promise.
+   */
+  function holdConference(calls, heldCallback) {
+    log("Holding the conference call.");
+
+    let deferred = Promise.defer();
+    let done = function() {
+      deferred.resolve();
+    };
+
+    let pending = ["conference.onholding", "conference.onheld"];
+    let receive = function(name) {
+      receivedPending(name, pending, done);
+    };
+
+    let check_onholding = StateEventChecker('holding', null);
+    let check_onheld = StateEventChecker('held', 'onholding');
+
+    for (let call of calls) {
+      let callName = "call (" + call.number + ')';
+
+      let onholding = callName + ".onholding";
+      pending.push(onholding);
+      check_onholding(call, callName, receive.bind(null, onholding));
+
+      let onheld = callName + ".onheld";
+      pending.push(onheld);
+      check_onheld(call, callName, receive.bind(null, onheld));
+    }
+
+    check_onholding(conference, "conference",
+                    receive.bind(null, "conference.onholding"));
+
+    check_onheld(conference, "conference", function() {
+      if (typeof heldCallback === 'function') {
+        heldCallback();
+      }
+      receive("conference.onheld");
+    });
+
+    conference.hold();
+
+    return deferred.promise;
+  }
+
+  /**
+   * Resume the conference.
+   *
+   * @param calls
+   *        An array of TelephonyCall objects existing in conference.
+   * @param connectedCallback [optional]
+   *        A callback function which is called when conference state becomes
+   *        connected.
+   * @return A deferred promise.
+   */
+  function resumeConference(calls, connectedCallback) {
+    log("Resuming the held conference call.");
+
+    let deferred = Promise.defer();
+    let done = function() {
+      deferred.resolve();
+    };
+
+    let pending = ["conference.onresuming", "conference.onconnected"];
+    let receive = function(name) {
+      receivedPending(name, pending, done);
+    };
+
+    let check_onresuming   = StateEventChecker('resuming', null);
+    let check_onconnected  = StateEventChecker('connected', 'onresuming');
+
+    for (let call of calls) {
+      let callName = "call (" + call.number + ')';
+
+      let onresuming = callName + ".onresuming";
+      pending.push(onresuming);
+      check_onresuming(call, callName, receive.bind(null, onresuming));
+
+      let onconnected = callName + ".onconnected";
+      pending.push(onconnected);
+      check_onconnected(call, callName, receive.bind(null, onconnected));
+    }
+
+    check_onresuming(conference, "conference",
+                     receive.bind(null, "conference.onresuming"));
+
+    check_onconnected(conference, "conference", function() {
+      if (typeof connectedCallback === 'function') {
+        connectedCallback();
+      }
+      receive("conference.onconnected");
+    });
+
+    conference.resume();
+
+    return deferred.promise;
+  }
+
+  /**
+   * Remove a call out of conference.
+   *
+   * @param callToRemove
+   *        A TelephonyCall object existing in conference.
+   * @param autoRemovedCalls
+   *        An array of TelephonyCall objects which is going to be automatically
+   *        removed. The length of the array should be 0 or 1.
+   * @param remainedCalls
+   *        An array of TelephonyCall objects which remain in conference.
+   * @param stateChangeCallback [optional]
+   *        A callback function which is called when conference state changes.
+   * @return A deferred promise.
+   */
+  function removeCallInConference(callToRemove, autoRemovedCalls, remainedCalls,
+                                  statechangeCallback) {
+    log("Removing a participant from the conference call.");
+
+    is(conference.state, 'connected');
+
+    let deferred = Promise.defer();
+    let done = function() {
+      deferred.resolve();
+    };
+
+    let pending = ["callToRemove.ongroupchange", "telephony.oncallschanged",
+                   "conference.oncallschanged", "conference.onstatechange"];
+    let receive = function(name) {
+      receivedPending(name, pending, done);
+    };
+
+    // Remained call in conference will be held.
+    for (let call of remainedCalls) {
+      let callName = "remainedCall (" + call.number + ')';
+
+      let onstatechange = callName + ".onstatechange";
+      pending.push(onstatechange);
+      check_onstatechange(call, callName, 'held',
+                          receive.bind(null, onstatechange));
+    }
+
+    // When a call is removed from conference with 2 calls, another one will be
+    // automatically removed from group and be put on hold.
+    for (let call of autoRemovedCalls) {
+      let callName = "autoRemovedCall (" + call.number + ')';
+
+      let ongroupchange = callName + ".ongroupchange";
+      pending.push(ongroupchange);
+      check_ongroupchange(call, callName, null,
+                          receive.bind(null, ongroupchange));
+
+      let onstatechange = callName + ".onstatechange";
+      pending.push(onstatechange);
+      check_onstatechange(call, callName, 'held',
+                          receive.bind(null, onstatechange));
+    }
+
+    check_ongroupchange(callToRemove, "callToRemove", null, function() {
+      is(callToRemove.state, 'connected');
+      receive("callToRemove.ongroupchange");
+    });
+
+    check_oncallschanged(telephony, 'telephony',
+                         autoRemovedCalls.concat(callToRemove),
+                         receive.bind(null, "telephony.oncallschanged"));
+
+    check_oncallschanged(conference, 'conference',
+                         autoRemovedCalls.concat(callToRemove), function() {
+      is(conference.calls.length, remainedCalls.length);
+      receive("conference.oncallschanged");
+    });
+
+    check_onstatechange(conference, 'conference',
+                        (remainedCalls.length ? 'held' : ''), function() {
+      ok(!conference.oncallschanged);
+      if (typeof statechangeCallback === 'function') {
+        statechangeCallback();
+      }
+      receive("conference.onstatechange");
+    });
+
+    conference.remove(callToRemove);
+
+    return deferred.promise;
+  }
+
+  /**
+   * Hangup a call in conference.
+   *
+   * @param callToHangUp
+   *        A TelephonyCall object existing in conference.
+   * @param autoRemovedCalls
+   *        An array of TelephonyCall objects which is going to be automatically
+   *        removed. The length of the array should be 0 or 1.
+   * @param remainedCalls
+   *        An array of TelephonyCall objects which remain in conference.
+   * @param stateChangeCallback [optional]
+   *        A callback function which is called when conference state changes.
+   * @return A deferred promise.
+   */
+  function hangUpCallInConference(callToHangUp, autoRemovedCalls, remainedCalls,
+                                  statechangeCallback) {
+    log("Release one call in conference.");
+
+    let deferred = Promise.defer();
+    let done = function() {
+      deferred.resolve();
+    };
+
+    let pending = ["conference.oncallschanged", "remoteHangUp"];
+    let receive = function(name) {
+      receivedPending(name, pending, done);
+    };
+
+    // When a call is hang up from conference with 2 calls, another one will be
+    // automatically removed from group.
+    for (let call of autoRemovedCalls) {
+      let callName = "autoRemovedCall (" + call.number + ')';
+
+      let ongroupchange = callName + ".ongroupchange";
+      pending.push(ongroupchange);
+      check_ongroupchange(call, callName, null,
+                          receive.bind(null, ongroupchange));
+    }
+
+    if (autoRemovedCalls.length) {
+      pending.push("telephony.oncallschanged");
+      check_oncallschanged(telephony, 'telephony',
+                           autoRemovedCalls,
+                           receive.bind(null, "telephony.oncallschanged"));
+    }
+
+    check_oncallschanged(conference, 'conference',
+                         autoRemovedCalls.concat(callToHangUp), function() {
+      is(conference.calls.length, remainedCalls.length);
+      receive("conference.oncallschanged");
+    });
+
+    if (remainedCalls.length === 0) {
+      pending.push("conference.onstatechange");
+      check_onstatechange(conference, 'conference', '', function() {
+        ok(!conference.oncallschanged);
+        if (typeof statechangeCallback === 'function') {
+          statechangeCallback();
+        }
+        receive("conference.onstatechange");
+      });
+    }
+
+    remoteHangUp(callToHangUp)
+      .then(receive.bind(null, "remoteHangUp"));
+
+    return deferred.promise;
+  }
+
+  /**
+   * Setup a conference with an outgoing call and an incoming call.
+   *
+   * @param outNumber
+   *        Number of an outgoing call.
+   * @param inNumber
+   *        Number of an incoming call.
+   * @return Promise<[outCall, inCall]>
+   */
+  function setupConferenceTwoCalls(outNumber, inNumber) {
+    log('Create conference with two calls.');
+
+    let outCall;
+    let inCall;
+    let outInfo = outCallStrPool(outNumber);
+    let inInfo = inCallStrPool(inNumber);
+
+    return Promise.resolve()
+      .then(checkInitialState)
+      .then(() => dial(outNumber))
+      .then(call => { outCall = call; })
+      .then(() => checkAll(outCall, [outCall], '', [], [outInfo.ringing]))
+      .then(() => remoteAnswer(outCall))
+      .then(() => checkAll(outCall, [outCall], '', [], [outInfo.active]))
+      .then(() => remoteDial(inNumber))
+      .then(call => { inCall = call; })
+      .then(() => checkAll(outCall, [outCall, inCall], '', [],
+                           [outInfo.active, inInfo.incoming]))
+      .then(() => answer(inCall))
+      .then(() => checkAll(inCall, [outCall, inCall], '', [],
+                           [outInfo.held, inInfo.active]))
+      .then(() => addCallsToConference([outCall, inCall], function() {
+        checkState(conference, [], 'connected', [outCall, inCall]);
+      }))
+      .then(() => checkAll(conference, [], 'connected', [outCall, inCall],
+                           [outInfo.active, inInfo.active]))
+      .then(() => {
+        return [outCall, inCall];
+      });
+  }
+
+  /**
+   * Setup a conference with an outgoing call and two incoming calls.
+   *
+   * @param outNumber
+   *        Number of an outgoing call.
+   * @param inNumber
+   *        Number of an incoming call.
+   * @param inNumber2
+   *        Number of an incoming call.
+   * @return Promise<[outCall, inCall, inCall2]>
+   */
+  function setupConferenceThreeCalls(outNumber, inNumber, inNumber2) {
+    log('Create conference with three calls.');
+
+    let outCall;
+    let inCall;
+    let inCall2;
+    let outInfo = outCallStrPool(outNumber);
+    let inInfo = inCallStrPool(inNumber);
+    let inInfo2 = inCallStrPool(inNumber2);
+
+    return Promise.resolve()
+      .then(() => setupConferenceTwoCalls(outNumber, inNumber))
+      .then(calls => {
+          outCall = calls[0];
+          inCall = calls[1];
+      })
+      .then(() => remoteDial(inNumber2))
+      .then(call => { inCall2 = call; })
+      .then(() => checkAll(conference, [inCall2], 'connected', [outCall, inCall],
+                           [outInfo.active, inInfo.active, inInfo2.incoming]))
+      .then(() => answer(inCall2, function() {
+        checkState(inCall2, [inCall2], 'held', [outCall, inCall]);
+      }))
+      .then(() => checkAll(inCall2, [inCall2], 'held', [outCall, inCall],
+                           [outInfo.held, inInfo.held, inInfo2.active]))
+      .then(() => addCallsToConference([inCall2], function() {
+        checkState(conference, [], 'connected', [outCall, inCall, inCall2]);
+      }))
+      .then(() => checkAll(conference, [],
+                           'connected', [outCall, inCall, inCall2],
+                           [outInfo.active, inInfo.active, inInfo2.active]))
+      .then(() => {
+        return [outCall, inCall, inCall2];
+      });
+  }
+
+  /**
+   * Setup a conference with an outgoing call and four incoming calls.
+   *
+   * @param outNumber
+   *        Number of an outgoing call.
+   * @param inNumber
+   *        Number of an incoming call.
+   * @param inNumber2
+   *        Number of an incoming call.
+   * @param inNumber3
+   *        Number of an incoming call.
+   * @param inNumber4
+   *        Number of an incoming call.
+   * @return Promise<[outCall, inCall, inCall2, inCall3, inCall4]>
+   */
+  function setupConferenceFiveCalls(outNumber, inNumber, inNumber2, inNumber3,
+                                    inNumber4) {
+    log('Create conference with five calls.');
+
+    let outCall;
+    let inCall;
+    let inCall2;
+    let inCall3;
+    let inCall4;
+    let outInfo = outCallStrPool(outNumber);
+    let inInfo = inCallStrPool(inNumber);
+    let inInfo2 = inCallStrPool(inNumber2);
+    let inInfo3 = inCallStrPool(inNumber3);
+    let inInfo4 = inCallStrPool(inNumber4);
+
+    return Promise.resolve()
+      .then(() => setupConferenceThreeCalls(outNumber, inNumber, inNumber2))
+      .then(calls => {
+        [outCall, inCall, inCall2] = calls;
+      })
+      .then(() => remoteDial(inNumber3))
+      .then(call => {inCall3 = call;})
+      .then(() => checkAll(conference, [inCall3], 'connected',
+                           [outCall, inCall, inCall2],
+                           [outInfo.active, inInfo.active, inInfo2.active,
+                           inInfo3.incoming]))
+      .then(() => answer(inCall3, function() {
+        checkState(inCall3, [inCall3], 'held', [outCall, inCall, inCall2]);
+      }))
+      .then(() => checkAll(inCall3, [inCall3], 'held',
+                           [outCall, inCall, inCall2],
+                           [outInfo.held, inInfo.held, inInfo2.held,
+                            inInfo3.active]))
+      .then(() => addCallsToConference([inCall3], function() {
+        checkState(conference, [], 'connected', [outCall, inCall, inCall2, inCall3]);
+      }))
+      .then(() => checkAll(conference, [], 'connected',
+                           [outCall, inCall, inCall2, inCall3],
+                           [outInfo.active, inInfo.active, inInfo2.active,
+                            inInfo3.active]))
+      .then(() => remoteDial(inNumber4))
+      .then(call => {inCall4 = call;})
+      .then(() => checkAll(conference, [inCall4], 'connected',
+                           [outCall, inCall, inCall2, inCall3],
+                           [outInfo.active, inInfo.active, inInfo2.active,
+                            inInfo3.active, inInfo4.incoming]))
+      .then(() => answer(inCall4, function() {
+        checkState(inCall4, [inCall4], 'held', [outCall, inCall, inCall2, inCall3]);
+      }))
+      .then(() => checkAll(inCall4, [inCall4], 'held',
+                           [outCall, inCall, inCall2, inCall3],
+                           [outInfo.held, inInfo.held, inInfo2.held,
+                            inInfo3.held, inInfo4.active]))
+      .then(() => addCallsToConference([inCall4], function() {
+        checkState(conference, [], 'connected', [outCall, inCall, inCall2,
+                                                 inCall3, inCall4]);
+      }))
+      .then(() => checkAll(conference, [], 'connected',
+                           [outCall, inCall, inCall2, inCall3, inCall4],
+                           [outInfo.active, inInfo.active, inInfo2.active,
+                            inInfo3.active, inInfo4.active]))
+      .then(() => {
+        return [outCall, inCall, inCall2, inCall3, inCall4];
+      });
+  }
+
+  /**
+   * Public members.
+   */
+
+  this.gCheckInitialState = checkInitialState;
+  this.gClearCalls = clearCalls;
+  this.gOutCallStrPool = outCallStrPool;
+  this.gInCallStrPool = inCallStrPool;
+  this.gCheckState = checkState;
+  this.gCheckAll = checkAll;
+  this.gDial = dial;
+  this.gAnswer = answer;
+  this.gRemoteDial = remoteDial;
+  this.gRemoteAnswer = remoteAnswer;
+  this.gRemoteHangUp = remoteHangUp;
+  this.gRemoteHangUpCalls = remoteHangUpCalls;
+  this.gAddCallsToConference = addCallsToConference;
+  this.gHoldConference = holdConference;
+  this.gResumeConference = resumeConference;
+  this.gRemoveCallInConference = removeCallInConference;
+  this.gHangUpCallInConference = hangUpCallInConference;
+  this.gSetupConferenceTwoCalls = setupConferenceTwoCalls;
+  this.gSetupConferenceThreeCalls = setupConferenceThreeCalls;
+  this.gSetupConferenceFiveCalls = setupConferenceFiveCalls;
+  this.gReceivedPending = receivedPending;
 }());
 
 function _startTest(permissions, test) {
   function permissionSetUp() {
     SpecialPowers.setBoolPref("dom.mozSettings.enabled", true);
     for (let per of permissions) {
       SpecialPowers.addPermission(per, true, document);
     }
@@ -102,17 +1069,19 @@ function _startTest(permissions, test) {
   }
 
   function setUp() {
     log("== Test SetUp ==");
     permissionSetUp();
     // Make sure that we get the telephony after adding permission.
     telephony = window.navigator.mozTelephony;
     ok(telephony);
-    return clearCalls().then(checkInitialState);
+    conference = telephony.conferenceGroup;
+    ok(conference);
+    return gClearCalls().then(gCheckInitialState);
   }
 
   // Extend finish() with tear down.
   finish = (function() {
     let originalFinish = finish;
 
     function tearDown() {
       log("== Test TearDown ==");
--- a/dom/telephony/test/marionette/manifest.ini
+++ b/dom/telephony/test/marionette/manifest.ini
@@ -37,15 +37,22 @@ disabled = Bug 820802
 [test_outgoing_onstatechange.js]
 disabled = Bug 821966
 [test_redundant_operations.js]
 disabled = Bug 821927
 [test_multiple_hold.js]
 disabled = Bug 821958
 [test_outgoing_emergency_in_airplane_mode.js]
 [test_emergency_label.js]
-[test_conference.js]
 [test_dsds_default_service_id.js]
 [test_call_mute.js]
 [test_dsds_normal_call.js]
 [test_dsds_connection_conflict.js]
 [test_audiomanager_phonestate.js]
 [test_outgoing_answer_radio_off.js]
+[test_conference_two_calls.js]
+[test_conference_add_error.js]
+[test_conference_remove_error.js]
+[test_conference_two_hangup_one.js]
+[test_conference_two_hold_resume.js]
+[test_conference_two_remove_one.js]
+[test_conference_three_hangup_one.js]
+[test_conference_three_remove_one.js]
--- a/dom/telephony/test/marionette/test_audiomanager_phonestate.js
+++ b/dom/telephony/test/marionette/test_audiomanager_phonestate.js
@@ -9,170 +9,16 @@ const AUDIO_MANAGER_CONTRACT_ID = "@mozi
 // See nsIAudioManager
 const PHONE_STATE_INVALID          = -2;
 const PHONE_STATE_CURRENT          = -1;
 const PHONE_STATE_NORMAL           = 0;
 const PHONE_STATE_RINGTONE         = 1;
 const PHONE_STATE_IN_CALL          = 2;
 const PHONE_STATE_IN_COMMUNICATION = 3;
 
-let conference;
-
-function checkTelephonyActiveAndCalls(active, calls) {
-  is(telephony.active, active, "telephony.active");
-  is(telephony.calls.length, calls.length, "telephony.calls");
-  for (let i = 0; i < calls.length; ++i) {
-    is(telephony.calls[i], calls[i]);
-  }
-}
-
-function checkConferenceStateAndCalls(state, calls) {
-  is(conference.state, state, "conference.state");
-  is(conference.calls.length, calls.length, "conference.calls");
-  for (let i = 0; i < calls.length; i++) {
-    is(conference.calls[i], calls[i]);
-  }
-}
-
-function checkInitialState() {
-  log("Verify initial state.");
-  ok(telephony.calls, 'telephony.call');
-  checkTelephonyActiveAndCalls(null, []);
-  ok(conference.calls, 'conference.calls');
-  checkConferenceStateAndCalls('', []);
-}
-
-function checkEventCallState(event, call, state) {
-  is(call, event.call, "event.call");
-  is(call.state, state, "call state");
-}
-
-function dial(number) {
-  log("Make an outgoing call: " + number);
-
-  let deferred = Promise.defer();
-
-  telephony.dial(number).then(call => {
-    ok(call);
-    is(call.number, number);
-    is(call.state, "dialing");
-
-    call.onalerting = function onalerting(event) {
-      call.onalerting = null;
-      log("Received 'onalerting' call event.");
-      checkEventCallState(event, call, "alerting");
-      deferred.resolve(call);
-    };
-  });
-
-  return deferred.promise;
-}
-
-function answer(call) {
-  log("Answering the incoming call.");
-
-  let deferred = Promise.defer();
-
-  call.onconnecting = function onconnectingIn(event) {
-    log("Received 'connecting' call event for incoming call.");
-    call.onconnecting = null;
-    checkEventCallState(event, call, "connecting");
-  };
-
-  call.onconnected = function onconnectedIn(event) {
-    log("Received 'connected' call event for incoming call.");
-    call.onconnected = null;
-    checkEventCallState(event, call, "connected");
-    ok(!call.onconnecting);
-    deferred.resolve(call);
-  };
-  call.answer();
-
-  return deferred.promise;
-}
-
-function remoteDial(number) {
-  log("Simulating an incoming call.");
-
-  let deferred = Promise.defer();
-
-  telephony.onincoming = function onincoming(event) {
-    log("Received 'incoming' call event.");
-    telephony.onimcoming = null;
-
-    let call = event.call;
-
-    ok(call);
-    is(call.number, number);
-    is(call.state, "incoming");
-
-    deferred.resolve(call);
-  };
-  emulator.run("gsm call " + number);
-
-  return deferred.promise;
-}
-
-function remoteAnswer(call) {
-  log("Remote answering the call.");
-
-  let deferred = Promise.defer();
-
-  call.onconnected = function onconnected(event) {
-    log("Received 'connected' call event.");
-    call.onconnected = null;
-    checkEventCallState(event, call, "connected");
-    deferred.resolve(call);
-  };
-  emulator.run("gsm accept " + call.number);
-
-  return deferred.promise;
-}
-
-function remoteHangUp(call) {
-  log("Remote hanging up the call.");
-
-  let deferred = Promise.defer();
-
-  call.ondisconnected = function ondisconnected(event) {
-    log("Received 'disconnected' call event.");
-    call.ondisconnected = null;
-    checkEventCallState(event, call, "disconnected");
-    deferred.resolve(call);
-  };
-  emulator.run("gsm cancel " + call.number);
-
-  return deferred.promise;
-}
-
-function remoteHangUpCalls(calls) {
-  let promise = Promise.resolve();
-
-  for (let call of calls) {
-    promise = promise.then(remoteHangUp.bind(null, call));
-  }
-
-  return promise;
-}
-
-// The length of callsToAdd should be 2.
-function addCallsToConference(callsToAdd) {
-  log("Add " + callsToAdd.length + " calls into conference.");
-
-  let deferred = Promise.defer();
-
-  conference.onconnected = function() {
-    deferred.resolve();
-  };
-
-  conference.add(callsToAdd[0], callsToAdd[1]);
-
-  return deferred.promise;
-}
-
 let audioManager;
 function checkStates(speakerEnabled, phoneState) {
   if (!audioManager) {
     audioManager = SpecialPowers.Cc[AUDIO_MANAGER_CONTRACT_ID]
                                 .getService(SpecialPowers.Ci.nsIAudioManager);
     ok(audioManager, "nsIAudioManager instance");
   }
 
@@ -198,69 +44,65 @@ function check(phoneStateOrig, phoneStat
   }
 
   telephony.speakerEnabled = false;
   checkStates(false, arguments.length > 2 ? phoneStateDisabled : phoneStateOrig);
 }
 
 // Start the test
 startTest(function() {
-  conference = telephony.conferenceGroup;
-  ok(conference);
-
   let outNumber = "5555550101";
   let inNumber  = "5555550201";
   let outCall;
   let inCall;
 
   Promise.resolve()
-    .then(checkInitialState)
     .then(() => check(PHONE_STATE_CURRENT, PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
 
     // Dial in
-    .then(() => remoteDial(inNumber))
+    .then(() => gRemoteDial(inNumber))
     .then(call => { inCall = call; })
     // TODO - Bug 948860: should this be {RINGTONE, RINGTONE, RINGTONE}?
     // From current UX spec, there is no chance that an user may enable speaker
     // during alerting, so basically this 'set speaker enable' action can't
     // happen in B2G.
     .then(() => check(PHONE_STATE_RINGTONE, PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
-    .then(() => answer(inCall))
+    .then(() => gAnswer(inCall))
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
     // Hang up all
-    .then(() => remoteHangUp(inCall))
+    .then(() => gRemoteHangUp(inCall))
     .then(() => check(PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
 
     // Dial out
-    .then(() => dial(outNumber))
+    .then(() => gDial(outNumber))
     .then(call => { outCall = call; })
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
-    .then(() => remoteAnswer(outCall))
+    .then(() => gRemoteAnswer(outCall))
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
     // Hang up all
-    .then(() => remoteHangUp(outCall))
+    .then(() => gRemoteHangUp(outCall))
     .then(() => check(PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
 
     // Dial out
-    .then(() => dial(outNumber))
+    .then(() => gDial(outNumber))
     .then(call => { outCall = call; })
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
-    .then(() => remoteAnswer(outCall))
+    .then(() => gRemoteAnswer(outCall))
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
     // Dial out and dial in
-    .then(() => remoteDial(inNumber))
+    .then(() => gRemoteDial(inNumber))
     .then(call => { inCall = call; })
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
-    .then(() => answer(inCall))
+    .then(() => gAnswer(inCall))
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
     // Conference
-    .then(() => addCallsToConference([outCall, inCall]))
+    .then(() => gAddCallsToConference([outCall, inCall]))
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
     // Hang up all
-    .then(() => remoteHangUpCalls([outCall, inCall]))
+    .then(() => gRemoteHangUpCalls([outCall, inCall]))
     .then(() => check(PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
 
     // End
     .then(null, error => {
       ok(false, 'promise rejects during test.');
     })
     .then(finish);
 });
deleted file mode 100644
--- a/dom/telephony/test/marionette/test_conference.js
+++ /dev/null
@@ -1,967 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 60000;
-MARIONETTE_HEAD_JS = 'head.js';
-
-let conference;
-
-/**
- * The functions are created to provide the string format of the emulator call
- * list results.
- *
- * Usage:
- *   let outInfo = outCallStrPool('911');
- *   outInfo.ringing == "outbound to 911        : ringing"
- *   outInfo.active  == "outbound to 911        : active"
- */
-function callStrPool(prefix, number) {
-  let padding = "           : ";
-  let numberInfo = prefix + number + padding.substr(number.length);
-
-  let info = {};
-  let states = ['ringing', 'incoming', 'active', 'held'];
-  for (let state of states) {
-    info[state] = numberInfo + state;
-  }
-
-  return info;
-}
-
-function outCallStrPool(number) {
-  return callStrPool("outbound to  ", number);
-}
-
-function inCallStrPool(number) {
-  return callStrPool("inbound from ", number);
-}
-
-function checkInitialState() {
-  log("Verify initial state.");
-  ok(telephony.calls, 'telephony.call');
-  checkTelephonyActiveAndCalls(null, []);
-  ok(conference.calls, 'conference.calls');
-  checkConferenceStateAndCalls('', []);
-}
-
-function checkEventCallState(event, call, state) {
-  is(call, event.call, "event.call");
-  is(call.state, state, "call state");
-}
-
-function checkTelephonyActiveAndCalls(active, calls) {
-  is(telephony.active, active, "telephony.active");
-  is(telephony.calls.length, calls.length, "telephony.calls");
-  for (let i = 0; i < calls.length; ++i) {
-    is(telephony.calls[i], calls[i]);
-  }
-}
-
-function checkConferenceStateAndCalls(state, calls) {
-  is(conference.state, state, "conference.state");
-  is(conference.calls.length, calls.length, "conference.calls");
-  for (let i = 0; i < calls.length; i++) {
-    is(conference.calls[i], calls[i]);
-  }
-}
-
-function checkState(active, calls, conferenceState, conferenceCalls) {
-  checkTelephonyActiveAndCalls(active, calls);
-  checkConferenceStateAndCalls(conferenceState, conferenceCalls);
-}
-
-function checkEmulatorCallList(expectedCallList) {
-  let deferred = Promise.defer();
-
-  emulator.run("gsm list", function(result) {
-    log("Call list is now: " + result);
-    for (let i = 0; i < expectedCallList.length; ++i) {
-      is(result[i], expectedCallList[i], "emulator calllist");
-    }
-    is(result[expectedCallList.length], "OK", "emulator calllist");
-    deferred.resolve();
-  });
-
-  return deferred.promise;
-}
-
-// Promise.
-function checkAll(active, calls, conferenceState, conferenceCalls, callList) {
-  checkState(active, calls, conferenceState, conferenceCalls);
-  return checkEmulatorCallList(callList);
-}
-
-// Make sure there's no pending event before we jump to the next case.
-function receivedPending(received, pending, nextAction) {
-  let index = pending.indexOf(received);
-  if (index != -1) {
-    pending.splice(index, 1);
-  }
-  if (pending.length === 0) {
-    nextAction();
-  }
-}
-
-function dial(number) {
-  log("Make an outgoing call: " + number);
-
-  let deferred = Promise.defer();
-
-  telephony.dial(number).then(call => {
-    ok(call);
-    is(call.number, number);
-    is(call.state, "dialing");
-
-    call.onalerting = function onalerting(event) {
-      call.onalerting = null;
-      log("Received 'onalerting' call event.");
-      checkEventCallState(event, call, "alerting");
-      deferred.resolve(call);
-    };
-  });
-
-  return deferred.promise;
-}
-
-// Answering an incoming call could trigger conference state change.
-function answer(call, conferenceStateChangeCallback) {
-  log("Answering the incoming call.");
-
-  let deferred = Promise.defer();
-  let done = function() {
-    deferred.resolve(call);
-  };
-
-  let pending = ["call.onconnected"];
-  let receive = function(name) {
-    receivedPending(name, pending, done);
-  };
-
-  // When there's already a connected conference call, answering a new incoming
-  // call triggers conference state change. We should wait for
-  // |conference.onstatechange| before checking the state of the conference call.
-  if (conference.state === "connected") {
-    pending.push("conference.onstatechange");
-    check_onstatechange(conference, "conference", "held", function() {
-      if (typeof conferenceStateChangeCallback === "function") {
-        conferenceStateChangeCallback();
-      }
-      receive("conference.onstatechange");
-    });
-  }
-
-  call.onconnecting = function onconnectingIn(event) {
-    log("Received 'connecting' call event for incoming call.");
-    call.onconnecting = null;
-    checkEventCallState(event, call, "connecting");
-  };
-
-  call.onconnected = function onconnectedIn(event) {
-    log("Received 'connected' call event for incoming call.");
-    call.onconnected = null;
-    checkEventCallState(event, call, "connected");
-    ok(!call.onconnecting);
-    receive("call.onconnected");
-  };
-  call.answer();
-
-  return deferred.promise;
-}
-
-function remoteDial(number) {
-  log("Simulating an incoming call.");
-
-  let deferred = Promise.defer();
-
-  telephony.onincoming = function onincoming(event) {
-    log("Received 'incoming' call event.");
-    telephony.onincoming = null;
-
-    let call = event.call;
-
-    ok(call);
-    is(call.number, number);
-    is(call.state, "incoming");
-
-    deferred.resolve(call);
-  };
-  emulator.run("gsm call " + number);
-
-  return deferred.promise;
-}
-
-function remoteAnswer(call) {
-  log("Remote answering the call.");
-
-  let deferred = Promise.defer();
-
-  call.onconnected = function onconnected(event) {
-    log("Received 'connected' call event.");
-    call.onconnected = null;
-    checkEventCallState(event, call, "connected");
-    deferred.resolve(call);
-  };
-  emulator.run("gsm accept " + call.number);
-
-  return deferred.promise;
-}
-
-function remoteHangUp(call) {
-  log("Remote hanging up the call.");
-
-  let deferred = Promise.defer();
-
-  call.ondisconnected = function ondisconnected(event) {
-    log("Received 'disconnected' call event.");
-    call.ondisconnected = null;
-    checkEventCallState(event, call, "disconnected");
-    deferred.resolve(call);
-  };
-  emulator.run("gsm cancel " + call.number);
-
-  return deferred.promise;
-}
-
-function remoteHangUpCalls(calls) {
-  let promise = Promise.resolve();
-
-  for (let call of calls) {
-    promise = promise.then(remoteHangUp.bind(null, call));
-  }
-
-  return promise;
-}
-
-// container might be telephony or conference.
-function check_oncallschanged(container, containerName, expectedCalls,
-                              callback) {
-  container.oncallschanged = function(event) {
-    log("Received 'callschanged' event for the " + containerName);
-    if (event.call) {
-      let index = expectedCalls.indexOf(event.call);
-      ok(index != -1);
-      expectedCalls.splice(index, 1);
-
-      if (expectedCalls.length === 0) {
-        container.oncallschanged = null;
-        callback();
-      }
-    }
-  };
-}
-
-function check_ongroupchange(call, callName, group, callback) {
-  call.ongroupchange = function(event) {
-    log("Received 'groupchange' event for the " + callName);
-    call.ongroupchange = null;
-
-    is(call.group, group);
-    callback();
-  };
-}
-
-function check_onstatechange(call, callName, state, callback) {
-  call.onstatechange = function(event) {
-    log("Received 'statechange' event for the " + callName);
-    call.onstatechange = null;
-
-    is(call.state, state);
-    callback();
-  };
-}
-
-function StateEventChecker(state, previousEvent) {
-  let event = 'on' + state;
-
-  return function(call, callName, callback) {
-    call[event] = function() {
-      log("Received '" + state + "' event for the " + callName);
-      call[event] = null;
-
-      if (previousEvent) {
-        // We always clear the event handler when the event is received.
-        // Therefore, if the corresponding handler is not existed, the expected
-        // previous event has been already received.
-        ok(!call[previousEvent]);
-      }
-      is(call.state, state);
-      callback();
-    };
-  };
-}
-
-let check_onholding    = StateEventChecker('holding', null);
-let check_onheld       = StateEventChecker('held', 'onholding');
-let check_onresuming   = StateEventChecker('resuming', null);
-let check_onconnected  = StateEventChecker('connected', 'onresuming');
-
-// The length of callsToAdd should be 1 or 2.
-function addCallsToConference(callsToAdd, connectedCallback) {
-  log("Add " + callsToAdd.length + " calls into conference.");
-
-  let deferred = Promise.defer();
-  let done = function() {
-    deferred.resolve();
-  };
-
-  let pending = ["conference.oncallschanged", "conference.onconnected"];
-  let receive = function(name) {
-    receivedPending(name, pending, done);
-  };
-
-  for (let call of callsToAdd) {
-    let callName = "callToAdd (" + call.number + ')';
-
-    let ongroupchange = callName + ".ongroupchange";
-    pending.push(ongroupchange);
-    check_ongroupchange(call, callName, conference,
-                        receive.bind(null, ongroupchange));
-
-    let onstatechange = callName + ".onstatechange";
-    pending.push(onstatechange);
-    check_onstatechange(call, callName, 'connected',
-                        receive.bind(null, onstatechange));
-  }
-
-  check_oncallschanged(conference, 'conference', callsToAdd,
-                       receive.bind(null, "conference.oncallschanged"));
-
-  check_onconnected(conference, "conference", function() {
-    ok(!conference.oncallschanged);
-    if (typeof connectedCallback === 'function') {
-      connectedCallback();
-    }
-    receive("conference.onconnected");
-  });
-
-  // Cannot use apply() through webidl, so just separate the cases to handle.
-  if (callsToAdd.length == 2) {
-    conference.add(callsToAdd[0], callsToAdd[1]);
-  } else {
-    conference.add(callsToAdd[0]);
-  }
-
-  return deferred.promise;
-}
-
-function holdConference(calls, heldCallback) {
-  log("Holding the conference call.");
-
-  let deferred = Promise.defer();
-  let done = function() {
-    deferred.resolve();
-  };
-
-  let pending = ["conference.onholding", "conference.onheld"];
-  let receive = function(name) {
-    receivedPending(name, pending, done);
-  };
-
-  for (let call of calls) {
-    let callName = "call (" + call.number + ')';
-
-    let onholding = callName + ".onholding";
-    pending.push(onholding);
-    check_onholding(call, callName, receive.bind(null, onholding));
-
-    let onheld = callName + ".onheld";
-    pending.push(onheld);
-    check_onheld(call, callName, receive.bind(null, onheld));
-  }
-
-  check_onholding(conference, "conference",
-                  receive.bind(null, "conference.onholding"));
-
-  check_onheld(conference, "conference", function() {
-    if (typeof heldCallback === 'function') {
-      heldCallback();
-    }
-    receive("conference.onheld");
-  });
-
-  conference.hold();
-
-  return deferred.promise;
-}
-
-function resumeConference(calls, connectedCallback) {
-  log("Resuming the held conference call.");
-
-  let deferred = Promise.defer();
-  let done = function() {
-    deferred.resolve();
-  };
-
-  let pending = ["conference.onresuming", "conference.onconnected"];
-  let receive = function(name) {
-    receivedPending(name, pending, done);
-  };
-
-  for (let call of calls) {
-    let callName = "call (" + call.number + ')';
-
-    let onresuming = callName + ".onresuming";
-    pending.push(onresuming);
-    check_onresuming(call, callName, receive.bind(null, onresuming));
-
-    let onconnected = callName + ".onconnected";
-    pending.push(onconnected);
-    check_onconnected(call, callName, receive.bind(null, onconnected));
-  }
-
-  check_onresuming(conference, "conference",
-                   receive.bind(null, "conference.onresuming"));
-
-  check_onconnected(conference, "conference", function() {
-    if (typeof connectedCallback === 'function') {
-      connectedCallback();
-    }
-    receive("conference.onconnected");
-  });
-
-  conference.resume();
-
-  return deferred.promise;
-}
-
-// The length of autoRemovedCalls should be 0 or 1.
-function removeCallInConference(callToRemove, autoRemovedCalls, remainedCalls,
-                                statechangeCallback) {
-  log("Removing a participant from the conference call.");
-
-  is(conference.state, 'connected');
-
-  let deferred = Promise.defer();
-  let done = function() {
-    deferred.resolve();
-  };
-
-  let pending = ["callToRemove.ongroupchange", "telephony.oncallschanged",
-                 "conference.oncallschanged", "conference.onstatechange"];
-  let receive = function(name) {
-    receivedPending(name, pending, done);
-  };
-
-  // Remained call in conference will be held.
-  for (let call of remainedCalls) {
-    let callName = "remainedCall (" + call.number + ')';
-
-    let onstatechange = callName + ".onstatechange";
-    pending.push(onstatechange);
-    check_onstatechange(call, callName, 'held',
-                        receive.bind(null, onstatechange));
-  }
-
-  // When a call is removed from conference with 2 calls, another one will be
-  // automatically removed from group and be put on hold.
-  for (let call of autoRemovedCalls) {
-    let callName = "autoRemovedCall (" + call.number + ')';
-
-    let ongroupchange = callName + ".ongroupchange";
-    pending.push(ongroupchange);
-    check_ongroupchange(call, callName, null,
-                        receive.bind(null, ongroupchange));
-
-    let onstatechange = callName + ".onstatechange";
-    pending.push(onstatechange);
-    check_onstatechange(call, callName, 'held',
-                        receive.bind(null, onstatechange));
-  }
-
-  check_ongroupchange(callToRemove, "callToRemove", null, function() {
-    is(callToRemove.state, 'connected');
-    receive("callToRemove.ongroupchange");
-  });
-
-  check_oncallschanged(telephony, 'telephony',
-                       autoRemovedCalls.concat(callToRemove),
-                       receive.bind(null, "telephony.oncallschanged"));
-
-  check_oncallschanged(conference, 'conference',
-                       autoRemovedCalls.concat(callToRemove), function() {
-    is(conference.calls.length, remainedCalls.length);
-    receive("conference.oncallschanged");
-  });
-
-  check_onstatechange(conference, 'conference',
-                      (remainedCalls.length ? 'held' : ''), function() {
-    ok(!conference.oncallschanged);
-    if (typeof statechangeCallback === 'function') {
-      statechangeCallback();
-    }
-    receive("conference.onstatechange");
-  });
-
-  conference.remove(callToRemove);
-
-  return deferred.promise;
-}
-
-// The length of autoRemovedCalls should be 0 or 1.
-function hangUpCallInConference(callToHangUp, autoRemovedCalls, remainedCalls,
-                                statechangeCallback) {
-  log("Release one call in conference.");
-
-  let deferred = Promise.defer();
-  let done = function() {
-    deferred.resolve();
-  };
-
-  let pending = ["conference.oncallschanged", "remoteHangUp"];
-  let receive = function(name) {
-    receivedPending(name, pending, done);
-  };
-
-  // When a call is hang up from conference with 2 calls, another one will be
-  // automatically removed from group.
-  for (let call of autoRemovedCalls) {
-    let callName = "autoRemovedCall (" + call.number + ')';
-
-    let ongroupchange = callName + ".ongroupchange";
-    pending.push(ongroupchange);
-    check_ongroupchange(call, callName, null,
-                        receive.bind(null, ongroupchange));
-  }
-
-  if (autoRemovedCalls.length) {
-    pending.push("telephony.oncallschanged");
-    check_oncallschanged(telephony, 'telephony',
-                         autoRemovedCalls,
-                         receive.bind(null, "telephony.oncallschanged"));
-  }
-
-  check_oncallschanged(conference, 'conference',
-                       autoRemovedCalls.concat(callToHangUp), function() {
-    is(conference.calls.length, remainedCalls.length);
-    receive("conference.oncallschanged");
-  });
-
-  if (remainedCalls.length === 0) {
-    pending.push("conference.onstatechange");
-    check_onstatechange(conference, 'conference', '', function() {
-      ok(!conference.oncallschanged);
-      if (typeof statechangeCallback === 'function') {
-        statechangeCallback();
-      }
-      receive("conference.onstatechange");
-    });
-  }
-
-  remoteHangUp(callToHangUp)
-    .then(receive.bind(null, "remoteHangUp"));
-
-  return deferred.promise;
-}
-
-function handleConferenceRemoveError(callToRemove) {
-  log('Handle conference remove error.');
-
-  let deferred = Promise.defer();
-
-  conference.onerror = function(evt) {
-    log('Receiving a conference error event.');
-    is(evt.name, 'removeError', 'conference removeError');
-
-    deferred.res