Merge m-c to fx-team a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Mon, 23 Mar 2015 17:04:25 -0700
changeset 265609 a8194e05e0477c62c5dd89cbf05c5665454b4a27
parent 265608 56ce9fdb62e3d166dada53e38d1a043bc87036db (current diff)
parent 265432 235a9cb26548a76b85a67af8845746ac27ca2e7a (diff)
child 265610 3fcf3b402ec799624ee4f2165d396fc0e905a022
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone39.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 a=merge CLOSED TREE
media/webrtc/signaling/src/sdp/sipcc/ccsdp.c
testing/web-platform/meta/html/semantics/grouping-content/the-li-element/grouping-li-novalue-MANUAL.html.ini
testing/web-platform/tests/DOMEvents/tests/approved/EventListener.dispatch.new.event.html
testing/web-platform/tests/conformance-checkers/html/elements/meta/name-empty-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/meta/name-leading-bom-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/meta/name-leading-whitespace-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/meta/name-rejected-cache-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/meta/name-rejected-no-email-collection-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/meta/name-trailing-pile-of-poo-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/meta/name-trailing-whitespace-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/meta/name-turkish-lowercase-dotless-i-novalid.html
testing/web-platform/tests/conformance-checkers/html/elements/meta/name-turkish-uppercase-dotted-i-novalid.html
testing/web-platform/tests/conformance-checkers/tools/meta-extensions
testing/web-platform/tests/conformance-checkers/tools/meta-name.py
testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-novalue-MANUAL.html
testing/web-platform/tests/html/semantics/scripting-1/the-script-element/script-for-onload.html
testing/web-platform/tests/webstorage/storage_builtins.js
testing/web-platform/tests/webstorage/storage_local_builtins.html
testing/web-platform/tests/webstorage/storage_local_clear.html
testing/web-platform/tests/webstorage/storage_local_clear_js.html
testing/web-platform/tests/webstorage/storage_local_getitem.html
testing/web-platform/tests/webstorage/storage_local_getitem_js.html
testing/web-platform/tests/webstorage/storage_local_in_js.html
testing/web-platform/tests/webstorage/storage_local_index_js.html
testing/web-platform/tests/webstorage/storage_local_key.html
testing/web-platform/tests/webstorage/storage_local_length.html
testing/web-platform/tests/webstorage/storage_local_length_js.html
testing/web-platform/tests/webstorage/storage_local_removeitem.html
testing/web-platform/tests/webstorage/storage_local_removeitem_js.html
testing/web-platform/tests/webstorage/storage_local_setitem.html
testing/web-platform/tests/webstorage/storage_local_setitem_js.html
testing/web-platform/tests/webstorage/storage_session_builtins.html
testing/web-platform/tests/webstorage/storage_session_clear.html
testing/web-platform/tests/webstorage/storage_session_clear_js.html
testing/web-platform/tests/webstorage/storage_session_getitem.html
testing/web-platform/tests/webstorage/storage_session_getitem_js.html
testing/web-platform/tests/webstorage/storage_session_in_js.html
testing/web-platform/tests/webstorage/storage_session_index_js.html
testing/web-platform/tests/webstorage/storage_session_key.html
testing/web-platform/tests/webstorage/storage_session_length.html
testing/web-platform/tests/webstorage/storage_session_length_js.html
testing/web-platform/tests/webstorage/storage_session_removeitem.html
testing/web-platform/tests/webstorage/storage_session_removeitem_js.html
testing/web-platform/tests/webstorage/storage_session_setitem.html
testing/web-platform/tests/webstorage/storage_session_setitem_js.html
--- a/accessible/html/HTMLTableAccessible.cpp
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -294,25 +294,28 @@ HTMLTableHeaderCellAccessible::
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableHeaderCellAccessible: Accessible implementation
 
 role
 HTMLTableHeaderCellAccessible::NativeRole()
 {
   // Check value of @scope attribute.
   static nsIContent::AttrValuesArray scopeValues[] =
-    {&nsGkAtoms::col, &nsGkAtoms::row, nullptr};
+    { &nsGkAtoms::col, &nsGkAtoms::colgroup,
+      &nsGkAtoms::row, &nsGkAtoms::rowgroup, nullptr };
   int32_t valueIdx =
     mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::scope,
                               scopeValues, eCaseMatters);
 
   switch (valueIdx) {
     case 0:
+    case 1:
       return roles::COLUMNHEADER;
-    case 1:
+    case 2:
+    case 3:
       return roles::ROWHEADER;
   }
 
   // Assume it's columnheader if there are headers in siblings, otherwise
   // rowheader.
   // This should iterate the flattened tree
   nsIContent* parentContent = mContent->GetParent();
   if (!parentContent) {
--- a/accessible/tests/mochitest/table/test_headers_table.html
+++ b/accessible/tests/mochitest/table/test_headers_table.html
@@ -151,16 +151,152 @@
           cell: "table6_cell",
           rowHeaderCells: [ "table6_rh" ],
           columnHeaderCells: [ "table6_ch" ]
         }
       ];
 
       testHeaderCells(headerInfoMap);
 
+      //////////////////////////////////////////////////////////////////////////
+      // @scope="rowgroup" and @scope="row"
+
+      headerInfoMap = [
+        {
+          cell: "t7_r1c1",
+          rowHeaderCells: [ "t7_Females", "t7_Mary" ],
+          columnHeaderCells: [ "t7_1km" ]
+        },
+        {
+          cell: "t7_r1c2",
+          rowHeaderCells: [ "t7_Females", "t7_Mary" ],
+          columnHeaderCells: [ "t7_5km" ]
+        },
+        {
+          cell: "t7_r1c3",
+          rowHeaderCells: [ "t7_Females", "t7_Mary" ],
+          columnHeaderCells: [ "t7_10km" ]
+        },
+        {
+          cell: "t7_r2c1",
+          rowHeaderCells: [ "t7_Females", "t7_Betsy" ],
+          columnHeaderCells: [ "t7_1km" ]
+        },
+        {
+          cell: "t7_r2c2",
+          rowHeaderCells: [ "t7_Females", "t7_Betsy" ],
+          columnHeaderCells: [ "t7_5km" ]
+        },
+        {
+          cell: "t7_r2c3",
+          rowHeaderCells: [ "t7_Females", "t7_Betsy" ],
+          columnHeaderCells: [ "t7_10km" ]
+        },
+        {
+          cell: "t7_r3c1",
+          rowHeaderCells: [ "t7_Males", "t7_Matt" ],
+          columnHeaderCells: [ "t7_1km" ]
+        },
+        {
+          cell: "t7_r3c2",
+          rowHeaderCells: [ "t7_Males", "t7_Matt" ],
+          columnHeaderCells: [ "t7_5km" ]
+        },
+        {
+          cell: "t7_r3c3",
+          rowHeaderCells: [ "t7_Males", "t7_Matt" ],
+          columnHeaderCells: [ "t7_10km" ]
+        },
+        {
+          cell: "t7_r4c1",
+          rowHeaderCells: [ "t7_Males", "t7_Todd" ],
+          columnHeaderCells: [ "t7_1km" ]
+        },
+        {
+          cell: "t7_r4c2",
+          rowHeaderCells: [ "t7_Males", "t7_Todd" ],
+          columnHeaderCells: [ "t7_5km" ]
+        },
+        {
+          cell: "t7_r4c3",
+          rowHeaderCells: [ "t7_Males", "t7_Todd" ],
+          columnHeaderCells: [ "t7_10km" ]
+        }
+      ];
+
+      testHeaderCells(headerInfoMap);
+
+      //////////////////////////////////////////////////////////////////////////
+      // @scope="colgroup" and @scope="col"
+
+      headerInfoMap = [
+        {
+          cell: "t8_r1c1",
+          rowHeaderCells: [ "t8_1km" ],
+          columnHeaderCells: [ "t7_Females", "t7_Mary" ]
+        },
+        {
+          cell: "t8_r1c2",
+          rowHeaderCells: [ "t8_5km" ],
+          columnHeaderCells: [ "t8_Females", "t8_Mary" ]
+        },
+        {
+          cell: "t8_r1c3",
+          rowHeaderCells: [ "t8_10km" ],
+          columnHeaderCells: [ "t8_Females", "t8_Mary" ]
+        },
+        {
+          cell: "t8_r1c4",
+          rowHeaderCells: [ "t8_1km" ],
+          columnHeaderCells: [ "t8_Females", "t8_Betsy" ]
+        },
+        {
+          cell: "t8_r2c1",
+          rowHeaderCells: [ "t8_5km" ],
+          columnHeaderCells: [ "t8_Females", "t8_Betsy" ]
+        },
+        {
+          cell: "t8_r2c2",
+          rowHeaderCells: [ "t8_10km" ],
+          columnHeaderCells: [ "t8_Females", "t8_Betsy" ]
+        },
+        {
+          cell: "t8_r2c3",
+          rowHeaderCells: [ "t8_1km" ],
+          columnHeaderCells: [ "t8_Males", "t8_Matt" ]
+        },
+        {
+          cell: "t8_r2c4",
+          rowHeaderCells: [ "t8_5km" ],
+          columnHeaderCells: [ "t8_Males", "t8_Matt" ]
+        },
+        {
+          cell: "t8_r3c1",
+          rowHeaderCells: [ "t8_10km" ],
+          columnHeaderCells: [ "t8_Males", "t8_Matt" ]
+        },
+        {
+          cell: "t8_r3c2",
+          rowHeaderCells: [ "t8_1km" ],
+          columnHeaderCells: [ "t8_Males", "t8_Todd" ]
+        },
+        {
+          cell: "t8_r3c3",
+          rowHeaderCells: [ "t8_5km" ],
+          columnHeaderCells: [ "t8_Males", "t8_Todd" ]
+        },
+        {
+          cell: "t8_r3c4",
+          rowHeaderCells: [ "t8_10km" ],
+          columnHeaderCells: [ "t8_Males", "t8_Todd" ]
+        }
+      ];
+
+      testHeaderCells(headerInfoMap);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 
@@ -170,16 +306,21 @@
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=512424">
     Bug 512424
   </a>
   <a target="_blank"
      title="Table headers not associated when header is a td element with no scope"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=704465">
     Bug 704465
   </a>
+  <a target="_blank"
+     title="Support rowgroup and colgroup HTML scope"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1141978">
+    Bug 1141978
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <table id="table1" border="1">
     <thead>
@@ -270,10 +411,92 @@
       <td>empty cell</th>
       <td id="table6_ch">colheader</td>
     </tr>
     <tr>
       <td id="table6_rh">rowheader</th>
       <td id="table6_cell" headers="table6_ch table6_rh">cell</td>
     </tr>
   </table>
+
+  <table id="table7" class="data complex" border="1">
+    <caption>Version 1 with rowgroup</caption>
+    <thead>
+    <tr>
+      <td colspan="2">&nbsp;</td>
+      <th id="t7_1km" scope="col">1 km</th>
+      <th id="t7_5km" scope="col">5 km</th>
+      <th id="t7_10km" scope="col">10 km</th>
+    </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <th id="t7_Females" rowspan="2" scope="rowgroup">Females</th>
+        <th id="t7_Mary" scope="row">Mary</th>
+        <td id="t7_r1c1">8:32</td>
+        <td id="t7_r1c2">28:04</td>
+        <td id="t7_r1c3">1:01:16</td>
+      </tr>
+      <tr>
+        <th id="t7_Betsy" scope="row">Betsy</th>
+        <td id="t7_r2c1">7:43</td>
+        <td id="t7_r2c2">26:47</td>
+        <td id="t7_r2c3">55:38</td>
+      </tr>
+      <tr>
+        <th id="t7_Males" rowspan="2" scope="rowgroup">Males</th>
+        <th id="t7_Matt" scope="row">Matt</th>
+        <td id="t7_r3c1">7:55</td>
+        <td id="t7_r3c2">27:29</td>
+        <td id="t7_r3c3">57:04</td>
+      </tr>
+      <tr>
+        <th id="t7_Todd" scope="row">Todd</th>
+        <td id="t7_r4c1">7:01</td>
+        <td id="t7_r4c2">24:21</td>
+        <td id="t7_r4c3">50:35</td>
+      </tr>
+    </tbody>
+  </table>
+
+  <table id="table8" class="data complex" border="1">
+    <caption>Version 2 with colgroup</caption>
+    <thead>
+      <tr>
+        <td rowspan="2">&nbsp;</td>
+        <th id="t8_Females" colspan="2" scope="colgroup">Females</th>
+        <th id="t8_Males" colspan="2" scope="colgroup">Males</th>
+      </tr>
+      <tr>
+        <th id="t8_Mary" scope="col">Mary</th>
+        <th id="t8_Betsy" scope="col">Betsy</th>
+        <th id="t8_Matt" scope="col">Matt</th>
+        <th id="t8_Todd" scope="col">Todd</th>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <th id="t8_1km" scope="row">1 km</th>
+        <td id="t8_r1c1">8:32</td>
+        <td id="t8_r1c2">7:43</td>
+        <td id="t8_r1c3">7:55</td>
+        <td id="t8_r1c4">7:01</td>
+      </tr>
+      <tr>
+        <th id="t8_5km" scope="row">5 km</th>
+        <td id="t8_r2c1">28:04</td>
+        <td id="t8_r2c2">26:47</td>
+        <td id="t8_r2c3">27:27</td>
+        <td id="t8_r2c4">24:21</td>
+      </tr>
+      <tr>
+        <th id="t8_10km" scope="row">10 km</th>
+        <td id="t8_r3c1">1:01:16</td>
+        <td id="t8_r3c2">55:38</td>
+        <td id="t8_r3c3">57:04</td>
+        <td id="t8_r3c4">50:35</td>
+      </tr>
+
+    </tbody>
+  </table>
+
 </body>
 </html>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/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="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- 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="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <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="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- 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="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
   <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="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/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="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- 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="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <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="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-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="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/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="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
   <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": "8eac260ee81a8aca05770d18c5736536d44ee7a7", 
+        "git_revision": "efebbafd12fc42ddcd378948b683a51106517660", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "1400d176ecef76d06b012fb082c246eb17d1d30f", 
+    "revision": "d8e53e5d917b1ce79aea842e8340ce82799cac3e", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/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="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
   <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/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/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="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8eac260ee81a8aca05770d18c5736536d44ee7a7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="efebbafd12fc42ddcd378948b683a51106517660"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="b685e3aab4fde7624d78993877a8f7910f2a5f06"/>
--- a/dom/base/nsDOMAttributeMap.cpp
+++ b/dom/base/nsDOMAttributeMap.cpp
@@ -46,27 +46,23 @@ RemoveMapRef(nsAttrHashKey::KeyType aKey
 {
   aData->SetMap(nullptr);
 
   return PL_DHASH_REMOVE;
 }
 
 nsDOMAttributeMap::~nsDOMAttributeMap()
 {
-  if (mAttributeCache) {
-    mAttributeCache->Enumerate(RemoveMapRef, nullptr);
-  }
+  mAttributeCache.Enumerate(RemoveMapRef, nullptr);
 }
 
 void
 nsDOMAttributeMap::DropReference()
 {
-  if (mAttributeCache) {
-    mAttributeCache->Enumerate(RemoveMapRef, nullptr);
-  }
+  mAttributeCache.Enumerate(RemoveMapRef, nullptr);
   mContent = nullptr;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMAttributeMap)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttributeMap)
   tmp->DropReference();
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
@@ -82,19 +78,17 @@ TraverseMapEntry(nsAttrHashKey::KeyType 
     static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
 
   cb->NoteXPCOMChild(static_cast<nsINode*>(aData.get()));
 
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMAttributeMap)
-  if (tmp->mAttributeCache) {
-    tmp->mAttributeCache->Enumerate(TraverseMapEntry, &cb);
-  }
+  tmp->mAttributeCache.Enumerate(TraverseMapEntry, &cb);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDOMAttributeMap)
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMAttributeMap)
   if (tmp->IsBlack()) {
@@ -138,79 +132,76 @@ SetOwnerDocumentFunc(nsAttrHashKey::KeyT
   nsresult rv = aData->SetOwnerDocument(static_cast<nsIDocument*>(aUserArg));
 
   return NS_FAILED(rv) ? PL_DHASH_STOP : PL_DHASH_NEXT;
 }
 
 nsresult
 nsDOMAttributeMap::SetOwnerDocument(nsIDocument* aDocument)
 {
-  if (mAttributeCache) {
-    uint32_t n = mAttributeCache->Enumerate(SetOwnerDocumentFunc, aDocument);
-    NS_ENSURE_TRUE(n == mAttributeCache->Count(), NS_ERROR_FAILURE);
-  }
+  uint32_t n = mAttributeCache.Enumerate(SetOwnerDocumentFunc, aDocument);
+  NS_ENSURE_TRUE(n == mAttributeCache.Count(), NS_ERROR_FAILURE);
+
   return NS_OK;
 }
 
 void
 nsDOMAttributeMap::DropAttribute(int32_t aNamespaceID, nsIAtom* aLocalName)
 {
   nsAttrKey attr(aNamespaceID, aLocalName);
-  if (mAttributeCache) {
-    Attr *node = mAttributeCache->GetWeak(attr);
-    if (node) {
-      // Break link to map
-      node->SetMap(nullptr);
+  Attr *node = mAttributeCache.GetWeak(attr);
+  if (node) {
+    // Break link to map
+    node->SetMap(nullptr);
 
-      // Remove from cache
-      mAttributeCache->Remove(attr);
-    }
+    // Remove from cache
+    mAttributeCache.Remove(attr);
   }
 }
 
 already_AddRefed<Attr>
 nsDOMAttributeMap::RemoveAttribute(mozilla::dom::NodeInfo* aNodeInfo)
 {
   NS_ASSERTION(aNodeInfo, "RemoveAttribute() called with aNodeInfo == nullptr!");
 
   nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
 
   nsRefPtr<Attr> node;
-  if (mAttributeCache && mAttributeCache->Get(attr, getter_AddRefs(node))) {
-    // Break link to map
-    node->SetMap(nullptr);
-
-    // Remove from cache
-    mAttributeCache->Remove(attr);
-  } else {
+  if (!mAttributeCache.Get(attr, getter_AddRefs(node))) {
     nsAutoString value;
     // As we are removing the attribute we need to set the current value in
     // the attribute node.
     mContent->GetAttr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom(), value);
     nsRefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
     node = new Attr(nullptr, ni.forget(), value, true);
   }
+  else {
+    // Break link to map
+    node->SetMap(nullptr);
+
+    // Remove from cache
+    mAttributeCache.Remove(attr);
+  }
 
   return node.forget();
 }
 
 Attr*
 nsDOMAttributeMap::GetAttribute(mozilla::dom::NodeInfo* aNodeInfo, bool aNsAware)
 {
   NS_ASSERTION(aNodeInfo, "GetAttribute() called with aNodeInfo == nullptr!");
 
   nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
 
-  EnsureAttributeCache();
-  Attr* node = mAttributeCache->GetWeak(attr);
+  Attr* node = mAttributeCache.GetWeak(attr);
   if (!node) {
     nsRefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
     nsRefPtr<Attr> newAttr =
       new Attr(this, ni.forget(), EmptyString(), aNsAware);
-    mAttributeCache->Put(attr, newAttr);
+    mAttributeCache.Put(attr, newAttr);
     node = newAttr;
   }
 
   return node;
 }
 
 Attr*
 nsDOMAttributeMap::NamedGetter(const nsAString& aAttrName, bool& aFound)
@@ -246,24 +237,16 @@ nsDOMAttributeMap::GetNamedItem(const ns
 {
   NS_ENSURE_ARG_POINTER(aAttribute);
 
   NS_IF_ADDREF(*aAttribute = GetNamedItem(aAttrName));
 
   return NS_OK;
 }
 
-void
-nsDOMAttributeMap::EnsureAttributeCache()
-{
-  if (!mAttributeCache) {
-    mAttributeCache = MakeUnique<AttrCache>();
-  }
-}
-
 NS_IMETHODIMP
 nsDOMAttributeMap::SetNamedItem(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
 {
   Attr* attribute = static_cast<Attr*>(aAttr);
   NS_ENSURE_ARG(attribute);
 
   ErrorResult rv;
   *aReturn = SetNamedItem(*attribute, rv).take();
@@ -360,18 +343,17 @@ nsDOMAttributeMap::SetNamedItemInternal(
   nsAutoString value;
   aAttr.GetValue(value);
 
   nsRefPtr<NodeInfo> ni = aAttr.NodeInfo();
 
   // Add the new attribute to the attribute map before updating
   // its value in the element. @see bug 364413.
   nsAttrKey attrkey(ni->NamespaceID(), ni->NameAtom());
-  EnsureAttributeCache();
-  mAttributeCache->Put(attrkey, &aAttr);
+  mAttributeCache.Put(attrkey, &aAttr);
   aAttr.SetMap(this);
 
   rv = mContent->SetAttr(ni->NamespaceID(), ni->NameAtom(),
                          ni->GetPrefixAtom(), value, true);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
     DropAttribute(ni->NamespaceID(), ni->NameAtom());
   }
@@ -549,43 +531,41 @@ nsDOMAttributeMap::RemoveNamedItemNS(con
   }
 
   return RemoveNamedItem(ni, aError);
 }
 
 uint32_t
 nsDOMAttributeMap::Count() const
 {
-  return mAttributeCache ? mAttributeCache->Count() : 0;
+  return mAttributeCache.Count();
 }
 
 uint32_t
 nsDOMAttributeMap::Enumerate(AttrCache::EnumReadFunction aFunc,
                              void *aUserArg) const
 {
-  return mAttributeCache ? mAttributeCache->EnumerateRead(aFunc, aUserArg) : 0;
+  return mAttributeCache.EnumerateRead(aFunc, aUserArg);
 }
 
 size_t
 AttrCacheSizeEnumerator(const nsAttrKey& aKey,
                         const nsRefPtr<Attr>& aValue,
                         MallocSizeOf aMallocSizeOf,
                         void* aUserArg)
 {
   return aMallocSizeOf(aValue.get());
 }
 
 size_t
 nsDOMAttributeMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
-  n += mAttributeCache
-     ? mAttributeCache->SizeOfExcludingThis(AttrCacheSizeEnumerator,
-                                            aMallocSizeOf)
-     : 0;
+  n += mAttributeCache.SizeOfExcludingThis(AttrCacheSizeEnumerator,
+                                           aMallocSizeOf);
 
   // NB: mContent is non-owning and thus not counted.
   return n;
 }
 
 /* virtual */ JSObject*
 nsDOMAttributeMap::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
--- a/dom/base/nsDOMAttributeMap.h
+++ b/dom/base/nsDOMAttributeMap.h
@@ -6,17 +6,16 @@
 /*
  * Implementation of the |attributes| property of DOM Core's Element object.
  */
 
 #ifndef nsDOMAttributeMap_h
 #define nsDOMAttributeMap_h
 
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/UniquePtr.h"
 #include "mozilla/dom/Attr.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMMozNamedAttrMap.h"
 #include "nsRefPtrHashtable.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
@@ -181,21 +180,19 @@ public:
 
 protected:
   virtual ~nsDOMAttributeMap();
 
 private:
   nsCOMPtr<Element> mContent;
 
   /**
-   * Cache of Attrs. It's usually empty, and thus initialized lazily.
+   * Cache of Attrs.
    */
-  mozilla::UniquePtr<AttrCache> mAttributeCache;
-
-  void EnsureAttributeCache();
+  AttrCache mAttributeCache;
 
   /**
    * SetNamedItem() (aWithNS = false) and SetNamedItemNS() (aWithNS =
    * true) implementation.
    */
   already_AddRefed<Attr>
   SetNamedItemInternal(Attr& aNode, bool aWithNS, ErrorResult& aError);
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -154,16 +154,28 @@ DOMInterfaces = {
 'BluetoothDiscoveryHandle': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothDiscoveryHandle',
 },
 
 'BluetoothGatt': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothGatt',
 },
 
+'BluetoothGattCharacteristic': {
+    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattCharacteristic',
+},
+
+'BluetoothGattDescriptor': {
+    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattDescriptor',
+},
+
+'BluetoothGattService': {
+    'nativeType': 'mozilla::dom::bluetooth::BluetoothGattService',
+},
+
 'BluetoothManager': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothManager',
 },
 
 'BluetoothPairingHandle': {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothPairingHandle',
 },
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -3279,17 +3279,19 @@ class CGWrapWithCacheMethod(CGAbstractMe
                 Argument('JS::MutableHandle<JSObject*>', 'aReflector')]
         CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'bool', args)
         self.properties = properties
 
     def definition_body(self):
         return fill(
             """
             $*{assertInheritance}
-            MOZ_ASSERT_IF(aGivenProto, !aCache->GetWrapper());
+            MOZ_ASSERT(!aCache->GetWrapper(),
+                       "You should probably not be using Wrap() directly; use "
+                       "GetOrCreateDOMReflector instead");
 
             MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
                        "nsISupports must be on our primary inheritance chain");
 
             JS::Rooted<JSObject*> parent(aCx, WrapNativeParent(aCx, aObject->GetParentObject()));
             if (!parent) {
               return false;
             }
--- a/dom/bluetooth2/BluetoothCommon.h
+++ b/dom/bluetooth2/BluetoothCommon.h
@@ -280,16 +280,26 @@ enum BluetoothSspVariant {
   SSP_VARIANT_PASSKEY_CONFIRMATION,
   SSP_VARIANT_PASSKEY_ENTRY,
   SSP_VARIANT_CONSENT,
   SSP_VARIANT_PASSKEY_NOTIFICATION
 };
 
 struct BluetoothUuid {
   uint8_t mUuid[16];
+
+  bool operator==(const BluetoothUuid& aOther) const
+  {
+    for (uint8_t i = 0; i < sizeof(mUuid); i++) {
+      if (mUuid[i] != aOther.mUuid[i]) {
+        return false;
+      }
+    }
+    return true;
+  }
 };
 
 struct BluetoothServiceRecord {
   BluetoothUuid mUuid;
   uint16_t mChannel;
   char mName[256];
 };
 
@@ -542,21 +552,31 @@ enum BluetoothGattStatus {
 
 struct BluetoothGattAdvData {
   uint8_t mAdvData[62];
 };
 
 struct BluetoothGattId {
   BluetoothUuid mUuid;
   uint8_t mInstanceId;
+
+  bool operator==(const BluetoothGattId& aOther) const
+  {
+    return mUuid == aOther.mUuid && mInstanceId == aOther.mInstanceId;
+  }
 };
 
 struct BluetoothGattServiceId {
   BluetoothGattId mId;
   uint8_t mIsPrimary;
+
+  bool operator==(const BluetoothGattServiceId& aOther) const
+  {
+    return mId == aOther.mId && mIsPrimary == aOther.mIsPrimary;
+  }
 };
 
 struct BluetoothGattReadParam {
   BluetoothGattServiceId mServiceId;
   BluetoothGattId mCharId;
   BluetoothGattId mDescriptorId;
   uint8_t mValue[BLUETOOTH_GATT_MAX_ATTR_LEN];
   uint16_t mValueLength;
--- a/dom/bluetooth2/BluetoothGatt.cpp
+++ b/dom/bluetooth2/BluetoothGatt.cpp
@@ -1,52 +1,48 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothUtils.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
 #include "mozilla/dom/bluetooth/BluetoothGatt.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/dom/BluetoothGattBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "nsIUUIDGenerator.h"
 #include "nsServiceManagerUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 USING_BLUETOOTH_NAMESPACE
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothGatt)
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothGatt,
-                                                DOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothGatt,
-                                                  DOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED(BluetoothGatt,
+                                   DOMEventTargetHelper,
+                                   mServices)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothGatt)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothGatt, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothGatt, DOMEventTargetHelper)
 
 BluetoothGatt::BluetoothGatt(nsPIDOMWindow* aWindow,
                              const nsAString& aDeviceAddr)
   : DOMEventTargetHelper(aWindow)
   , mAppUuid(EmptyString())
   , mClientIf(0)
   , mConnectionState(BluetoothConnectionState::Disconnected)
   , mDeviceAddr(aDeviceAddr)
+  , mDiscoveringServices(false)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(!mDeviceAddr.IsEmpty());
 }
 
 BluetoothGatt::~BluetoothGatt()
 {
   BluetoothService* bs = BluetoothService::Get();
@@ -211,16 +207,46 @@ BluetoothGatt::ReadRemoteRssi(ErrorResul
 
   nsRefPtr<BluetoothReplyRunnable> result =
     new ReadRemoteRssiTask(promise);
   bs->GattClientReadRemoteRssiInternal(mClientIf, mDeviceAddr, result);
 
   return promise.forget();
 }
 
+already_AddRefed<Promise>
+BluetoothGatt::DiscoverServices(ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
+  if (!global) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
+
+  BT_ENSURE_TRUE_REJECT(
+    mConnectionState == BluetoothConnectionState::Connected &&
+    !mDiscoveringServices,
+    NS_ERROR_DOM_INVALID_STATE_ERR);
+
+  BluetoothService* bs = BluetoothService::Get();
+  BT_ENSURE_TRUE_REJECT(bs, NS_ERROR_NOT_AVAILABLE);
+
+  mDiscoveringServices = true;
+  nsRefPtr<BluetoothReplyRunnable> result =
+    new BluetoothVoidReplyRunnable(nullptr /* DOMRequest */,
+                                   promise,
+                                   NS_LITERAL_STRING("DiscoverGattServices"));
+  bs->DiscoverGattServicesInternal(mAppUuid, result);
+
+  return promise.forget();
+}
+
 void
 BluetoothGatt::UpdateConnectionState(BluetoothConnectionState aState)
 {
   BT_API2_LOGR("GATT connection state changes to: %d", int(aState));
   mConnectionState = aState;
 
   // Dispatch connectionstatechanged event to application
   nsCOMPtr<nsIDOMEvent> event;
@@ -231,16 +257,32 @@ BluetoothGatt::UpdateConnectionState(Blu
                         false,
                         false);
   NS_ENSURE_SUCCESS_VOID(rv);
 
   DispatchTrustedEvent(event);
 }
 
 void
+BluetoothGatt::HandleServicesDiscovered(const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattServiceId);
+
+  const InfallibleTArray<BluetoothGattServiceId>& serviceIds =
+    aValue.get_ArrayOfBluetoothGattServiceId();
+
+  for (uint32_t i = 0; i < serviceIds.Length(); i++) {
+    mServices.AppendElement(new BluetoothGattService(
+      GetParentObject(), mAppUuid, serviceIds[i]));
+  }
+
+  BluetoothGattBinding::ClearCachedServicesValue(this);
+}
+
+void
 BluetoothGatt::Notify(const BluetoothSignal& aData)
 {
   BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
 
   BluetoothValue v = aData.value();
   if (aData.name().EqualsLiteral("ClientRegistered")) {
     MOZ_ASSERT(v.type() == BluetoothValue::Tuint32_t);
     mClientIf = v.get_uint32_t();
@@ -248,16 +290,28 @@ BluetoothGatt::Notify(const BluetoothSig
     mClientIf = 0;
   } else if (aData.name().EqualsLiteral(GATT_CONNECTION_STATE_CHANGED_ID)) {
     MOZ_ASSERT(v.type() == BluetoothValue::Tbool);
 
     BluetoothConnectionState state =
       v.get_bool() ? BluetoothConnectionState::Connected
                    : BluetoothConnectionState::Disconnected;
     UpdateConnectionState(state);
+  } else if (aData.name().EqualsLiteral("ServicesDiscovered")) {
+    HandleServicesDiscovered(v);
+  } else if (aData.name().EqualsLiteral("DiscoverCompleted")) {
+    MOZ_ASSERT(v.type() == BluetoothValue::Tbool);
+
+    bool isDiscoverSuccess = v.get_bool();
+    if (!isDiscoverSuccess) { // Clean all discovered attributes if failed
+      mServices.Clear();
+      BluetoothGattBinding::ClearCachedServicesValue(this);
+    }
+
+    mDiscoveringServices = false;
   } else {
     BT_WARNING("Not handling GATT signal: %s",
                NS_ConvertUTF16toUTF8(aData.name()).get());
   }
 }
 
 JSObject*
 BluetoothGatt::WrapObject(JSContext* aContext, JS::Handle<JSObject*> aGivenProto)
--- a/dom/bluetooth2/BluetoothGatt.h
+++ b/dom/bluetooth2/BluetoothGatt.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothgatt_h__
 #define mozilla_dom_bluetooth_bluetoothgatt_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/BluetoothGattBinding.h"
 #include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattService.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 class Promise;
 }
 }
 
@@ -36,26 +37,32 @@ public:
   /****************************************************************************
    * Attribute Getters
    ***************************************************************************/
   BluetoothConnectionState ConnectionState() const
   {
     return mConnectionState;
   }
 
+  void GetServices(nsTArray<nsRefPtr<BluetoothGattService>>& aServices) const
+  {
+    aServices = mServices;
+  }
+
   /****************************************************************************
    * Event Handlers
    ***************************************************************************/
   IMPL_EVENT_HANDLER(connectionstatechanged);
 
   /****************************************************************************
    * Methods (Web API Implementation)
    ***************************************************************************/
   already_AddRefed<Promise> Connect(ErrorResult& aRv);
   already_AddRefed<Promise> Disconnect(ErrorResult& aRv);
+  already_AddRefed<Promise> DiscoverServices(ErrorResult& aRv);
   already_AddRefed<Promise> ReadRemoteRssi(ErrorResult& aRv);
 
   /****************************************************************************
    * Others
    ***************************************************************************/
   void Notify(const BluetoothSignal& aParam); // BluetoothSignalObserver
 
   nsPIDOMWindow* GetParentObject() const
@@ -82,16 +89,25 @@ private:
 
   /**
    * Generate a random uuid.
    *
    * @param aUuidString [out] String to store the generated uuid.
    */
   void GenerateUuid(nsAString &aUuidString);
 
+  /**
+   * Add newly discovered GATT services into mServices and update the cache
+   * value of mServices.
+   *
+   * @param aValue [in] BluetoothValue which contains an array of
+   *                    BluetoothGattServiceId of all discovered services.
+   */
+  void HandleServicesDiscovered(const BluetoothValue& aValue);
+
   /****************************************************************************
    * Variables
    ***************************************************************************/
   /**
    * Random generated UUID of this GATT client.
    */
   nsString mAppUuid;
 
@@ -105,13 +121,23 @@ private:
    * Connection state of this remote device.
    */
   BluetoothConnectionState mConnectionState;
 
   /**
    * Address of the remote device.
    */
   nsString mDeviceAddr;
+
+  /**
+   * Array of discovered services from the remote GATT server.
+   */
+  nsTArray<nsRefPtr<BluetoothGattService>> mServices;
+
+  /**
+   * Indicate whether there is ongoing discoverServices request or not.
+   */
+  bool mDiscoveringServices;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattCharacteristic.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BluetoothService.h"
+#include "BluetoothUtils.h"
+#include "mozilla/dom/BluetoothGattCharacteristicBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
+#include "mozilla/dom/bluetooth/BluetoothGattDescriptor.h"
+#include "mozilla/dom/bluetooth/BluetoothGattService.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+USING_BLUETOOTH_NAMESPACE
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
+  BluetoothGattCharacteristic, mOwner, mService, mDescriptors)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattCharacteristic)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattCharacteristic)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattCharacteristic)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+BluetoothGattCharacteristic::BluetoothGattCharacteristic(
+  nsPIDOMWindow* aOwner,
+  BluetoothGattService* aService,
+  const BluetoothGattId& aCharId)
+  : mOwner(aOwner)
+  , mService(aService)
+  , mCharId(aCharId)
+{
+  MOZ_ASSERT(aOwner);
+  MOZ_ASSERT(mService);
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  // Generate bluetooth signal path and a string representation to provide uuid
+  // of this characteristic to applications
+  nsString path;
+  GeneratePathFromGattId(mCharId, path, mUuidStr);
+  bs->RegisterBluetoothSignalHandler(path, this);
+}
+
+BluetoothGattCharacteristic::~BluetoothGattCharacteristic()
+{
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  nsString path;
+  GeneratePathFromGattId(mCharId, path);
+  bs->UnregisterBluetoothSignalHandler(path, this);
+}
+
+void
+BluetoothGattCharacteristic::HandleDescriptorsDiscovered(
+  const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattId);
+
+  const InfallibleTArray<BluetoothGattId>& descriptorIds =
+    aValue.get_ArrayOfBluetoothGattId();
+
+  for (uint32_t i = 0; i < descriptorIds.Length(); i++) {
+    mDescriptors.AppendElement(new BluetoothGattDescriptor(
+      GetParentObject(), this, descriptorIds[i]));
+  }
+
+  BluetoothGattCharacteristicBinding::ClearCachedDescriptorsValue(this);
+}
+
+void
+BluetoothGattCharacteristic::Notify(const BluetoothSignal& aData)
+{
+  BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+
+  BluetoothValue v = aData.value();
+  if (aData.name().EqualsLiteral("DescriptorsDiscovered")) {
+    HandleDescriptorsDiscovered(v);
+  } else {
+    BT_WARNING("Not handling GATT Characteristic signal: %s",
+               NS_ConvertUTF16toUTF8(aData.name()).get());
+  }
+}
+
+JSObject*
+BluetoothGattCharacteristic::WrapObject(JSContext* aContext,
+                                        JS::Handle<JSObject*> aGivenProto)
+{
+  return BluetoothGattCharacteristicBinding::Wrap(aContext, this, aGivenProto);
+}
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattCharacteristic.h
@@ -0,0 +1,120 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothgattcharacteristic_h__
+#define mozilla_dom_bluetooth_bluetoothgattcharacteristic_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BluetoothGattCharacteristicBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattDescriptor.h"
+#include "nsCOMPtr.h"
+#include "nsWrapperCache.h"
+#include "nsPIDOMWindow.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothGattService;
+class BluetoothSignal;
+class BluetoothValue;
+
+class BluetoothGattCharacteristic final : public nsISupports
+                                        , public nsWrapperCache
+                                        , public BluetoothSignalObserver
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothGattCharacteristic)
+
+  /****************************************************************************
+   * Attribute Getters
+   ***************************************************************************/
+  BluetoothGattService* Service() const
+  {
+    return mService;
+  }
+
+  void GetDescriptors(
+    nsTArray<nsRefPtr<BluetoothGattDescriptor>>& aDescriptors) const
+  {
+    aDescriptors = mDescriptors;
+  }
+
+  void GetUuid(nsString& aUuidStr) const
+  {
+    aUuidStr = mUuidStr;
+  }
+
+  int InstanceId() const
+  {
+    return mCharId.mInstanceId;
+  }
+
+  /****************************************************************************
+   * Others
+   ***************************************************************************/
+  const BluetoothGattId& GetCharacteristicId() const
+  {
+    return mCharId;
+  }
+
+  void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
+
+  nsPIDOMWindow* GetParentObject() const
+  {
+     return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  BluetoothGattCharacteristic(nsPIDOMWindow* aOwner,
+                              BluetoothGattService* aService,
+                              const BluetoothGattId& aCharId);
+
+private:
+  ~BluetoothGattCharacteristic();
+
+  /**
+   * Add newly discovered GATT descriptors into mDescriptors and update the
+   * cache value of mDescriptors.
+   *
+   * @param aValue [in] BluetoothValue which contains an array of
+   *                    BluetoothGattId of all discovered descriptors.
+   */
+  void HandleDescriptorsDiscovered(const BluetoothValue& aValue);
+
+  /****************************************************************************
+   * Variables
+   ***************************************************************************/
+  nsCOMPtr<nsPIDOMWindow> mOwner;
+
+  /**
+   * Service that this characteristic belongs to.
+   */
+  nsRefPtr<BluetoothGattService> mService;
+
+  /**
+   * Array of discovered descriptors for this characteristic.
+   */
+  nsTArray<nsRefPtr<BluetoothGattDescriptor>> mDescriptors;
+
+  /**
+   * GattId of this GATT characteristic which contains
+   * 1) mUuid: UUID of this characteristic in byte array format.
+   * 2) mInstanceId: Instance id of this characteristic.
+   */
+  BluetoothGattId mCharId;
+
+  /**
+   * UUID string of this GATT characteristic.
+   */
+  nsString mUuidStr;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattDescriptor.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BluetoothService.h"
+#include "BluetoothUtils.h"
+#include "mozilla/dom/BluetoothGattDescriptorBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
+#include "mozilla/dom/bluetooth/BluetoothGattDescriptor.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+USING_BLUETOOTH_NAMESPACE
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
+  BluetoothGattDescriptor, mOwner, mCharacteristic)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattDescriptor)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattDescriptor)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattDescriptor)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+BluetoothGattDescriptor::BluetoothGattDescriptor(
+  nsPIDOMWindow* aOwner,
+  BluetoothGattCharacteristic* aCharacteristic,
+  const BluetoothGattId& aDescriptorId)
+  : mOwner(aOwner)
+  , mCharacteristic(aCharacteristic)
+  , mDescriptorId(aDescriptorId)
+{
+  MOZ_ASSERT(aOwner);
+  MOZ_ASSERT(aCharacteristic);
+
+  // Generate a string representation to provide uuid of this descriptor to
+  // applications
+  ReversedUuidToString(aDescriptorId.mUuid, mUuidStr);
+}
+
+BluetoothGattDescriptor::~BluetoothGattDescriptor()
+{
+}
+
+JSObject*
+BluetoothGattDescriptor::WrapObject(JSContext* aContext,
+                                    JS::Handle<JSObject*> aGivenProto)
+{
+  return BluetoothGattDescriptorBinding::Wrap(aContext, this, aGivenProto);
+}
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattDescriptor.h
@@ -0,0 +1,88 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothgattdescriptor_h__
+#define mozilla_dom_bluetooth_bluetoothgattdescriptor_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BluetoothGattDescriptorBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "nsCOMPtr.h"
+#include "nsWrapperCache.h"
+#include "nsPIDOMWindow.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothGattCharacteristic;
+class BluetoothSignal;
+class BluetoothValue;
+
+class BluetoothGattDescriptor final : public nsISupports
+                                    , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothGattDescriptor)
+
+  /****************************************************************************
+   * Attribute Getters
+   ***************************************************************************/
+  BluetoothGattCharacteristic* Characteristic() const
+  {
+    return mCharacteristic;
+  }
+
+  void GetUuid(nsString& aUuidStr) const
+  {
+    aUuidStr = mUuidStr;
+  }
+
+  /****************************************************************************
+   * Others
+   ***************************************************************************/
+  void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
+
+  nsPIDOMWindow* GetParentObject() const
+  {
+     return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  BluetoothGattDescriptor(nsPIDOMWindow* aOwner,
+                          BluetoothGattCharacteristic* aCharacteristic,
+                          const BluetoothGattId& aDescriptorId);
+
+private:
+  ~BluetoothGattDescriptor();
+
+  /****************************************************************************
+   * Variables
+   ***************************************************************************/
+  nsCOMPtr<nsPIDOMWindow> mOwner;
+
+  /**
+   * Characteristic that this descriptor belongs to.
+   */
+  nsRefPtr<BluetoothGattCharacteristic> mCharacteristic;
+
+  /**
+   * GattId of this GATT descriptor which contains
+   * 1) mUuid: UUID of this descriptor in byte array format.
+   * 2) mInstanceId: Instance id of this descriptor.
+   */
+  BluetoothGattId mDescriptorId;
+
+  /**
+   * UUID string of this GATT descriptor.
+   */
+  nsString mUuidStr;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattService.cpp
@@ -0,0 +1,114 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BluetoothService.h"
+#include "BluetoothUtils.h"
+#include "mozilla/dom/BluetoothGattServiceBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
+#include "mozilla/dom/bluetooth/BluetoothGattService.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+USING_BLUETOOTH_NAMESPACE
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
+  BluetoothGattService, mOwner, mIncludedServices, mCharacteristics)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothGattService)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothGattService)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothGattService)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+BluetoothGattService::BluetoothGattService(
+  nsPIDOMWindow* aOwner, const nsAString& aAppUuid,
+  const BluetoothGattServiceId& aServiceId)
+  : mOwner(aOwner)
+  , mAppUuid(aAppUuid)
+  , mServiceId(aServiceId)
+{
+  MOZ_ASSERT(aOwner);
+  MOZ_ASSERT(!mAppUuid.IsEmpty());
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  // Generate bluetooth signal path and a string representation to provide
+  // uuid of this service to applications
+  nsString path;
+  GeneratePathFromGattId(mServiceId.mId, path, mUuidStr);
+  bs->RegisterBluetoothSignalHandler(path, this);
+}
+
+BluetoothGattService::~BluetoothGattService()
+{
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  nsString path;
+  GeneratePathFromGattId(mServiceId.mId, path);
+  bs->UnregisterBluetoothSignalHandler(path, this);
+}
+
+void
+BluetoothGattService::HandleIncludedServicesDiscovered(
+  const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattServiceId);
+
+  const InfallibleTArray<BluetoothGattServiceId>& includedServIds =
+    aValue.get_ArrayOfBluetoothGattServiceId();
+
+  for (uint32_t i = 0; i < includedServIds.Length(); i++) {
+    mIncludedServices.AppendElement(new BluetoothGattService(
+      GetParentObject(), mAppUuid, includedServIds[i]));
+  }
+
+  BluetoothGattServiceBinding::ClearCachedIncludedServicesValue(this);
+}
+
+void
+BluetoothGattService::HandleCharacteristicsDiscovered(
+  const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothGattId);
+
+  const InfallibleTArray<BluetoothGattId>& characteristicIds =
+    aValue.get_ArrayOfBluetoothGattId();
+
+  for (uint32_t i = 0; i < characteristicIds.Length(); i++) {
+    mCharacteristics.AppendElement(new BluetoothGattCharacteristic(
+      GetParentObject(), this, characteristicIds[i]));
+  }
+
+  BluetoothGattServiceBinding::ClearCachedCharacteristicsValue(this);
+}
+
+void
+BluetoothGattService::Notify(const BluetoothSignal& aData)
+{
+  BT_LOGD("[D] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
+
+  BluetoothValue v = aData.value();
+  if (aData.name().EqualsLiteral("IncludedServicesDiscovered")) {
+    HandleIncludedServicesDiscovered(v);
+  } else if (aData.name().EqualsLiteral("CharacteristicsDiscovered")) {
+    HandleCharacteristicsDiscovered(v);
+  } else {
+    BT_WARNING("Not handling GATT Service signal: %s",
+               NS_ConvertUTF16toUTF8(aData.name()).get());
+  }
+}
+
+JSObject*
+BluetoothGattService::WrapObject(JSContext* aContext,
+                                 JS::Handle<JSObject*> aGivenProto)
+{
+  return BluetoothGattServiceBinding::Wrap(aContext, this, aGivenProto);
+}
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/BluetoothGattService.h
@@ -0,0 +1,146 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothgattservice_h__
+#define mozilla_dom_bluetooth_bluetoothgattservice_h__
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BluetoothGattServiceBinding.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
+#include "mozilla/dom/bluetooth/BluetoothGattCharacteristic.h"
+#include "nsCOMPtr.h"
+#include "nsWrapperCache.h"
+#include "nsPIDOMWindow.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothSignal;
+class BluetoothValue;
+
+class BluetoothGattService final : public nsISupports
+                                 , public nsWrapperCache
+                                 , public BluetoothSignalObserver
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothGattService)
+
+  /****************************************************************************
+   * Attribute Getters
+   ***************************************************************************/
+  bool IsPrimary() const
+  {
+    return mServiceId.mIsPrimary;
+  }
+
+  void GetUuid(nsString& aUuidStr) const
+  {
+    aUuidStr = mUuidStr;
+  }
+
+  int InstanceId() const
+  {
+    return mServiceId.mId.mInstanceId;
+  }
+
+  void GetIncludedServices(
+    nsTArray<nsRefPtr<BluetoothGattService>>& aIncludedServices) const
+  {
+    aIncludedServices = mIncludedServices;
+  }
+
+  void GetCharacteristics(
+    nsTArray<nsRefPtr<BluetoothGattCharacteristic>>& aCharacteristics) const
+  {
+    aCharacteristics = mCharacteristics;
+  }
+
+  /****************************************************************************
+   * Others
+   ***************************************************************************/
+  const nsAString& GetAppUuid() const
+  {
+    return mAppUuid;
+  }
+
+  const BluetoothGattServiceId& GetServiceId() const
+  {
+    return mServiceId;
+  }
+
+  void Notify(const BluetoothSignal& aData); // BluetoothSignalObserver
+
+  nsPIDOMWindow* GetParentObject() const
+  {
+     return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  BluetoothGattService(nsPIDOMWindow* aOwner,
+                       const nsAString& aAppUuid,
+                       const BluetoothGattServiceId& aServiceId);
+
+private:
+  ~BluetoothGattService();
+
+  /**
+   * Add newly discovered GATT included services into mIncludedServices and
+   * update the cache value of mIncludedServices.
+   *
+   * @param aValue [in] BluetoothValue which contains an array of
+   *                    BluetoothGattServiceId of all discovered included
+   *                    services.
+   */
+  void HandleIncludedServicesDiscovered(const BluetoothValue& aValue);
+
+  /**
+   * Add newly discovered GATT characteristics into mCharacteristics and
+   * update the cache value of mCharacteristics.
+   *
+   * @param aValue [in] BluetoothValue which contains an array of
+   *                    BluetoothGattId of all discovered characteristics.
+   */
+  void HandleCharacteristicsDiscovered(const BluetoothValue& aValue);
+
+  /****************************************************************************
+   * Variables
+   ***************************************************************************/
+  nsCOMPtr<nsPIDOMWindow> mOwner;
+
+  /**
+   * UUID of the GATT client.
+   */
+  nsString mAppUuid;
+
+  /**
+   * ServiceId of this GATT service which contains
+   * 1) mId.mUuid: UUID of this service in byte array format.
+   * 2) mId.mInstanceId: Instance id of this service.
+   * 3) mIsPrimary: Indicate whether this is a primary service or not.
+   */
+  BluetoothGattServiceId mServiceId;
+
+  /**
+   * UUID string of this GATT service.
+   */
+  nsString mUuidStr;
+
+  /**
+   * Array of discovered included services for this service.
+   */
+  nsTArray<nsRefPtr<BluetoothGattService>> mIncludedServices;
+
+  /**
+   * Array of discovered characteristics for this service.
+   */
+  nsTArray<nsRefPtr<BluetoothGattCharacteristic>> mCharacteristics;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
--- a/dom/bluetooth2/BluetoothInterface.h
+++ b/dom/bluetooth2/BluetoothInterface.h
@@ -686,30 +686,34 @@ public:
 
   /* Clear the attribute cache for a given device*/
   virtual void Refresh(int aClientIf,
                        const nsAString& aBdAddr,
                        BluetoothGattClientResultHandler* aRes) = 0;
 
   /* Enumerate Attributes */
   virtual void SearchService(int aConnId,
+                             bool aSearchAll,
                              const BluetoothUuid& aUuid,
                              BluetoothGattClientResultHandler* aRes) = 0;
   virtual void GetIncludedService(
     int aConnId,
     const BluetoothGattServiceId& aServiceId,
+    bool aFirst,
     const BluetoothGattServiceId& aStartServiceId,
     BluetoothGattClientResultHandler* aRes) = 0;
   virtual void GetCharacteristic(int aConnId,
                                  const BluetoothGattServiceId& aServiceId,
+                                 bool aFirst,
                                  const BluetoothGattId& aStartCharId,
                                  BluetoothGattClientResultHandler* aRes) = 0;
   virtual void GetDescriptor(int aConnId,
                              const BluetoothGattServiceId& aServiceId,
                              const BluetoothGattId& aCharId,
+                             bool aFirst,
                              const BluetoothGattId& aDescriptorId,
                              BluetoothGattClientResultHandler* aRes) = 0;
 
   /* Read / Write An Attribute */
   virtual void ReadCharacteristic(int aConnId,
                                   const BluetoothGattServiceId& aServiceId,
                                   const BluetoothGattId& aCharId,
                                   int aAuthReq,
--- a/dom/bluetooth2/BluetoothService.h
+++ b/dom/bluetooth2/BluetoothService.h
@@ -338,16 +338,24 @@ public:
    * (platform specific implementation)
    */
   virtual void
   DisconnectGattClientInternal(const nsAString& aAppUuid,
                                const nsAString& aDeviceAddress,
                                BluetoothReplyRunnable* aRunnable) = 0;
 
   /**
+   * Discover GATT services, characteristic, descriptors from a remote GATT
+   * server. (platform specific implementation)
+   */
+  virtual void
+  DiscoverGattServicesInternal(const nsAString& aAppUuid,
+                               BluetoothReplyRunnable* aRunnable) = 0;
+
+  /**
    * Unregister a GATT client. (platform specific implementation)
    */
   virtual void
   UnregisterGattClientInternal(int aClientIf,
                                BluetoothReplyRunnable* aRunnable) = 0;
 
   /**
    * Request RSSI for a remote GATT server. (platform specific implementation)
--- a/dom/bluetooth2/BluetoothUtils.cpp
+++ b/dom/bluetooth2/BluetoothUtils.cpp
@@ -36,16 +36,27 @@ UuidToString(const BluetoothUuid& aUuid,
           ntohs(uuid2), ntohs(uuid3),
           ntohl(uuid4), ntohs(uuid5));
 
   aString.Truncate();
   aString.AssignLiteral(uuidStr);
 }
 
 void
+ReversedUuidToString(const BluetoothUuid& aUuid, nsAString& aString)
+{
+  BluetoothUuid uuid;
+  for (uint8_t i = 0; i < 16; i++) {
+    uuid.mUuid[i] = aUuid.mUuid[15 - i];
+  }
+
+  UuidToString(uuid, aString);
+}
+
+void
 StringToUuid(const char* aString, BluetoothUuid& aUuid)
 {
   uint32_t uuid0, uuid4;
   uint16_t uuid1, uuid2, uuid3, uuid5;
 
   sscanf(aString, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
          &uuid0, &uuid1, &uuid2, &uuid3, &uuid4, &uuid5);
 
@@ -59,16 +70,36 @@ StringToUuid(const char* aString, Blueto
   memcpy(&aUuid.mUuid[0], &uuid0, sizeof(uint32_t));
   memcpy(&aUuid.mUuid[4], &uuid1, sizeof(uint16_t));
   memcpy(&aUuid.mUuid[6], &uuid2, sizeof(uint16_t));
   memcpy(&aUuid.mUuid[8], &uuid3, sizeof(uint16_t));
   memcpy(&aUuid.mUuid[10], &uuid4, sizeof(uint32_t));
   memcpy(&aUuid.mUuid[14], &uuid5, sizeof(uint16_t));
 }
 
+void
+GeneratePathFromGattId(const BluetoothGattId& aId,
+                       nsAString& aPath,
+                       nsAString& aUuidStr)
+{
+  ReversedUuidToString(aId.mUuid, aUuidStr);
+
+  aPath.Assign(aUuidStr);
+  aPath.AppendLiteral("_");
+  aPath.AppendInt(aId.mInstanceId);
+}
+
+void
+GeneratePathFromGattId(const BluetoothGattId& aId,
+                       nsAString& aPath)
+{
+  nsString uuidStr;
+  GeneratePathFromGattId(aId, aPath, uuidStr);
+}
+
 /**
  * |SetJsObject| is an internal function used by |BroadcastSystemMessage| only
  */
 bool
 SetJsObject(JSContext* aContext,
             const BluetoothValue& aValue,
             JS::Handle<JSObject*> aObj)
 {
--- a/dom/bluetooth2/BluetoothUtils.h
+++ b/dom/bluetooth2/BluetoothUtils.h
@@ -17,31 +17,72 @@ class BluetoothReplyRunnable;
 class BluetoothValue;
 
 //
 // BluetoothUuid <-> uuid string conversion
 //
 
 /**
  * Convert BluetoothUuid object to xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx uuid string.
- * This utility function is used by gecko internal only to convert BluetoothUuid
- * created by bluetooth stack to uuid string representation.
+ *
+ * Note: This utility function is used by gecko internal only to convert
+ * BluetoothUuid created by bluetooth stack to uuid string representation.
  */
 void
 UuidToString(const BluetoothUuid& aUuid, nsAString& aString);
 
 /**
+ * Convert BluetoothUuid object in a reversed byte order to
+ * xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx uuid string.
+ * Bluedroid stack reports the BluetoothUuid in a reversed byte order for
+ * GATT service, characteristic, descriptor uuids.
+ *
+ * Note: This utility function is used by gecko internal only to convert
+ * BluetoothUuid in a reversed byte order created by bluetooth stack to uuid
+ * string representation.
+ */
+void
+ReversedUuidToString(const BluetoothUuid& aUuid, nsAString& aString);
+
+/**
  * Convert xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx uuid string to BluetoothUuid object.
- * This utility function is used by gecko internal only to convert uuid string
- * created by gecko back to BluetoothUuid representation.
+ *
+ * Note: This utility function is used by gecko internal only to convert uuid
+ * string created by gecko back to BluetoothUuid representation.
  */
 void
 StringToUuid(const char* aString, BluetoothUuid& aUuid);
 
 //
+// Generate bluetooth signal path from GattId
+//
+
+/**
+ * Generate bluetooth signal path and UUID string from a GattId.
+ *
+ * @param aId      [in] GattId value to convert.
+ * @param aPath    [out] Bluetooth signal path generated from aId.
+ * @param aUuidStr [out] UUID string generated from aId.
+ */
+void
+GeneratePathFromGattId(const BluetoothGattId& aId,
+                       nsAString& aPath,
+                       nsAString& aUuidStr);
+
+/**
+ * Generate bluetooth signal path from a GattId.
+ *
+ * @param aId   [in] GattId value to convert.
+ * @param aPath [out] Bluetooth signal path generated from aId.
+ */
+void
+GeneratePathFromGattId(const BluetoothGattId& aId,
+                       nsAString& aPath);
+
+//
 // Broadcast system message
 //
 
 bool
 BroadcastSystemMessage(const nsAString& aType,
                        const BluetoothValue& aData);
 
 bool
--- a/dom/bluetooth2/bluedroid/BluetoothGattHALInterface.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothGattHALInterface.cpp
@@ -616,24 +616,26 @@ BluetoothGattClientHALInterface::Refresh
     DispatchBluetoothGattClientHALResult(
       aRes, &BluetoothGattClientResultHandler::Refresh,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
 void
 BluetoothGattClientHALInterface::SearchService(
-  int aConnId, const BluetoothUuid& aUuid,
+  int aConnId, bool aSearchAll, const BluetoothUuid& aUuid,
   BluetoothGattClientResultHandler* aRes)
 {
   bt_status_t status;
 #if ANDROID_VERSION >= 19
   bt_uuid_t uuid;
 
-  if (NS_SUCCEEDED(Convert(aUuid, uuid))) {
+  if (aSearchAll) {
+    status = mInterface->search_service(aConnId, 0);
+  } else if (NS_SUCCEEDED(Convert(aUuid, uuid))) {
     status = mInterface->search_service(aConnId, &uuid);
   } else {
     status = BT_STATUS_PARM_INVALID;
   }
 #else
   status = BT_STATUS_UNSUPPORTED;
 #endif
 
@@ -642,26 +644,28 @@ BluetoothGattClientHALInterface::SearchS
       aRes, &BluetoothGattClientResultHandler::SearchService,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
 void
 BluetoothGattClientHALInterface::GetIncludedService(
   int aConnId, const BluetoothGattServiceId& aServiceId,
-  const BluetoothGattServiceId& aStartServiceId,
+  bool aFirst, const BluetoothGattServiceId& aStartServiceId,
   BluetoothGattClientResultHandler* aRes)
 {
   bt_status_t status;
 #if ANDROID_VERSION >= 19
   btgatt_srvc_id_t serviceId;
   btgatt_srvc_id_t startServiceId;
 
-  if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
-      NS_SUCCEEDED(Convert(aStartServiceId, startServiceId))) {
+  if (aFirst && NS_SUCCEEDED(Convert(aServiceId, serviceId))) {
+    status = mInterface->get_included_service(aConnId, &serviceId, 0);
+  } else if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
+             NS_SUCCEEDED(Convert(aStartServiceId, startServiceId))) {
     status = mInterface->get_included_service(aConnId, &serviceId,
                                               &startServiceId);
   } else {
     status = BT_STATUS_PARM_INVALID;
   }
 #else
   status = BT_STATUS_UNSUPPORTED;
 #endif
@@ -671,26 +675,28 @@ BluetoothGattClientHALInterface::GetIncl
       aRes, &BluetoothGattClientResultHandler::GetIncludedService,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
 void
 BluetoothGattClientHALInterface::GetCharacteristic(
   int aConnId, const BluetoothGattServiceId& aServiceId,
-  const BluetoothGattId& aStartCharId,
+  bool aFirst, const BluetoothGattId& aStartCharId,
   BluetoothGattClientResultHandler* aRes)
 {
   bt_status_t status;
 #if ANDROID_VERSION >= 19
   btgatt_srvc_id_t serviceId;
   btgatt_gatt_id_t startCharId;
 
-  if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
-      NS_SUCCEEDED(Convert(aStartCharId, startCharId))) {
+  if (aFirst && NS_SUCCEEDED(Convert(aServiceId, serviceId))) {
+    status = mInterface->get_characteristic(aConnId, &serviceId, 0);
+  } else if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
+             NS_SUCCEEDED(Convert(aStartCharId, startCharId))) {
     status = mInterface->get_characteristic(aConnId, &serviceId, &startCharId);
   } else {
     status = BT_STATUS_PARM_INVALID;
   }
 #else
   status = BT_STATUS_UNSUPPORTED;
 #endif
 
@@ -699,29 +705,32 @@ BluetoothGattClientHALInterface::GetChar
       aRes, &BluetoothGattClientResultHandler::GetCharacteristic,
       ConvertDefault(status, STATUS_FAIL));
   }
 }
 
 void
 BluetoothGattClientHALInterface::GetDescriptor(
   int aConnId, const BluetoothGattServiceId& aServiceId,
-  const BluetoothGattId& aCharId,
-  const BluetoothGattId& aDescriptorId,
-  BluetoothGattClientResultHandler* aRes)
+  const BluetoothGattId& aCharId, bool aFirst,
+  const BluetoothGattId& aDescriptorId, BluetoothGattClientResultHandler* aRes)
 {
   bt_status_t status;
 #if ANDROID_VERSION >= 19
   btgatt_srvc_id_t serviceId;
   btgatt_gatt_id_t charId;
   btgatt_gatt_id_t descriptorId;
 
-  if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
-      NS_SUCCEEDED(Convert(aCharId, charId)) &&
-      NS_SUCCEEDED(Convert(aDescriptorId, descriptorId))) {
+  if (aFirst &&
+      NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
+      NS_SUCCEEDED(Convert(aCharId, charId))) {
+    status = mInterface->get_descriptor(aConnId, &serviceId, &charId, 0);
+  } else if (NS_SUCCEEDED(Convert(aServiceId, serviceId)) &&
+             NS_SUCCEEDED(Convert(aCharId, charId)) &&
+             NS_SUCCEEDED(Convert(aDescriptorId, descriptorId))) {
     status = mInterface->get_descriptor(aConnId, &serviceId, &charId,
                                         &descriptorId);
   } else {
     status = BT_STATUS_PARM_INVALID;
   }
 #else
   status = BT_STATUS_UNSUPPORTED;
 #endif
--- a/dom/bluetooth2/bluedroid/BluetoothGattHALInterface.h
+++ b/dom/bluetooth2/bluedroid/BluetoothGattHALInterface.h
@@ -51,29 +51,33 @@ public:
 
   /* Clear the attribute cache for a given device*/
   void Refresh(int aClientIf,
                const nsAString& aBdAddr,
                BluetoothGattClientResultHandler* aRes);
 
   /* Enumerate Attributes */
   void SearchService(int aConnId,
+                     bool aSearchAll,
                      const BluetoothUuid& aUuid,
                      BluetoothGattClientResultHandler* aRes);
   void GetIncludedService(int aConnId,
                           const BluetoothGattServiceId& aServiceId,
+                          bool aFirst,
                           const BluetoothGattServiceId& aStartServiceId,
                           BluetoothGattClientResultHandler* aRes);
   void GetCharacteristic(int aConnId,
                          const BluetoothGattServiceId& aServiceId,
+                         bool aFirst,
                          const BluetoothGattId& aStartCharId,
                          BluetoothGattClientResultHandler* aRes);
   void GetDescriptor(int aConnId,
                      const BluetoothGattServiceId& aServiceId,
                      const BluetoothGattId& aCharId,
+                     bool aFirst,
                      const BluetoothGattId& aDescriptorId,
                      BluetoothGattClientResultHandler* aRes);
 
   /* Read / Write An Attribute */
   void ReadCharacteristic(int aConnId,
                           const BluetoothGattServiceId& aServiceId,
                           const BluetoothGattId& aCharId,
                           int aAuthReq,
--- a/dom/bluetooth2/bluedroid/BluetoothGattManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothGattManager.cpp
@@ -1,22 +1,22 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BluetoothGattManager.h"
 
-#include "BluetoothCommon.h"
 #include "BluetoothInterface.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothUtils.h"
 #include "MainThreadUtils.h"
+#include "mozilla/dom/bluetooth/BluetoothCommon.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIObserverService.h"
 #include "nsThreadUtils.h"
 
 #define ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(runnable)                       \
   do {                                                                        \
     if (!sBluetoothGattInterface) {                                           \
@@ -32,47 +32,87 @@ USING_BLUETOOTH_NAMESPACE
 namespace {
   StaticRefPtr<BluetoothGattManager> sBluetoothGattManager;
   static BluetoothGattInterface* sBluetoothGattInterface;
   static BluetoothGattClientInterface* sBluetoothGattClientInterface;
 } // anonymous namespace
 
 bool BluetoothGattManager::mInShutdown = false;
 
-class BluetoothGattClient;
 static StaticAutoPtr<nsTArray<nsRefPtr<BluetoothGattClient> > > sClients;
 
-class BluetoothGattClient final : public nsISupports
+class mozilla::dom::bluetooth::BluetoothGattClient final : public nsISupports
 {
 public:
   NS_DECL_ISUPPORTS
 
   BluetoothGattClient(const nsAString& aAppUuid, const nsAString& aDeviceAddr)
   : mAppUuid(aAppUuid)
   , mDeviceAddr(aDeviceAddr)
   , mClientIf(0)
   , mConnId(0)
   { }
 
   ~BluetoothGattClient()
   {
     mConnectRunnable = nullptr;
     mDisconnectRunnable = nullptr;
+    mDiscoverRunnable = nullptr;
     mUnregisterClientRunnable = nullptr;
     mReadRemoteRssiRunnable = nullptr;
   }
 
+  void NotifyDiscoverCompleted(bool aSuccess)
+  {
+    MOZ_ASSERT(!mAppUuid.IsEmpty());
+    MOZ_ASSERT(mDiscoverRunnable);
+
+    BluetoothService* bs = BluetoothService::Get();
+    NS_ENSURE_TRUE_VOID(bs);
+
+    // Notify application to clear the cache values of
+    // service/characteristic/descriptor.
+    bs->DistributeSignal(NS_LITERAL_STRING("DiscoverCompleted"),
+                         mAppUuid,
+                         BluetoothValue(aSuccess));
+
+    // Resolve/Reject the Promise.
+    if (aSuccess) {
+      DispatchReplySuccess(mDiscoverRunnable);
+    } else {
+      DispatchReplyError(mDiscoverRunnable,
+                         NS_LITERAL_STRING("Discover failed"));
+    }
+
+    // Cleanup
+    mServices.Clear();
+    mIncludedServices.Clear();
+    mCharacteristics.Clear();
+    mDescriptors.Clear();
+    mDiscoverRunnable = nullptr;
+  }
+
   nsString mAppUuid;
   nsString mDeviceAddr;
   int mClientIf;
   int mConnId;
   nsRefPtr<BluetoothReplyRunnable> mConnectRunnable;
   nsRefPtr<BluetoothReplyRunnable> mDisconnectRunnable;
+  nsRefPtr<BluetoothReplyRunnable> mDiscoverRunnable;
+  nsRefPtr<BluetoothReplyRunnable> mReadRemoteRssiRunnable;
   nsRefPtr<BluetoothReplyRunnable> mUnregisterClientRunnable;
-  nsRefPtr<BluetoothReplyRunnable> mReadRemoteRssiRunnable;
+
+  /**
+   * These temporary arrays are used only during discover operations.
+   * All of them are empty if there are no ongoing discover operations.
+   */
+  nsTArray<BluetoothGattServiceId> mServices;
+  nsTArray<BluetoothGattServiceId> mIncludedServices;
+  nsTArray<BluetoothGattId> mCharacteristics;
+  nsTArray<BluetoothGattId> mDescriptors;
 };
 
 NS_IMPL_ISUPPORTS0(BluetoothGattClient)
 
 class UuidComparator
 {
 public:
   bool Equals(const nsRefPtr<BluetoothGattClient>& aClient,
@@ -87,16 +127,26 @@ class ClientIfComparator
 public:
   bool Equals(const nsRefPtr<BluetoothGattClient>& aClient,
               int aClientIf) const
   {
     return aClient->mClientIf == aClientIf;
   }
 };
 
+class ConnIdComparator
+{
+public:
+  bool Equals(const nsRefPtr<BluetoothGattClient>& aClient,
+              int aConnId) const
+  {
+    return aClient->mConnId == aConnId;
+  }
+};
+
 BluetoothGattManager*
 BluetoothGattManager::Get()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // If sBluetoothGattManager already exists, exit early
   if (sBluetoothGattManager) {
     return sBluetoothGattManager;
@@ -333,23 +383,17 @@ BluetoothGattManager::UnregisterClient(i
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    ClientIfComparator());
-
-  // Reject the unregister request if the client is not found
-  if (index == sClients->NoIndex) {
-    DispatchReplyError(aRunnable,
-                       NS_LITERAL_STRING("Unregister GATT client failed"));
-    return;
-  }
+  MOZ_ASSERT(index != sClients->NoIndex);
 
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mUnregisterClientRunnable = aRunnable;
 
   sBluetoothGattClientInterface->UnregisterClient(
     aClientIf,
     new UnregisterClientResultHandler(client));
 }
@@ -464,33 +508,88 @@ BluetoothGattManager::Disconnect(const n
                                  BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
 
   size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
-
-  // Reject the disconnect request if the client is not found
-  if (index == sClients->NoIndex) {
-    DispatchReplyError(aRunnable, NS_LITERAL_STRING("Disconnect failed"));
-    return;
-  }
+  MOZ_ASSERT(index != sClients->NoIndex);
 
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mDisconnectRunnable = aRunnable;
 
   sBluetoothGattClientInterface->Disconnect(
     client->mClientIf,
     aDeviceAddr,
     client->mConnId,
     new DisconnectResultHandler(client));
 }
 
+class BluetoothGattManager::DiscoverResultHandler final
+  : public BluetoothGattClientResultHandler
+{
+public:
+  DiscoverResultHandler(BluetoothGattClient* aClient)
+  : mClient(aClient)
+  {
+    MOZ_ASSERT(mClient);
+  }
+
+  void OnError(BluetoothStatus aStatus) override
+  {
+    BT_WARNING("BluetoothGattClientInterface::Discover failed: %d",
+               (int)aStatus);
+
+    mClient->NotifyDiscoverCompleted(false);
+  }
+
+private:
+  nsRefPtr<BluetoothGattClient> mClient;
+};
+
+void
+BluetoothGattManager::Discover(const nsAString& aAppUuid,
+                               BluetoothReplyRunnable* aRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aRunnable);
+
+  ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
+
+  size_t index = sClients->IndexOf(aAppUuid, 0 /* Start */, UuidComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
+  MOZ_ASSERT(client->mConnId > 0);
+  MOZ_ASSERT(!client->mDiscoverRunnable);
+
+  client->mDiscoverRunnable = aRunnable;
+
+  /**
+   * Discover all services/characteristics/descriptors offered by the remote
+   * GATT server.
+   *
+   * The discover procesure includes following steps.
+   * 1) Discover all services.
+   * 2) After all services are discovered, for each service S, we will do
+   *    following actions.
+   *    2-1) Discover all included services of service S.
+   *    2-2) Discover all characteristics of service S.
+   *    2-3) Discover all descriptors of those characteristics discovered in
+   *         2-2).
+   */
+  sBluetoothGattClientInterface->SearchService(
+    client->mConnId,
+    true, // search all services
+    BluetoothUuid(),
+    new DiscoverResultHandler(client));
+}
+
 class BluetoothGattManager::ReadRemoteRssiResultHandler final
   : public BluetoothGattClientResultHandler
 {
 public:
   ReadRemoteRssiResultHandler(BluetoothGattClient* aClient)
   : mClient(aClient)
   {
     MOZ_ASSERT(mClient);
@@ -522,23 +621,17 @@ BluetoothGattManager::ReadRemoteRssi(int
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRunnable);
 
   ENSURE_GATT_CLIENT_INTF_IS_READY_VOID(aRunnable);
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    ClientIfComparator());
-
-  // Reject the read remote rssi request if the client is not found
-  if (index == sClients->NoIndex) {
-    DispatchReplyError(aRunnable,
-                       NS_LITERAL_STRING("Read remote RSSI failed"));
-    return;
-  }
+  MOZ_ASSERT(index != sClients->NoIndex);
 
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
   client->mReadRemoteRssiRunnable = aRunnable;
 
   sBluetoothGattClientInterface->ReadRemoteRssi(
     aClientIf, aDeviceAddr,
     new ReadRemoteRssiResultHandler(client));
 }
@@ -553,17 +646,17 @@ BluetoothGattManager::RegisterClientNoti
 {
   BT_API2_LOGR("Client Registered, clientIf = %d", aClientIf);
   MOZ_ASSERT(NS_IsMainThread());
 
   nsString uuid;
   UuidToString(aAppUuid, uuid);
 
   size_t index = sClients->IndexOf(uuid, 0 /* Start */, UuidComparator());
-  NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
+  MOZ_ASSERT(index != sClients->NoIndex);
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   if (aStatus != GATT_STATUS_SUCCESS) {
     BT_API2_LOGR(
       "RegisterClient failed, clientIf = %d, status = %d, appUuid = %s",
@@ -616,17 +709,17 @@ BluetoothGattManager::ConnectNotificatio
   BT_API2_LOGR();
   MOZ_ASSERT(NS_IsMainThread());
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    ClientIfComparator());
-  NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
+  MOZ_ASSERT(index != sClients->NoIndex);
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
 
   if (aStatus != GATT_STATUS_SUCCESS) {
     BT_API2_LOGR("Connect failed, clientIf = %d, connId = %d, status = %d",
                  aClientIf, aConnId, aStatus);
 
     // Notify BluetoothGatt that the client remains disconnected
     bs->DistributeSignal(
@@ -668,17 +761,17 @@ BluetoothGattManager::DisconnectNotifica
   BT_API2_LOGR();
   MOZ_ASSERT(NS_IsMainThread());
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   size_t index = sClients->IndexOf(aClientIf, 0 /* Start */,
                                    ClientIfComparator());
-  NS_ENSURE_TRUE_VOID(index != sClients->NoIndex);
+  MOZ_ASSERT(index != sClients->NoIndex);
   nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
 
   if (aStatus != GATT_STATUS_SUCCESS) {
     // Notify BluetoothGatt that the client remains connected
     bs->DistributeSignal(
       NS_LITERAL_STRING(GATT_CONNECTION_STATE_CHANGED_ID),
       client->mAppUuid,
       BluetoothValue(true)); // Connected
@@ -706,45 +799,205 @@ BluetoothGattManager::DisconnectNotifica
     DispatchReplySuccess(client->mDisconnectRunnable);
     client->mDisconnectRunnable = nullptr;
   }
 }
 
 void
 BluetoothGattManager::SearchCompleteNotification(int aConnId,
                                                  BluetoothGattStatus aStatus)
-{ }
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
+                                   ConnIdComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
+  MOZ_ASSERT(client->mDiscoverRunnable);
+
+  if (aStatus != GATT_STATUS_SUCCESS) {
+    client->NotifyDiscoverCompleted(false);
+    return;
+  }
+
+  // Notify BluetoothGatt to create all services
+  bs->DistributeSignal(NS_LITERAL_STRING("ServicesDiscovered"),
+                       client->mAppUuid,
+                       BluetoothValue(client->mServices));
+
+  // All services are discovered, continue to search included services of each
+  // service if existed, otherwise, notify application that discover completed
+  if (!client->mServices.IsEmpty()) {
+    sBluetoothGattClientInterface->GetIncludedService(
+      aConnId,
+      client->mServices[0], // start from first service
+      true, // first included service
+      BluetoothGattServiceId(),
+      new DiscoverResultHandler(client));
+  } else {
+    client->NotifyDiscoverCompleted(true);
+  }
+}
 
 void
 BluetoothGattManager::SearchResultNotification(
   int aConnId, const BluetoothGattServiceId& aServiceId)
-{ }
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
+                                   ConnIdComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  // Save to mServices for distributing to application and discovering
+  // included services, characteristics of this service later
+  sClients->ElementAt(index)->mServices.AppendElement(aServiceId);
+}
 
 void
 BluetoothGattManager::GetCharacteristicNotification(
   int aConnId, BluetoothGattStatus aStatus,
   const BluetoothGattServiceId& aServiceId,
   const BluetoothGattId& aCharId,
   int aCharProperty)
-{ }
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
+                                   ConnIdComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
+  MOZ_ASSERT(client->mDiscoverRunnable);
+
+  if (aStatus == GATT_STATUS_SUCCESS) {
+    // Save to mCharacteristics for distributing to applications and
+    // discovering descriptors of this characteristic later
+    client->mCharacteristics.AppendElement(aCharId);
+
+    // Get next characteristic of this service
+    sBluetoothGattClientInterface->GetCharacteristic(
+      aConnId,
+      aServiceId,
+      false,
+      aCharId,
+      new DiscoverResultHandler(client));
+  } else { // all characteristics of this service are discovered
+    // Notify BluetoothGattService to create characteristics then proceed
+    nsString path;
+    GeneratePathFromGattId(aServiceId.mId, path);
+
+    bs->DistributeSignal(NS_LITERAL_STRING("CharacteristicsDiscovered"),
+                         path,
+                         BluetoothValue(client->mCharacteristics));
+
+    ProceedDiscoverProcess(client, aServiceId);
+  }
+}
 
 void
 BluetoothGattManager::GetDescriptorNotification(
   int aConnId, BluetoothGattStatus aStatus,
   const BluetoothGattServiceId& aServiceId,
   const BluetoothGattId& aCharId,
   const BluetoothGattId& aDescriptorId)
-{ }
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
+                                   ConnIdComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
+  MOZ_ASSERT(client->mDiscoverRunnable);
+
+  if (aStatus == GATT_STATUS_SUCCESS) {
+    // Save to mDescriptors for distributing to applications
+    client->mDescriptors.AppendElement(aDescriptorId);
+
+    // Get next descriptor of this characteristic
+    sBluetoothGattClientInterface->GetDescriptor(
+      aConnId,
+      aServiceId,
+      aCharId,
+      false,
+      aDescriptorId,
+      new DiscoverResultHandler(client));
+  } else { // all descriptors of this characteristic are discovered
+    // Notify BluetoothGattCharacteristic to create descriptors then proceed
+    nsString path;
+    GeneratePathFromGattId(aCharId, path);
+
+    bs->DistributeSignal(NS_LITERAL_STRING("DescriptorsDiscovered"),
+                         path,
+                         BluetoothValue(client->mDescriptors));
+    client->mDescriptors.Clear();
+
+    ProceedDiscoverProcess(client, aServiceId);
+  }
+}
 
 void
 BluetoothGattManager::GetIncludedServiceNotification(
   int aConnId, BluetoothGattStatus aStatus,
   const BluetoothGattServiceId& aServiceId,
   const BluetoothGattServiceId& aIncludedServId)
-{ }
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  NS_ENSURE_TRUE_VOID(bs);
+
+  size_t index = sClients->IndexOf(aConnId, 0 /* Start */,
+                                   ConnIdComparator());
+  MOZ_ASSERT(index != sClients->NoIndex);
+
+  nsRefPtr<BluetoothGattClient> client = sClients->ElementAt(index);
+  MOZ_ASSERT(client->mDiscoverRunnable);
+
+  if (aStatus == GATT_STATUS_SUCCESS) {
+    // Save to mIncludedServices for distributing to applications
+    client->mIncludedServices.AppendElement(aIncludedServId);
+
+    // Get next included service of this service
+    sBluetoothGattClientInterface->GetIncludedService(
+      aConnId,
+      aServiceId,
+      false,
+      aIncludedServId,
+      new DiscoverResultHandler(client));
+  } else { // all included services of this service are discovered
+    // Notify BluetoothGattService to create included services
+    nsString path;
+    GeneratePathFromGattId(aServiceId.mId, path);
+
+    bs->DistributeSignal(NS_LITERAL_STRING("IncludedServicesDiscovered"),
+                         path,
+                         BluetoothValue(client->mIncludedServices));
+    client->mIncludedServices.Clear();
+
+    // Start to discover characteristics of this service
+    sBluetoothGattClientInterface->GetCharacteristic(
+      aConnId,
+      aServiceId,
+      true, // first characteristic
+      BluetoothGattId(),
+      new DiscoverResultHandler(client));
+  }
+}
 
 void
 BluetoothGattManager::RegisterNotificationNotification(
   int aConnId, int aIsRegister, BluetoothGattStatus aStatus,
   const BluetoothGattServiceId& aServiceId,
   const BluetoothGattId& aCharId)
 { }
 
@@ -858,9 +1111,50 @@ BluetoothGattManager::Observe(nsISupport
 void
 BluetoothGattManager::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mInShutdown = true;
   sBluetoothGattManager = nullptr;
 }
 
+void
+BluetoothGattManager::ProceedDiscoverProcess(
+  BluetoothGattClient* aClient,
+  const BluetoothGattServiceId& aServiceId)
+{
+  /**
+   * This function will be called to decide how to proceed the discover process
+   * after discovering all characteristics of a given service, or after
+   * discovering all descriptors of a given characteristic.
+   *
+   * There are three cases here,
+   * 1) mCharacteristics is not empty:
+   *      Proceed to discover descriptors of the first saved characteristic.
+   * 2) mCharacteristics is empty but mServices is not empty:
+   *      This service does not have any saved characteristics left, proceed to
+   *      discover included services of the next service.
+   * 3) Both arrays are already empty:
+   *      Discover is done, notify application.
+   */
+  if (!aClient->mCharacteristics.IsEmpty()) {
+    sBluetoothGattClientInterface->GetDescriptor(
+      aClient->mConnId,
+      aServiceId,
+      aClient->mCharacteristics[0],
+      true, // first descriptor
+      BluetoothGattId(),
+      new DiscoverResultHandler(aClient));
+    aClient->mCharacteristics.RemoveElementAt(0);
+  } else if (!aClient->mServices.IsEmpty()) {
+    sBluetoothGattClientInterface->GetIncludedService(
+      aClient->mConnId,
+      aClient->mServices[0],
+      true, // first included service
+      BluetoothGattServiceId(),
+      new DiscoverResultHandler(aClient));
+    aClient->mServices.RemoveElementAt(0);
+  } else {
+    aClient->NotifyDiscoverCompleted(true);
+  }
+}
+
 NS_IMPL_ISUPPORTS(BluetoothGattManager, nsIObserver)
--- a/dom/bluetooth2/bluedroid/BluetoothGattManager.h
+++ b/dom/bluetooth2/bluedroid/BluetoothGattManager.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_bluetooth_bluetoothgattmanager_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothInterface.h"
 #include "BluetoothProfileManagerBase.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
+class BluetoothGattClient;
 class BluetoothReplyRunnable;
 
 class BluetoothGattManager final : public nsIObserver
                                      , public BluetoothGattNotificationHandler
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
@@ -30,31 +31,35 @@ public:
   void Connect(const nsAString& aAppUuid,
                const nsAString& aDeviceAddr,
                BluetoothReplyRunnable* aRunnable);
 
   void Disconnect(const nsAString& aAppUuid,
                   const nsAString& aDeviceAddr,
                   BluetoothReplyRunnable* aRunnable);
 
+  void Discover(const nsAString& aAppUuid,
+                BluetoothReplyRunnable* aRunnable);
+
   void UnregisterClient(int aClientIf,
                         BluetoothReplyRunnable* aRunnable);
 
   void ReadRemoteRssi(int aClientIf,
                       const nsAString& aDeviceAddr,
                       BluetoothReplyRunnable* aRunnable);
 
 private:
   class CleanupResultHandler;
   class CleanupResultHandlerRunnable;
   class InitGattResultHandler;
   class RegisterClientResultHandler;
   class UnregisterClientResultHandler;
   class ConnectResultHandler;
   class DisconnectResultHandler;
+  class DiscoverResultHandler;
   class ReadRemoteRssiResultHandler;
 
   BluetoothGattManager();
 
   void HandleShutdown();
 
   void RegisterClientNotification(BluetoothGattStatus aStatus,
                                   int aClientIf,
@@ -132,14 +137,17 @@ private:
   void ReadRemoteRssiNotification(int aClientIf,
                                   const nsAString& aBdAddr,
                                   int aRssi,
                                   BluetoothGattStatus aStatus) override;
 
   void ListenNotification(BluetoothGattStatus aStatus,
                           int aServerIf) override;
 
+  void ProceedDiscoverProcess(BluetoothGattClient* aClient,
+                              const BluetoothGattServiceId& aServiceId);
+
   static bool mInShutdown;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
@@ -1113,16 +1113,30 @@ BluetoothServiceBluedroid::DisconnectGat
 
   BluetoothGattManager* gatt = BluetoothGattManager::Get();
   ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
 
   gatt->Disconnect(aAppUuid, aDeviceAddress, aRunnable);
 }
 
 void
+BluetoothServiceBluedroid::DiscoverGattServicesInternal(
+  const nsAString& aAppUuid, BluetoothReplyRunnable* aRunnable)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
+
+  BluetoothGattManager* gatt = BluetoothGattManager::Get();
+  ENSURE_GATT_MGR_IS_READY_VOID(gatt, aRunnable);
+
+  gatt->Discover(aAppUuid, aRunnable);
+}
+
+void
 BluetoothServiceBluedroid::UnregisterGattClientInternal(
   int aClientIf, BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY_VOID(aRunnable);
 
   BluetoothGattManager* gatt = BluetoothGattManager::Get();
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.h
@@ -180,16 +180,20 @@ public:
                             BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   DisconnectGattClientInternal(const nsAString& aAppUuid,
                                const nsAString& aDeviceAddress,
                                BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
+  DiscoverGattServicesInternal(const nsAString& aAppUuid,
+                               BluetoothReplyRunnable* aRunnable) override;
+
+  virtual void
   UnregisterGattClientInternal(int aClientIf,
                                BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   GattClientReadRemoteRssiInternal(
     int aClientIf, const nsAString& aDeviceAddress,
     BluetoothReplyRunnable* aRunnable) override;
 
--- a/dom/bluetooth2/bluez/BluetoothDBusService.cpp
+++ b/dom/bluetooth2/bluez/BluetoothDBusService.cpp
@@ -4287,16 +4287,22 @@ BluetoothDBusService::ConnectGattClientI
 void
 BluetoothDBusService::DisconnectGattClientInternal(
   const nsAString& aAppUuid, const nsAString& aDeviceAddress,
   BluetoothReplyRunnable* aRunnable)
 {
 }
 
 void
+BluetoothDBusService::DiscoverGattServicesInternal(
+  const nsAString& aAppUuid, BluetoothReplyRunnable* aRunnable)
+{
+}
+
+void
 BluetoothDBusService::UnregisterGattClientInternal(
   int aClientIf, BluetoothReplyRunnable* aRunnable)
 {
 }
 
 void
 BluetoothDBusService::GattClientReadRemoteRssiInternal(
   int aClientIf, const nsAString& aDeviceAddress,
--- a/dom/bluetooth2/bluez/BluetoothDBusService.h
+++ b/dom/bluetooth2/bluez/BluetoothDBusService.h
@@ -189,16 +189,21 @@ public:
   ConnectGattClientInternal(const nsAString& aAppUuid,
                             const nsAString& aDeviceAddress,
                             BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   DisconnectGattClientInternal(const nsAString& aAppUuid,
                                const nsAString& aDeviceAddress,
                                BluetoothReplyRunnable* aRunnable) override;
+
+  virtual void
+  DiscoverGattServicesInternal(const nsAString& aAppUuid,
+                               BluetoothReplyRunnable* aRunnable) override;
+
   virtual void
   UnregisterGattClientInternal(int aClientIf,
                                BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   GattClientReadRemoteRssiInternal(
     int aClientIf, const nsAString& aDeviceAddress,
     BluetoothReplyRunnable* aRunnable) override;
--- a/dom/bluetooth2/ipc/BluetoothMessageUtils.h
+++ b/dom/bluetooth2/ipc/BluetoothMessageUtils.h
@@ -23,11 +23,78 @@ struct ParamTraits<mozilla::dom::bluetoo
 template <>
 struct ParamTraits<mozilla::dom::bluetooth::BluetoothStatus>
   : public ContiguousEnumSerializer<
              mozilla::dom::bluetooth::BluetoothStatus,
              mozilla::dom::bluetooth::STATUS_SUCCESS,
              mozilla::dom::bluetooth::STATUS_RMT_DEV_DOWN>
 { };
 
+template <>
+struct ParamTraits<mozilla::dom::bluetooth::BluetoothUuid>
+{
+  typedef mozilla::dom::bluetooth::BluetoothUuid paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    for (uint8_t i = 0; i < 16; i++) {
+      WriteParam(aMsg, aParam.mUuid[i]);
+    }
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    for (uint8_t i = 0; i < 16; i++) {
+      if (!ReadParam(aMsg, aIter, &(aResult->mUuid[i]))) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::dom::bluetooth::BluetoothGattId>
+{
+  typedef mozilla::dom::bluetooth::BluetoothGattId paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mUuid);
+    WriteParam(aMsg, aParam.mInstanceId);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->mUuid)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mInstanceId))) {
+      return false;
+    }
+
+    return true;
+  }
+};
+
+template <>
+struct ParamTraits<mozilla::dom::bluetooth::BluetoothGattServiceId>
+{
+  typedef mozilla::dom::bluetooth::BluetoothGattServiceId paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam)
+  {
+    WriteParam(aMsg, aParam.mId);
+    WriteParam(aMsg, aParam.mIsPrimary);
+  }
+
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
+  {
+    if (!ReadParam(aMsg, aIter, &(aResult->mId)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mIsPrimary))) {
+      return false;
+    }
+
+    return true;
+  }
+};
 } // namespace IPC
 
 #endif // mozilla_dom_bluetooth_ipc_bluetoothmessageutils_h__
--- a/dom/bluetooth2/ipc/BluetoothParent.cpp
+++ b/dom/bluetooth2/ipc/BluetoothParent.cpp
@@ -249,16 +249,18 @@ BluetoothParent::RecvPBluetoothRequestCo
     case Request::TSendMetaDataRequest:
       return actor->DoRequest(aRequest.get_SendMetaDataRequest());
     case Request::TSendPlayStatusRequest:
       return actor->DoRequest(aRequest.get_SendPlayStatusRequest());
     case Request::TConnectGattClientRequest:
       return actor->DoRequest(aRequest.get_ConnectGattClientRequest());
     case Request::TDisconnectGattClientRequest:
       return actor->DoRequest(aRequest.get_DisconnectGattClientRequest());
+    case Request::TDiscoverGattServicesRequest:
+      return actor->DoRequest(aRequest.get_DiscoverGattServicesRequest());
     case Request::TUnregisterGattClientRequest:
       return actor->DoRequest(aRequest.get_UnregisterGattClientRequest());
     case Request::TGattClientReadRemoteRssiRequest:
       return actor->DoRequest(aRequest.get_GattClientReadRemoteRssiRequest());
     default:
       MOZ_CRASH("Unknown type!");
   }
 
@@ -715,16 +717,28 @@ BluetoothRequestParent::DoRequest(const 
   mService->DisconnectGattClientInternal(aRequest.appUuid(),
                                          aRequest.deviceAddress(),
                                          mReplyRunnable.get());
 
   return true;
 }
 
 bool
+BluetoothRequestParent::DoRequest(const DiscoverGattServicesRequest& aRequest)
+{
+  MOZ_ASSERT(mService);
+  MOZ_ASSERT(mRequestType == Request::TDiscoverGattServicesRequest);
+
+  mService->DiscoverGattServicesInternal(aRequest.appUuid(),
+                                         mReplyRunnable.get());
+
+  return true;
+}
+
+bool
 BluetoothRequestParent::DoRequest(const UnregisterGattClientRequest& aRequest)
 {
   MOZ_ASSERT(mService);
   MOZ_ASSERT(mRequestType == Request::TUnregisterGattClientRequest);
 
   mService->UnregisterGattClientInternal(aRequest.clientIf(),
                                          mReplyRunnable.get());
 
--- a/dom/bluetooth2/ipc/BluetoothParent.h
+++ b/dom/bluetooth2/ipc/BluetoothParent.h
@@ -219,16 +219,19 @@ protected:
 
   bool
   DoRequest(const ConnectGattClientRequest& aRequest);
 
   bool
   DoRequest(const DisconnectGattClientRequest& aRequest);
 
   bool
+  DoRequest(const DiscoverGattServicesRequest& aRequest);
+
+  bool
   DoRequest(const UnregisterGattClientRequest& aRequest);
 
   bool
   DoRequest(const GattClientReadRemoteRssiRequest& aRequest);
 };
 
 END_BLUETOOTH_NAMESPACE
 
--- a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp
+++ b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp
@@ -393,16 +393,24 @@ BluetoothServiceChildProcess::Disconnect
   const nsAString& aAppUuid, const nsAString& aDeviceAddress,
   BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable,
     DisconnectGattClientRequest(nsString(aAppUuid), nsString(aDeviceAddress)));
 }
 
 void
+BluetoothServiceChildProcess::DiscoverGattServicesInternal(
+  const nsAString& aAppUuid, BluetoothReplyRunnable* aRunnable)
+{
+  SendRequest(aRunnable,
+    DiscoverGattServicesRequest(nsString(aAppUuid)));
+}
+
+void
 BluetoothServiceChildProcess::UnregisterGattClientInternal(
   int aClientIf, BluetoothReplyRunnable* aRunnable)
 {
   SendRequest(aRunnable, UnregisterGattClientRequest(aClientIf));
 }
 
 void
 BluetoothServiceChildProcess::GattClientReadRemoteRssiInternal(
--- a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.h
@@ -198,16 +198,20 @@ public:
                             BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   DisconnectGattClientInternal(const nsAString& aAppUuid,
                                const nsAString& aDeviceAddress,
                                BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
+  DiscoverGattServicesInternal(const nsAString& aAppUuid,
+                               BluetoothReplyRunnable* aRunnable) override;
+
+  virtual void
   UnregisterGattClientInternal(int aClientIf,
                                BluetoothReplyRunnable* aRunnable) override;
 
   virtual void
   GattClientReadRemoteRssiInternal(int aClientIf,
                                    const nsAString& aDeviceAddress,
                                    BluetoothReplyRunnable* aRunnable) override;
 
--- a/dom/bluetooth2/ipc/BluetoothTypes.ipdlh
+++ b/dom/bluetooth2/ipc/BluetoothTypes.ipdlh
@@ -1,15 +1,20 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-using mozilla::dom::bluetooth::BluetoothStatus from "mozilla/dom/bluetooth/BluetoothCommon.h";
+using mozilla::dom::bluetooth::BluetoothGattId
+  from "mozilla/dom/bluetooth/BluetoothCommon.h";
+using mozilla::dom::bluetooth::BluetoothGattServiceId
+  from "mozilla/dom/bluetooth/BluetoothCommon.h";
+using mozilla::dom::bluetooth::BluetoothStatus
+  from "mozilla/dom/bluetooth/BluetoothCommon.h";
 
 namespace mozilla {
 namespace dom {
 namespace bluetooth {
 
 /**
  * Value structure for returns from bluetooth. Currently modeled after dbus
  * returns, which can be a 32-bit int, an UTF16 string, a bool, or an array of
@@ -18,16 +23,18 @@ namespace bluetooth {
 union BluetoothValue
 {
   uint32_t;
   nsString;
   bool;
   nsString[];
   uint8_t[];
   BluetoothNamedValue[];
+  BluetoothGattId[];
+  BluetoothGattServiceId[];
 };
 
 /**
  * Key-value pair for dicts returned by the bluetooth backend. Used for things
  * like property updates, where the property will have a name and a type.
  */
 struct BluetoothNamedValue
 {
--- a/dom/bluetooth2/ipc/PBluetooth.ipdl
+++ b/dom/bluetooth2/ipc/PBluetooth.ipdl
@@ -183,16 +183,21 @@ struct ConnectGattClientRequest
 };
 
 struct DisconnectGattClientRequest
 {
   nsString appUuid;
   nsString deviceAddress;
 };
 
+struct DiscoverGattServicesRequest
+{
+  nsString appUuid;
+};
+
 struct UnregisterGattClientRequest
 {
   int clientIf;
 };
 
 struct GattClientReadRemoteRssiRequest
 {
   int clientIf;
@@ -228,16 +233,17 @@ union Request
   IsScoConnectedRequest;
   AnswerWaitingCallRequest;
   IgnoreWaitingCallRequest;
   ToggleCallsRequest;
   SendMetaDataRequest;
   SendPlayStatusRequest;
   ConnectGattClientRequest;
   DisconnectGattClientRequest;
+  DiscoverGattServicesRequest;
   UnregisterGattClientRequest;
   GattClientReadRemoteRssiRequest;
 };
 
 protocol PBluetooth
 {
   manager PContent;
   manages PBluetoothRequest;
--- a/dom/bluetooth2/moz.build
+++ b/dom/bluetooth2/moz.build
@@ -6,16 +6,19 @@
 
 if CONFIG['MOZ_B2G_BT']:
     SOURCES += [
         'BluetoothAdapter.cpp',
         'BluetoothClassOfDevice.cpp',
         'BluetoothDevice.cpp',
         'BluetoothDiscoveryHandle.cpp',
         'BluetoothGatt.cpp',
+        'BluetoothGattCharacteristic.cpp',
+        'BluetoothGattDescriptor.cpp',
+        'BluetoothGattService.cpp',
         'BluetoothHidManager.cpp',
         'BluetoothInterface.cpp',
         'BluetoothManager.cpp',
         'BluetoothPairingHandle.cpp',
         'BluetoothPairingListener.cpp',
         'BluetoothProfileController.cpp',
         'BluetoothReplyRunnable.cpp',
         'BluetoothService.cpp',
@@ -119,16 +122,19 @@ EXPORTS.mozilla.dom.bluetooth.ipc += [
 
 EXPORTS.mozilla.dom.bluetooth += [
     'BluetoothAdapter.h',
     'BluetoothClassOfDevice.h',
     'BluetoothCommon.h',
     'BluetoothDevice.h',
     'BluetoothDiscoveryHandle.h',
     'BluetoothGatt.h',
+    'BluetoothGattCharacteristic.h',
+    'BluetoothGattDescriptor.h',
+    'BluetoothGattService.h',
     'BluetoothManager.h',
     'BluetoothPairingHandle.h',
     'BluetoothPairingListener.h',
 ]
 
 IPDL_SOURCES += [
     'ipc/BluetoothTypes.ipdlh',
     'ipc/PBluetooth.ipdl',
--- a/dom/canvas/WebGL1Context.h
+++ b/dom/canvas/WebGL1Context.h
@@ -29,13 +29,15 @@ public:
     // nsWrapperCache
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
 private:
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) override;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) override;
+
+    virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_1_CONTEXT_H_
--- a/dom/canvas/WebGL1ContextUniforms.cpp
+++ b/dom/canvas/WebGL1ContextUniforms.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGL1Context.h"
 
-using namespace mozilla;
+namespace mozilla {
 
 bool
 WebGL1Context::ValidateAttribPointerType(bool /*integerMode*/, GLenum type, GLsizei* out_alignment, const char* info)
 {
     MOZ_ASSERT(out_alignment);
     if (!out_alignment)
         return false;
 
@@ -28,8 +28,22 @@ WebGL1Context::ValidateAttribPointerType
     case LOCAL_GL_FLOAT:
         *out_alignment = 4;
         return true;
     }
 
     ErrorInvalidEnumInfo(info, type);
     return false;
 }
+
+bool
+WebGL1Context::ValidateUniformMatrixTranspose(bool transpose, const char* info)
+{
+    if (transpose) {
+        ErrorInvalidValue("%s: transpose must be FALSE as per the "
+                          "OpenGL ES 2.0 spec", info);
+        return false;
+    }
+
+    return true;
+}
+
+} // namespace mozilla
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -103,36 +103,106 @@ public:
     // Programs and shaders - WebGL2ContextPrograms.cpp
     GLint GetFragDataLocation(WebGLProgram* program, const nsAString& name);
 
 
     // -------------------------------------------------------------------------
     // Uniforms and attributes - WebGL2ContextUniforms.cpp
     void VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride, GLintptr offset);
 
+    // GL 3.0 & ES 3.0
     void Uniform1ui(WebGLUniformLocation* location, GLuint v0);
     void Uniform2ui(WebGLUniformLocation* location, GLuint v0, GLuint v1);
     void Uniform3ui(WebGLUniformLocation* location, GLuint v0, GLuint v1, GLuint v2);
     void Uniform4ui(WebGLUniformLocation* location, GLuint v0, GLuint v1, GLuint v2, GLuint v3);
-    void Uniform1uiv(WebGLUniformLocation* location, const dom::Sequence<GLuint>& value);
-    void Uniform2uiv(WebGLUniformLocation* location, const dom::Sequence<GLuint>& value);
-    void Uniform3uiv(WebGLUniformLocation* location, const dom::Sequence<GLuint>& value);
-    void Uniform4uiv(WebGLUniformLocation* location, const dom::Sequence<GLuint>& value);
-    void UniformMatrix2x3fv(WebGLUniformLocation* location, bool transpose, const dom::Float32Array& value);
-    void UniformMatrix2x3fv(WebGLUniformLocation* location, bool transpose, const dom::Sequence<GLfloat>& value);
-    void UniformMatrix3x2fv(WebGLUniformLocation* location, bool transpose, const dom::Float32Array& value);
-    void UniformMatrix3x2fv(WebGLUniformLocation* location, bool transpose, const dom::Sequence<GLfloat>& value);
-    void UniformMatrix2x4fv(WebGLUniformLocation* location, bool transpose, const dom::Float32Array& value);
-    void UniformMatrix2x4fv(WebGLUniformLocation* location, bool transpose, const dom::Sequence<GLfloat>& value);
-    void UniformMatrix4x2fv(WebGLUniformLocation* location, bool transpose, const dom::Float32Array& value);
-    void UniformMatrix4x2fv(WebGLUniformLocation* location, bool transpose, const dom::Sequence<GLfloat>& value);
-    void UniformMatrix3x4fv(WebGLUniformLocation* location, bool transpose, const dom::Float32Array& value);
-    void UniformMatrix3x4fv(WebGLUniformLocation* location, bool transpose, const dom::Sequence<GLfloat>& value);
-    void UniformMatrix4x3fv(WebGLUniformLocation* location, bool transpose, const dom::Float32Array& value);
-    void UniformMatrix4x3fv(WebGLUniformLocation* location, bool transpose, const dom::Sequence<GLfloat>& value);
+
+private:
+    void Uniform1uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
+    void Uniform2uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
+    void Uniform3uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
+    void Uniform4uiv_base(WebGLUniformLocation* loc, size_t arrayLength, const GLuint* data);
+
+public:
+    void Uniform1uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
+        Uniform1uiv_base(loc,arr.Length(), arr.Elements());
+    }
+    void Uniform2uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
+        Uniform2uiv_base(loc,arr.Length(), arr.Elements());
+    }
+    void Uniform3uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
+        Uniform3uiv_base(loc,arr.Length(), arr.Elements());
+    }
+    void Uniform4uiv(WebGLUniformLocation* loc, const dom::Sequence<GLuint>& arr) {
+        Uniform4uiv_base(loc,arr.Length(), arr.Elements());
+    }
+
+private:
+    void UniformMatrix2x3fv_base(WebGLUniformLocation* loc, bool transpose,
+                                 size_t arrayLength, const GLfloat* data);
+    void UniformMatrix3x2fv_base(WebGLUniformLocation* loc, bool transpose,
+                                 size_t arrayLength, const GLfloat* data);
+    void UniformMatrix2x4fv_base(WebGLUniformLocation* loc, bool transpose,
+                                 size_t arrayLength, const GLfloat* data);
+    void UniformMatrix4x2fv_base(WebGLUniformLocation* loc, bool transpose,
+                                 size_t arrayLength, const GLfloat* data);
+    void UniformMatrix3x4fv_base(WebGLUniformLocation* loc, bool transpose,
+                                 size_t arrayLength, const GLfloat* data);
+    void UniformMatrix4x3fv_base(WebGLUniformLocation* loc, bool transpose,
+                                 size_t arrayLength, const GLfloat* data);
+
+public:
+    // GL 2.1 & ES 3.0
+    void UniformMatrix2x3fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
+        UniformMatrix2x3fv_base(loc, transpose, value.Length(), value.Elements());
+    }
+    void UniformMatrix2x4fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
+        UniformMatrix2x4fv_base(loc, transpose, value.Length(), value.Elements());
+    }
+    void UniformMatrix3x2fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
+        UniformMatrix3x2fv_base(loc, transpose, value.Length(), value.Elements());
+    }
+    void UniformMatrix3x4fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
+        UniformMatrix3x4fv_base(loc, transpose, value.Length(), value.Elements());
+    }
+    void UniformMatrix4x2fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
+        UniformMatrix4x2fv_base(loc, transpose, value.Length(), value.Elements());
+    }
+    void UniformMatrix4x3fv(WebGLUniformLocation* loc, bool transpose, const dom::Sequence<GLfloat>& value){
+        UniformMatrix4x3fv_base(loc, transpose, value.Length(), value.Elements());
+    }
+
+    void UniformMatrix2x3fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
+        value.ComputeLengthAndData();
+        UniformMatrix2x3fv_base(loc, transpose, value.Length(), value.Data());
+    }
+
+    void UniformMatrix2x4fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
+        value.ComputeLengthAndData();
+        UniformMatrix2x4fv_base(loc, transpose, value.Length(), value.Data());
+    }
+
+    void UniformMatrix3x2fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
+        value.ComputeLengthAndData();
+        UniformMatrix3x2fv_base(loc, transpose, value.Length(), value.Data());
+    }
+
+    void UniformMatrix3x4fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
+        value.ComputeLengthAndData();
+        UniformMatrix3x4fv_base(loc, transpose, value.Length(), value.Data());
+    }
+
+    void UniformMatrix4x2fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
+        value.ComputeLengthAndData();
+        UniformMatrix4x2fv_base(loc, transpose, value.Length(), value.Data());
+    }
+
+    void UniformMatrix4x3fv(WebGLUniformLocation* loc, bool transpose, const dom::Float32Array& value){
+        value.ComputeLengthAndData();
+        UniformMatrix4x3fv_base(loc, transpose, value.Length(), value.Data());
+    }
 
 private:
     void VertexAttribI4iv(GLuint index, size_t length, const GLint* v);
     void VertexAttribI4uiv(GLuint index, size_t length, const GLuint* v);
 
 public:
     // GL 3.0 & ES 3.0
     void VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w);
@@ -273,13 +343,15 @@ private:
     bool ValidateTexStorage(GLenum target, GLsizei levels, GLenum internalformat,
                                 GLsizei width, GLsizei height, GLsizei depth,
                                 const char* info);
 
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) override;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) override;
     virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) override;
+
+    virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) override;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/canvas/WebGL2ContextSync.cpp
+++ b/dom/canvas/WebGL2ContextSync.cpp
@@ -1,52 +1,133 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGL2Context.h"
+
 #include "GLContext.h"
+#include "WebGLSync.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // -------------------------------------------------------------------------
 // Sync objects
 
 already_AddRefed<WebGLSync>
 WebGL2Context::FenceSync(GLenum condition, GLbitfield flags)
 {
-    MOZ_CRASH("Not Implemented.");
-    return nullptr;
+   if (IsContextLost())
+       return nullptr;
+
+   if (condition != LOCAL_GL_SYNC_GPU_COMMANDS_COMPLETE) {
+       ErrorInvalidEnum("fenceSync: condition must be SYNC_GPU_COMMANDS_COMPLETE");
+       return nullptr;
+   }
+
+   if (flags != 0) {
+       ErrorInvalidValue("fenceSync: flags must be 0");
+       return nullptr;
+   }
+
+   MakeContextCurrent();
+   nsRefPtr<WebGLSync> globj = new WebGLSync(this, condition, flags);
+   return globj.forget();
 }
 
 bool
 WebGL2Context::IsSync(WebGLSync* sync)
 {
-    MOZ_CRASH("Not Implemented.");
-    return false;
+   if (IsContextLost())
+       return false;
+
+   return ValidateObjectAllowDeleted("isSync", sync) && !sync->IsDeleted();
 }
 
 void
 WebGL2Context::DeleteSync(WebGLSync* sync)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!ValidateObjectAllowDeletedOrNull("deleteSync", sync))
+        return;
+
+    if (!sync || sync->IsDeleted())
+        return;
+
+    sync->RequestDelete();
 }
 
 GLenum
 WebGL2Context::ClientWaitSync(WebGLSync* sync, GLbitfield flags, GLuint64 timeout)
 {
-    MOZ_CRASH("Not Implemented.");
-    return LOCAL_GL_FALSE;
+    if (IsContextLost())
+        return LOCAL_GL_WAIT_FAILED;
+
+    if (!sync || sync->IsDeleted()) {
+        ErrorInvalidValue("clientWaitSync: sync is not a sync object.");
+        return LOCAL_GL_WAIT_FAILED;
+    }
+
+    if (flags != 0 && flags != LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) {
+        ErrorInvalidValue("clientWaitSync: flag must be SYNC_FLUSH_COMMANDS_BIT or 0");
+        return LOCAL_GL_WAIT_FAILED;
+    }
+
+    MakeContextCurrent();
+    return gl->fClientWaitSync(sync->mGLName, flags, timeout);
 }
 
 void
 WebGL2Context::WaitSync(WebGLSync* sync, GLbitfield flags, GLuint64 timeout)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!sync || sync->IsDeleted()) {
+        ErrorInvalidValue("waitSync: sync is not a sync object.");
+        return;
+    }
+
+    if (flags != 0) {
+        ErrorInvalidValue("waitSync: flags must be 0");
+        return;
+    }
+
+    if (timeout != LOCAL_GL_TIMEOUT_IGNORED) {
+        ErrorInvalidValue("waitSync: timeout must be TIMEOUT_IGNORED");
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fWaitSync(sync->mGLName, flags, timeout);
 }
 
 void
 WebGL2Context::GetSyncParameter(JSContext*, WebGLSync* sync, GLenum pname, JS::MutableHandleValue retval)
 {
-    MOZ_CRASH("Not Implemented.");
+    if (IsContextLost())
+        return;
+
+    if (!sync || sync->IsDeleted()) {
+        ErrorInvalidValue("getSyncParameter: sync is not a sync object.");
+        return;
+    }
+
+    retval.set(JS::NullValue());
+
+    GLint result = 0;
+    switch (pname) {
+    case LOCAL_GL_OBJECT_TYPE:
+    case LOCAL_GL_SYNC_STATUS:
+    case LOCAL_GL_SYNC_CONDITION:
+    case LOCAL_GL_SYNC_FLAGS:
+        MakeContextCurrent();
+        gl->fGetSynciv(sync->mGLName, pname, 1, nullptr, &result);
+        retval.set(JS::Int32Value(result));
+        break;
+    }
+
+    ErrorInvalidEnum("getSyncParameter: Invalid pname 0x%04x", pname);
 }
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -69,16 +69,22 @@ WebGL2Context::ValidateAttribPointerType
             return true;
         }
     }
 
     ErrorInvalidEnum("%s: invalid enum value 0x%x", info, type);
     return false;
 }
 
+bool
+WebGL2Context::ValidateUniformMatrixTranspose(bool /*transpose*/, const char* /*info*/)
+{
+    return true;
+}
+
 // -------------------------------------------------------------------------
 // Uniforms and attributes
 
 void
 WebGL2Context::VertexAttribIPointer(GLuint index, GLint size, GLenum type, GLsizei stride,
                                     GLintptr offset)
 {
     if (IsContextLost())
@@ -108,150 +114,232 @@ WebGL2Context::VertexAttribIPointer(GLui
     vd.normalized = false;
     vd.integer = true;
 
     MakeContextCurrent();
     gl->fVertexAttribIPointer(index, size, type, stride, reinterpret_cast<void*>(offset));
 }
 
 void
-WebGL2Context::Uniform1ui(WebGLUniformLocation* location, GLuint v0)
+WebGL2Context::Uniform1ui(WebGLUniformLocation* loc, GLuint v0)
 {
-    MOZ_CRASH("Not Implemented.");
-}
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_UNSIGNED_INT, "uniform1ui", &rawLoc))
+        return;
 
-void
-WebGL2Context::Uniform2ui(WebGLUniformLocation* location, GLuint v0, GLuint v1)
-{
-    MOZ_CRASH("Not Implemented.");
+    MakeContextCurrent();
+    gl->fUniform1ui(rawLoc, v0);
 }
 
 void
-WebGL2Context::Uniform3ui(WebGLUniformLocation* location, GLuint v0, GLuint v1, GLuint v2)
+WebGL2Context::Uniform2ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1)
 {
-    MOZ_CRASH("Not Implemented.");
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_UNSIGNED_INT, "uniform2ui", &rawLoc))
+        return;
+
+    MakeContextCurrent();
+    gl->fUniform2ui(rawLoc, v0, v1);
 }
 
 void
-WebGL2Context::Uniform4ui(WebGLUniformLocation* location, GLuint v0, GLuint v1,
-                          GLuint v2, GLuint v3)
+WebGL2Context::Uniform3ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1, GLuint v2)
 {
-    MOZ_CRASH("Not Implemented.");
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_UNSIGNED_INT, "uniform3ui", &rawLoc))
+        return;
+
+    MakeContextCurrent();
+    gl->fUniform3ui(rawLoc, v0, v1, v2);
 }
 
 void
-WebGL2Context::Uniform1uiv(WebGLUniformLocation* location,
-                           const dom::Sequence<GLuint>& value)
+WebGL2Context::Uniform4ui(WebGLUniformLocation* loc, GLuint v0, GLuint v1, GLuint v2, GLuint v3)
 {
-    MOZ_CRASH("Not Implemented.");
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_UNSIGNED_INT, "uniform4ui", &rawLoc))
+        return;
+
+    MakeContextCurrent();
+    gl->fUniform4ui(rawLoc, v0, v1, v2, v3);
 }
 
 void
-WebGL2Context::Uniform2uiv(WebGLUniformLocation* location,
-                           const dom::Sequence<GLuint>& value)
+WebGL2Context::Uniform1uiv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                                const GLuint* data)
 {
-    MOZ_CRASH("Not Implemented.");
-}
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
 
-void
-WebGL2Context::Uniform3uiv(WebGLUniformLocation* location,
-                           const dom::Sequence<GLuint>& value)
-{
-    MOZ_CRASH("Not Implemented.");
+    if (!ValidateUniformArraySetter(loc, 1, LOCAL_GL_UNSIGNED_INT, arrayLength,
+                                    "uniform1uiv", &rawLoc, &numElementsToUpload))
+    {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniform1uiv(rawLoc, numElementsToUpload, data);
 }
 
 void
-WebGL2Context::Uniform4uiv(WebGLUniformLocation* location,
-                           const dom::Sequence<GLuint>& value)
+WebGL2Context::Uniform2uiv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                                const GLuint* data)
 {
-    MOZ_CRASH("Not Implemented.");
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+
+    if (!ValidateUniformArraySetter(loc, 2, LOCAL_GL_UNSIGNED_INT, arrayLength,
+                                    "uniform2uiv", &rawLoc, &numElementsToUpload))
+    {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniform2uiv(rawLoc, numElementsToUpload, data);
 }
 
 void
-WebGL2Context::UniformMatrix2x3fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Float32Array& value)
+WebGL2Context::Uniform3uiv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                                const GLuint* data)
 {
-    MOZ_CRASH("Not Implemented.");
-}
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
 
-void
-WebGL2Context::UniformMatrix2x3fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Sequence<GLfloat>& value)
-{
-    MOZ_CRASH("Not Implemented.");
+    if (!ValidateUniformArraySetter(loc, 3, LOCAL_GL_UNSIGNED_INT, arrayLength,
+                                    "uniform3uiv", &rawLoc, &numElementsToUpload))
+    {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniform1uiv(rawLoc, numElementsToUpload, data);
 }
 
 void
-WebGL2Context::UniformMatrix3x2fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Float32Array& value)
+WebGL2Context::Uniform4uiv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                                const GLuint* data)
 {
-    MOZ_CRASH("Not Implemented.");
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+
+    if (!ValidateUniformArraySetter(loc, 4, LOCAL_GL_UNSIGNED_INT, arrayLength,
+                                    "uniform4uiv", &rawLoc, &numElementsToUpload)) {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniform4uiv(rawLoc, numElementsToUpload, data);
 }
 
 void
-WebGL2Context::UniformMatrix3x2fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Sequence<GLfloat>& value)
+WebGL2Context::UniformMatrix2x3fv_base(WebGLUniformLocation* loc, bool transpose,
+                                       size_t arrayLength, const GLfloat* data)
 {
-    MOZ_CRASH("Not Implemented.");
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+
+    if (!ValidateUniformMatrixArraySetter(loc, 2, 3, LOCAL_GL_FLOAT, arrayLength,
+                                          transpose, "uniformMatrix2x3fv",
+                                          &rawLoc, &numElementsToUpload))
+    {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniformMatrix2x3fv(rawLoc, numElementsToUpload, transpose, data);
 }
 
 void
-WebGL2Context::UniformMatrix2x4fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Float32Array& value)
+WebGL2Context::UniformMatrix2x4fv_base(WebGLUniformLocation* loc, bool transpose,
+                                       size_t arrayLength, const GLfloat* data)
 {
-    MOZ_CRASH("Not Implemented.");
-}
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
 
-void
-WebGL2Context::UniformMatrix2x4fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Sequence<GLfloat>& value)
-{
-    MOZ_CRASH("Not Implemented.");
+    if (!ValidateUniformMatrixArraySetter(loc, 2, 4, LOCAL_GL_FLOAT, arrayLength,
+                                          transpose, "uniformMatrix2x4fv",
+                                          &rawLoc, &numElementsToUpload))
+    {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniformMatrix2x4fv(rawLoc, numElementsToUpload, transpose, data);
 }
 
 void
-WebGL2Context::UniformMatrix4x2fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Float32Array& value)
+WebGL2Context::UniformMatrix3x2fv_base(WebGLUniformLocation* loc, bool transpose,
+                                       size_t arrayLength, const GLfloat* data)
 {
-    MOZ_CRASH("Not Implemented.");
-}
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
 
-void
-WebGL2Context::UniformMatrix4x2fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Sequence<GLfloat>& value)
-{
-    MOZ_CRASH("Not Implemented.");
+    if (!ValidateUniformMatrixArraySetter(loc, 3, 2, LOCAL_GL_FLOAT, arrayLength,
+                                          transpose, "uniformMatrix3x2fv",
+                                          &rawLoc, &numElementsToUpload))
+    {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniformMatrix3x2fv(rawLoc, numElementsToUpload, transpose, data);
 }
 
 void
-WebGL2Context::UniformMatrix3x4fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Float32Array& value)
+WebGL2Context::UniformMatrix3x4fv_base(WebGLUniformLocation* loc, bool transpose,
+                                       size_t arrayLength, const GLfloat* data)
 {
-    MOZ_CRASH("Not Implemented.");
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+
+    if (!ValidateUniformMatrixArraySetter(loc, 3, 4, LOCAL_GL_FLOAT, arrayLength,
+                                          transpose, "uniformMatrix3x4fv",
+                                          &rawLoc, &numElementsToUpload))
+    {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniformMatrix3x4fv(rawLoc, numElementsToUpload, transpose, data);
 }
 
 void
-WebGL2Context::UniformMatrix3x4fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Sequence<GLfloat>& value)
+WebGL2Context::UniformMatrix4x2fv_base(WebGLUniformLocation* loc, bool transpose,
+                                       size_t arrayLength, const GLfloat* data)
 {
-    MOZ_CRASH("Not Implemented.");
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+
+    if (!ValidateUniformMatrixArraySetter(loc, 4, 2, LOCAL_GL_FLOAT, arrayLength,
+                                          transpose, "uniformMatrix4x2fv",
+                                          &rawLoc, &numElementsToUpload))
+    {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniformMatrix4x2fv(rawLoc, numElementsToUpload, transpose, data);
 }
 
 void
-WebGL2Context::UniformMatrix4x3fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Float32Array& value)
+WebGL2Context::UniformMatrix4x3fv_base(WebGLUniformLocation* loc, bool transpose,
+                                       size_t arrayLength, const GLfloat* data)
 {
-    MOZ_CRASH("Not Implemented.");
-}
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
 
-void
-WebGL2Context::UniformMatrix4x3fv(WebGLUniformLocation* location, bool transpose,
-                                  const dom::Sequence<GLfloat>& value)
-{
-    MOZ_CRASH("Not Implemented.");
+    if (!ValidateUniformMatrixArraySetter(loc, 4, 3, LOCAL_GL_FLOAT, arrayLength,
+                                          transpose, "uniformMatrix4x3fv",
+                                          &rawLoc, &numElementsToUpload))
+    {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniformMatrix4x3fv(rawLoc, numElementsToUpload, transpose, data);
 }
 
 void
 WebGL2Context::VertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w)
 {
     if (IsContextLost())
         return;
 
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -810,69 +810,69 @@ public:
         UniformMatrix2fv_base(loc, transpose, value.Length(), value.Data());
     }
     void UniformMatrix2fv(WebGLUniformLocation* loc, WebGLboolean transpose,
                           const dom::Sequence<float>& value)
     {
         UniformMatrix2fv_base(loc, transpose, value.Length(),
                               value.Elements());
     }
-    void UniformMatrix2fv_base(WebGLUniformLocation* loc,
-                               WebGLboolean transpose, size_t arrayLength,
-                               const float* data);
+    void UniformMatrix2fv_base(WebGLUniformLocation* loc, bool transpose,
+                               size_t arrayLength, const float* data);
 
     void UniformMatrix3fv(WebGLUniformLocation* loc, WebGLboolean transpose,
                           const dom::Float32Array& value)
     {
         value.ComputeLengthAndData();
         UniformMatrix3fv_base(loc, transpose, value.Length(), value.Data());
     }
     void UniformMatrix3fv(WebGLUniformLocation* loc, WebGLboolean transpose,
                           const dom::Sequence<float>& value)
     {
         UniformMatrix3fv_base(loc, transpose, value.Length(), value.Elements());
     }
-    void UniformMatrix3fv_base(WebGLUniformLocation* loc,
-                               WebGLboolean transpose, size_t arrayLength,
-                               const float* data);
+    void UniformMatrix3fv_base(WebGLUniformLocation* loc, bool transpose,
+                               size_t arrayLength, const float* data);
 
     void UniformMatrix4fv(WebGLUniformLocation* loc, WebGLboolean transpose,
                           const dom::Float32Array& value)
     {
         value.ComputeLengthAndData();
         UniformMatrix4fv_base(loc, transpose, value.Length(), value.Data());
     }
-    void UniformMatrix4fv(WebGLUniformLocation* loc, WebGLboolean transpose,
+    void UniformMatrix4fv(WebGLUniformLocation* loc, bool transpose,
                           const dom::Sequence<float>& value)
     {
         UniformMatrix4fv_base(loc, transpose, value.Length(),
                               value.Elements());
     }
-    void UniformMatrix4fv_base(WebGLUniformLocation* loc,
-                               WebGLboolean transpose, size_t arrayLength,
-                               const float* data);
+    void UniformMatrix4fv_base(WebGLUniformLocation* loc, bool transpose,
+                               size_t arrayLength, const float* data);
 
     void UseProgram(WebGLProgram* prog);
 
     bool ValidateAttribArraySetter(const char* name, uint32_t count,
                                    uint32_t arrayLength);
     bool ValidateUniformLocation(WebGLUniformLocation* loc, const char* funcName);
     bool ValidateUniformSetter(WebGLUniformLocation* loc, uint8_t setterSize,
                                GLenum setterType, const char* info,
                                GLuint* out_rawLoc);
     bool ValidateUniformArraySetter(WebGLUniformLocation* loc,
                                     uint8_t setterElemSize, GLenum setterType,
                                     size_t setterArraySize, const char* info,
                                     GLuint* out_rawLoc,
                                     GLsizei* out_numElementsToUpload);
     bool ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
-                                          uint8_t setterDims, GLenum setterType,
+                                          uint8_t setterCols,
+                                          uint8_t setterRows,
+                                          GLenum setterType,
                                           size_t setterArraySize,
                                           bool setterTranspose,
-                                          const char* info, GLuint* out_rawLoc,
+                                          const char* info,
+                                          GLuint* out_rawLoc,
                                           GLsizei* out_numElementsToUpload);
     void ValidateProgram(WebGLProgram* prog);
     bool ValidateUniformLocation(const char* info, WebGLUniformLocation* loc);
     bool ValidateSamplerUniformSetter(const char* info,
                                       WebGLUniformLocation* loc, GLint value);
     void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
 // -----------------------------------------------------------------------------
 // WEBGL_lose_context
@@ -1384,16 +1384,17 @@ private:
 
 private:
     // -------------------------------------------------------------------------
     // Context customization points
     virtual bool ValidateAttribPointerType(bool integerMode, GLenum type, GLsizei* alignment, const char* info) = 0;
     virtual bool ValidateBufferTarget(GLenum target, const char* info) = 0;
     virtual bool ValidateBufferIndexedTarget(GLenum target, const char* info) = 0;
     virtual bool ValidateBufferForTarget(GLenum target, WebGLBuffer* buffer, const char* info) = 0;
+    virtual bool ValidateUniformMatrixTranspose(bool transpose, const char* info) = 0;
 
 protected:
     int32_t MaxTextureSizeForTarget(TexTarget target) const {
         return (target == LOCAL_GL_TEXTURE_2D) ? mGLMaxTextureSize
                                                : mGLMaxCubeMapTextureSize;
     }
 
     int32_t
@@ -1586,16 +1587,17 @@ public:
     friend class WebGLTexture;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
     friend class WebGLBuffer;
     friend class WebGLSampler;
     friend class WebGLShader;
+    friend class WebGLSync;
     friend class WebGLTransformFeedback;
     friend class WebGLUniformLocation;
     friend class WebGLVertexArray;
     friend class WebGLVertexArrayFake;
     friend class WebGLVertexArrayGL;
 };
 
 // used by DOM bindings in conjunction with GetParentObject
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -2799,51 +2799,51 @@ WebGLContext::Uniform4fv_base(WebGLUnifo
 // Matrix
 
 void
 WebGLContext::UniformMatrix2fv_base(WebGLUniformLocation* loc, bool transpose,
                                     size_t arrayLength, const float* data)
 {
     GLuint rawLoc;
     GLsizei numElementsToUpload;
-    if (!ValidateUniformMatrixArraySetter(loc, 2, LOCAL_GL_FLOAT, arrayLength,
+    if (!ValidateUniformMatrixArraySetter(loc, 2, 2, LOCAL_GL_FLOAT, arrayLength,
                                           transpose, "uniformMatrix2fv",
                                           &rawLoc, &numElementsToUpload))
     {
         return;
     }
 
     MakeContextCurrent();
     gl->fUniformMatrix2fv(rawLoc, numElementsToUpload, false, data);
 }
 
 void
 WebGLContext::UniformMatrix3fv_base(WebGLUniformLocation* loc, bool transpose,
                                     size_t arrayLength, const float* data)
 {
     GLuint rawLoc;
     GLsizei numElementsToUpload;
-    if (!ValidateUniformMatrixArraySetter(loc, 3, LOCAL_GL_FLOAT, arrayLength,
+    if (!ValidateUniformMatrixArraySetter(loc, 3, 3, LOCAL_GL_FLOAT, arrayLength,
                                           transpose, "uniformMatrix3fv",
                                           &rawLoc, &numElementsToUpload))
     {
         return;
     }
 
     MakeContextCurrent();
     gl->fUniformMatrix3fv(rawLoc, numElementsToUpload, false, data);
 }
 
 void
 WebGLContext::UniformMatrix4fv_base(WebGLUniformLocation* loc, bool transpose,
                                     size_t arrayLength, const float* data)
 {
     GLuint rawLoc;
     GLsizei numElementsToUpload;
-    if (!ValidateUniformMatrixArraySetter(loc, 4, LOCAL_GL_FLOAT, arrayLength,
+    if (!ValidateUniformMatrixArraySetter(loc, 4, 4, LOCAL_GL_FLOAT, arrayLength,
                                           transpose, "uniformMatrix4fv",
                                           &rawLoc, &numElementsToUpload))
     {
         return;
     }
 
     MakeContextCurrent();
     gl->fUniformMatrix4fv(rawLoc, numElementsToUpload, false, data);
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -1534,42 +1534,41 @@ WebGLContext::ValidateUniformArraySetter
     *out_rawLoc = loc->mLoc;
     *out_numElementsToUpload = std::min((size_t)loc->mActiveInfo->mElemCount,
                                         setterArraySize / setterElemSize);
     return true;
 }
 
 bool
 WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
-                                               uint8_t setterDims,
+                                               uint8_t setterCols,
+                                               uint8_t setterRows,
                                                GLenum setterType,
                                                size_t setterArraySize,
                                                bool setterTranspose,
                                                const char* funcName,
                                                GLuint* const out_rawLoc,
                                                GLsizei* const out_numElementsToUpload)
 {
-    uint8_t setterElemSize = setterDims * setterDims;
+    uint8_t setterElemSize = setterCols * setterRows;
 
     if (IsContextLost())
         return false;
 
     if (!ValidateUniformLocation(loc, funcName))
         return false;
 
     if (!loc->ValidateSizeAndType(setterElemSize, setterType, this, funcName))
         return false;
 
     if (!loc->ValidateArrayLength(setterElemSize, setterArraySize, this, funcName))
         return false;
 
-    if (setterTranspose) {
-        ErrorInvalidValue("%s: `transpose` must be false.", funcName);
+    if (!ValidateUniformMatrixTranspose(setterTranspose, funcName))
         return false;
-    }
 
     *out_rawLoc = loc->mLoc;
     *out_numElementsToUpload = std::min((size_t)loc->mActiveInfo->mElemCount,
                                         setterArraySize / setterElemSize);
     return true;
 }
 
 bool
--- a/dom/canvas/WebGLRenderbuffer.cpp
+++ b/dom/canvas/WebGLRenderbuffer.cpp
@@ -174,16 +174,18 @@ RenderbufferStorageMaybeMultisample(gl::
                                  height);
     }
 }
 
 void
 WebGLRenderbuffer::RenderbufferStorage(GLsizei samples, GLenum internalFormat,
                                        GLsizei width, GLsizei height) const
 {
+    MOZ_ASSERT(mContext->mBoundRenderbuffer == this);
+
     gl::GLContext* gl = mContext->gl;
     MOZ_ASSERT(samples >= 0 && samples <= 256); // Sanity check.
 
     GLenum primaryFormat = internalFormat;
     GLenum secondaryFormat = 0;
 
     if (NeedsDepthStencilEmu(mContext->gl, primaryFormat)) {
         primaryFormat = DepthStencilDepthFormat(gl);
--- a/dom/canvas/WebGLSync.cpp
+++ b/dom/canvas/WebGLSync.cpp
@@ -4,36 +4,40 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLSync.h"
 
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 
 namespace mozilla {
 
-WebGLSync::WebGLSync(WebGLContext* webgl):
-    WebGLContextBoundObject(webgl)
+WebGLSync::WebGLSync(WebGLContext* webgl, GLenum condition, GLbitfield flags)
+    : WebGLContextBoundObject(webgl)
 {
-    MOZ_CRASH("Not Implemented.");
+   mGLName = mContext->gl->fFenceSync(condition, flags);
 }
 
 WebGLSync::~WebGLSync()
-{}
+{
+    DeleteOnce();
+}
 
 void
 WebGLSync::Delete()
 {
-    MOZ_CRASH("Not Implemented.");
+    mContext->MakeContextCurrent();
+    mContext->gl->fDeleteSync(mGLName);
+    mGLName = 0;
+    LinkedListElement<WebGLSync>::remove();
 }
 
 WebGLContext*
 WebGLSync::GetParentObject() const
 {
-    MOZ_CRASH("Not Implemented.");
-    return nullptr;
+    return Context();
 }
 
 // -------------------------------------------------------------------------
 // IMPLEMENT NS
 JSObject*
 WebGLSync::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
 {
     return dom::WebGLSyncBinding::Wrap(cx, this, aGivenProto);
--- a/dom/canvas/WebGLSync.h
+++ b/dom/canvas/WebGLSync.h
@@ -16,25 +16,27 @@ class WebGLSync final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLSync>
     , public LinkedListElement<WebGLSync>
     , public WebGLContextBoundObject
 {
     friend class WebGL2Context;
 
 public:
-    explicit WebGLSync(WebGLContext* webgl);
+    WebGLSync(WebGLContext* webgl, GLenum condition, GLbitfield flags);
 
     void Delete();
     WebGLContext* GetParentObject() const;
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLSync)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLSync)
 
 private:
     ~WebGLSync();
+
+    GLsync mGLName;
 };
 
 } // namespace mozilla
 
 #endif // WEBGL_SYNC_H_
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -594,20 +594,20 @@ ClearWithTempFB(WebGLContext* webgl, GLu
 
     if (mask & LOCAL_GL_COLOR_BUFFER_BIT) {
         // Nope, it already had one.
         return false;
     }
 
     gl::ScopedRenderbuffer rb(gl);
     {
-        gl::ScopedBindRenderbuffer(gl, rb.RB());
-        gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER,
-                                 LOCAL_GL_RGBA4,
-                                 width, height);
+        // Only GLES guarantees RGBA4.
+        GLenum format = gl->IsGLES() ? LOCAL_GL_RGBA4 : LOCAL_GL_RGBA8;
+        gl::ScopedBindRenderbuffer rbBinding(gl, rb.RB());
+        gl->fRenderbufferStorage(LOCAL_GL_RENDERBUFFER, format, width, height);
     }
 
     gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                  LOCAL_GL_RENDERBUFFER, rb.RB());
     mask |= LOCAL_GL_COLOR_BUFFER_BIT;
 
     // Last chance!
     return ClearByMask(webgl, mask);
--- a/dom/canvas/WebGLUniformLocation.cpp
+++ b/dom/canvas/WebGLUniformLocation.cpp
@@ -45,59 +45,77 @@ WebGLUniformLocation::ValidateForProgram
     }
 
     return true;
 }
 
 static bool
 IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType)
 {
+    // The order in this switch matches table 2.10 from OpenGL ES
+    // 3.0.4 (Aug 27, 2014) es_spec_3.0.4.pdf
     switch (uniformType) {
+    case LOCAL_GL_FLOAT:
+    case LOCAL_GL_FLOAT_VEC2:
+    case LOCAL_GL_FLOAT_VEC3:
+    case LOCAL_GL_FLOAT_VEC4:
+        return setterType == LOCAL_GL_FLOAT;
+
+    case LOCAL_GL_INT:
+    case LOCAL_GL_INT_VEC2:
+    case LOCAL_GL_INT_VEC3:
+    case LOCAL_GL_INT_VEC4:
+        return setterType == LOCAL_GL_INT;
+
+    case LOCAL_GL_UNSIGNED_INT:
+    case LOCAL_GL_UNSIGNED_INT_VEC2:
+    case LOCAL_GL_UNSIGNED_INT_VEC3:
+    case LOCAL_GL_UNSIGNED_INT_VEC4:
+        return setterType == LOCAL_GL_UNSIGNED_INT;
+
+        /* bool can be set via any function: 0, 0.0f -> FALSE, _ -> TRUE */
     case LOCAL_GL_BOOL:
     case LOCAL_GL_BOOL_VEC2:
     case LOCAL_GL_BOOL_VEC3:
     case LOCAL_GL_BOOL_VEC4:
-        return setterType == LOCAL_GL_INT ||
-               setterType == LOCAL_GL_FLOAT; // GLfloat(0.0) sets a bool to false.
+        return (setterType == LOCAL_GL_INT   ||
+                setterType == LOCAL_GL_FLOAT ||
+                setterType == LOCAL_GL_UNSIGNED_INT);
 
-    case LOCAL_GL_INT:
-    case LOCAL_GL_INT_SAMPLER_2D:
-    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
-    case LOCAL_GL_INT_SAMPLER_3D:
-    case LOCAL_GL_INT_SAMPLER_CUBE:
-    case LOCAL_GL_INT_VEC2:
-    case LOCAL_GL_INT_VEC3:
-    case LOCAL_GL_INT_VEC4:
+    case LOCAL_GL_FLOAT_MAT2:
+    case LOCAL_GL_FLOAT_MAT3:
+    case LOCAL_GL_FLOAT_MAT4:
+    case LOCAL_GL_FLOAT_MAT2x3:
+    case LOCAL_GL_FLOAT_MAT2x4:
+    case LOCAL_GL_FLOAT_MAT3x2:
+    case LOCAL_GL_FLOAT_MAT3x4:
+    case LOCAL_GL_FLOAT_MAT4x2:
+    case LOCAL_GL_FLOAT_MAT4x3:
+        return setterType == LOCAL_GL_FLOAT;
+
+        /* Samplers can only be set via Uniform1i */
     case LOCAL_GL_SAMPLER_2D:
+    case LOCAL_GL_SAMPLER_3D:
+    case LOCAL_GL_SAMPLER_CUBE:
+    case LOCAL_GL_SAMPLER_2D_SHADOW:
     case LOCAL_GL_SAMPLER_2D_ARRAY:
     case LOCAL_GL_SAMPLER_2D_ARRAY_SHADOW:
-    case LOCAL_GL_SAMPLER_2D_SHADOW:
-    case LOCAL_GL_SAMPLER_CUBE:
     case LOCAL_GL_SAMPLER_CUBE_SHADOW:
+
+    case LOCAL_GL_INT_SAMPLER_2D:
+    case LOCAL_GL_INT_SAMPLER_3D:
+    case LOCAL_GL_INT_SAMPLER_CUBE:
+    case LOCAL_GL_INT_SAMPLER_2D_ARRAY:
+
     case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D:
-    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
     case LOCAL_GL_UNSIGNED_INT_SAMPLER_3D:
     case LOCAL_GL_UNSIGNED_INT_SAMPLER_CUBE:
+    case LOCAL_GL_UNSIGNED_INT_SAMPLER_2D_ARRAY:
         return setterType == LOCAL_GL_INT;
 
-    case LOCAL_GL_FLOAT:
-    case LOCAL_GL_FLOAT_MAT2:
-    case LOCAL_GL_FLOAT_MAT2x3:
-    case LOCAL_GL_FLOAT_MAT2x4:
-    case LOCAL_GL_FLOAT_MAT3:
-    case LOCAL_GL_FLOAT_MAT3x2:
-    case LOCAL_GL_FLOAT_MAT3x4:
-    case LOCAL_GL_FLOAT_MAT4:
-    case LOCAL_GL_FLOAT_MAT4x2:
-    case LOCAL_GL_FLOAT_MAT4x3:
-    case LOCAL_GL_FLOAT_VEC2:
-    case LOCAL_GL_FLOAT_VEC3:
-    case LOCAL_GL_FLOAT_VEC4:
-        return setterType == LOCAL_GL_FLOAT;
-
     default:
         MOZ_CRASH("Bad `uniformType`.");
     }
 }
 
 bool
 WebGLUniformLocation::ValidateSizeAndType(uint8_t setterElemSize, GLenum setterType,
                                           WebGLContext* webgl, const char* funcName) const
--- a/dom/datastore/DataStoreService.cpp
+++ b/dom/datastore/DataStoreService.cpp
@@ -1015,20 +1015,22 @@ DataStoreService::GetDataStoresResolve(n
     nsRefPtr<DataStore> exposedStore = new DataStore(aWindow);
 
     ErrorResult error;
     exposedStore->SetDataStoreImpl(*dataStoreObj, error);
     if (error.Failed()) {
       return;
     }
 
-    JS::Rooted<JSObject*> obj(cx, exposedStore->WrapObject(cx, JS::NullPtr()));
-    MOZ_ASSERT(obj);
+    JS::Rooted<JS::Value> exposedObject(cx);
+    if (!GetOrCreateDOMReflector(cx, exposedStore, &exposedObject)) {
+      JS_ClearPendingException(cx);
+      return;
+    }
 
-    JS::Rooted<JS::Value> exposedObject(cx, JS::ObjectValue(*obj));
     dataStore->SetExposedObject(exposedObject);
 
     counter->AppendDataStore(cx, exposedStore, dataStore);
   }
 }
 
 // Thie method populates 'aStores' with the list of DataStores with 'aName' as
 // name and available for this 'aAppId'.
--- a/dom/html/HTMLAudioElement.cpp
+++ b/dom/html/HTMLAudioElement.cpp
@@ -38,17 +38,18 @@ HTMLAudioElement::HTMLAudioElement(alrea
 
 HTMLAudioElement::~HTMLAudioElement()
 {
 }
 
 bool
 HTMLAudioElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
-  return HasAttr(kNameSpaceID_None, nsGkAtoms::controls);
+  return HasAttr(kNameSpaceID_None, nsGkAtoms::controls) ||
+         HTMLMediaElement::IsInteractiveHTMLContent(aIgnoreTabindex);
 }
 
 already_AddRefed<HTMLAudioElement>
 HTMLAudioElement::Audio(const GlobalObject& aGlobal,
                         const Optional<nsAString>& aSrc,
                         ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobal.GetAsSupports());
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -146,17 +146,18 @@ NS_IMPL_STRING_ATTR(HTMLImageElement, Lo
 NS_IMPL_URI_ATTR(HTMLImageElement, Src, src)
 NS_IMPL_STRING_ATTR(HTMLImageElement, Srcset, srcset)
 NS_IMPL_STRING_ATTR(HTMLImageElement, UseMap, usemap)
 NS_IMPL_INT_ATTR(HTMLImageElement, Vspace, vspace)
 
 bool
 HTMLImageElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
-  return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap);
+  return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap) ||
+          nsGenericHTMLElement::IsInteractiveHTMLContent(aIgnoreTabindex);
 }
 
 bool
 HTMLImageElement::IsSrcsetEnabled()
 {
   return Preferences::GetBool(kPrefSrcsetEnabled, false);
 }
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -3219,17 +3219,18 @@ HTMLInputElement::Focus(ErrorResult& aEr
   }
 
   return;
 }
 
 bool
 HTMLInputElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
-  return mType != NS_FORM_INPUT_HIDDEN;
+  return mType != NS_FORM_INPUT_HIDDEN ||
+         nsGenericHTMLFormElementWithState::IsInteractiveHTMLContent(aIgnoreTabindex);
 }
 
 NS_IMETHODIMP
 HTMLInputElement::Select()
 {
   if (mType == NS_FORM_INPUT_NUMBER) {
     nsNumberControlFrame* numberControlFrame =
       do_QueryFrame(GetPrimaryFrame());
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -3231,23 +3231,23 @@ void HTMLMediaElement::PlaybackEnded()
 
 void HTMLMediaElement::SeekStarted()
 {
   DispatchAsyncEvent(NS_LITERAL_STRING("seeking"));
   // Set the Variable if the Seekstarted while active playing
   if(mPlayingThroughTheAudioChannel) {
     mPlayingThroughTheAudioChannelBeforeSeek = true;
   }
-  FireTimeUpdate(false);
 }
 
 void HTMLMediaElement::SeekCompleted()
 {
   mPlayingBeforeSeek = false;
   SetPlayedOrSeeked(true);
+  FireTimeUpdate(false);
   DispatchAsyncEvent(NS_LITERAL_STRING("seeked"));
   // We changed whether we're seeking so we need to AddRemoveSelfReference
   AddRemoveSelfReference();
   if (mTextTrackManager) {
     mTextTrackManager->DidSeek();
   }
   if (mCurrentPlayRangeStart == -1.0) {
     mCurrentPlayRangeStart = CurrentTime();
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -47,17 +47,18 @@ HTMLObjectElement::~HTMLObjectElement()
 {
   UnregisterActivityObserver();
   DestroyImageLoadingContent();
 }
 
 bool
 HTMLObjectElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
-  return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap);
+  return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap) ||
+         nsGenericHTMLFormElement::IsInteractiveHTMLContent(aIgnoreTabindex);
 }
 
 bool
 HTMLObjectElement::IsDoneAddingChildren()
 {
   return mIsDoneAddingChildren;
 }
 
--- a/dom/html/HTMLVideoElement.cpp
+++ b/dom/html/HTMLVideoElement.cpp
@@ -124,17 +124,18 @@ nsresult HTMLVideoElement::SetAcceptHead
   return aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                     value,
                                     false);
 }
 
 bool
 HTMLVideoElement::IsInteractiveHTMLContent(bool aIgnoreTabindex) const
 {
-  return HasAttr(kNameSpaceID_None, nsGkAtoms::controls);
+  return HasAttr(kNameSpaceID_None, nsGkAtoms::controls) ||
+         HTMLMediaElement::IsInteractiveHTMLContent(aIgnoreTabindex);
 }
 
 uint32_t HTMLVideoElement::MozParsedFrames() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   if (!sVideoStatsEnabled) {
     return 0;
   }
--- a/dom/html/test/forms/test_interactive_content_in_label.html
+++ b/dom/html/test/forms/test_interactive_content_in_label.html
@@ -13,71 +13,54 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=229925">Mozilla Bug 229925</a>
 <p id="display"></p>
 <form action="#">
   <label>
     <span id="text">label</span>
     <input type="button" id="target" value="target">
 
-    <a id="yes1" href="#">a</a>
-    <audio id="yes2" controls></audio>
-    <button id="yes3">button</button>
-    <details id="yes4">details</details>
-    <embed id="yes5">embed</embed>
-    <iframe id="yes6" src="data:text/plain," style="width: 16px; height: 16px;"></iframe>
-    <img id="yes7" src="data:image/png," usemap="#map">
-    <input id="yes8" type="text" size="4">
-    <keygen id="yes9">
-    <label id="yes10">label</label>
-    <object id="yes11" usemap="#map">object</object>
-    <select id="yes12"><option>select</option></select>
-    <textarea id="yes13" cols="1" rows="1"></textarea>
-    <video id="yes14" controls></video>
+    <a class="yes" href="#">a</a>
+    <audio class="yes" controls></audio>
+    <button class="yes">button</button>
+    <details class="yes">details</details>
+    <embed class="yes">embed</embed>
+    <iframe class="yes" src="data:text/plain," style="width: 16px; height: 16px;"></iframe>
+    <img class="yes" src="data:image/png," usemap="#map">
+    <input class="yes" type="text" size="4">
+    <keygen class="yes">
+    <label class="yes">label</label>
+    <object class="yes" usemap="#map">object</object>
+    <select class="yes"><option>select</option></select>
+    <textarea class="yes" cols="1" rows="1"></textarea>
+    <video class="yes" controls></video>
 
-    <audio id="no1"></audio>
-    <img id="no2" src="data:image/png,">
-    <input id="no3" type="hidden">
-    <object id="no4">object</object>
-    <video id="no5"></video>
-    <span id="no6" tabindex="1">tabindex</span>
+    <audio class="no"></audio>
+    <img class="no" src="data:image/png,">
+    <input class="no" type="hidden">
+    <object class="no">object</object>
+    <video class="no"></video>
+
+    <span class="no" tabindex="1">tabindex</span>
+    <audio class="no" tabindex="1"></audio>
+    <img class="no" src="data:image/png," tabindex="1">
+    <input class="no" type="hidden" tabindex="1">
+    <object class="no" tabindex="1">object</object>
+    <video class="no" tabindex="1"></video>
   </label>
 </form>
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 229925 **/
 
 var target = document.getElementById("target");
 
-var yes_nodes = [
-  document.getElementById("yes1"),
-  document.getElementById("yes2"),
-  document.getElementById("yes3"),
-  document.getElementById("yes4"),
-  document.getElementById("yes5"),
-  document.getElementById("yes6"),
-  document.getElementById("yes7"),
-  document.getElementById("yes8"),
-  document.getElementById("yes9"),
-  document.getElementById("yes10"),
-  document.getElementById("yes11"),
-  document.getElementById("yes12"),
-  document.getElementById("yes13"),
-  document.getElementById("yes14"),
-];
+var yes_nodes = Array.from(document.getElementsByClassName("yes"));
 
-var no_nodes = [
-  document.getElementById("text"),
-  document.getElementById("no1"),
-  document.getElementById("no2"),
-  document.getElementById("no3"),
-  document.getElementById("no4"),
-  document.getElementById("no5"),
-  document.getElementById("no6"),
-];
+var no_nodes = Array.from(document.getElementsByClassName("no"));
 
 var target_clicked = false;
 target.addEventListener("click", function() {
   target_clicked = true;
 });
 
 var node;
 for (node of yes_nodes) {
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -519,17 +519,16 @@ skip-if = buildapp == 'mulet' || buildap
 [test_track.html]
 [test_track_disabled.html]
 [test_ul_attributes_reflection.html]
 [test_undoManager.html]
 [test_video_wakelock.html]
 skip-if = toolkit == 'android' || (toolkit == 'gonk' && debug) #bug 871015, bug 881443
 [test_input_files_not_nsIFile.html]
 [test_ignoreuserfocus.html]
-skip-if = (toolkit == 'gonk' && debug)
 [test_fragment_form_pointer.html]
 [test_bug1682.html]
 [test_bug1823.html]
 [test_bug57600.html]
 [test_bug196523.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage
 [test_bug199692.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' || e10s #bug 811644 #Bug 931116, b2g desktop specific, initial triage
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -2527,17 +2527,17 @@ MediaDecoderStateMachine::SeekCompleted(
   mPlayDuration = newCurrentTime - mStartTime;
 
   mDecoder->StartProgressUpdates();
 
   // Change state to DECODING or COMPLETED now. SeekingStopped will
   // call MediaDecoderStateMachine::Seek to reset our state to SEEKING
   // if we need to seek again.
 
-  bool isLiveStream = mDecoder->GetResource()->GetLength() == -1;
+  bool isLiveStream = mDecoder->GetResource()->IsLiveStream();
   if (mPendingSeek.Exists()) {
     // A new seek target came in while we were processing the old one. No rest
     // for the seeking.
     DECODER_LOG("A new seek came along while we were finishing the old one - staying in SEEKING");
     SetState(DECODER_STATE_SEEKING);
   } else if (GetMediaTime() == mEndTime && !isLiveStream) {
     // Seeked to end of media, move to COMPLETED state. Note we don't do
     // this if we're playing a live stream, since the end of media will advance
@@ -2735,17 +2735,17 @@ nsresult MediaDecoderStateMachine::RunSt
       TimeStamp now = TimeStamp::Now();
       NS_ASSERTION(!mBufferingStart.IsNull(), "Must know buffering start time.");
 
       // With buffering heuristics we will remain in the buffering state if
       // we've not decoded enough data to begin playback, or if we've not
       // downloaded a reasonable amount of data inside our buffering time.
       if (mReader->UseBufferingHeuristics()) {
         TimeDuration elapsed = now - mBufferingStart;
-        bool isLiveStream = resource->GetLength() == -1;
+        bool isLiveStream = resource->IsLiveStream();
         if ((isLiveStream || !mDecoder->CanPlayThrough()) &&
               elapsed < TimeDuration::FromSeconds(mBufferingWait * mPlaybackRate) &&
               (mQuickBuffering ? HasLowDecodedData(mQuickBufferingLowDataThresholdUsecs)
                                : HasLowUndecodedData(mBufferingWait * USECS_PER_S)) &&
               mDecoder->IsExpectingMoreData())
         {
           DECODER_LOG("Buffering: wait %ds, timeout in %.3lfs %s",
                       mBufferingWait, mBufferingWait - elapsed.ToSeconds(),
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -408,16 +408,22 @@ public:
     return nullptr;
   }
 
   // Return true if the stream is a live stream
   virtual bool IsRealTime() {
     return false;
   }
 
+  // Returns true if the resource is a live stream.
+  virtual bool IsLiveStream()
+  {
+    return GetLength() == -1;
+  }
+
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
     return 0;
   }
 
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
--- a/dom/media/fmp4/apple/AppleATDecoder.cpp
+++ b/dom/media/fmp4/apple/AppleATDecoder.cpp
@@ -150,18 +150,16 @@ static OSStatus
                               void* aUserData)
 {
   PassthroughUserData* userData = (PassthroughUserData*)aUserData;
   if (!userData->mDataSize) {
     *aNumDataPackets = 0;
     return kNoMoreDataErr;
   }
 
-  LOG("AudioConverter wants %u packets of audio data\n", *aNumDataPackets);
-
   if (aPacketDesc) {
     userData->mPacket.mStartOffset = 0;
     userData->mPacket.mVariableFramesInPacket = 0;
     userData->mPacket.mDataByteSize = userData->mDataSize;
     *aPacketDesc = &userData->mPacket;
   }
 
   aData->mBuffers[0].mNumberChannels = userData->mChannels;
@@ -248,40 +246,40 @@ AppleATDecoder::DecodeSample(mp4_demuxer
 
     if (rv && rv != kNoMoreDataErr) {
       LOG("Error decoding audio stream: %d\n", rv);
       return NS_ERROR_FAILURE;
     }
 
     if (numFrames) {
       outputData.AppendElements(decoded.get(), numFrames * channels);
-      LOG("%d frames decoded", numFrames);
     }
 
     if (rv == kNoMoreDataErr) {
-      LOG("done processing compressed packet");
       break;
     }
   } while (true);
 
   if (outputData.IsEmpty()) {
     return NS_OK;
   }
 
   size_t numFrames = outputData.Length() / channels;
   int rate = mOutputFormat.mSampleRate;
   CheckedInt<Microseconds> duration = FramesToUsecs(numFrames, rate);
   if (!duration.isValid()) {
     NS_WARNING("Invalid count of accumulated audio samples");
     return NS_ERROR_FAILURE;
   }
 
+#ifdef LOG_SAMPLE_DECODE
   LOG("pushed audio at time %lfs; duration %lfs\n",
       (double)aSample->composition_timestamp / USECS_PER_S,
       (double)duration.value() / USECS_PER_S);
+#endif
 
   nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[outputData.Length()]);
   PodCopy(data.get(), &outputData[0], outputData.Length());
   nsRefPtr<AudioData> audio = new AudioData(aSample->byte_offset,
                                             aSample->composition_timestamp,
                                             duration.value(),
                                             numFrames,
                                             data.forget(),
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -292,25 +292,24 @@ MediaSource::EndOfStream(const Optional<
   if (mReadyState != MediaSourceReadyState::Open ||
       mSourceBuffers->AnyUpdating()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   SetReadyState(MediaSourceReadyState::Ended);
   mSourceBuffers->Ended();
-  mDecoder->Ended();
   if (!aError.WasPassed()) {
     mDecoder->SetMediaSourceDuration(mSourceBuffers->GetHighestBufferedEndTime(),
                                      MSRangeRemovalAction::SKIP);
     if (aRv.Failed()) {
       return;
     }
-    // TODO:
-    //   Notify media element that all data is now available.
+    // Notify reader that all data is now available.
+    mDecoder->Ended(true);
     return;
   }
   switch (aError.Value()) {
   case MediaSourceEndOfStreamError::Network:
     // TODO: If media element has a readyState of:
     //   HAVE_NOTHING -> run resource fetch algorithm
     // > HAVE_NOTHING -> run "interrupted" steps of resource fetch
     break;
@@ -451,16 +450,20 @@ MediaSource::SetReadyState(MediaSourceRe
 
   MediaSourceReadyState oldState = mReadyState;
   mReadyState = aState;
 
   if (mReadyState == MediaSourceReadyState::Open &&
       (oldState == MediaSourceReadyState::Closed ||
        oldState == MediaSourceReadyState::Ended)) {
     QueueAsyncSimpleEvent("sourceopen");
+    if (oldState == MediaSourceReadyState::Ended) {
+      // Notify reader that more data may come.
+      mDecoder->Ended(false);
+    }
     return;
   }
 
   if (mReadyState == MediaSourceReadyState::Ended &&
       oldState == MediaSourceReadyState::Open) {
     QueueAsyncSimpleEvent("sourceended");
     return;
   }
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -151,20 +151,21 @@ MediaSourceDecoder::RemoveTrackBuffer(Tr
 void
 MediaSourceDecoder::OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo)
 {
   MOZ_ASSERT(mReader);
   mReader->OnTrackBufferConfigured(aTrackBuffer, aInfo);
 }
 
 void
-MediaSourceDecoder::Ended()
+MediaSourceDecoder::Ended(bool aEnded)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  mReader->Ended();
+  static_cast<MediaSourceResource*>(GetResource())->SetEnded(aEnded);
+  mReader->Ended(aEnded);
   mon.NotifyAll();
 }
 
 bool
 MediaSourceDecoder::IsExpectingMoreData()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   return !mReader->IsEnded();
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -48,17 +48,17 @@ public:
   void DetachMediaSource();
 
   already_AddRefed<SourceBufferDecoder> CreateSubDecoder(const nsACString& aType,
                                                          int64_t aTimestampOffset /* microseconds */);
   void AddTrackBuffer(TrackBuffer* aTrackBuffer);
   void RemoveTrackBuffer(TrackBuffer* aTrackBuffer);
   void OnTrackBufferConfigured(TrackBuffer* aTrackBuffer, const MediaInfo& aInfo);
 
-  void Ended();
+  void Ended(bool aEnded);
   bool IsExpectingMoreData() override;
 
   // Return the duration of the video in seconds.
   virtual double GetDuration() override;
 
   void SetInitialDuration(int64_t aDuration);
   void SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction);
   double GetMediaSourceDuration();
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -34,32 +34,27 @@ extern PRLogModuleInfo* GetMediaSourceLo
 
 // When a stream hits EOS it needs to decide what other stream to switch to. Due
 // to inaccuracies is determining buffer end frames (Bug 1065207) and rounding
 // issues we use a fuzz factor to determine the end time of this stream for
 // switching to the new stream. This value is based on the end of frame
 // default value used in Blink, kDefaultBufferDurationInMs.
 #define EOS_FUZZ_US 125000
 
-// Audio and video source buffers often have a slight duration
-// discrepency. We want to handle the case where a source buffer is smaller than
-// another by more than EOS_FUZZ_US so we can properly detect EOS in IsNearEnd().
-// This value was chosen at random, to cater for most streams seen in the wild.
-#define DURATION_DIFFERENCE_FUZZ 300000
-
 using mozilla::dom::TimeRanges;
 
 namespace mozilla {
 
 MediaSourceReader::MediaSourceReader(MediaSourceDecoder* aDecoder)
   : MediaDecoderReader(aDecoder)
   , mLastAudioTime(0)
   , mLastVideoTime(0)
   , mPendingSeekTime(-1)
   , mWaitingForSeekData(false)
+  , mSeekToEnd(false)
   , mTimeThreshold(0)
   , mDropAudioBeforeThreshold(false)
   , mDropVideoBeforeThreshold(false)
   , mAudioDiscontinuity(false)
   , mVideoDiscontinuity(false)
   , mEnded(false)
   , mMediaSourceDuration(0)
   , mHasEssentialTrackBuffers(false)
@@ -786,16 +781,18 @@ void
 MediaSourceReader::NotifyTimeRangesChanged()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mWaitingForSeekData) {
     //post a task to the decode queue to try to complete the pending seek.
     RefPtr<nsIRunnable> task(NS_NewRunnableMethod(
         this, &MediaSourceReader::AttemptSeek));
     GetTaskQueue()->Dispatch(task.forget());
+  } else {
+    MaybeNotifyHaveData();
   }
 }
 
 nsRefPtr<MediaDecoderReader::SeekPromise>
 MediaSourceReader::Seek(int64_t aTime, int64_t aIgnored /* Used only for ogg which is non-MSE */)
 {
   MSE_DEBUG("Seek(aTime=%lld, aEnd=%lld, aCurrent=%lld)",
             aTime);
@@ -887,24 +884,28 @@ MediaSourceReader::OnVideoSeekFailed(nsr
   mVideoSeekRequest.Complete();
   mPendingSeekTime = -1;
   mSeekPromise.Reject(aResult, __func__);
 }
 
 void
 MediaSourceReader::DoAudioSeek()
 {
-  if (SwitchAudioSource(&mPendingSeekTime) == SOURCE_NONE) {
+  int64_t seekTime = mPendingSeekTime;
+  if (mSeekToEnd) {
+    seekTime = LastSampleTime(MediaData::AUDIO_DATA);
+  }
+  if (SwitchAudioSource(&seekTime) == SOURCE_NONE) {
     // Data we need got evicted since the last time we checked for data
     // availability. Abort current seek attempt.
     mWaitingForSeekData = true;
     return;
   }
   GetAudioReader()->ResetDecode();
-  mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(mPendingSeekTime), 0)
+  mAudioSeekRequest.Begin(GetAudioReader()->Seek(GetReaderAudioTime(seekTime), 0)
                          ->RefableThen(GetTaskQueue(), __func__, this,
                                        &MediaSourceReader::OnAudioSeekCompleted,
                                        &MediaSourceReader::OnAudioSeekFailed));
   MSE_DEBUG("reader=%p", GetAudioReader());
 }
 
 void
 MediaSourceReader::OnAudioSeekCompleted(int64_t aTime)
@@ -929,17 +930,19 @@ MediaSourceReader::AttemptSeek()
 {
   // Make sure we don't hold the monitor while calling into the reader
   // Seek methods since it can deadlock.
   {
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     if (!mWaitingForSeekData) {
       return;
     }
-    if (!TrackBuffersContainTime(mPendingSeekTime)) {
+
+    mSeekToEnd = IsEnded() && mPendingSeekTime >= mMediaSourceDuration * USECS_PER_S;
+    if (!mSeekToEnd && !TrackBuffersContainTime(mPendingSeekTime)) {
       mVideoSourceDecoder = nullptr;
       mAudioSourceDecoder = nullptr;
       return;
     }
     mWaitingForSeekData = false;
   }
 
   // Decoding discontinuity upon seek, reset last times to seek target.
@@ -953,24 +956,28 @@ MediaSourceReader::AttemptSeek()
   } else {
     MOZ_CRASH();
   }
 }
 
 void
 MediaSourceReader::DoVideoSeek()
 {
-  if (SwitchVideoSource(&mPendingSeekTime) == SOURCE_NONE) {
+  int64_t seekTime = mPendingSeekTime;
+  if (mSeekToEnd) {
+    seekTime = LastSampleTime(MediaData::VIDEO_DATA);
+  }
+  if (SwitchVideoSource(&seekTime) == SOURCE_NONE) {
     // Data we need got evicted since the last time we checked for data
     // availability. Abort current seek attempt.
     mWaitingForSeekData = true;
     return;
   }
   GetVideoReader()->ResetDecode();
-  mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(mPendingSeekTime), 0)
+  mVideoSeekRequest.Begin(GetVideoReader()->Seek(GetReaderVideoTime(seekTime), 0)
                           ->RefableThen(GetTaskQueue(), __func__, this,
                                         &MediaSourceReader::OnVideoSeekCompleted,
                                         &MediaSourceReader::OnVideoSeekFailed));
   MSE_DEBUG("reader=%p", GetVideoReader());
 }
 
 nsresult
 MediaSourceReader::GetBuffered(dom::TimeRanges* aBuffered)
@@ -1022,26 +1029,34 @@ MediaSourceReader::WaitForData(MediaData
   return p;
 }
 
 void
 MediaSourceReader::MaybeNotifyHaveData()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   bool haveAudio = false, haveVideo = false;
-  if (!IsSeeking() && mAudioTrack && HaveData(mLastAudioTime, MediaData::AUDIO_DATA)) {
-    haveAudio = true;
-    WaitPromise(MediaData::AUDIO_DATA).ResolveIfExists(MediaData::AUDIO_DATA, __func__);
+  bool ended = IsEnded();
+  // If we are in ended mode, we will resolve any pending wait promises.
+  // The next Request*Data will handle END_OF_STREAM or going back into waiting
+  // mode.
+  if (!IsSeeking() && mAudioTrack) {
+    haveAudio = HaveData(mLastAudioTime, MediaData::AUDIO_DATA);
+    if (ended || haveAudio) {
+      WaitPromise(MediaData::AUDIO_DATA).ResolveIfExists(MediaData::AUDIO_DATA, __func__);
+    }
   }
-  if (!IsSeeking() && mVideoTrack && HaveData(mLastVideoTime, MediaData::VIDEO_DATA)) {
-    haveVideo = true;
-    WaitPromise(MediaData::VIDEO_DATA).ResolveIfExists(MediaData::VIDEO_DATA, __func__);
+  if (!IsSeeking() && mVideoTrack) {
+    haveVideo = HaveData(mLastVideoTime, MediaData::VIDEO_DATA);
+    if (ended || haveVideo) {
+      WaitPromise(MediaData::VIDEO_DATA).ResolveIfExists(MediaData::VIDEO_DATA, __func__);
+    }
   }
-  MSE_DEBUG("isSeeking=%d haveAudio=%d, haveVideo=%d",
-            IsSeeking(), haveAudio, haveVideo);
+  MSE_DEBUG("isSeeking=%d haveAudio=%d, haveVideo=%d ended=%d",
+            IsSeeking(), haveAudio, haveVideo, ended);
 }
 
 static void
 CombineEncryptionData(EncryptionInfo& aTo, const EncryptionInfo& aFrom)
 {
   if (!aFrom.mIsEncrypted) {
     return;
   }
@@ -1122,51 +1137,60 @@ MediaSourceReader::ReadUpdatedMetadata(M
     const MediaInfo& info = GetVideoReader()->GetMediaInfo();
     MOZ_ASSERT(info.HasVideo());
     mInfo.mVideo = info.mVideo;
   }
   *aInfo = mInfo;
 }
 
 void
-MediaSourceReader::Ended()
+MediaSourceReader::Ended(bool aEnded)
 {
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
-  mEnded = true;
+  mEnded = aEnded;
+  if (aEnded) {
+    // post a task to the decode queue to try to complete any pending
+    // seek or wait
+    RefPtr<nsIRunnable> task(NS_NewRunnableMethod(
+        this, &MediaSourceReader::NotifyTimeRangesChanged));
+    GetTaskQueue()->Dispatch(task.forget());
+  }
 }
 
 bool
 MediaSourceReader::IsEnded()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   return mEnded;
 }
 
 bool
 MediaSourceReader::IsNearEnd(MediaData::Type aType, int64_t aTime)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (!mEnded) {
     return false;
   }
-  if (aTime >= (mMediaSourceDuration * USECS_PER_S - EOS_FUZZ_US)) {
-    return true;
-  }
-  // We may have discrepencies between the mediasource duration and the
-  // sourcebuffer end time (mMediaSourceDuration == max(audio.EndTime, video.EndTime)
-  // If the sourcebuffer duration is close enough to the mediasource duration,
-  // then use it instead to determine if we're near the end.
   TrackBuffer* trackBuffer =
     aType == MediaData::AUDIO_DATA ? mAudioTrack : mVideoTrack;
   nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
   trackBuffer->Buffered(buffered);
-  if ((mMediaSourceDuration - buffered->GetEndTime()) * USECS_PER_S <= DURATION_DIFFERENCE_FUZZ) {
-    return aTime >= std::floor(buffered->GetEndTime() * USECS_PER_S);
-  }
-  return false;
+  return aTime >= (buffered->GetEndTime() * USECS_PER_S - EOS_FUZZ_US);
+}
+
+int64_t
+MediaSourceReader::LastSampleTime(MediaData::Type aType)
+{
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
+
+  TrackBuffer* trackBuffer =
+  aType == MediaData::AUDIO_DATA ? mAudioTrack : mVideoTrack;
+  nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
+  trackBuffer->Buffered(buffered);
+  return buffered->GetEndTime() * USECS_PER_S - 1;
 }
 
 void
 MediaSourceReader::SetMediaSourceDuration(double aDuration)
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mMediaSourceDuration = aDuration;
 }
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -130,21 +130,21 @@ public:
     ReentrantMonitorAutoEnter decoderMon(mDecoder->GetReentrantMonitor());
     return mDecoder->IsShutdown();
   }
 
   // Return true if all of the active tracks contain data for the specified time.
   bool TrackBuffersContainTime(int64_t aTime);
 
   // Mark the reader to indicate that EndOfStream has been called on our MediaSource
-  void Ended();
-
-  // Return true if the Ended method has been called
+  // and that the media element has all the media data.
+  // If called with true, the reader will come out of ended mode.
+  void Ended(bool aEnded);
+  // Return true if reader has all of the media data.
   bool IsEnded();
-  bool IsNearEnd(MediaData::Type aType, int64_t aTime /* microseconds */);
 
   // Set the duration of the attached mediasource element.
   void SetMediaSourceDuration(double aDuration /* seconds */);
 
 #ifdef MOZ_EME
   nsresult SetCDMProxy(CDMProxy* aProxy);
 #endif
 
@@ -222,16 +222,19 @@ private:
   already_AddRefed<SourceBufferDecoder> SelectDecoder(int64_t aTarget /* microseconds */,
                                                       int64_t aTolerance /* microseconds */,
                                                       const nsTArray<nsRefPtr<SourceBufferDecoder>>& aTrackDecoders);
   bool HaveData(int64_t aTarget, MediaData::Type aType);
 
   void AttemptSeek();
   bool IsSeeking() { return mPendingSeekTime != -1; }
 
+  bool IsNearEnd(MediaData::Type aType, int64_t aTime /* microseconds */);
+  int64_t LastSampleTime(MediaData::Type aType);
+
   nsRefPtr<SourceBufferDecoder> mAudioSourceDecoder;
   nsRefPtr<SourceBufferDecoder> mVideoSourceDecoder;
 
   nsTArray<nsRefPtr<TrackBuffer>> mTrackBuffers;
   nsTArray<nsRefPtr<TrackBuffer>> mShutdownTrackBuffers;
   nsTArray<nsRefPtr<TrackBuffer>> mEssentialTrackBuffers;
   nsRefPtr<TrackBuffer> mAudioTrack;
   nsRefPtr<TrackBuffer> mVideoTrack;
@@ -260,16 +263,17 @@ private:
   MediaPromiseConsumerHolder<SeekPromise> mAudioSeekRequest;
   MediaPromiseConsumerHolder<SeekPromise> mVideoSeekRequest;
   MediaPromiseHolder<SeekPromise> mSeekPromise;
 
   // Temporary seek information while we wait for the data
   // to be added to the track buffer.
   int64_t mPendingSeekTime;
   bool mWaitingForSeekData;
+  bool mSeekToEnd;
 
   int64_t mTimeThreshold;
   bool mDropAudioBeforeThreshold;
   bool mDropVideoBeforeThreshold;
 
   bool mAudioDiscontinuity;
   bool mVideoDiscontinuity;
 
--- a/dom/media/mediasource/MediaSourceResource.h
+++ b/dom/media/mediasource/MediaSourceResource.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_MEDIASOURCERESOURCE_H_
 #define MOZILLA_MEDIASOURCERESOURCE_H_
 
 #include "MediaResource.h"
+#include "mozilla/Monitor.h"
 #include "prlog.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
 
 #define MSE_DEBUG(arg, ...) PR_LOG(GetMediaSourceLog(), PR_LOG_DEBUG, ("MediaSourceResource(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 #else
 #define MSE_DEBUG(...)
@@ -21,17 +22,20 @@ extern PRLogModuleInfo* GetMediaSourceLo
 #define UNIMPLEMENTED() MSE_DEBUG("UNIMPLEMENTED FUNCTION at %s:%d", __FILE__, __LINE__)
 
 namespace mozilla {
 
 class MediaSourceResource final : public MediaResource
 {
 public:
   explicit MediaSourceResource(nsIPrincipal* aPrincipal = nullptr)
-    : mPrincipal(aPrincipal) {}
+    : mPrincipal(aPrincipal)
+    , mMonitor("MediaSourceResource")
+    , mEnded(false)
+    {}
 
   virtual nsresult Close() override { return NS_OK; }
   virtual void Suspend(bool aCloseImmediately) override { UNIMPLEMENTED(); }
   virtual void Resume() override { UNIMPLEMENTED(); }
   virtual bool CanClone() override { UNIMPLEMENTED(); return false; }
   virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) override { UNIMPLEMENTED(); return nullptr; }
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) override { UNIMPLEMENTED(); }
   virtual void SetPlaybackRate(uint32_t aBytesPerSecond) override  { UNIMPLEMENTED(); }
@@ -61,32 +65,45 @@ public:
     UNIMPLEMENTED();
     aRanges.AppendElement(MediaByteRange(0, GetLength()));
     return NS_OK;
   }
 
   virtual bool IsTransportSeekable() override { return true; }
   virtual const nsCString& GetContentType() const override { return mType; }
 
+  virtual bool IsLiveStream() override
+  {
+    MonitorAutoLock mon(mMonitor);
+    return !mEnded;
+  }
+  void SetEnded(bool aEnded)
+  {
+    MonitorAutoLock mon(mMonitor);
+    mEnded = aEnded;
+  }
+
 private:
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     size_t size = MediaResource::SizeOfExcludingThis(aMallocSizeOf);
     size += mType.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
 
     return size;
   }
 
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   nsRefPtr<nsIPrincipal> mPrincipal;
   const nsCString mType;
+  Monitor mMonitor;
+  bool mEnded; // protected by mMonitor
 };
 
 } // namespace mozilla
 
 #undef MSE_DEBUG
 #undef UNIMPLEMENTED
 
 #endif /* MOZILLA_MEDIASOURCERESOURCE_H_ */
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -141,22 +141,26 @@ skip-if = toolkit == 'gonk' # b2g (Bug 1
 skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
 [test_peerConnection_throwInCallbacks.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_toJSON.html]
 skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
 
 [test_peerConnection_twoAudioStreams.html]
 skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+[test_peerConnection_twoAudioTracksInOneStream.html]
+skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
 [test_peerConnection_twoAudioVideoStreams.html]
 skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078)
 [test_peerConnection_twoAudioVideoStreamsCombined.html]
 skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078)
 [test_peerConnection_twoVideoStreams.html]
 skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078)
+[test_peerConnection_twoVideoTracksInOneStream.html]
+skip-if = (toolkit == 'gonk' || (e10s && debug)) # b2g (Bug 1059867) or fd exhaustion on e10s debug intermittent (Bug 1126078)
 [test_peerConnection_addSecondAudioStream.html]
 skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
 [test_peerConnection_answererAddSecondAudioStream.html]
 skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
 [test_peerConnection_removeAudioTrack.html]
 skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
 [test_peerConnection_removeThenAddAudioTrack.html]
 skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_twoAudioTracksInOneStream.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1145407",
+    title: "Multistream: Two audio tracks in one stream"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    test.chain.insertAfter("PC_REMOTE_GET_OFFER", [
+        function PC_REMOTE_OVERRIDE_STREAM_IDS_IN_OFFER(test) {
+          test._local_offer.sdp = test._local_offer.sdp.replace(
+              /a=msid:[^\s]*/g,
+              "a=msid:foo");
+        },
+        function PC_REMOTE_OVERRIDE_EXPECTED_STREAM_IDS(test) {
+          Object.keys(
+              test.pcRemote.expectedRemoteTrackInfoById).forEach(trackId => {
+                test.pcRemote.expectedRemoteTrackInfoById[trackId].streamId = "foo";
+              });
+        }
+    ]);
+    test.chain.insertAfter("PC_LOCAL_GET_ANSWER", [
+        function PC_LOCAL_OVERRIDE_STREAM_IDS_IN_ANSWER(test) {
+          test._remote_answer.sdp = test._remote_answer.sdp.replace(
+              /a=msid:[^\s]*/g,
+              "a=msid:foo");
+        },
+        function PC_LOCAL_OVERRIDE_EXPECTED_STREAM_IDS(test) {
+          Object.keys(
+              test.pcLocal.expectedRemoteTrackInfoById).forEach(trackId => {
+                test.pcLocal.expectedRemoteTrackInfoById[trackId].streamId = "foo";
+              });
+        }
+    ]);
+    test.setMediaConstraints([{audio: true}, {audio: true}],
+                             [{audio: true}, {audio: true}]);
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_twoVideoTracksInOneStream.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1145407",
+    title: "Multistream: Two video tracks in offerer stream"
+  });
+
+  var test;
+  runNetworkTest(function (options) {
+    test = new PeerConnectionTest(options);
+    test.chain.insertAfter("PC_REMOTE_GET_OFFER", [
+        function PC_REMOTE_OVERRIDE_STREAM_IDS_IN_OFFER(test) {
+          test._local_offer.sdp = test._local_offer.sdp.replace(
+              /a=msid:[^\s]*/g,
+              "a=msid:foo");
+        },
+        function PC_REMOTE_OVERRIDE_EXPECTED_STREAM_IDS(test) {
+          Object.keys(
+              test.pcRemote.expectedRemoteTrackInfoById).forEach(trackId => {
+                test.pcRemote.expectedRemoteTrackInfoById[trackId].streamId = "foo";
+              });
+        }
+    ]);
+    test.chain.insertAfter("PC_LOCAL_GET_ANSWER", [
+        function PC_LOCAL_OVERRIDE_STREAM_IDS_IN_ANSWER(test) {
+          test._remote_answer.sdp = test._remote_answer.sdp.replace(
+              /a=msid:[^\s]*/g,
+              "a=msid:foo");
+        },
+        function PC_LOCAL_OVERRIDE_EXPECTED_STREAM_IDS(test) {
+          Object.keys(
+              test.pcLocal.expectedRemoteTrackInfoById).forEach(trackId => {
+                test.pcLocal.expectedRemoteTrackInfoById[trackId].streamId = "foo";
+              });
+        }
+    ]);
+    test.setMediaConstraints([{video: true}, {video: true}],
+                             [{video: true}, {video: true}]);
+    test.run();
+  });
+</script>
+</pre>
+</body>
+</html>
--- a/dom/network/tests/test_udpsocket.html
+++ b/dom/network/tests/test_udpsocket.html
@@ -307,23 +307,23 @@ function testCloseBeforeOpened() {
   return socket.close().then(function() {
     ok(true, 'expected closedPromise to be resolved');
   }).then(socket.opened);
 }
 
 function testOpenWithoutClose() {
   info('test for open without close');
 
-  let opened = [];
+  let closed = [];
   for (let i = 0; i < 50; i++) {
     let socket = new UDPSocket();
-    opened.push(socket.opened);
+    closed.push(socket.closed);
   }
 
-  return Promise.all(opened);
+  return Promise.all(closed);
 }
 
 function testBFCache() {
   info('test for bfcache behavior');
 
   let socket = new UDPSocket();
 
   return socket.opened.then(function() {
@@ -372,16 +372,20 @@ function runTest() {
   .then(testOpenFailed)
   .then(testSendBeforeOpen)
   .then(testCloseBeforeOpened)
   .then(testOpenWithoutClose)
   .then(testBFCache)
   .then(function() {
     info('test finished');
     SimpleTest.finish();
+  })
+  .catch(function(err) {
+    ok(false, 'test failed due to: ' + err);
+    SimpleTest.finish();
   });
 }
 
 window.addEventListener('load', function () {
   SpecialPowers.pushPermissions([
     {type: 'udp-socket', allow: true, context: document}], function() {
     SpecialPowers.pushPrefEnv({
       'set': [
--- a/dom/plugins/test/mochitest/cocoa_focus.html
+++ b/dom/plugins/test/mochitest/cocoa_focus.html
@@ -10,17 +10,16 @@
       window.opener.SimpleTest.is(aLeft, aRight, aMessage);
     }
 
     function ok(aValue, aMessage) {
       window.opener.SimpleTest.ok(aValue, aMessage);
     }
 
     function runTests() {
-      netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
       var utils = SpecialPowers.DOMWindowUtils;
       var scale = utils.screenPixelsPerCSSPixel;
 
       var plugin1 = document.getElementById("plugin1"); // What we're testing.
       var plugin2 = document.getElementById("plugin2"); // Dummy.
 
       var plugin1Bounds = plugin1.getBoundingClientRect();
       var plugin2Bounds = plugin2.getBoundingClientRect();
@@ -62,17 +61,17 @@
 
       is(plugin1.getFocusState(), true, "(1) Plugin should have focus.");
       is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
 
       // Make sure window activation state changes don't spontaneously
       // change plugin focus.
 
       // Blur the window.
-      window.blur();
+      SpecialPowers.focus(opener);
 
       is(plugin1.getFocusState(), true, "(2) Plugin should still have focus.");
       is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
 
       // Focus the window.
       window.focus();
 
       is(plugin1.getFocusState(), true, "(3) Plugin should still have focus.");
@@ -90,26 +89,26 @@
       // changes that took place while the window was inactive.
 
       // Give the plugin focus (the window is already focused).
       utils.sendNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseDown, 0, plugin1);
       utils.sendNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseUp, 0, plugin1);
       expectedEventCount++;
 
       // Blur the window.
-      window.blur();
+      SpecialPowers.focus(opener);
 
       // Take focus from the plugin while the window is blurred.
       plugin2.focus();
 
       is(plugin1.getFocusState(), true, "(5) Plugin should still have focus.");
       is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
 
       // Focus the window.
-      window.focus();
+      SpecialPowers.focus(window);
       expectedEventCount++;
 
       is(plugin1.getFocusState(), false, "(6) Plugin should not have focus.");
       is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
 
       window.opener.testsFinished();
     }
 
--- a/dom/plugins/test/mochitest/cocoa_window_focus.html
+++ b/dom/plugins/test/mochitest/cocoa_window_focus.html
@@ -48,33 +48,30 @@
         is(plugin1.getTopLevelWindowActivationEventCount(), expectedEventCount, "Window focus event count should be " + expectedEventCount);
 
         is(plugin2.getTopLevelWindowActivationState(), true, "Activation state should be: activated");
         is(plugin2.getTopLevelWindowActivationEventCount(), expectedEventCount, "Window focus event count should be " + expectedEventCount);
       } catch (e) {
         ok(false, "Plugin does not know its initial top-level window activation state!");
       }
 
-      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-      var fm = SpecialPowers.Cc["@mozilla.org/focus-manager;1"].
-                             getService(SpecialPowers.Ci.nsIFocusManager);
+      var fm = SpecialPowers.Services.focus;
 
       waitForFocus(function() {
         // Make sure the plugin handled the focus event before checking.
         executeSoon(function() {
           expectedEventCount++;
 
           is(plugin1.getTopLevelWindowActivationState(), false, "Activation state should be: deactivated");
           is(plugin1.getTopLevelWindowActivationEventCount(), expectedEventCount, "Window focus event count should be " + expectedEventCount);
 
           is(plugin2.getTopLevelWindowActivationState(), false, "Activation state should be: deactivated");
           is(plugin2.getTopLevelWindowActivationEventCount(), expectedEventCount, "Window focus event count should be " + expectedEventCount);
 
           // Bring our window back to the front and make sure plugins were properly notified.
-          netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
           fm.focusedWindow = window;
 
           waitForFocus(function() {
             // Make sure the plugin handled the focus event before checking.
             executeSoon(function() {
               expectedEventCount++;
 
               is(plugin1.getTopLevelWindowActivationState(), true, "Activation state should be: activated");
@@ -86,14 +83,13 @@
               window.opener.testsFinished();
             });
           }, window);
         });
       }, window.opener);
 
       // Send our window to the back and make sure plugins were properly notified.
       // Calling window.blur() is not allowed.
-      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
       fm.focusedWindow = window.opener;
     }
   </script>
 </body>
 </html>
--- a/dom/plugins/test/mochitest/test_cocoa_focus.html
+++ b/dom/plugins/test/mochitest/test_cocoa_focus.html
@@ -1,13 +1,14 @@
 <html>
 <head>
   <title>NPCocoaEventFocusChanged Tests</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="utils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 
 <body onload="runTests()">
   <script class="testbody" type="application/javascript">
     SimpleTest.waitForExplicitFinish();
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
     var gOtherWindow;
--- a/dom/plugins/test/mochitest/test_cocoa_window_focus.html
+++ b/dom/plugins/test/mochitest/test_cocoa_window_focus.html
@@ -1,13 +1,14 @@
 <html>
 <head>
   <title>NPCocoaEventWindowFocusChanged Tests</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="utils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 
 <body onload="runTests()">
   <script class="testbody" type="application/javascript">
     SimpleTest.waitForExplicitFinish();
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
 
     var gOtherWindow;
--- a/dom/webidl/BluetoothGatt.webidl
+++ b/dom/webidl/BluetoothGatt.webidl
@@ -2,16 +2,18 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 [CheckPermissions="bluetooth"]
 interface BluetoothGatt : EventTarget
 {
+  [Cached, Pure]
+  readonly attribute sequence<BluetoothGattService> services;
   readonly attribute BluetoothConnectionState       connectionState;
 
   // Fired when attribute connectionState changed
            attribute EventHandler                   onconnectionstatechanged;
 
   /**
    * Connect/Disconnect to the remote BLE device if the connectionState is
    * disconnected/connected. Otherwise, the Promise will be rejected directly.
@@ -24,16 +26,24 @@ interface BluetoothGatt : EventTarget
    *   3) Promise is resolved or rejected according to the operation result.
    */
   [NewObject]
   Promise<void>                                     connect();
   [NewObject]
   Promise<void>                                     disconnect();
 
   /**
+   * Discover services, characteristics, descriptors offered by the remote GATT
+   * server. The promise will be rejected if the connState is not connected or
+   * operation fails.
+   */
+  [NewObject]
+  Promise<void>                                     discoverServices();
+
+  /**
    * Read RSSI for the remote BLE device if the connectState is connected.
    * Otherwise, the Promise will be rejected directly.
    */
   [NewObject]
   Promise<short>                                    readRemoteRssi();
 };
 
 enum BluetoothConnectionState
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothGattCharacteristic.webidl
@@ -0,0 +1,16 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+[CheckPermissions="bluetooth"]
+interface BluetoothGattCharacteristic
+{
+  readonly attribute BluetoothGattService                   service;
+  [Cached, Pure]
+  readonly attribute sequence<BluetoothGattDescriptor>      descriptors;
+
+  readonly attribute DOMString                              uuid;
+  readonly attribute unsigned short                         instanceId;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothGattDescriptor.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+[CheckPermissions="bluetooth"]
+interface BluetoothGattDescriptor
+{
+  readonly attribute BluetoothGattCharacteristic            characteristic;
+  readonly attribute DOMString                              uuid;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothGattService.webidl
@@ -0,0 +1,18 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+[CheckPermissions="bluetooth"]
+interface BluetoothGattService
+{
+  [Cached, Pure]
+  readonly attribute sequence<BluetoothGattCharacteristic>  characteristics;
+  [Cached, Pure]
+  readonly attribute sequence<BluetoothGattService>         includedServices;
+
+  readonly attribute boolean                                isPrimary;
+  readonly attribute DOMString                              uuid;
+  readonly attribute unsigned short                         instanceId;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -632,16 +632,19 @@ if CONFIG['MOZ_DEBUG']:
 if CONFIG['MOZ_B2G_BT']:
     if CONFIG['MOZ_B2G_BT_API_V2']:
         WEBIDL_FILES += [
             'BluetoothAdapter2.webidl',
             'BluetoothClassOfDevice.webidl',
             'BluetoothDevice2.webidl',
             'BluetoothDiscoveryHandle.webidl',
             'BluetoothGatt.webidl',
+            'BluetoothGattCharacteristic.webidl',
+            'BluetoothGattDescriptor.webidl',
+            'BluetoothGattService.webidl',
             'BluetoothManager2.webidl',
             'BluetoothPairingHandle.webidl',
             'BluetoothPairingListener.webidl',
         ]
     else:
         WEBIDL_FILES += [
             'BluetoothAdapter.webidl',
             'BluetoothDevice.webidl',
--- a/gfx/layers/LayersLogging.h
+++ b/gfx/layers/LayersLogging.h
@@ -181,23 +181,25 @@ AppendToString(std::stringstream& aStrea
   aStream << sfx;
 }
 
 template<class src, class dst>
 void
 AppendToString(std::stringstream& aStream, const mozilla::gfx::ScaleFactors2D<src, dst>& scale,
                const char* pfx="", const char* sfx="")
 {
+  aStream << pfx;
   std::streamsize oldPrecision = aStream.precision(3);
   if (scale.AreScalesSame()) {
     aStream << scale.xScale;
   } else {
     aStream << '(' << scale.xScale << ',' << scale.yScale << ')';
   }
   aStream.precision(oldPrecision);
+  aStream << sfx;
 }
 
 void
 AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix4x4& m,
                const char* pfx="", const char* sfx="");
 
 void
 AppendToString(std::stringstream& aStream, const mozilla::gfx::Matrix5x4& m,
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -3,20 +3,22 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_TILEDLAYERBUFFER_H
 #define GFX_TILEDLAYERBUFFER_H
 
 // Debug defines
 //#define GFX_TILEDLAYER_DEBUG_OVERLAY
 //#define GFX_TILEDLAYER_PREF_WARNINGS
+//#define GFX_TILEDLAYER_RETAINING_LOG
 
 #include <stdint.h>                     // for uint16_t, uint32_t
 #include <sys/types.h>                  // for int32_t
 #include "gfxPlatform.h"                // for GetTileWidth/GetTileHeight
+#include "LayersLogging.h"              // for print_stderr
 #include "mozilla/gfx/Logging.h"        // for gfxCriticalError
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArray.h"                   // for nsTArray
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
@@ -109,26 +111,16 @@ public:
   Tile GetTile(const nsIntPoint& aTileOrigin) const;
 
   // Given a tile x, y relative to the top left of the layer, this function
   // will return the tile for
   // (x*GetScaledTileSize().width, y*GetScaledTileSize().height,
   //  GetScaledTileSize().width, GetScaledTileSize().height)
   Tile GetTile(int x, int y) const;
 
-  // This operates the same as GetTile(aTileOrigin), but will also replace the
-  // specified tile with the placeholder tile. This does not call ReleaseTile
-  // on the removed tile.
-  bool RemoveTile(const nsIntPoint& aTileOrigin, Tile& aRemovedTile);
-
-  // This operates the same as GetTile(x, y), but will also replace the
-  // specified tile with the placeholder tile. This does not call ReleaseTile
-  // on the removed tile.
-  bool RemoveTile(int x, int y, Tile& aRemovedTile);
-
   const gfx::IntSize& GetTileSize() const { return mTileSize; }
 
   gfx::IntSize GetScaledTileSize() const { return RoundedToInt(gfx::Size(mTileSize) / mResolution); }
 
   unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
 
   const nsIntRegion& GetValidRegion() const { return mValidRegion; }
   const nsIntRegion& GetPaintedRegion() const { return mPaintedRegion; }
@@ -280,41 +272,16 @@ TiledLayerBuffer<Derived, Tile>::GetTile
 
 template<typename Derived, typename Tile> Tile
 TiledLayerBuffer<Derived, Tile>::GetTile(int x, int y) const
 {
   int index = x * mRetainedHeight + y;
   return mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
 }
 
-template<typename Derived, typename Tile> bool
-TiledLayerBuffer<Derived, Tile>::RemoveTile(const nsIntPoint& aTileOrigin,
-                                            Tile& aRemovedTile)
-{
-  gfx::IntSize scaledTileSize = GetScaledTileSize();
-  int firstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
-  int firstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
-  return RemoveTile(floor_div(aTileOrigin.x, scaledTileSize.width) - firstTileX,
-                    floor_div(aTileOrigin.y, scaledTileSize.height) - firstTileY,
-                    aRemovedTile);
-}
-
-template<typename Derived, typename Tile> bool
-TiledLayerBuffer<Derived, Tile>::RemoveTile(int x, int y, Tile& aRemovedTile)
-{
-  int index = x * mRetainedHeight + y;
-  const Tile& tileToRemove = mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
-  if (!tileToRemove.IsPlaceholderTile()) {
-    aRemovedTile = tileToRemove;
-    mRetainedTiles[index] = AsDerived().GetPlaceholderTile();
-    return true;
-  }
-  return false;
-}
-
 template<typename Derived, typename Tile> void
 TiledLayerBuffer<Derived, Tile>::Dump(std::stringstream& aStream,
                                       const char* aPrefix,
                                       bool aDumpHtml)
 {
   nsIntRect visibleRect = GetValidRegion().GetBounds();
   gfx::IntSize scaledTileSize = GetScaledTileSize();
   for (int32_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) {
@@ -357,16 +324,32 @@ TiledLayerBuffer<Derived, Tile>::Update(
   const nsIntPoint newBufferOrigin(RoundDownToTileEdge(newBound.x, scaledTileSize.width),
                                    RoundDownToTileEdge(newBound.y, scaledTileSize.height));
 
   // This is the reason we break the style guide with newValidRegion instead
   // of aNewValidRegion - so that the names match better and code easier to read
   const nsIntRegion& oldValidRegion = mValidRegion;
   const int oldRetainedHeight = mRetainedHeight;
 
+#ifdef GFX_TILEDLAYER_RETAINING_LOG
+  { // scope ss
+    std::stringstream ss;
+    ss << "TiledLayerBuffer " << this << " starting update"
+       << " on bounds ";
+    AppendToString(ss, newBound);
+    ss << " with mResolution=" << mResolution << "\n";
+    for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+      ss << "mRetainedTiles[" << i << "] = ";
+      mRetainedTiles[i].Dump(ss);
+      ss << "\n";
+    }
+    print_stderr(ss);
+  }
+#endif
+
   // Pass 1: Recycle valid content from the old buffer
   // Recycle tiles from the old buffer that contain valid regions.
   // Insert placeholders tiles if we have no valid area for that tile
   // which we will allocate in pass 2.
   // TODO: Add a tile pool to reduce new allocation
   int tileX = 0;
   int tileY = 0;
   int tilesMissing = 0;
@@ -426,16 +409,31 @@ TiledLayerBuffer<Derived, Tile>::Update(
     x += width;
   }
 
   // Keep track of the number of horizontal/vertical tiles
   // in the buffer so that we can easily look up a tile.
   mRetainedWidth = tileX;
   mRetainedHeight = tileY;
 
+#ifdef GFX_TILEDLAYER_RETAINING_LOG
+  { // scope ss
+    std::stringstream ss;
+    ss << "TiledLayerBuffer " << this << " finished pass 1 of update;"
+       << " tilesMissing=" << tilesMissing << "\n";
+    for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
+      ss << "oldRetainedTiles[" << i << "] = ";
+      oldRetainedTiles[i].Dump(ss);
+      ss << "\n";
+    }
+    print_stderr(ss);
+  }
+#endif
+
+
   // Pass 1.5: Release excess tiles in oldRetainedTiles
   // Tiles in oldRetainedTiles that aren't in newRetainedTiles will be recycled
   // before creating new ones, but there could still be excess unnecessary
   // tiles. As tiles may not have a fixed memory cost (for example, due to
   // double-buffering), we should release these excess tiles first.
   int oldTileCount = 0;
   for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
     Tile oldTile = oldRetainedTiles[i];
@@ -466,16 +464,34 @@ TiledLayerBuffer<Derived, Tile>::Update(
                        << " old valid " << oldValidRegion.ToString().get()
                        << " old painted " << oldAndPainted.ToString().get()
                        << " new valid " << newValidRegion.ToString().get();
   }
 #endif
 
   nsIntRegion regionToPaint(aPaintRegion);
 
+#ifdef GFX_TILEDLAYER_RETAINING_LOG
+  { // scope ss
+    std::stringstream ss;
+    ss << "TiledLayerBuffer " << this << " finished pass 1.5 of update\n";
+    for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
+      ss << "oldRetainedTiles[" << i << "] = ";
+      oldRetainedTiles[i].Dump(ss);
+      ss << "\n";
+    }
+    for (size_t i = 0; i < newRetainedTiles.Length(); i++) {
+      ss << "newRetainedTiles[" << i << "] = ";
+      newRetainedTiles[i].Dump(ss);
+      ss << "\n";
+    }
+    print_stderr(ss);
+  }
+#endif
+
   // Pass 2: Validate
   // We know at this point that any tile in the new buffer that had valid content
   // from the previous buffer is placed correctly in the new buffer.
   // We know that any tile in the old buffer that isn't a place holder is
   // of no use and can be recycled.
   // We also know that any place holder tile in the new buffer must be
   // allocated.
   tileX = 0;
@@ -560,16 +576,35 @@ TiledLayerBuffer<Derived, Tile>::Update(
     x += width;
   }
 
   AsDerived().PostValidate(aPaintRegion);
   for (unsigned int i = 0; i < newRetainedTiles.Length(); ++i) {
     AsDerived().UnlockTile(newRetainedTiles[i]);
   }
 
+#ifdef GFX_TILEDLAYER_RETAINING_LOG
+  { // scope ss
+    std::stringstream ss;
+    ss << "TiledLayerBuffer " << this << " finished pass 2 of update;"
+       << " oldTileCount=" << oldTileCount << "\n";
+    for (size_t i = 0; i < oldRetainedTiles.Length(); i++) {
+      ss << "oldRetainedTiles[" << i << "] = ";
+      oldRetainedTiles[i].Dump(ss);
+      ss << "\n";
+    }
+    for (size_t i = 0; i < newRetainedTiles.Length(); i++) {
+      ss << "newRetainedTiles[" << i << "] = ";
+      newRetainedTiles[i].Dump(ss);
+      ss << "\n";
+    }
+    print_stderr(ss);
+  }
+#endif
+
   // At this point, oldTileCount should be zero
   MOZ_ASSERT(oldTileCount == 0, "Failed to release old tiles");
 
   mRetainedTiles = newRetainedTiles;
   mValidRegion = newValidRegion;
   mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
 }
 
--- a/gfx/layers/client/TextureClientPool.cpp
+++ b/gfx/layers/client/TextureClientPool.cpp
@@ -6,16 +6,19 @@
 #include "TextureClientPool.h"
 #include "CompositableClient.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
 
 #include "gfxPrefs.h"
 
 #include "nsComponentManagerUtils.h"
 
+#define TCP_LOG(...)
+//#define TCP_LOG(...) printf_stderr(__VA_ARGS__);
+
 namespace mozilla {
 namespace layers {
 
 static void
 ShrinkCallback(nsITimer *aTimer, void *aClosure)
 {
   static_cast<TextureClientPool*>(aClosure)->ShrinkToMinimumSize();
 }
@@ -27,16 +30,18 @@ TextureClientPool::TextureClientPool(gfx
                                      ISurfaceAllocator *aAllocator)
   : mFormat(aFormat)
   , mSize(aSize)
   , mMaxTextureClients(aMaxTextureClients)
   , mShrinkTimeoutMsec(aShrinkTimeoutMsec)
   , mOutstandingClients(0)
   , mSurfaceAllocator(aAllocator)
 {
+  TCP_LOG("TexturePool %p created with max size %u and timeout %u\n",
+      this, mMaxTextureClients, aShrinkTimeoutMsec);
   mTimer = do_CreateInstance("@mozilla.org/timer;1");
   if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
     gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
   }
 }
 
 TextureClientPool::~TextureClientPool()
 {
@@ -84,16 +89,18 @@ TextureClientPool::GetTextureClient()
   if (mTextureClients.size()) {
     mOutstandingClients++;
     textureClient = mTextureClients.top();
     mTextureClients.pop();
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
     DebugOnly<bool> ok = TestClientPool("fetch", textureClient, this);
     MOZ_ASSERT(ok);
 #endif
+    TCP_LOG("TexturePool %p giving %p from pool; size %u outstanding %u\n",
+        this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
     return textureClient;
   }
 
   // We're increasing the number of outstanding TextureClients without reusing a
   // client, we may need to free a deferred-return TextureClient.
   ShrinkToMaximumSize();
 
   // No unused clients in the pool, create one
@@ -108,16 +115,18 @@ TextureClientPool::GetTextureClient()
   }
 
   mOutstandingClients++;
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
   if (textureClient) {
     textureClient->mPoolTracker = this;
   }
 #endif
+  TCP_LOG("TexturePool %p giving new %p; size %u outstanding %u\n",
+      this, textureClient.get(), mTextureClients.size(), mOutstandingClients);
   return textureClient;
 }
 
 void
 TextureClientPool::ReturnTextureClient(TextureClient *aClient)
 {
   if (!aClient) {
     return;
@@ -125,105 +134,138 @@ TextureClientPool::ReturnTextureClient(T
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
   DebugOnly<bool> ok = TestClientPool("return", aClient, this);
   MOZ_ASSERT(ok);
 #endif
   // Add the client to the pool:
   MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
   mOutstandingClients--;
   mTextureClients.push(aClient);
+  TCP_LOG("TexturePool %p had client %p returned; size %u outstanding %u\n",
+      this, aClient, mTextureClients.size(), mOutstandingClients);
 
   // Shrink down if we're beyond our maximum size
   ShrinkToMaximumSize();
 
   // Kick off the pool shrinking timer if there are still more unused texture
   // clients than our desired minimum cache size.
   if (mTextureClients.size() > sMinCacheSize) {
+    TCP_LOG("TexturePool %p scheduling a shrink-to-min-size\n", this);
     mTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
                                  nsITimer::TYPE_ONE_SHOT);
   }
 }
 
 void
 TextureClientPool::ReturnTextureClientDeferred(TextureClient *aClient)
 {
   if (!aClient) {
     return;
   }
 #ifdef GFX_DEBUG_TRACK_CLIENTS_IN_POOL
   DebugOnly<bool> ok = TestClientPool("defer", aClient, this);
   MOZ_ASSERT(ok);
 #endif
   mTextureClientsDeferred.push(aClient);
+  TCP_LOG("TexturePool %p had client %p defer-returned, size %u outstanding %u\n",
+      this, aClient, mTextureClientsDeferred.size(), mOutstandingClients);
   ShrinkToMaximumSize();
 }
 
 void
 TextureClientPool::ShrinkToMaximumSize()
 {
   uint32_t totalClientsOutstanding = mTextureClients.size() + mOutstandingClients;
+  TCP_LOG("TexturePool %p shrinking to max size %u; total outstanding %u\n",
+      this, mMaxTextureClients, totalClientsOutstanding);
 
   // We're over our desired maximum size, immediately shrink down to the
   // maximum, or zero if we have too many outstanding texture clients.
   // We cull from the deferred TextureClients first, as we can't reuse those
   // until they get returned.
   while (totalClientsOutstanding > mMaxTextureClients) {
     if (mTextureClientsDeferred.size()) {
       MOZ_ASSERT(mOutstandingClients > 0);
       mOutstandingClients--;
+      TCP_LOG("TexturePool %p dropped deferred client %p; %u remaining\n",
+          this, mTextureClientsDeferred.top().get(),
+          mTextureClientsDeferred.size() - 1);
       mTextureClientsDeferred.pop();
     } else {
       if (!mTextureClients.size()) {
         // Getting here means we're over our desired number of TextureClients
         // with none in the pool. This can happen for pathological cases, or
         // it could mean that mMaxTextureClients needs adjusting for whatever
         // device we're running on.
+        TCP_LOG("TexturePool %p encountering pathological case!\n", this);
         break;
       }
+      TCP_LOG("TexturePool %p dropped non-deferred client %p; %u remaining\n",
+          this, mTextureClients.top().get(), mTextureClients.size() - 1);
       mTextureClients.pop();
     }
     totalClientsOutstanding--;
   }
 }
 
 void
 TextureClientPool::ShrinkToMinimumSize()
 {
+  TCP_LOG("TexturePool %p shrinking to minimum size %u\n", this, sMinCacheSize);
   while (mTextureClients.size() > sMinCacheSize) {
+    TCP_LOG("TexturePool %p popped %p; shrunk to %u\n",
+        this, mTextureClients.top().get(), mTextureClients.size() - 1);
     mTextureClients.pop();
   }
 }
 
 void
 TextureClientPool::ReturnDeferredClients()
 {
+  TCP_LOG("TexturePool %p returning %u deferred clients to pool\n",
+      this, mTextureClientsDeferred.size());
   while (!mTextureClientsDeferred.empty()) {
     mTextureClients.push(mTextureClientsDeferred.top());
     mTextureClientsDeferred.pop();
 
     MOZ_ASSERT(mOutstandingClients > 0);
     mOutstandingClients--;
   }
   ShrinkToMaximumSize();
 
   // Kick off the pool shrinking timer if there are still more unused texture
   // clients than our desired minimum cache size.
   if (mTextureClients.size() > sMinCacheSize) {
+    TCP_LOG("TexturePool %p kicking off shrink-to-min timer\n", this);
     mTimer->InitWithFuncCallback(ShrinkCallback, this, mShrinkTimeoutMsec,
                                  nsITimer::TYPE_ONE_SHOT);
   }
 }
 
 void
+TextureClientPool::ReportClientLost()
+{
+  MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
+  mOutstandingClients--;
+  TCP_LOG("TexturePool %p getting report client lost; down to %u outstanding\n",
+      this, mOutstandingClients);
+}
+
+void
 TextureClientPool::Clear()
 {
+  TCP_LOG("TexturePool %p getting cleared\n", this);
   while (!mTextureClients.empty()) {
+    TCP_LOG("TexturePool %p releasing client %p\n",
+        this, mTextureClients.top().get());
     mTextureClients.pop();
   }
   while (!mTextureClientsDeferred.empty()) {
     MOZ_ASSERT(mOutstandingClients > 0);
     mOutstandingClients--;
+    TCP_LOG("TexturePool %p releasing deferred client %p\n",
+        this, mTextureClientsDeferred.top().get());
     mTextureClientsDeferred.pop();
   }
 }
 
 }
 }
--- a/gfx/layers/client/TextureClientPool.h
+++ b/gfx/layers/client/TextureClientPool.h
@@ -70,20 +70,17 @@ public:
    * ReturnTextureClientDeferred.
    */
   void ReturnDeferredClients();
 
   /**
    * Report that a client retrieved via GetTextureClient() has become
    * unusable, so that it will no longer be tracked.
    */
-  void ReportClientLost() {
-    MOZ_ASSERT(mOutstandingClients > mTextureClientsDeferred.size());
-    mOutstandingClients--;
-  }
+  void ReportClientLost();
 
   /**
    * Calling this will cause the pool to attempt to relinquish any unused
    * clients.
    */
   void Clear();
 
   gfx::SurfaceFormat GetFormat() { return mFormat; }
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -535,16 +535,28 @@ TileClient::operator=(const TileClient& 
   mLastUpdate = o.mLastUpdate;
 #endif
   mManager = o.mManager;
   mInvalidFront = o.mInvalidFront;
   mInvalidBack = o.mInvalidBack;
   return *this;
 }
 
+void
+TileClient::Dump(std::stringstream& aStream)
+{
+  aStream << "TileClient(bb=" << (TextureClient*)mBackBuffer << " fb=" << mFrontBuffer.get();
+  if (mBackBufferOnWhite) {
+    aStream << " bbow=" << mBackBufferOnWhite.get();
+  }
+  if (mFrontBufferOnWhite) {
+    aStream << " fbow=" << mFrontBufferOnWhite.get();
+  }
+  aStream << ")";
+}
 
 void
 TileClient::Flip()
 {
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
   if (mFrontBuffer && mFrontBuffer->GetIPDLActor() &&
       mCompositableClient && mCompositableClient->GetIPDLActor()) {
     // remove old buffer from CompositableHost
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -209,16 +209,21 @@ struct TileClient
     DiscardBackBuffer();
   }
 
   nsExpirationState *GetExpirationState() { return &mExpirationState; }
 
   TileDescriptor GetTileDescriptor();
 
   /**
+   * For debugging.
+   */
+  void Dump(std::stringstream& aStream);
+
+  /**
   * Swaps the front and back buffers.
   */
   void Flip();
 
   void DumpTexture(std::stringstream& aStream) {
     // TODO We should combine the OnWhite/OnBlack here an just output a single image.
     CompositableClient::DumpTextureClient(aStream, mFrontBuffer);
   }
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -101,16 +101,20 @@ public:
   bool IsPlaceholderTile() const { return mTextureHost == nullptr; }
 
   void ReadUnlock() {
     if (mSharedLock) {
       mSharedLock->ReadUnlock();
     }
   }
 
+  void Dump(std::stringstream& aStream) {
+    aStream << "TileHost(...)"; // fill in as needed
+  }
+
   void DumpTexture(std::stringstream& aStream) {
     // TODO We should combine the OnWhite/OnBlack here an just output a single image.
     CompositableHost::DumpTextureHost(aStream, mTextureHost);
   }
 
   RefPtr<gfxSharedReadLock> mSharedLock;
   CompositableTextureHostRef mTextureHost;
   CompositableTextureHostRef mTextureHostOnWhite;
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1226,23 +1226,26 @@ gfxTextRun::CopyGlyphDataFrom(gfxTextRun
             }
         }
         dstGlyphs[i] = g;
     }
 
     // Copy glyph runs
     GlyphRunIterator iter(aSource, aStart, aLength);
 #ifdef DEBUG
-    gfxFont *lastFont = nullptr;
+    GlyphRun *prevRun = nullptr;
 #endif
     while (iter.NextRun()) {
         gfxFont *font = iter.GetGlyphRun()->mFont;
-        NS_ASSERTION(font != lastFont, "Glyphruns not coalesced?");
+        NS_ASSERTION(!prevRun || prevRun->mFont != iter.GetGlyphRun()->mFont ||
+                     prevRun->mMatchType != iter.GetGlyphRun()->mMatchType ||
+                     prevRun->mOrientation != iter.GetGlyphRun()->mOrientation,
+                     "Glyphruns not coalesced?");
 #ifdef DEBUG
-        lastFont = font;
+        prevRun = iter.GetGlyphRun();
         uint32_t end = iter.GetStringEnd();
 #endif
         uint32_t start = iter.GetStringStart();
 
         // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
         // Although it's unusual (and not desirable), it's possible for us to assign
         // different fonts to a base character and a following diacritic.
         // Example on OSX 10.5/10.6 with default fonts installed:
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2435,16 +2435,33 @@ DumpStringRepresentation(JSContext *cx, 
 
     str->dumpRepresentation(stderr, 0);
 
     args.rval().setUndefined();
     return true;
 }
 #endif
 
+static bool
+SetLazyParsingEnabled(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (argc < 1) {
+        JS_ReportError(cx, "setLazyParsingEnabled: need an argument");
+        return false;
+    }
+
+    bool arg = ToBoolean(args.get(0));
+    JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(!arg);
+
+    args.rval().setUndefined();
+    return true;
+}
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment' [, 'shrinking'])",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc.\n"
 "  If 'shrinking' is passed as the optional second argument, perform a\n"
 "  shrinking GC rather than a normal GC."),
@@ -2817,16 +2834,20 @@ gc::ZealModeHelpText),
 "  because the object is a revoked proxy)."),
 
 #ifdef DEBUG
     JS_FN_HELP("dumpStringRepresentation", DumpStringRepresentation, 1, 0,
 "dumpStringRepresentation(str)",
 "  Print a human-readable description of how the string |str| is represented.\n"),
 #endif
 
+    JS_FN_HELP("setLazyParsingEnabled", SetLazyParsingEnabled, 1, 0,
+"setLazyParsingEnabled(bool)",
+"  Enable or disable lazy parsing in the current compartment.  The default is enabled."),
+
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
 
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -150,18 +150,17 @@ CanLazilyParse(ExclusiveContext *cx, con
         !options.hasPollutedGlobalScope &&
         !cx->compartment()->options().discardSource() &&
         !options.sourceIsLazy;
 }
 
 static void
 MarkFunctionsWithinEvalScript(JSScript *script)
 {
-    // Mark top level functions in an eval script as being within an eval and,
-    // if applicable, inside a with statement.
+    // Mark top level functions in an eval script as being within an eval.
 
     if (!script->hasObjects())
         return;
 
     ObjectArray *objects = script->objects();
     size_t start = script->innerObjectsStart();
 
     for (size_t i = start; i < objects->length; i++) {
@@ -287,27 +286,24 @@ frontend::CompileScript(ExclusiveContext
 
     bool savedCallerFun = evalCaller && evalCaller->functionOrCallerFunction();
     Rooted<JSScript*> script(cx, JSScript::Create(cx, evalStaticScope, savedCallerFun,
                                                   options, staticLevel, sourceObject, 0,
                                                   srcBuf.length()));
     if (!script)
         return nullptr;
 
-    // We can specialize a bit for the given scope chain if that scope chain is the global object.
-    JSObject *globalScope =
-        scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : nullptr;
-    MOZ_ASSERT_IF(globalScope, globalScope->isNative());
-    MOZ_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
-
+    bool insideNonGlobalEval =
+        evalStaticScope && evalStaticScope->enclosingScopeForStaticScopeIter();
     BytecodeEmitter::EmitterMode emitterMode =
         options.selfHostingMode ? BytecodeEmitter::SelfHosting : BytecodeEmitter::Normal;
     BytecodeEmitter bce(/* parent = */ nullptr, &parser, &globalsc, script,
                         /* lazyScript = */ js::NullPtr(), options.forEval,
-                        evalCaller, evalStaticScope, !!globalScope, options.lineno, emitterMode);
+                        evalCaller, evalStaticScope, insideNonGlobalEval,
+                        options.lineno, emitterMode);
     if (!bce.init())
         return nullptr;
 
     // Syntax parsing may cause us to restart processing of top level
     // statements in the script. Use Maybe<> so that the parse context can be
     // reset when this occurs.
     Maybe<ParseContext<FullParseHandler> > pc;
 
@@ -511,33 +507,40 @@ frontend::CompileLazyFunction(JSContext 
 
     if (lazy->directlyInsideEval())
         script->setDirectlyInsideEval();
     if (lazy->usesArgumentsApplyAndThis())
         script->setUsesArgumentsApplyAndThis();
     if (lazy->hasBeenCloned())
         script->setHasBeenCloned();
 
+    /*
+     * We just pass false for insideNonGlobalEval and insideEval, because we
+     * don't actually know whether we are or not.  The only consumer of those
+     * booleans is TryConvertFreeName, and it has special machinery to avoid
+     * doing bad things when a lazy function is inside eval.
+     */
+    MOZ_ASSERT(!options.forEval);
     BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->pn_funbox, script, lazy,
-                        options.forEval, /* evalCaller = */ js::NullPtr(),
+                        /* insideEval = */ false, /* evalCaller = */ js::NullPtr(),
                         /* evalStaticScope = */ js::NullPtr(),
-                        /* hasGlobalScope = */ true, options.lineno,
+                        /* insideNonGlobalEval = */ false, options.lineno,
                         BytecodeEmitter::LazyFunction);
     if (!bce.init())
         return false;
 
     return EmitFunctionScript(cx, &bce, pn->pn_body);
 }
 
 // Compile a JS function body, which might appear as the value of an event
 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
 static bool
 CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, const ReadOnlyCompileOptions &options,
                     const AutoNameVector &formals, SourceBufferHolder &srcBuf,
-                    HandleObject enclosingScope, GeneratorKind generatorKind)
+                    HandleObject enclosingStaticScope, GeneratorKind generatorKind)
 {
     js::TraceLoggerThread *logger = js::TraceLoggerForMainThread(cx->runtime());
     js::TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, options);
     js::AutoTraceLog scriptLogger(logger, event);
     js::AutoTraceLog typeLogger(logger, TraceLogger_ParserCompileFunction);
 
     // FIXME: make Function pass in two strings and parse them as arguments and
     // ProgramElements respectively.
@@ -628,37 +631,29 @@ CompileFunctionBody(JSContext *cx, Mutab
         return false;
 
     if (!SetSourceMap(cx, parser.tokenStream, ss))
         return false;
 
     if (fn->pn_funbox->function()->isInterpreted()) {
         MOZ_ASSERT(fun == fn->pn_funbox->function());
 
-        Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingScope, false, options,
+        Rooted<JSScript*> script(cx, JSScript::Create(cx, enclosingStaticScope, false, options,
                                                       /* staticLevel = */ 0, sourceObject,
                                                       /* sourceStart = */ 0, srcBuf.length()));
         if (!script)
             return false;
 
         script->bindings = fn->pn_funbox->bindings;
 
-        /*
-         * The reason for checking fun->environment() below is that certain
-         * consumers of JS::CompileFunction, namely
-         * EventListenerManager::CompileEventHandlerInternal, passes in a
-         * nullptr environment. This compiled function is never used, but
-         * instead is cloned immediately onto the right scope chain.
-         */
         BytecodeEmitter funbce(/* parent = */ nullptr, &parser, fn->pn_funbox, script,
                                /* lazyScript = */ js::NullPtr(), /* insideEval = */ false,
                                /* evalCaller = */ js::NullPtr(),
                                /* evalStaticScope = */ js::NullPtr(),
-                               fun->environment() && fun->environment()->is<GlobalObject>(),
-                               options.lineno);
+                               /* insideNonGlobalEval = */ false, options.lineno);
         if (!funbce.init())
             return false;
 
         if (!EmitFunctionScript(cx, &funbce, fn->pn_body))
             return false;
     } else {
         fun.set(fn->pn_funbox->function());
         MOZ_ASSERT(IsAsmJSModuleNative(fun->native()));
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -106,17 +106,18 @@ struct LoopStmtInfo : public StmtInfoBCE
 
 } // anonymous namespace
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
                                  Parser<FullParseHandler> *parser, SharedContext *sc,
                                  HandleScript script, Handle<LazyScript *> lazyScript,
                                  bool insideEval, HandleScript evalCaller,
                                  Handle<StaticEvalObject *> staticEvalScope,
-                                 bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode)
+                                 bool insideNonGlobalEval, uint32_t lineNum,
+                                 EmitterMode emitterMode)
   : sc(sc),
     cx(sc->context),
     parent(parent),
     script(cx, script),
     lazyScript(cx, lazyScript),
     prolog(cx, lineNum),
     main(cx, lineNum),
     current(&main),
@@ -137,17 +138,17 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
     blockScopeList(cx),
     yieldOffsetList(cx),
     typesetCount(0),
     hasSingletons(false),
     hasTryFinally(false),
     emittingForInit(false),
     emittingRunOnceLambda(false),
     insideEval(insideEval),
-    hasGlobalScope(hasGlobalScope),
+    insideNonGlobalEval(insideNonGlobalEval),
     emitterMode(emitterMode)
 {
     MOZ_ASSERT_IF(evalCaller, insideEval);
     MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript);
     // Function scripts are never eval scripts.
     MOZ_ASSERT_IF(evalStaticScope, !sc->isFunctionBox());
 }
 
@@ -1582,24 +1583,34 @@ TryConvertFreeName(BytecodeEmitter *bce,
 
                     pn->setOp(op);
                     JS_ALWAYS_TRUE(pn->pn_cookie.set(bce->parser->tokenStream, hops, slot));
                     return true;
                 }
                 hops++;
             }
 
+            // If this walk up and check for directlyInsideEval is ever removed,
+            // we'll need to adjust CompileLazyFunction to better communicate
+            // whether we're inside eval to the BytecodeEmitter.  For now, this
+            // walk is why CompileLazyFunction can claim that it's never inside
+            // eval.
             if (script->funHasExtensibleScope() || script->directlyInsideEval())
                 return false;
         }
     }
 
     // Unbound names aren't recognizable global-property references if the
-    // script isn't running against its global object.
-    if (!bce->script->compileAndGo() || !bce->hasGlobalScope)
+    // script is inside a non-global eval call.
+    if (bce->insideNonGlobalEval)
+        return false;
+
+    // Skip trying to use GNAME ops if we know our script has a polluted
+    // global scope, since they'll just get treated as NAME ops anyway.
+    if (bce->script->hasPollutedGlobalScope())
         return false;
 
     // Deoptimized names also aren't necessarily globals.
     if (pn->isDeoptimized())
         return false;
 
     if (bce->sc->isFunctionBox()) {
         // Unbound names in function code may not be globals if new locals can
@@ -1618,16 +1629,20 @@ TryConvertFreeName(BytecodeEmitter *bce,
     //        'var x; ' +
     //        'eval("print(x)");'); // "undefined", not "GLOBAL"
     //
     // Given the enclosing eval code's strictness and its bindings (neither is
     // readily available now), we could exactly check global-ness, but it's not
     // worth the trouble for doubly-nested eval code.  So we conservatively
     // approximate.  If the outer eval code is strict, then this eval code will
     // be: thus, don't optimize if we're compiling strict code inside an eval.
+    //
+    // Though actually, we don't even need an inner eval.  We could just as well
+    // have a lambda inside that outer strict mode eval and it would run into
+    // the same issue.
     if (bce->insideEval && bce->sc->strict())
         return false;
 
     JSOp op;
     switch (pn->getOp()) {
       case JSOP_GETNAME:  op = JSOP_GETGNAME; break;
       case JSOP_SETNAME:  op = StrictifySetNameOp(JSOP_SETGNAME, bce); break;
       case JSOP_SETCONST:
@@ -2289,17 +2304,17 @@ BytecodeEmitter::emitNameOp(ParseNode *p
         } else {
             if (!emitAtomOp(pn, op))
                 return false;
         }
     }
 
     /* Need to provide |this| value for call */
     if (callContext) {
-        if (op == JSOP_GETNAME) {
+        if (op == JSOP_GETNAME || op == JSOP_GETGNAME) {
             JSOp thisOp = needsImplicitThis() ? JSOP_IMPLICITTHIS : JSOP_GIMPLICITTHIS;
             if (!emitAtomOp(pn, thisOp))
                 return false;
         } else {
             if (!emit1(JSOP_UNDEFINED))
                 return false;
         }
     }
@@ -5305,17 +5320,17 @@ EmitFunc(ExclusiveContext *cx, BytecodeE
                 return false;
 
             script->bindings = funbox->bindings;
 
             uint32_t lineNum = bce->parser->tokenStream.srcCoords.lineNum(pn->pn_pos.begin);
             BytecodeEmitter bce2(bce, bce->parser, funbox, script, /* lazyScript = */ js::NullPtr(),
                                  bce->insideEval, bce->evalCaller,
                                  /* evalStaticScope = */ js::NullPtr(),
-                                 bce->hasGlobalScope, lineNum, bce->emitterMode);
+                                 bce->insideNonGlobalEval, lineNum, bce->emitterMode);
             if (!bce2.init())
                 return false;
 
             /* We measured the max scope depth when we parsed the function. */
             if (!EmitFunctionScript(cx, &bce2, pn->pn_body))
                 return false;
 
             if (funbox->usesArguments && funbox->usesApply && funbox->usesThis)
@@ -6778,17 +6793,17 @@ BytecodeEmitter::emitLexicalInitializati
     if (!bindNameToSlot(pn))
         return false;
 
     jsatomid atomIndex;
     if (!maybeEmitVarDecl(globalDefOp, pn, &atomIndex))
         return false;
 
     if (pn->getOp() != JSOP_INITLEXICAL) {
-        bool global = js_CodeSpec[pn->getOp()].format & JOF_GNAME;
+        bool global = IsGlobalOp(pn->getOp());
         if (!emitIndex32(global ? JSOP_BINDGNAME : JSOP_BINDNAME, atomIndex))
             return false;
         if (!emit1(JSOP_SWAP))
             return false;
     }
 
     if (!pn->pn_cookie.isFree()) {
         if (!emitVarOp(pn, pn->getOp()))
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -185,18 +185,18 @@ struct BytecodeEmitter
     bool            emittingRunOnceLambda:1; /* true while emitting a lambda which is only
                                                 expected to run once. */
 
     bool isRunOnceLambda();
 
     bool            insideEval:1;       /* True if compiling an eval-expression or a function
                                            nested inside an eval. */
 
-    const bool      hasGlobalScope:1;   /* frontend::CompileScript's scope chain is the
-                                           global object */
+    const bool      insideNonGlobalEval:1;  /* True if this is a direct eval
+                                               call in some non-global scope. */
 
     enum EmitterMode {
         Normal,
 
         /*
          * Emit JSOP_GETINTRINSIC instead of JSOP_GETNAME and assert that
          * JSOP_GETNAME and JSOP_*GNAME don't ever get emitted. See the comment
          * for the field |selfHostingMode| in Parser.h for details.
@@ -216,17 +216,17 @@ struct BytecodeEmitter
      * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
      * space above their tempMark points. This means that you cannot alloc from
      * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
      * destruction.
      */
     BytecodeEmitter(BytecodeEmitter *parent, Parser<FullParseHandler> *parser, SharedContext *sc,
                     HandleScript script, Handle<LazyScript *> lazyScript,
                     bool insideEval, HandleScript evalCaller,
-                    Handle<StaticEvalObject *> evalStaticScope, bool hasGlobalScope,
+                    Handle<StaticEvalObject *> evalStaticScope, bool insideNonGlobalEval,
                     uint32_t lineNum, EmitterMode emitterMode = Normal);
     bool init();
     bool updateLocalsToFrameSlots();
 
     bool isAliasedName(ParseNode *pn);
 
     MOZ_ALWAYS_INLINE
     bool makeAtomIndex(JSAtom *atom, jsatomid *indexp) {
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -98,16 +98,18 @@ InitMemorySubsystem()
     if (pageSize == 0) {
         SYSTEM_INFO sysinfo;
         GetSystemInfo(&sysinfo);
         pageSize = sysinfo.dwPageSize;
         allocGranularity = sysinfo.dwAllocationGranularity;
     }
 }
 
+#  if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
+
 static inline void *
 MapMemoryAt(void *desired, size_t length, int flags, int prot = PAGE_READWRITE)
 {
     return VirtualAlloc(desired, length, flags, prot);
 }
 
 static inline void *
 MapMemory(size_t length, int flags, int prot = PAGE_READWRITE)
@@ -274,16 +276,67 @@ size_t
 GetPageFaultCount()
 {
     PROCESS_MEMORY_COUNTERS pmc;
     if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
         return 0;
     return pmc.PageFaultCount;
 }
 
+#  else // Various APIs are unavailable.
+
+void *
+MapAlignedPages(size_t size, size_t alignment)
+{
+    MOZ_ASSERT(size >= alignment);
+    MOZ_ASSERT(size % alignment == 0);
+    MOZ_ASSERT(size % pageSize == 0);
+    MOZ_ASSERT(alignment % allocGranularity == 0);
+
+    void *p = _aligned_malloc(size, alignment);
+
+    MOZ_ASSERT(OffsetFromAligned(p, alignment) == 0);
+    return p;
+}
+
+static void *
+MapAlignedPagesLastDitch(size_t size, size_t alignment)
+{
+    return nullptr;
+}
+
+void
+UnmapPages(void *p, size_t size)
+{
+    _aligned_free(p);
+}
+
+bool
+MarkPagesUnused(void *p, size_t size)
+{
+    MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
+    return true;
+}
+
+bool
+MarkPagesInUse(void *p, size_t size)
+{
+    MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
+    return true;
+}
+
+size_t
+GetPageFaultCount()
+{
+    // GetProcessMemoryInfo is unavailable.
+    return 0;
+}
+
+#  endif
+
 void *
 AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment)
 {
     // TODO: Bug 988813 - Support memory mapped array buffer for Windows platform.
     return nullptr;
 }
 
 // Deallocate mapped memory for object.
@@ -318,16 +371,22 @@ MapAlignedPages(size_t size, size_t alig
     int flags = MAP_PRIVATE | MAP_ANON | MAP_ALIGN | MAP_NOSYNC;
 
     void *p = mmap((caddr_t)alignment, size, prot, flags, -1, 0);
     if (p == MAP_FAILED)
         return nullptr;
     return p;
 }
 
+static void *
+MapAlignedPagesLastDitch(size_t size, size_t alignment)
+{
+    return nullptr;
+}
+
 void
 UnmapPages(void *p, size_t size)
 {
     MOZ_ALWAYS_TRUE(0 == munmap((caddr_t)p, size));
 }
 
 bool
 MarkPagesUnused(void *p, size_t size)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1106982-2.js
@@ -0,0 +1,20 @@
+var x = "wrong";
+var t = {x: "x"};
+var hits = 0;
+var p = new Proxy(t, {
+    has(t, id) {
+        var found = id in t;
+        if (++hits == 2)
+            delete t[id];
+        return found;
+    },
+    get(t, id) { return t[id]; }
+});
+evaluate(`function testFunc() {
+    x += " x";
+}`, { compileAndGo: false });
+
+var cloneFunc = clone(testFunc, p);
+cloneFunc();
+assertEq(hits, 2);
+assertEq(t.x, "undefined x");
--- a/js/src/jit-test/tests/basic/bug1106982.js
+++ b/js/src/jit-test/tests/basic/bug1106982.js
@@ -7,10 +7,13 @@ var p = new Proxy(t, {
         if (++hits == 2)
             delete t[id];
         return found;
     },
     get(t, id) { return t[id]; }
 });
 with (p)
     x += " x";
+// If you change this testcase (e.g. because we fix the number of
+// has() calls we end up making to match spec better), don't forget to
+// update bug1106982-2.js too.  See also bug 1145641.
 assertEq(hits, 2);
 assertEq(t.x, "undefined x");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/eval-scopes.js
@@ -0,0 +1,135 @@
+function bytecode(f) {
+    if (typeof disassemble !== "function")
+        return "unavailable";
+    var d = disassemble(f);
+    return d.slice(d.indexOf("main:"), d.indexOf("\n\n"));
+}
+
+function hasGname(f, v, hasIt = true) {
+    // Do a try-catch that prints the full stack, so we can tell
+    // _which_ part of this test failed.
+    try {
+	var b = bytecode(f);
+	if (b != "unavailable") {
+	    assertEq(b.contains(`getgname "${v}"`), hasIt);
+	    assertEq(b.contains(`getname "${v}"`), !hasIt);
+	}
+    } catch (e) {
+	print(e.stack);
+	throw e;
+    }
+}
+
+var x = "outer";
+
+setLazyParsingEnabled(false);
+{
+    let x = "inner";
+    eval("function g() { assertEq(x, 'inner');} g()");
+    eval("function g2() { (function nest() { assertEq(x, 'inner'); })(); } g2()");
+}
+eval(`
+     function g3() {
+	 assertEq(x, 'outer');
+     }
+     g3();
+     hasGname(g3, 'x');
+     `);
+eval(`
+     function g4() {
+	 function nest() { assertEq(x, 'outer'); }
+	 nest();
+	 return nest;
+     }
+     hasGname(g4(), 'x');
+     `);
+setLazyParsingEnabled(true);
+
+{
+    let x = "inner";
+    eval("function h() { assertEq(x, 'inner');} h()");
+    eval("function h2() { (function nest() { assertEq(x, 'inner'); })(); } h2()");
+}
+// It sure would be nice if we could run the h3/h4 tests below, but it turns out
+// that lazy functions and eval don't play together all that well.  See bug
+// 1146080.  For now, assert we have no gname, so people will notice if they
+// accidentally fix it and adjust this test accordingly.
+eval(`
+     function h3() {
+	 assertEq(x, 'outer');
+     }
+     h3();
+     hasGname(h3, 'x', false);
+     `);
+eval(`
+     function h4() {
+	 function nest() { assertEq(x, 'outer'); }
+	 nest();
+	 return nest;
+     }
+     hasGname(h4(), 'x', false);
+     `);
+
+setLazyParsingEnabled(false);
+with ({}) {
+    let x = "inner";
+    eval("function i() { assertEq(x, 'inner');} i()");
+    eval("function i2() { (function nest() { assertEq(x, 'inner'); })(); } i2()");
+}
+setLazyParsingEnabled(true);
+
+with ({}) {
+    let x = "inner";
+    eval("function j() { assertEq(x, 'inner');} j()");
+    eval("function j2() { (function nest() { assertEq(x, 'inner'); })(); } j2()");
+}
+
+setLazyParsingEnabled(false);
+(function () {
+    var x = "inner";
+    eval("function k() { assertEq(x, 'inner');} k()");
+    eval("function k2() { (function nest() { assertEq(x, 'inner'); })(); } k2()");
+})();
+setLazyParsingEnabled(true);
+
+(function () {
+    let x = "inner";
+    eval("function l() { assertEq(x, 'inner');} l()");
+    eval("function l2() { (function nest() { assertEq(x, 'inner'); })(); } l2()");
+})();
+
+var y1 = 5;
+eval(`
+     'use strict';
+     var y1 = 6;
+     assertEq(y1, 6);
+     (function() { assertEq(y1, 6); })()
+     `);
+assertEq(y1, 5);
+
+eval(`
+     'use strict';
+     var y2 = 6;
+     assertEq(y2, 6);
+     (function() { assertEq(y2, 6); })()
+     `);
+
+setLazyParsingEnabled(false);
+
+var y3 = 5;
+eval(`
+     'use strict';
+     var y3 = 6;
+     assertEq(y3, 6);
+     (function() { assertEq(y3, 6); })()
+     `);
+assertEq(y3, 5);
+
+eval(`
+     'use strict';
+     var y4 = 6;
+     assertEq(y4, 6);
+     (function() { assertEq(y4, 6); })()
+     `);
+
+setLazyParsingEnabled(true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/function-gname.js
@@ -0,0 +1,49 @@
+function bytecode(f) {
+    if (typeof disassemble !== "function")
+        return "unavailable";
+    var d = disassemble(f);
+    return d.slice(d.indexOf("main:"), d.indexOf("\n\n"));
+}
+
+function hasGname(f, v) {
+    // Do a try-catch that prints the full stack, so we can tell
+    // _which_ part of this test failed.
+    try {
+	var b = bytecode(f);
+	if (b != "unavailable") {
+	    assertEq(b.contains(`getgname "${v}"`), true);
+	    assertEq(b.contains(`getname "${v}"`), false);
+	}
+    } catch (e) {
+	print(e.stack);
+	throw e;
+    }
+}
+
+var x = "outer";
+
+var f1 = new Function("assertEq(x, 'outer')");
+f1();
+hasGname(f1, 'x');
+
+setLazyParsingEnabled(false);
+var f2 = new Function("assertEq(x, 'outer')");
+f2();
+hasGname(f2, 'x');
+setLazyParsingEnabled(true);
+
+{
+    let x = "inner";
+    var f3 = new Function("assertEq(x, 'outer')");
+    f3();
+    hasGname(f3, 'x');
+}
+
+setLazyParsingEnabled(false);
+{
+    let x = "inner";
+    var f4 = new Function("assertEq(x, 'outer')");
+    f4();
+    hasGname(f4, 'x');
+}
+setLazyParsingEnabled(true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/weird-scopechains.js
@@ -0,0 +1,36 @@
+function checkNameLookup() {
+    return "global";
+}
+
+function assertWithMessage(got, expected, message) {
+    assertEq(message + ": " + got, message + ": " + expected);
+}
+
+// Create our test func via "evaluate" so it won't be compileAndGo and
+// we can clone it.
+evaluate(`function testFunc() {
+    assertWithMessage(checkNameLookup(), "local", "nameLookup");
+    assertWithMessage(checkThisBinding(), "local", "thisBinding");
+
+    // Important: lambda needs to close over "reason", so it won't just get the
+    // scope of testFunc as its scope.  Instead it'll get the Call object
+    // "reason" lives in.
+    var reason = " in lambda in Call";
+    (function() {
+	assertWithMessage(checkNameLookup(), "local", "nameLookup" + reason);
+	assertWithMessage(checkThisBinding(), "local", "thisBinding" + reason);
+    })();
+}`, { compileAndGo: false });
+
+var obj = {
+    checkNameLookup: function() {
+	return "local";
+    },
+
+    checkThisBinding: function() {
+	return this.checkNameLookup();
+    },
+};
+
+var cloneFunc = clone(testFunc, obj);
+cloneFunc();
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -700,23 +700,23 @@ InitFromBailout(JSContext *cx, HandleScr
                 // not resume into baseline code, but instead into
                 // HandleExceptionBaseline, so *do* set the scope chain here.
                 if (iter.pcOffset() != 0 || iter.resumeAfter() ||
                     (excInfo && excInfo->propagatingIonExceptionForDebugMode()))
                 {
                     scopeChain = fun->environment();
                 }
             } else {
-                // For global, compile-and-go scripts the scope chain is the
-                // script's global (Ion does not compile non-compile-and-go
-                // scripts). Also note that it's invalid to resume into the
-                // prologue in this case because the prologue expects the scope
-                // chain in R1 for eval and global scripts.
+                // For global scripts without a polluted global scope the scope
+                // chain is the script's global (Ion does not compile scripts
+                // with a polluted global scope). Also note that it's invalid to
+                // resume into the prologue in this case because the prologue
+                // expects the scope chain in R1 for eval and global scripts.
                 MOZ_ASSERT(!script->isForEval());
-                MOZ_ASSERT(script->compileAndGo());
+                MOZ_ASSERT(!script->hasPollutedGlobalScope());
                 scopeChain = &(script->global());
             }
         }
 
         // Make sure to add HAS_RVAL to flags here because setFlags() below
         // will clobber it.
         returnValue = iter.read();
         flags |= BaselineFrame::HAS_RVAL;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2051,16 +2051,19 @@ BaselineCompiler::emit_JSOP_IN()
 
     frame.push(R0);
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_GETGNAME()
 {
+    if (script->hasPollutedGlobalScope())
+        return emit_JSOP_GETNAME();
+
     RootedPropertyName name(cx, script->getName(pc));
 
     if (name == cx->names().undefined) {
         frame.push(UndefinedValue());
         return true;
     }
     if (name == cx->names().NaN) {
         frame.push(cx->runtime()->NaNValue);
@@ -2083,18 +2086,22 @@ BaselineCompiler::emit_JSOP_GETGNAME()
     // Mark R0 as pushed stack value.
     frame.push(R0);
     return true;
 }
 
 bool
 BaselineCompiler::emit_JSOP_BINDGNAME()
 {
-    frame.push(ObjectValue(script->global()));
-    return true;
+    if (!script->hasPollutedGlobalScope()) {
+        frame.push(ObjectValue(script->global()));
+        return true;
+    }
+
+    return emit_JSOP_BINDNAME();
 }
 
 bool
 BaselineCompiler::emit_JSOP_SETPROP()
 {
     // Keep lhs in R0, rhs in R1.
     frame.popRegsAndSync(2);
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -6056,16 +6056,18 @@ DoGetNameFallback(JSContext *cx, Baselin
     jsbytecode *pc = stub->icEntry()->pc(script);
     mozilla::DebugOnly<JSOp> op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetName(%s)", js_CodeName[JSOp(*pc)]);
 
     MOZ_ASSERT(op == JSOP_GETNAME || op == JSOP_GETGNAME);
 
     RootedPropertyName name(cx, script->getName(pc));
 
+    static_assert(JSOP_GETGNAME_LENGTH == JSOP_GETNAME_LENGTH,
+                  "Otherwise our check for JSOP_TYPEOF isn't ok");
     if (JSOp(pc[JSOP_GETGNAME_LENGTH]) == JSOP_TYPEOF) {
         if (!GetScopeNameForTypeOf(cx, scopeChain, name, res))
             return false;
     } else {
         if (!GetScopeName(cx, scopeChain, name, res))
             return false;
     }
 
@@ -6082,17 +6084,17 @@ DoGetNameFallback(JSContext *cx, Baselin
     // Attach new stub.
     if (stub->numOptimizedStubs() >= ICGetName_Fallback::MAX_OPTIMIZED_STUBS) {
         // TODO: Discard all stubs in this IC and replace with generic stub.
         return true;
     }
 
     bool attached = false;
     bool isTemporarilyUnoptimizable = false;
-    if (js_CodeSpec[*pc].format & JOF_GNAME) {
+    if (IsGlobalOp(JSOp(*pc)) && !script->hasPollutedGlobalScope()) {
         Handle<GlobalObject*> global = scopeChain.as<GlobalObject>();
         if (!TryAttachGlobalNameStub(cx, script, pc, stub, global, name, &attached,
                                      &isTemporarilyUnoptimizable))
         {
             return false;
         }
     } else {
         if (!TryAttachScopeNameStub(cx, script, stub, scopeChain, name, &attached))
@@ -6197,17 +6199,17 @@ ICGetName_Scope<NumHops>::Compiler::gene
 static bool
 DoBindNameFallback(JSContext *cx, BaselineFrame *frame, ICBindName_Fallback *stub,
                    HandleObject scopeChain, MutableHandleValue res)
 {
     jsbytecode *pc = stub->icEntry()->pc(frame->script());
     mozilla::DebugOnly<JSOp> op = JSOp(*pc);
     FallbackICSpew(cx, stub, "BindName(%s)", js_CodeName[JSOp(*pc)]);
 
-    MOZ_ASSERT(op == JSOP_BINDNAME);
+    MOZ_ASSERT(op == JSOP_BINDNAME || op == JSOP_BINDGNAME);
 
     RootedPropertyName name(cx, frame->script()->getName(pc));
 
     RootedObject scope(cx);
     if (!LookupNameUnqualified(cx, name, scopeChain, &scope))
         return false;
 
     res.setObject(*scope);
--- a/js/src/jit/BytecodeAnalysis.cpp
+++ b/js/src/jit/BytecodeAnalysis.cpp
@@ -146,28 +146,36 @@ BytecodeAnalysis::init(TempAllocator &al
                 if (catchFinallyRanges[i].contains(offset))
                     infos_[offset].loopEntryInCatchOrFinally = true;
             }
             break;
 
           case JSOP_GETNAME:
           case JSOP_BINDNAME:
           case JSOP_SETNAME:
+          case JSOP_STRICTSETNAME:
           case JSOP_DELNAME:
           case JSOP_GETALIASEDVAR:
           case JSOP_SETALIASEDVAR:
           case JSOP_LAMBDA:
           case JSOP_LAMBDA_ARROW:
           case JSOP_DEFFUN:
           case JSOP_DEFVAR:
           case JSOP_DEFCONST:
           case JSOP_SETCONST:
             usesScopeChain_ = true;
             break;
 
+          case JSOP_GETGNAME:
+          case JSOP_SETGNAME:
+          case JSOP_STRICTSETGNAME:
+            if (script_->hasPollutedGlobalScope())
+                usesScopeChain_ = true;
+            break;
+
           case JSOP_FINALLY:
             hasTryFinally_ = true;
             break;
 
           case JSOP_SETARG:
             hasSetArg_ = true;
             break;
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3485,22 +3485,22 @@ CodeGenerator::visitCallDirectEvalV(LCal
     pushArg(ToValue(lir, LCallDirectEvalV::Argument));
     pushArg(ToValue(lir, LCallDirectEvalV::ThisValue));
     pushArg(ImmGCPtr(gen->info().script()));
     pushArg(scopeChain);
 
     callVM(DirectEvalValueInfo, lir);
 }
 
-// Registers safe for use before generatePrologue().
-static const uint32_t EntryTempMask = Registers::TempMask & ~(1 << OsrFrameReg.code());
-
 void
 CodeGenerator::generateArgumentsChecks(bool bailout)
 {
+    // Registers safe for use before generatePrologue().
+    static const uint32_t EntryTempMask = Registers::TempMask & ~(1 << OsrFrameReg.code());
+
     // This function can be used the normal way to check the argument types,
     // before entering the function and bailout when arguments don't match.
     // For debug purpose, this is can also be used to force/check that the
     // arguments are correct. Upon fail it will hit a breakpoint.
 
     MIRGraph &mir = gen->graph();
     MResumePoint *rp = mir.entryResumePoint();
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2030,21 +2030,22 @@ CheckScript(JSContext *cx, JSScript *scr
         return false;
     }
 
     if (script->isGenerator()) {
         TrackAndSpewIonAbort(cx, script, "generator script");
         return false;
     }
 
-    if (!script->compileAndGo() && !script->functionNonDelazifying()) {
-        // Support non-CNG functions but not other scripts. For global scripts,
-        // IonBuilder currently uses the global object as scope chain, this is
-        // not valid for non-CNG code.
-        TrackAndSpewIonAbort(cx, script, "not compile-and-go");
+    if (script->hasPollutedGlobalScope() && !script->functionNonDelazifying()) {
+        // Support functions with a polluted global scope but not other
+        // scripts. For global scripts, IonBuilder currently uses the global
+        // object as scope chain, this is not valid when the script has a
+        // polluted global scope.
+        TrackAndSpewIonAbort(cx, script, "has polluted global scope");
         return false;
     }
 
     return true;
 }
 
 static MethodStatus
 CheckScriptSize(JSContext *cx, JSScript* script)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1195,18 +1195,20 @@ IonBuilder::initScopeChain(MDefinition *
                     return false;
             }
 
             scope = createCallObject(callee, scope);
             if (!scope)
                 return false;
         }
     } else {
-        // For CNG global scripts, the scope chain is the global object.
-        MOZ_ASSERT(script()->compileAndGo());
+        // For global scripts without a polluted global scope, the scope chain
+        // is the global object.
+        MOZ_ASSERT(!script()->isForEval());
+        MOZ_ASSERT(!script()->hasPollutedGlobalScope());
         scope = constant(ObjectValue(script()->global()));
     }
 
     current->setScopeChain(scope);
     return true;
 }
 
 bool
@@ -1811,42 +1813,47 @@ IonBuilder::inspectOpcode(JSOp op)
         return pushConstant(Int32Value(GET_INT8(pc)));
 
       case JSOP_UINT16:
         return pushConstant(Int32Value(GET_UINT16(pc)));
 
       case JSOP_GETGNAME:
       {
         PropertyName *name = info().getAtom(pc)->asPropertyName();
-        return jsop_getgname(name);
+        if (!script()->hasPollutedGlobalScope())
+            return jsop_getgname(name);
+        return jsop_getname(name);
       }
 
-      case JSOP_BINDGNAME:
-        return pushConstant(ObjectValue(script()->global()));
-
       case JSOP_SETGNAME:
       case JSOP_STRICTSETGNAME:
       {
         PropertyName *name = info().getAtom(pc)->asPropertyName();
+        if (script()->hasPollutedGlobalScope())
+            return jsop_setprop(name);
         JSObject *obj = &script()->global();
         return setStaticName(obj, name);
       }
 
       case JSOP_GETNAME:
       {
         PropertyName *name = info().getAtom(pc)->asPropertyName();
         return jsop_getname(name);
       }
 
       case JSOP_GETINTRINSIC:
       {
         PropertyName *name = info().getAtom(pc)->asPropertyName();
         return jsop_intrinsic(name);
       }
 
+      case JSOP_BINDGNAME:
+        if (!script()->hasPollutedGlobalScope())
+            return pushConstant(ObjectValue(script()->global()));
+        // Fall through to JSOP_BINDNAME
       case JSOP_BINDNAME:
         return jsop_bindname(info().getName(pc));
 
       case JSOP_DUP:
         current->pushSlot(current->stackDepth() - 1);
         return true;
 
       case JSOP_DUP2:
@@ -7563,17 +7570,17 @@ IonBuilder::jsop_getgname(PropertyName *
 
     return jsop_getname(name);
 }
 
 bool
 IonBuilder::jsop_getname(PropertyName *name)
 {
     MDefinition *object;
-    if (js_CodeSpec[*pc].format & JOF_GNAME) {
+    if (IsGlobalOp(JSOp(*pc)) && !script()->hasPollutedGlobalScope()) {
         MInstruction *global = constant(ObjectValue(script()->global()));
         object = global;
     } else {
         current->push(current->scopeChain());
         object = current->pop();
     }
 
     MGetNameCache *ins;
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -2491,19 +2491,20 @@ InlineFrameIterator::computeScopeChain(V
     }
 
     // Note we can hit this case even for heavyweight functions, in case we
     // are walking the frame during the function prologue, before the scope
     // chain has been initialized.
     if (isFunctionFrame())
         return callee(fallback)->environment();
 
-    // Ion does not handle scripts that are not compile-and-go.
+    // Ion does not handle non-function scripts that have anything other than
+    // the global on their scope chain.
     MOZ_ASSERT(!script()->isForEval());
-    MOZ_ASSERT(script()->compileAndGo());
+    MOZ_ASSERT(!script()->hasPollutedGlobalScope());
     return &script()->global();
 }
 
 bool
 InlineFrameIterator::isFunctionFrame() const
 {
     return !!calleeTemplate_;
 }
--- a/js/src/jit/none/Architecture-none.h
+++ b/js/src/jit/none/Architecture-none.h
@@ -106,16 +106,17 @@ struct FloatRegister
     bool isDouble() const { MOZ_CRASH(); }
     bool isInt32x4() const { MOZ_CRASH(); }
     bool isFloat32x4() const { MOZ_CRASH(); }
     FloatRegister asSingle() const { MOZ_CRASH(); }
     FloatRegister asDouble() const { MOZ_CRASH(); }
     FloatRegister asInt32x4() const { MOZ_CRASH(); }
     FloatRegister asFloat32x4() const { MOZ_CRASH(); }
     Code code() const { MOZ_CRASH(); }
+    Encoding encoding() const { MOZ_CRASH(); }
     const char *name() const { MOZ_CRASH(); }
     bool volatile_() const { MOZ_CRASH(); }
     bool operator != (FloatRegister) const { MOZ_CRASH(); }
     bool operator == (FloatRegister) const { MOZ_CRASH(); }
     bool aliases(FloatRegister) const { MOZ_CRASH(); }
     uint32_t numAliased() const { MOZ_CRASH(); }
     void aliased(uint32_t, FloatRegister *) { MOZ_CRASH(); }
     bool equiv(FloatRegister) const { MOZ_CRASH(); }
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -127,16 +127,22 @@ class Assembler : public AssemblerShared
     static void PatchInstructionImmediate(uint8_t *, PatchedImmPtr) { MOZ_CRASH(); }
     static int32_t ExtractCodeLabelOffset(uint8_t *) { MOZ_CRASH(); }
 
     static void ToggleToJmp(CodeLocationLabel) { MOZ_CRASH(); }
     static void ToggleToCmp(CodeLocationLabel) { MOZ_CRASH(); }
     static void ToggleCall(CodeLocationLabel, bool) { MOZ_CRASH(); }
 
     static uintptr_t GetPointer(uint8_t *) { MOZ_CRASH(); }
+
+    void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end,
+                                     const Disassembler::HeapAccess &heapAccess)
+    {
+        MOZ_CRASH();
+    }
 };
 
 class Operand
 {
   public:
     Operand (const Address &) { MOZ_CRASH();}
 
 };
--- a/js/src/jsapi-tests/testGCAllocator.cpp
+++ b/js/src/jsapi-tests/testGCAllocator.cpp
@@ -25,24 +25,29 @@
 #include <sys/types.h>
 #include <unistd.h>
 #else
 #error "Memory mapping functions are not defined for your OS."
 #endif
 
 BEGIN_TEST(testGCAllocator)
 {
+    size_t PageSize = 0;
 #if defined(XP_WIN)
+#  if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
     SYSTEM_INFO sysinfo;
     GetSystemInfo(&sysinfo);
-    const size_t PageSize = sysinfo.dwPageSize;
+    PageSize = sysinfo.dwPageSize;
+#  else // Various APIs are unavailable. This test is disabled.
+    return true;
+#  endif
 #elif defined(SOLARIS)
     return true;
 #elif defined(XP_UNIX)
-    const size_t PageSize = size_t(sysconf(_SC_PAGESIZE));
+    PageSize = size_t(sysconf(_SC_PAGESIZE));
 #else
     return true;
 #endif
 
     /* Finish any ongoing background free activity. */
     js::gc::AutoFinishGC finishGC(rt);
 
     bool growUp;
@@ -264,16 +269,17 @@ positionIsCorrect(const char *str, void 
     if (result != desired) {
         while (--tempChunks >= 0)
             js::gc::UnmapPages(chunkPool[tempChunks], 2 * Chunk);
     }
     return result == desired;
 }
 
 #if defined(XP_WIN)
+#  if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
 
 void *
 mapMemoryAt(void *desired, size_t length)
 {
     return VirtualAlloc(desired, length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
 }
 
 void *
@@ -283,18 +289,29 @@ mapMemory(size_t length)
 }
 
 void
 unmapPages(void *p, size_t size)
 {
     MOZ_ALWAYS_TRUE(VirtualFree(p, 0, MEM_RELEASE));
 }
 
-#elif defined(SOLARIS)
-// This test doesn't apply to Solaris.
+#  else // Various APIs are unavailable. This test is disabled.
+
+void *mapMemoryAt(void *desired, size_t length) { return nullptr; }
+void *mapMemory(size_t length) { return nullptr; }
+void unmapPages(void *p, size_t size) { }
+
+#  endif
+#elif defined(SOLARIS) // This test doesn't apply to Solaris.
+
+void *mapMemoryAt(void *desired, size_t length) { return nullptr; }
+void *mapMemory(size_t length) { return nullptr; }
+void unmapPages(void *p, size_t size) { }
+
 #elif defined(XP_UNIX)
 
 void *
 mapMemoryAt(void *desired, size_t length)
 {
 #if defined(__ia64__) || (defined(__sparc64__) && defined(__NetBSD__))
     MOZ_RELEASE_ASSERT(0xffff800000000000ULL & (uintptr_t(desired) + length - 1) == 0);
 #endif
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -638,17 +638,18 @@ js::XDRInterpretedFunction(XDRState<mode
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, MutableHandleFunction);
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_DECODE> *, HandleObject, HandleScript, MutableHandleFunction);
 
 JSObject *
-js::CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun)
+js::CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun,
+                           PollutedGlobalScopeOption polluted)
 {
     /* NB: Keep this in sync with XDRInterpretedFunction. */
     RootedObject cloneProto(cx);
     if (srcFun->isStarGenerator()) {
         cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
         if (!cloneProto)
             return nullptr;
     }
@@ -660,17 +661,17 @@ js::CloneFunctionAndScript(JSContext *cx
                                                   JSFunction::INTERPRETED, NullPtr(), NullPtr(),
                                                   cloneProto, allocKind, TenuredObject));
     if (!clone)
         return nullptr;
 
     RootedScript srcScript(cx, srcFun->getOrCreateScript(cx));
     if (!srcScript)
         return nullptr;
-    RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, clone, srcScript));
+    RootedScript clonedScript(cx, CloneScript(cx, enclosingScope, clone, srcScript, polluted));
     if (!clonedScript)
         return nullptr;
 
     clone->setArgCount(srcFun->nargs());
     clone->setFlags(srcFun->flags());
     clone->initAtom(srcFun->displayAtom());
     clone->initScript(clonedScript);
     clonedScript->setFunction(clone);
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -649,17 +649,18 @@ namespace js {
 JSString *FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen);
 
 template<XDRMode mode>
 bool
 XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope,
                        HandleScript enclosingScript, MutableHandleFunction objp);
 
 extern JSObject *
-CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun);
+CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
+                       PollutedGlobalScopeOption polluted);
 
 /*
  * Report an error that call.thisv is not compatible with the specified class,
  * assuming that the method (clasp->name).prototype.<name of callee function>
  * is what was called.
  */
 extern void
 ReportIncompatibleMethod(JSContext *cx, CallReceiver call, const Class *clasp);
--- a/js/src/jsnativestack.cpp
+++ b/js/src/jsnativestack.cpp
@@ -42,16 +42,20 @@ js::GetNativeStackBaseImpl()
         MOV pTib, EAX
     }
     return static_cast<void*>(pTib->StackBase);
 
 # elif defined(_M_X64)
     PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
     return reinterpret_cast<void*>(pTib->StackBase);
 
+# elif defined(_M_ARM)
+    PNT_TIB pTib = reinterpret_cast<PNT_TIB>(NtCurrentTeb());
+    return static_cast<void*>(pTib->StackBase);
+
 # elif defined(_WIN32) && defined(__GNUC__)
     NT_TIB* pTib;
     asm ("movl %%fs:0x18, %0\n" : "=r" (pTib));
     return static_cast<void*>(pTib->StackBase);
 
 # endif
 }
 
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3026,17 +3026,17 @@ js::CloneScript(JSContext *cx, HandleObj
                     RootedObject enclosingScope(cx);
                     if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::Function)
                         enclosingScope = fun;
                     else if (ssi.type() == StaticScopeIter<CanGC>::Block)
                         enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())];
                     else
                         enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())];
 
-                    clone = CloneFunctionAndScript(cx, enclosingScope, innerFun);
+                    clone = CloneFunctionAndScript(cx, enclosingScope, innerFun, polluted);
                 }
             } else {
                 /*
                  * Clone object literals emitted for the JSOP_NEWOBJECT opcode. We only emit that
                  * instead of the less-optimized JSOP_NEWINIT for self-hosted code or code compiled
                  * with JSOPTION_COMPILE_N_GO set. As we don't clone the latter type of code, this
                  * case should only ever be hit when cloning objects from self-hosted code.
                  */
--- a/js/src/tests/jstests.py
+++ b/js/src/tests/jstests.py
@@ -105,16 +105,18 @@ def parse_args():
                           help='Run under JS debugger.')
     harness_og.add_option('--passthrough', action='store_true',
                           help='Run tests with stdin/stdout attached to'
                           ' caller.')
     harness_og.add_option('--valgrind', action='store_true',
                           help='Run tests in valgrind.')
     harness_og.add_option('--valgrind-args', default='',
                           help='Extra args to pass to valgrind.')
+    harness_og.add_option('--rr', action='store_true',
+                          help='Run tests under RR record-and-replay debugger.')
     op.add_option_group(harness_og)
 
     input_og = OptionGroup(op, "Inputs", "Change what tests are run.")
     input_og.add_option('-f', '--file', dest='test_file', action='append',
                         help='Get tests from the given file.')
     input_og.add_option('-x', '--exclude-file', action='append',
                         help='Exclude tests from the given file.')
     input_og.add_option('-d', '--exclude-random', dest='random',
@@ -169,27 +171,29 @@ def parse_args():
     if len(args) > 0:
         options.js_shell = abspath(args[0])
         requested_paths |= set(args[1:])
 
     # If we do not have a shell, we must be in a special mode.
     if options.js_shell is None and not options.make_manifests:
         op.error('missing JS_SHELL argument')
 
-    # Valgrind and gdb are mutually exclusive.
-    if options.valgrind and options.debug:
-        op.error("--valgrind and --debug are mutually exclusive.")
+    # Valgrind, gdb, and rr are mutually exclusive.
+    if sum(map(lambda e: 1 if e else 0, [options.valgrind, options.debug, options.rr])) > 1:
+        op.error("--valgrind, --debug, and --rr are mutually exclusive.")
 
     # Fill the debugger field, as needed.
     prefix = options.debugger.split() if options.debug else []
     if options.valgrind:
         prefix = ['valgrind'] + options.valgrind_args.split()
         if os.uname()[0] == 'Darwin':
             prefix.append('--dsymutil=yes')
         options.show_output = True
+    if options.rr:
+        prefix = ['rr', 'record']
 
     js_cmd_args = options.shell_args.split()
     if options.jorendb:
         options.passthrough = True
         options.hide_progress = True
         options.worker_count = 1
         debugger_path = realpath(os.path.join(
             abspath(dirname(abspath(__file__))),
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -298,18 +298,20 @@ SetAliasedVarOperation(JSContext *cx, JS
 inline bool
 SetNameOperation(JSContext *cx, JSScript *script, jsbytecode *pc, HandleObject scope,
                  HandleValue val)
 {
     MOZ_ASSERT(*pc == JSOP_SETNAME ||
                *pc == JSOP_STRICTSETNAME ||
                *pc == JSOP_SETGNAME ||
                *pc == JSOP_STRICTSETGNAME);
-    MOZ_ASSERT_IF(*pc == JSOP_SETGNAME, scope == cx->global());
-    MOZ_ASSERT_IF(*pc == JSOP_STRICTSETGNAME, scope == cx->global());
+    MOZ_ASSERT_IF(*pc == JSOP_SETGNAME && !script->hasPollutedGlobalScope(),
+                  scope == cx->global());
+    MOZ_ASSERT_IF(*pc == JSOP_STRICTSETGNAME && !script->hasPollutedGlobalScope(),
+                  scope == cx->global());
 
     bool strict = *pc == JSOP_STRICTSETNAME || *pc == JSOP_STRICTSETGNAME;
     RootedPropertyName name(cx, script->getName(pc));
     RootedValue valCopy(cx, val);
 
     // In strict mode, assigning to an undeclared global variable is an
     // error. To detect this, we call NativeSetProperty directly and pass
     // Unqualified. It stores the error, if any, in |result|.
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -283,17 +283,17 @@ GetNameOperation(JSContext *cx, Interpre
      * Skip along the scope chain to the enclosing global object. This is
      * used for GNAME opcodes where the bytecode emitter has determined a
      * name access must be on the global. It also insulates us from bugs
      * in the emitter: type inference will assume that GNAME opcodes are
      * accessing the global object, and the inferred behavior should match
      * the actual behavior even if the id could be found on the scope chain
      * before the global object.
      */
-    if (IsGlobalOp(JSOp(*pc)))
+    if (IsGlobalOp(JSOp(*pc)) && !fp->script()->hasPollutedGlobalScope())
         obj = &obj->global();
 
     Shape *shape = nullptr;
     JSObject *scope = nullptr, *pobj = nullptr;
     if (LookupNameNoGC(cx, name, obj, &scope, &pobj, &shape)) {
         if (FetchNameNoGC(pobj, shape, vp))
             return CheckUninitializedLexical(cx, name, vp);
     }
@@ -2034,38 +2034,43 @@ CASE(JSOP_SETCONST)
     RootedObject &obj = rootObject0;
     obj = &REGS.fp()->varObj();
 
     if (!SetConstOperation(cx, obj, name, rval))
         goto error;
 }
 END_CASE(JSOP_SETCONST)
 
-CASE(JSOP_BINDGNAME)
-    PUSH_OBJECT(REGS.fp()->global());
-END_CASE(JSOP_BINDGNAME)
-
 CASE(JSOP_BINDINTRINSIC)
     PUSH_OBJECT(*cx->global()->intrinsicsHolder());
 END_CASE(JSOP_BINDINTRINSIC)
 
+CASE(JSOP_BINDGNAME)
 CASE(JSOP_BINDNAME)
 {
-    RootedObject &scopeChain = rootObject0;
-    scopeChain = REGS.fp()->scopeChain();
-
-    RootedPropertyName &name = rootName0;
-    name = script->getName(REGS.pc);
-
-    /* Assigning to an undeclared name adds a property to the global object. */
-    RootedObject &scope = rootObject1;
-    if (!LookupNameUnqualified(cx, name, scopeChain, &scope))
-        goto error;
-
-    PUSH_OBJECT(*scope);
+    JSOp op = JSOp(*REGS.pc);
+    if (op == JSOP_BINDNAME || script->hasPollutedGlobalScope()) {
+        RootedObject &scopeChain = rootObject0;
+        scopeChain = REGS.fp()->scopeChain();
+
+        RootedPropertyName &name = rootName0;
+        name = script->getName(REGS.pc);
+
+        /* Assigning to an undeclared name adds a property to the global object. */
+        RootedObject &scope = rootObject1;
+        if (!LookupNameUnqualified(cx, name, scopeChain, &scope))
+            goto error;
+
+        PUSH_OBJECT(*scope);
+    } else {
+        PUSH_OBJECT(REGS.fp()->global());
+    }
+
+    static_assert(JSOP_BINDNAME_LENGTH == JSOP_BINDGNAME_LENGTH,
+                  "We're sharing the END_CASE so the lengths better match");
 }
 END_CASE(JSOP_BINDNAME)
 
 #define BITWISE_OP(OP)                                                        \
     JS_BEGIN_MACRO                                                            \
         int32_t i, j;                                                         \
         if (!ToInt32(cx, REGS.stackHandleAt(-2), &i))                         \
             goto error;                                                       \
@@ -2462,16 +2467,19 @@ CASE(JSOP_SETGNAME)
 CASE(JSOP_STRICTSETGNAME)
 CASE(JSOP_SETNAME)
 CASE(JSOP_STRICTSETNAME)
 {
     static_assert(JSOP_SETNAME_LENGTH == JSOP_STRICTSETNAME_LENGTH,
                   "setname and strictsetname must be the same size");
     static_assert(JSOP_SETGNAME_LENGTH == JSOP_STRICTSETGNAME_LENGTH,
                   "setganem adn strictsetgname must be the same size");
+    static_assert(JSOP_SETNAME_LENGTH == JSOP_SETGNAME_LENGTH,
+                  "We're sharing the END_CASE so the lengths better match");
+
     RootedObject &scope = rootObject0;
     scope = &REGS.sp[-2].toObject();
     HandleValue value = REGS.stackHandleAt(-1);
 
     if (!SetNameOperation(cx, script, REGS.pc, scope, value))
         goto error;
 
     REGS.sp[-2] = REGS.sp[-1];
@@ -2717,29 +2725,33 @@ CASE(JSOP_GIMPLICITTHIS)
         RootedValue &v = rootValue0;
         if (!ComputeImplicitThis(cx, scope, &v))
             goto error;
         PUSH_COPY(v);
     } else {
         // Treat it like JSOP_UNDEFINED.
         PUSH_UNDEFINED();
     }
+    static_assert(JSOP_IMPLICITTHIS_LENGTH == JSOP_GIMPLICITTHIS_LENGTH,
+                  "We're sharing the END_CASE so the lengths better match");
 }
 END_CASE(JSOP_IMPLICITTHIS)
 
 CASE(JSOP_GETGNAME)
 CASE(JSOP_GETNAME)
 {
     RootedValue &rval = rootValue0;
 
     if (!GetNameOperation(cx, REGS.fp(), REGS.pc, &rval))
         goto error;
 
     PUSH_COPY(rval);
     TypeScript::Monitor(cx, script, REGS.pc, rval);
+    static_assert(JSOP_GETNAME_LENGTH == JSOP_GETGNAME_LENGTH,
+                  "We're sharing the END_CASE so the lengths better match");
 }
 END_CASE(JSOP_GETNAME)
 
 CASE(JSOP_GETINTRINSIC)
 {
     RootedValue &rval = rootValue0;
 
     if (!GetIntrinsicOperation(cx, REGS.pc, &rval))
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -1494,44 +1494,48 @@ 1234567890123456789012345678901234567890
      *   Category: Statements
      *   Type: Function
      *   Operands:
      *   Stack: =>
      */ \
     macro(JSOP_RETRVAL,       153,"retrval",    NULL,       1,  0,  0,  JOF_BYTE) \
     \
     /*
-     * Looks up name on global scope and pushes its value onto the stack.
+     * Looks up name on global scope and pushes its value onto the stack, unless
+     * the script has a polluted global, in which case it acts just like
+     * JSOP_NAME.
      *
      * Free variable references that must either be found on the global or a
      * ReferenceError.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: => val
      */ \
     macro(JSOP_GETGNAME,      154,"getgname",  NULL,       5,  0,  1, JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_GNAME) \
     /*
      * Pops the top two values on the stack as 'val' and 'scope', sets property
      * of 'scope' as 'val' and pushes 'val' back on the stack.
      *
-     * 'scope' should be the global scope.
+     * 'scope' should be the global scope unless the script has a polluted
+     * global scope, in which case acts like JSOP_SETNAME.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: scope, val => val
      */ \
     macro(JSOP_SETGNAME,      155,"setgname",  NULL,       5,  2,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSLOPPY) \
     \
     /*
      * Pops the top two values on the stack as 'val' and 'scope', sets property
      * of 'scope' as 'val' and pushes 'val' back on the stack. Throws a
      * TypeError if the set fails, per strict mode semantics.
      *
-     * 'scope' should be the global scope.
+     * 'scope' should be the global scope unless the script has a polluted
+     * global scope, in which case acts like JSOP_STRICTSETNAME.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: scope, val => val
      */ \
     macro(JSOP_STRICTSETGNAME, 156, "strict-setgname",  NULL,       5,  2,  1, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME|JOF_CHECKSTRICT) \
     /*
      * Pushes the implicit 'this' value for calls to the associated name onto
@@ -1764,19 +1768,20 @@ 1234567890123456789012345678901234567890
      */ \
     macro(JSOP_DEBUGAFTERYIELD,  208, "debugafteryield",  NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED209,     209, "unused209",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED210,     210, "unused210",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED211,     211, "unused211",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED212,     212, "unused212",    NULL,  1,  0,  0,  JOF_BYTE) \
     macro(JSOP_UNUSED213,     213, "unused213",    NULL,  1,  0,  0,  JOF_BYTE) \
     /*
-     * Pushes the global scope onto the stack.
+     * Pushes the global scope onto the stack if the script doesn't have a
+     * polluted global scope.  Otherwise will act like JSOP_BINDNAME.
      *
-     * 'nameIndex' is not used.
+     * 'nameIndex' is only used when acting like JSOP_BINDNAME.
      *   Category: Variables and Scopes
      *   Type: Free Variables
      *   Operands: uint32_t nameIndex
      *   Stack: => global
      */ \
     macro(JSOP_BINDGNAME,     214, "bindgname",    NULL,  5,  0,  1,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME) \
     \
     /*
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -2563,19 +2563,29 @@ RemoveReferencedNames(JSContext *cx, Han
     //   care about entraining variables unnecessarily.
 
     for (jsbytecode *pc = script->code(); pc != script->codeEnd(); pc += GetBytecodeLength(pc)) {
         PropertyName *name;
 
         switch (JSOp(*pc)) {
           case JSOP_GETNAME:
           case JSOP_SETNAME:
+          case JSOP_STRICTSETNAME:
             name = script->getName(pc);
             break;
 
+          case JSOP_GETGNAME:
+          case JSOP_SETGNAME:
+          case JSOP_STRICTSETGNAME:
+            if (script->hasPollutedGlobalScope())
+                name = script->getName(pc);
+            else
+                name = nullptr;
+            break;
+
           case JSOP_GETALIASEDVAR:
           case JSOP_SETALIASEDVAR:
             name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
             break;
 
           default:
             name = nullptr;
             break;
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -171,18 +171,20 @@ UnboxedLayout::makeConstructorCode(JSCon
             HeapTypeSet *types = group->maybeGetProperty(IdToTypeId(NameToId(property.name)));
 
             Label notObject;
             masm.branchTestObject(Assembler::NotEqual, valueOperand,
                                   types->mightBeMIRType(MIRType_Null) ? &notObject : &failureStoreObject);
 
             Register payloadReg = masm.extractObject(valueOperand, scratch1);
 
-            if (!types->hasType(TypeSet::AnyObjectType()))
-                masm.guardObjectType(payloadReg, types, scratch2, &failureStoreObject);
+            if (!types->hasType(TypeSet::AnyObjectType())) {
+                Register scratch = (payloadReg == scratch1) ? scratch2 : scratch1;
+                masm.guardObjectType(payloadReg, types, scratch, &failureStoreObject);
+            }
 
             masm.storeUnboxedProperty(targetAddress, JSVAL_TYPE_OBJECT,
                                       TypedOrValueRegister(MIRType_Object,
                                                            AnyRegister(payloadReg)), nullptr);
 
             if (notObject.used()) {
                 Label done;
                 masm.jump(&done);
@@ -202,17 +204,17 @@ UnboxedLayout::makeConstructorCode(JSCon
 
     if (object != ReturnReg)
         masm.movePtr(object, ReturnReg);
 
     // Restore non-volatile registers which were saved on entry.
     for (GeneralRegisterBackwardIterator iter(savedNonVolatileRegisters); iter.more(); ++iter)
         masm.Pop(*iter);
 
-    masm.ret();
+    masm.abiret();
 
     masm.bind(&failureStoreOther);
 
     // There was a failure while storing a value which cannot be stored at all
     // in the unboxed object. Initialize the object so it is safe for GC and
     // return null.
     masm.initUnboxedObjectContents(object, templateObject);
 
@@ -236,17 +238,17 @@ UnboxedLayout::makeConstructorCode(JSCon
 
     // Initialize the object so it is safe for GC.
     masm.initUnboxedObjectContents(object, templateObject);
 
     masm.movePtr(ImmWord(CLEAR_CONSTRUCTOR_CODE_TOKEN), object);
     masm.jump(&done);
 
     Linker linker(masm);
-    AutoFlushICache afc("RegExp");
+    AutoFlushICache afc("UnboxedObject");
     JitCode *code = linker.newCode<NoGC>(cx, OTHER_CODE);
     if (!code)
         return false;
 
     layout.setConstructorCode(code);
     return true;
 }
 
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,17 +24,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 265;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 267;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
 static_assert(JSErr_Limit == 388,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -444,17 +444,17 @@ public:
 
 protected:
   typedef nsTArray<RuleValue> RuleValueList;
   void AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
                          const RuleSelectorPair& aRuleInfo);
   void AppendUniversalRule(const RuleSelectorPair& aRuleInfo);
 
   int32_t     mRuleCount;
-  // The hashtables are lazily initialized.
+
   PLDHashTable mIdTable;
   PLDHashTable mClassTable;
   PLDHashTable mTagTable;
   PLDHashTable mNameSpaceTable;
   RuleValueList mUniversalRules;
 
   struct EnumData {
     const RuleValue* mCurValue;
@@ -503,16 +503,30 @@ RuleHash::RuleHash(bool aQuirksMode)
     mElementUniversalCalls(0),
     mElementNameSpaceCalls(0),
     mElementTagCalls(0),
     mElementClassCalls(0),
     mElementIdCalls(0)
 #endif
 {
   MOZ_COUNT_CTOR(RuleHash);
+
+  PL_DHashTableInit(&mIdTable, aQuirksMode ? &RuleHash_IdTable_CIOps.ops
+                                           : &RuleHash_IdTable_CSOps.ops,
+                    sizeof(RuleHashTableEntry));
+
+  PL_DHashTableInit(&mClassTable, aQuirksMode ? &RuleHash_ClassTable_CIOps.ops
+                                              : &RuleHash_ClassTable_CSOps.ops,
+                    sizeof(RuleHashTableEntry));
+
+  PL_DHashTableInit(&mTagTable, &RuleHash_TagTable_Ops,
+                    sizeof(RuleHashTagTableEntry));
+
+  PL_DHashTableInit(&mNameSpaceTable, &RuleHash_NameSpaceTable_Ops,
+                    sizeof(RuleHashTableEntry));
 }
 
 RuleHash::~RuleHash()
 {
   MOZ_COUNT_DTOR(RuleHash);
 #ifdef RULE_HASH_STATS
   printf(
 "RuleHash(%p):\n"
@@ -545,28 +559,20 @@ RuleHash::~RuleHash()
 #endif // RULE_HASH_STATS
   // Rule Values are arena allocated no need to delete them. Their destructor
   // isn't doing any cleanup. So we dont even bother to enumerate through
   // the hash tables and call their destructors.
   if (nullptr != mEnumList) {
     delete [] mEnumList;
   }
   // delete arena for strings and small objects
-  if (mIdTable.IsInitialized()) {
-    PL_DHashTableFinish(&mIdTable);
-  }
-  if (mClassTable.IsInitialized()) {
-    PL_DHashTableFinish(&mClassTable);
-  }
-  if (mTagTable.IsInitialized()) {
-    PL_DHashTableFinish(&mTagTable);
-  }
-  if (mNameSpaceTable.IsInitialized()) {
-    PL_DHashTableFinish(&mNameSpaceTable);
-  }
+  PL_DHashTableFinish(&mIdTable);
+  PL_DHashTableFinish(&mClassTable);
+  PL_DHashTableFinish(&mTagTable);
+  PL_DHashTableFinish(&mNameSpaceTable);
 }
 
 void RuleHash::AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
                                  const RuleSelectorPair& aRuleInfo)
 {
   // Get a new or existing entry.
   RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
     (PL_DHashTableAdd(aTable, aKey, fallible));
@@ -595,54 +601,34 @@ void RuleHash::AppendUniversalRule(const
 
 void RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo)
 {
   nsCSSSelector *selector = aRuleInfo.mSelector;
   if (selector->IsPseudoElement()) {
     selector = selector->mNext;
   }
   if (nullptr != selector->mIDList) {
-    if (!mIdTable.IsInitialized()) {
-      PL_DHashTableInit(&mIdTable,
-                        mQuirksMode ? &RuleHash_IdTable_CIOps.ops
-                                    : &RuleHash_IdTable_CSOps.ops,
-                        sizeof(RuleHashTableEntry));
-    }
     AppendRuleToTable(&mIdTable, selector->mIDList->mAtom, aRuleInfo);
     RULE_HASH_STAT_INCREMENT(mIdSelectors);
   }
   else if (nullptr != selector->mClassList) {
-    if (!mClassTable.IsInitialized()) {
-      PL_DHashTableInit(&mClassTable,
-                        mQuirksMode ? &RuleHash_ClassTable_CIOps.ops
-                                    : &RuleHash_ClassTable_CSOps.ops,
-                        sizeof(RuleHashTableEntry));
-    }
     AppendRuleToTable(&mClassTable, selector->mClassList->mAtom, aRuleInfo);
     RULE_HASH_STAT_INCREMENT(mClassSelectors);
   }
   else if (selector->mLowercaseTag) {
     RuleValue ruleValue(aRuleInfo, mRuleCount++, mQuirksMode);
-    if (!mTagTable.IsInitialized()) {
-      PL_DHashTableInit(&mTagTable, &RuleHash_TagTable_Ops,
-                        sizeof(RuleHashTagTableEntry));
-    }
     AppendRuleToTagTable(&mTagTable, selector->mLowercaseTag, ruleValue);
     RULE_HASH_STAT_INCREMENT(mTagSelectors);
     if (selector->mCasedTag &&
         selector->mCasedTag != selector->mLowercaseTag) {
       AppendRuleToTagTable(&mTagTable, selector->mCasedTag, ruleValue);
       RULE_HASH_STAT_INCREMENT(mTagSelectors);
     }
   }
   else if (kNameSpaceID_Unknown != selector->mNameSpace) {
-    if (!mNameSpaceTable.IsInitialized()) {
-      PL_DHashTableInit(&mNameSpaceTable, &RuleHash_NameSpaceTable_Ops,
-                        sizeof(RuleHashTableEntry));
-    }
     AppendRuleToTable(&mNameSpaceTable,
                       NS_INT32_TO_PTR(selector->mNameSpace), aRuleInfo);
     RULE_HASH_STAT_INCREMENT(mNameSpaceSelectors);
   }
   else {  // universal tag selector
     AppendUniversalRule(aRuleInfo);
     RULE_HASH_STAT_INCREMENT(mUniversalSelectors);
   }
@@ -689,41 +675,41 @@ void RuleHash::EnumerateAllRules(Element
   int32_t valueCount = 0;
   RULE_HASH_STAT_INCREMENT(mElementsMatched);
 
   if (mUniversalRules.Length() != 0) { // universal rules
     mEnumList[valueCount++] = ToEnumData(mUniversalRules);
     RULE_HASH_STAT_INCREMENT_LIST_COUNT(mUniversalRules, mElementUniversalCalls);
   }
   // universal rules within the namespace
-  if (kNameSpaceID_Unknown != nameSpace && mNameSpaceTable.IsInitialized()) {
+  if (kNameSpaceID_Unknown != nameSpace && mNameSpaceTable.EntryCount() > 0) {
     RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
                                            (PL_DHashTableSearch(&mNameSpaceTable, NS_INT32_TO_PTR(nameSpace)));
     if (entry) {
       mEnumList[valueCount++] = ToEnumData(entry->mRules);
       RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementNameSpaceCalls);
     }
   }
-  if (mTagTable.IsInitialized()) {
+  if (mTagTable.EntryCount() > 0) {
     RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
                                            (PL_DHashTableSearch(&mTagTable, tag));
     if (entry) {
       mEnumList[valueCount++] = ToEnumData(entry->mRules);
       RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementTagCalls);
     }
   }
-  if (id && mIdTable.IsInitialized()) {
+  if (id && mIdTable.EntryCount() > 0) {
     RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
                                            (PL_DHashTableSearch(&mIdTable, id));
     if (entry) {
       mEnumList[valueCount++] = ToEnumData(entry->mRules);
       RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementIdCalls);
     }
   }
-  if (mClassTable.IsInitialized()) {
+  if (mClassTable.EntryCount() > 0) {
     for (int32_t index = 0; index < classCount; ++index) {
       RuleHashTableEntry *entry = static_cast<RuleHashTableEntry*>
                                              (PL_DHashTableSearch(&mClassTable, classList->AtomAt(index)));
       if (entry) {
         mEnumList[valueCount++] = ToEnumData(entry->mRules);
         RULE_HASH_STAT_INCREMENT_LIST_COUNT(entry->mRules, mElementClassCalls);
       }
     }
@@ -776,39 +762,31 @@ SizeOfRuleHashTableEntry(PLDHashEntryHdr
   return entry->mRules.SizeOfExcludingThis(aMallocSizeOf);
 }
 
 size_t
 RuleHash::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t n = 0;
 
-  if (mIdTable.IsInitialized()) {
-    n += PL_DHashTableSizeOfExcludingThis(&mIdTable,
-                                          SizeOfRuleHashTableEntry,
-                                          aMallocSizeOf);
-  }
-
-  if (mClassTable.IsInitialized()) {
-    n += PL_DHashTableSizeOfExcludingThis(&mClassTable,
-                                          SizeOfRuleHashTableEntry,
-                                          aMallocSizeOf);
-  }
-
-  if (mTagTable.IsInitialized()) {
-    n += PL_DHashTableSizeOfExcludingThis(&mTagTable,
-                                          SizeOfRuleHashTableEntry,
-                                          aMallocSizeOf);
-  }
-
-  if (mNameSpaceTable.IsInitialized()) {
-    n += PL_DHashTableSizeOfExcludingThis(&mNameSpaceTable,
-                                          SizeOfRuleHashTableEntry,
-                                          aMallocSizeOf);
-  }
+  n += PL_DHashTableSizeOfExcludingThis(&mIdTable,
+                                        SizeOfRuleHashTableEntry,
+                                        aMallocSizeOf);
+
+  n += PL_DHashTableSizeOfExcludingThis(&mClassTable,
+                                        SizeOfRuleHashTableEntry,
+                                        aMallocSizeOf);
+
+  n += PL_DHashTableSizeOfExcludingThis(&mTagTable,
+                                        SizeOfRuleHashTableEntry,
+                                        aMallocSizeOf);
+
+  n += PL_DHashTableSizeOfExcludingThis(&mNameSpaceTable,
+                                        SizeOfRuleHashTableEntry,
+                                        aMallocSizeOf);
 
   n += mUniversalRules.SizeOfExcludingThis(aMallocSizeOf);
 
   return n;
 }
 
 size_t
 RuleHash::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
--- a/media/libopus/moz.build
+++ b/media/libopus/moz.build
@@ -65,33 +65,34 @@ LOCAL_INCLUDES += [
     'silk',
     'src',
 ]
 
 # sources.mozbuild is generated from gen-sources.py when a new libopus is
 # imported.
 include('sources.mozbuild')
 
-SOURCES += celt_sources
-SOURCES += silk_sources
-SOURCES += opus_sources
+UNIFIED_SOURCES += celt_sources
+SOURCES += celt_nonunified_sources
+UNIFIED_SOURCES += silk_sources
+UNIFIED_SOURCES += opus_sources
 
 if CONFIG['MOZ_SAMPLE_TYPE_FLOAT32']:
     LOCAL_INCLUDES += [
         'silk/float',
     ]
-    SOURCES += silk_sources_float
-    SOURCES += opus_sources_float
+    UNIFIED_SOURCES += silk_sources_float
+    UNIFIED_SOURCES += opus_sources_float
 else:
     LOCAL_INCLUDES += [
         'silk/fixed',
     ]
-    SOURCES += silk_sources_fixed
+    UNIFIED_SOURCES += silk_sources_fixed
 # for webrtc
-    SOURCES += opus_sources_float
+    UNIFIED_SOURCES += opus_sources_float
 
 if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_AS']:
     SOURCES += celt_sources_arm
     GENERATED_SOURCES += [ '%s.%s' % (f, CONFIG['ASM_SUFFIX']) for f in [
         'celt_pitch_xcorr_arm-gnu',
     ]]
     # -Os is significantly slower, enable -O3 unless optimization is disabled
     if CONFIG['MOZ_OPTIMIZE']:
--- a/media/libopus/sources.mozbuild
+++ b/media/libopus/sources.mozbuild
@@ -1,30 +1,35 @@
 # THIS FILE WAS AUTOMATICALLY GENERATED BY gen-sources.py. DO NOT EDIT.
 celt_sources = [
     'celt/bands.c',
     'celt/celt.c',
-    'celt/celt_decoder.c',
-    'celt/celt_encoder.c',
     'celt/celt_lpc.c',
     'celt/cwrs.c',
     'celt/entcode.c',
     'celt/entdec.c',
     'celt/entenc.c',
     'celt/kiss_fft.c',
     'celt/laplace.c',
     'celt/mathops.c',
     'celt/mdct.c',
     'celt/modes.c',
     'celt/pitch.c',
     'celt/quant_bands.c',
     'celt/rate.c',
     'celt/vq.c',
 ]
 
+celt_nonunified_sources = [
+    # Disabled because of name clash of opus_custom_encoder_get_size.
+    'celt/celt_decoder.c',
+    # Disabled because of name clash of opus_custom_encoder_get_size.
+    'celt/celt_encoder.c',
+]
+
 celt_sources_arm = [
     'celt/arm/arm_celt_map.c',
     'celt/arm/armcpu.c',
 ]
 
 celt_sources_arm_asm = [
     'celt/arm/celt_pitch_xcorr_arm.s',
 ]
--- a/media/webrtc/moz.build
+++ b/media/webrtc/moz.build
@@ -62,17 +62,16 @@ if CONFIG['MOZ_WEBRTC_SIGNALING']:
         'signaling/src/mediapipeline/MediaPipelineFilter.cpp',
         'signaling/src/mediapipeline/SrtpFlow.cpp',
         'signaling/src/peerconnection/MediaPipelineFactory.cpp',
         'signaling/src/peerconnection/MediaStreamList.cpp',
         'signaling/src/peerconnection/PeerConnectionCtx.cpp',
         'signaling/src/peerconnection/PeerConnectionImpl.cpp',
         'signaling/src/peerconnection/PeerConnectionMedia.cpp',
         'signaling/src/peerconnection/WebrtcGlobalInformation.cpp',
-        'signaling/src/sdp/sipcc/ccsdp.c',
         'signaling/src/sdp/sipcc/cpr_string.c',
         'signaling/src/sdp/sipcc/sdp_access.c',
         'signaling/src/sdp/sipcc/sdp_attr.c',
         'signaling/src/sdp/sipcc/sdp_attr_access.c',
         'signaling/src/sdp/sipcc/sdp_base64.c',
         'signaling/src/sdp/sipcc/sdp_config.c',
         'signaling/src/sdp/sipcc/sdp_main.c',
         'signaling/src/sdp/sipcc/sdp_services_unix.c',
--- a/media/webrtc/signaling/src/jsep/JsepCodecDescription.h
+++ b/media/webrtc/signaling/src/jsep/JsepCodecDescription.h
@@ -33,20 +33,19 @@ struct JsepCodecDescription {
         mEnabled(enabled)
   {
   }
   virtual ~JsepCodecDescription() {}
 
   virtual JsepCodecDescription* Clone() const = 0;
   virtual void AddFmtps(SdpFmtpAttributeList& fmtp) const = 0;
   virtual void AddRtcpFbs(SdpRtcpFbAttributeList& rtcpfb) const = 0;
-  virtual bool LoadFmtps(const SdpFmtpAttributeList::Parameters& params) = 0;
-  virtual bool LoadRtcpFbs(
-      const SdpRtcpFbAttributeList::Feedback& feedback) = 0;
 
+  // TODO(bug 1142105): This probably should be a helper function in
+  // /sdp
   static bool
   GetPtAsInt(const std::string& ptString, uint16_t* ptOutparam)
   {
     char* end;
     unsigned long pt = strtoul(ptString.c_str(), &end, 10);
     size_t length = static_cast<size_t>(end - ptString.c_str());
     if ((pt > UINT16_MAX) || (length != ptString.size())) {
       return false;
@@ -75,27 +74,50 @@ struct JsepCodecDescription {
     }
 
     const SdpRtpmapAttributeList::Rtpmap& entry = rtpmap.GetEntry(fmt);
 
     if (mType == remoteMsection.GetMediaType()
         && (mName == entry.name)
         && (mClock == entry.clock)
         && (mChannels == entry.channels)) {
-      return ParametersMatch(FindParameters(entry.pt, remoteMsection));
+      return ParametersMatch(entry.pt, remoteMsection);
     }
     return false;
   }
 
   virtual bool
-  ParametersMatch(const SdpFmtpAttributeList::Parameters* fmtp) const
+  ParametersMatch(const std::string& fmt,
+                  const SdpMediaSection& remoteMsection) const
   {
     return true;
   }
 
+  UniquePtr<JsepCodecDescription>
+  MakeNegotiatedCodec(const std::string& pt,
+                      const SdpMediaSection& remoteMsection) const
+  {
+    UniquePtr<JsepCodecDescription> negotiated(Clone());
+
+    if (!negotiated->Negotiate(pt, remoteMsection)) {
+      negotiated.reset();
+    }
+
+    return negotiated;
+  }
+
+  virtual bool
+  Negotiate(const std::string& pt, const SdpMediaSection& remoteMsection)
+  {
+    mDefaultPt = pt;
+    return true;
+  }
+
+  // TODO(bug 1142105): This probably should be a helper function in
+  // /sdp
   static const SdpFmtpAttributeList::Parameters*
   FindParameters(const std::string& pt,
                  const mozilla::SdpMediaSection& remoteMsection)
   {
     const SdpAttributeList& attrs = remoteMsection.GetAttributeList();
 
     if (attrs.HasAttribute(SdpAttribute::kFmtpAttribute)) {
       const SdpFmtpAttributeList& fmtps = attrs.GetFmtp();
@@ -103,50 +125,56 @@ struct JsepCodecDescription {
         if (i->format == pt && i->parameters) {
           return i->parameters.get();
         }
       }
     }
     return nullptr;
   }
 
-  virtual JsepCodecDescription*
-  MakeNegotiatedCodec(const mozilla::SdpMediaSection& remoteMsection,
-                      const std::string& pt,
-                      bool sending) const
+  UniquePtr<JsepCodecDescription>
+  MakeSendCodec(const mozilla::SdpMediaSection& remoteMsection,
+                const std::string& pt) const
   {
-    UniquePtr<JsepCodecDescription> negotiated(Clone());
-    negotiated->mDefaultPt = pt;
-
-    const SdpAttributeList& attrs = remoteMsection.GetAttributeList();
+    UniquePtr<JsepCodecDescription> sendCodec(Clone());
+    sendCodec->mDefaultPt = pt;
 
-    if (sending) {
-      auto* parameters = FindParameters(negotiated->mDefaultPt, remoteMsection);
-      if (parameters) {
-        if (!negotiated->LoadFmtps(*parameters)) {
-          // Remote parameters were invalid
-          return nullptr;
-        }
-      }
-    } else {
-      // If a receive track, we need to pay attention to remote end's rtcp-fb
-      if (attrs.HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
-        auto& rtcpfbs = attrs.GetRtcpFb().mFeedbacks;
-        for (auto i = rtcpfbs.begin(); i != rtcpfbs.end(); ++i) {
-          if (i->pt == negotiated->mDefaultPt || i->pt == "*") {
-            if (!negotiated->LoadRtcpFbs(*i)) {
-              // Remote parameters were invalid
-              return nullptr;
-            }
-          }
-        }
-      }
+    if (!sendCodec->LoadSendParameters(remoteMsection, pt)) {
+      sendCodec.reset();
     }
 
-    return negotiated.release();
+    return sendCodec;
+  }
+
+  UniquePtr<JsepCodecDescription>
+  MakeRecvCodec(const mozilla::SdpMediaSection& remoteMsection,
+                const std::string& pt) const
+  {
+    UniquePtr<JsepCodecDescription> recvCodec(Clone());
+    recvCodec->mDefaultPt = pt;
+
+    if (!recvCodec->LoadRecvParameters(remoteMsection, pt)) {
+      recvCodec.reset();
+    }
+
+    return recvCodec;
+  }
+
+  virtual bool LoadSendParameters(
+      const mozilla::SdpMediaSection& remoteMsection,
+      const std::string& pt)
+  {
+    return true;
+  }
+
+  virtual bool LoadRecvParameters(
+      const mozilla::SdpMediaSection& remoteMsection,
+      const std::string& pt)
+  {
+    return true;
   }
 
   virtual void
   AddToMediaSection(SdpMediaSection& msection) const
   {
     if (mEnabled && msection.GetMediaType() == mType) {
       if (mType == SdpMediaSection::kApplication) {
         // Hack: using mChannels for number of streams
@@ -229,30 +257,16 @@ struct JsepAudioCodecDescription : publi
   }
 
   virtual void
   AddRtcpFbs(SdpRtcpFbAttributeList& rtcpfb) const override
   {
     // TODO: Do we want to add anything?
   }
 
-  virtual bool
-  LoadFmtps(const SdpFmtpAttributeList::Parameters& params) override
-  {
-    // TODO
-    return true;
-  }
-
-  virtual bool
-  LoadRtcpFbs(const SdpRtcpFbAttributeList::Feedback& feedback) override
-  {
-    // Nothing to do
-    return true;
-  }
-
   JSEP_CODEC_CLONE(JsepAudioCodecDescription)
 
   uint32_t mPacketSize;
   uint32_t mBitrate;
 };
 
 struct JsepVideoCodecDescription : public JsepCodecDescription {
   JsepVideoCodecDescription(const std::string& defaultPt,
@@ -311,57 +325,195 @@ struct JsepVideoCodecDescription : publi
     // Just hard code for now
     rtcpfb.PushEntry(mDefaultPt, SdpRtcpFbAttributeList::kNack);
     rtcpfb.PushEntry(
         mDefaultPt, SdpRtcpFbAttributeList::kNack, SdpRtcpFbAttributeList::pli);
     rtcpfb.PushEntry(
         mDefaultPt, SdpRtcpFbAttributeList::kCcm, SdpRtcpFbAttributeList::fir);
   }
 
+  SdpFmtpAttributeList::H264Parameters
+  GetH264Parameters(const std::string& pt,
+                    const SdpMediaSection& msection) const
+  {
+    // Will contain defaults if nothing else
+    SdpFmtpAttributeList::H264Parameters result;
+    auto* params = FindParameters(pt, msection);
+
+    if (params && params->codec_type == SdpRtpmapAttributeList::kH264) {
+      result =
+        static_cast<const SdpFmtpAttributeList::H264Parameters&>(*params);
+    }
+
+    return result;
+  }
+
+  SdpFmtpAttributeList::VP8Parameters
+  GetVP8Parameters(const std::string& pt,
+                   const SdpMediaSection& msection) const
+  {
+    SdpRtpmapAttributeList::CodecType expectedType(
+        mName == "VP8" ?
+        SdpRtpmapAttributeList::kVP8 :
+        SdpRtpmapAttributeList::kVP9);
+
+    // Will contain defaults if nothing else
+    SdpFmtpAttributeList::VP8Parameters result(expectedType);
+    auto* params = FindParameters(pt, msection);
+
+    if (params && params->codec_type == expectedType) {
+      result =
+        static_cast<const SdpFmtpAttributeList::VP8Parameters&>(*params);
+    }
+
+    return result;
+  }
+
   virtual bool
-  LoadFmtps(const SdpFmtpAttributeList::Parameters& params) override
+  Negotiate(const std::string& pt,
+            const SdpMediaSection& remoteMsection) override
+  {
+    if (mName == "H264") {
+      SdpFmtpAttributeList::H264Parameters h264Params(
+          GetH264Parameters(pt, remoteMsection));
+      if (!h264Params.level_asymmetry_allowed) {
+        SetSaneH264Level(std::min(GetSaneH264Level(h264Params.profile_level_id),
+                                  GetSaneH264Level(mProfileLevelId)),
+                         &mProfileLevelId);
+      }
+
+      // TODO(bug 1143709): max-recv-level support
+    }
+
+    return JsepCodecDescription::Negotiate(pt, remoteMsection);
+  }
+
+  // Maps the not-so-sane encoding of H264 level into something that is
+  // ordered in the way one would expect
+  // 1b is 0xAB, everything else is the level left-shifted one half-byte
+  // (eg; 1.0 is 0xA0, 1.1 is 0xB0, 3.1 is 0x1F0)
+  static uint32_t
+  GetSaneH264Level(uint32_t profileLevelId)
   {
-    switch (params.codec_type) {
-      case SdpRtpmapAttributeList::kH264:
-        LoadH264Parameters(params);
-        break;
-      case SdpRtpmapAttributeList::kVP9:
-        // VP8 and VP9 share the same SDP parameters thus far
-      case SdpRtpmapAttributeList::kVP8:
-        LoadVP8Parameters(params);
-        break;
-      case SdpRtpmapAttributeList::kiLBC:
-      case SdpRtpmapAttributeList::kiSAC:
-      case SdpRtpmapAttributeList::kOpus:
-      case SdpRtpmapAttributeList::kG722:
-      case SdpRtpmapAttributeList::kPCMU:
-      case SdpRtpmapAttributeList::kPCMA:
-      case SdpRtpmapAttributeList::kOtherCodec:
-        MOZ_ASSERT(false, "Invalid codec type for video");
+    uint32_t profileIdc = (profileLevelId >> 16);
+
+    if (profileIdc == 0x42 || profileIdc == 0x4D || profileIdc == 0x58) {
+      if ((profileLevelId & 0x10FF) == 0x100B) {
+        // Level 1b
+        return 0xAB;
+      }
+    }
+
+    uint32_t level = profileLevelId & 0xFF;
+
+    if (level == 0x09) {
+      // Another way to encode level 1b
+      return 0xAB;
+    }
+
+    return level << 4;
+  }
+
+  static void
+  SetSaneH264Level(uint32_t level, uint32_t* profileLevelId)
+  {
+    uint32_t profileIdc = (*profileLevelId >> 16);
+    uint32_t levelMask = 0xFF;
+
+    if (profileIdc == 0x42 || profileIdc == 0x4d || profileIdc == 0x58) {
+      levelMask = 0x10FF;
+      if (level == 0xAB) {
+        // Level 1b
+        level = 0x100B;
+      } else {
+        // Not 1b, just shift
+        level = level >> 4;
+      }
+    } else if (level == 0xAB) {
+      // Another way to encode 1b
+      level = 0x09;
+    } else {
+      // Not 1b, just shift
+      level = level >> 4;
+    }
+
+    *profileLevelId = (*profileLevelId & ~levelMask) | level;
+  }
+
+  virtual bool
+  LoadSendParameters(const mozilla::SdpMediaSection& remoteMsection,
+                     const std::string& pt) override
+  {
+
+    if (mName == "H264") {
+      SdpFmtpAttributeList::H264Parameters h264Params(
+          GetH264Parameters(pt, remoteMsection));
+
+      if (!h264Params.level_asymmetry_allowed) {
+        SetSaneH264Level(std::min(GetSaneH264Level(h264Params.profile_level_id),
+                                  GetSaneH264Level(mProfileLevelId)),
+                         &mProfileLevelId);
+      } else {
+        SetSaneH264Level(GetSaneH264Level(h264Params.profile_level_id),
+                         &mProfileLevelId);
+      }
+
+      mMaxFs = h264Params.max_fs;
+      mMaxMbps = h264Params.max_mbps;
+      mMaxCpb = h264Params.max_cpb;
+      mMaxDpb = h264Params.max_dpb;
+      mMaxBr = h264Params.max_br;
+      mSpropParameterSets = h264Params.sprop_parameter_sets;
+    } else if (mName == "VP8" || mName == "VP9") {
+      SdpFmtpAttributeList::VP8Parameters vp8Params(
+          GetVP8Parameters(pt, remoteMsection));
+
+      mMaxFs = vp8Params.max_fs;
+      mMaxFr = vp8Params.max_fr;
     }
     return true;
   }
 
   virtual bool
-  LoadRtcpFbs(const SdpRtcpFbAttributeList::Feedback& feedback) override
+  LoadRecvParameters(const mozilla::SdpMediaSection& remoteMsection,
+                     const std::string& pt) override
   {
-    switch (feedback.type) {
-      case SdpRtcpFbAttributeList::kAck:
-        mAckFbTypes.push_back(feedback.parameter);
-        break;
-      case SdpRtcpFbAttributeList::kCcm:
-        mCcmFbTypes.push_back(feedback.parameter);
-        break;
-      case SdpRtcpFbAttributeList::kNack:
-        mNackFbTypes.push_back(feedback.parameter);
-        break;
-      case SdpRtcpFbAttributeList::kApp:
-      case SdpRtcpFbAttributeList::kTrrInt:
-        // We don't support these, ignore.
-        {}
+    const SdpAttributeList& attrs(remoteMsection.GetAttributeList());
+
+    if (attrs.HasAttribute(SdpAttribute::kRtcpFbAttribute)) {
+      auto& rtcpfbs = attrs.GetRtcpFb().mFeedbacks;
+      for (auto i = rtcpfbs.begin(); i != rtcpfbs.end(); ++i) {
+        if (i->pt == mDefaultPt || i->pt == "*") {
+          switch (i->type) {
+            case SdpRtcpFbAttributeList::kAck:
+              mAckFbTypes.push_back(i->parameter);
+              break;
+            case SdpRtcpFbAttributeList::kCcm:
+              mCcmFbTypes.push_back(i->parameter);
+              break;
+            case SdpRtcpFbAttributeList::kNack:
+              mNackFbTypes.push_back(i->parameter);
+              break;
+            case SdpRtcpFbAttributeList::kApp:
+            case SdpRtcpFbAttributeList::kTrrInt:
+              // We don't support these, ignore.
+              {}
+          }
+        }
+      }
+    }
+
+    if (mName == "H264") {
+      SdpFmtpAttributeList::H264Parameters h264Params(
+          GetH264Parameters(pt, remoteMsection));
+      if (!h264Params.level_asymmetry_allowed) {
+        SetSaneH264Level(std::min(GetSaneH264Level(h264Params.profile_level_id),
+                                  GetSaneH264Level(mProfileLevelId)),
+                         &mProfileLevelId);
+      }
     }
     return true;
   }
 
   enum Subprofile {
     kH264ConstrainedBaseline,
     kH264Baseline,
     kH264Main,
@@ -480,70 +632,36 @@ struct JsepVideoCodecDescription : publi
     if ((profileLevelId & 0xFFFF00) == 0x2C1000) {
       return kH264CALVC44;
     }
 
     return kH264UnknownSubprofile;
   }
 
   virtual bool
-  ParametersMatch(const SdpFmtpAttributeList::Parameters* fmtp) const
-      override
+  ParametersMatch(const std::string& fmt,
+                  const SdpMediaSection& remoteMsection) const override
   {
     if (mName == "H264") {
-      if (!fmtp) {
-        // No fmtp means that we cannot assume level asymmetry is allowed,
-        // and since we have no way of knowing the profile-level-id, we can't
-        // say that we match.
+      SdpFmtpAttributeList::H264Parameters h264Params(
+          GetH264Parameters(fmt, remoteMsection));
+
+      if (h264Params.packetization_mode != mPacketizationMode) {
         return false;
       }
 
-      auto* h264Params =
-          static_cast<const SdpFmtpAttributeList::H264Parameters*>(fmtp);
-
-      if (!h264Params->level_asymmetry_allowed) {
-        if (GetSubprofile(h264Params->profile_level_id) !=
-            GetSubprofile(mProfileLevelId)) {
-          return false;
-        }
-      }
-
-      if (h264Params->packetization_mode != mPacketizationMode) {
+      if (GetSubprofile(h264Params.profile_level_id) !=
+          GetSubprofile(mProfileLevelId)) {
         return false;
       }
     }
+
     return true;
   }
 
-  void
-  LoadH264Parameters(const SdpFmtpAttributeList::Parameters& params)
-  {
-    const SdpFmtpAttributeList::H264Parameters& h264Params =
-        static_cast<const SdpFmtpAttributeList::H264Parameters&>(params);
-
-    mMaxFs = h264Params.max_fs;
-    mProfileLevelId = h264Params.profile_level_id;
-    mPacketizationMode = h264Params.packetization_mode;
-    mMaxMbps = h264Params.max_mbps;
-    mMaxCpb = h264Params.max_cpb;
-    mMaxDpb = h264Params.max_dpb;
-    mMaxBr = h264Params.max_br;
-    mSpropParameterSets = h264Params.sprop_parameter_sets;
-  }
-
-  void
-  LoadVP8Parameters(const SdpFmtpAttributeList::Parameters& params)
-  {
-    const SdpFmtpAttributeList::VP8Parameters& vp8Params =
-        static_cast<const SdpFmtpAttributeList::VP8Parameters&>(params);
-
-    mMaxFs = vp8Params.max_fs;
-    mMaxFr = vp8Params.max_fr;
-  }
-
   JSEP_CODEC_CLONE(JsepVideoCodecDescription)
 
   std::vector<std::string> mAckFbTypes;
   std::vector<std::string> mNackFbTypes;
   std::vector<std::string> mCcmFbTypes;
 
   uint32_t mMaxFs;
 
@@ -575,30 +693,16 @@ struct JsepApplicationCodecDescription :
   }
 
   virtual void
   AddRtcpFbs(SdpRtcpFbAttributeList& rtcpfb) const override
   {
     // Nothing to do here.
   }
 
-  virtual bool
-  LoadFmtps(const SdpFmtpAttributeList::Parameters& params) override
-  {
-    // TODO: Is there anything to do here?
-    return true;
-  }
-
-  virtual bool
-  LoadRtcpFbs(const SdpRtcpFbAttributeList::Feedback& feedback) override
-  {
-    // Nothing to do
-    return true;
-  }
-
   JSEP_CODEC_CLONE(JsepApplicationCodecDescription)
 
   // Override, uses sctpmap instead of rtpmap
   virtual bool
   Matches(const std::string& fmt,
           const SdpMediaSection& remoteMsection) const override
   {
     auto& attrs = remoteMsection.GetAttributeList();
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -911,18 +911,22 @@ void
 JsepSessionImpl::AddCommonCodecs(const SdpMediaSection& remoteMsection,
                                  SdpMediaSection* msection)
 {
   const std::vector<std::string>& formats = remoteMsection.GetFormats();
 
   for (auto fmt = formats.begin(); fmt != formats.end(); ++fmt) {
     JsepCodecDescription* codec = FindMatchingCodec(*fmt, remoteMsection);
     if (codec) {
-      codec->mDefaultPt = *fmt; // Reflect the other side's PT
-      codec->AddToMediaSection(*msection);
+      UniquePtr<JsepCodecDescription> negotiated(
+          codec->MakeNegotiatedCodec(*fmt, remoteMsection));
+      if (negotiated) {
+        negotiated->AddToMediaSection(*msection);
+        codec->mDefaultPt = *fmt; // Remember the other side's PT
+      }
       // TODO(bug 1099351): Once bug 1073475 is fixed on all supported
       // versions, we can remove this limitation.
       break;
     }
   }
 }
 
 void
@@ -1676,40 +1680,51 @@ JsepSessionImpl::NegotiateTrack(const Sd
     JsepCodecDescription* codec = FindMatchingCodec(*fmt, remoteMsection);
 
     if (!codec) {
       continue;
     }
 
     bool sending = (direction == JsepTrack::kJsepTrackSending);
 
-    // We need to take the remote side's parameters into account so we can
-    // configure our send media.
-    // |codec| is assumed to have the necessary state about our own config
-    // in order to negotiate.
-    JsepCodecDescription* negotiated =
-        codec->MakeNegotiatedCodec(remoteMsection, *fmt, sending);
-
-    if (!negotiated) {
+    // Everywhere else in JsepSessionImpl, a JsepCodecDescription describes
+    // what one side puts in its SDP. However, we don't want that here; we want
+    // a JsepCodecDescription that instead encapsulates all the parameters
+    // that deal with sending (or receiving). For sending, some of these
+    // parameters will come from the local codec config (eg; rtcp-fb), others
+    // will come from the remote SDP (eg; max-fps), and still others can only be
+    // determined by inspecting both local config and remote SDP (eg;
+    // profile-level-id when level-asymmetry-allowed is 0).
+    UniquePtr<JsepCodecDescription> sendOrReceiveCodec;
+
+    if (sending) {
+      sendOrReceiveCodec =
+        Move(codec->MakeSendCodec(remoteMsection, *fmt));
+    } else {
+      sendOrReceiveCodec =
+        Move(codec->MakeRecvCodec(remoteMsection, *fmt));
+    }
+
+    if (!sendOrReceiveCodec) {
       continue;
     }
 
     if (remoteMsection.GetMediaType() == SdpMediaSection::kAudio ||
         remoteMsection.GetMediaType() == SdpMediaSection::kVideo) {
       // Sanity-check that payload type can work with RTP
       uint16_t payloadType;
-      if (!negotiated->GetPtAsInt(&payloadType) ||
+      if (!sendOrReceiveCodec->GetPtAsInt(&payloadType) ||
           payloadType > UINT8_MAX) {
         JSEP_SET_ERROR("audio/video payload type is not an 8 bit unsigned int: "
                        << *fmt);
         return NS_ERROR_INVALID_ARG;
       }
     }
 
-    negotiatedDetails->mCodecs.push_back(negotiated);
+    negotiatedDetails->mCodecs.push_back(sendOrReceiveCodec.release());
     break;
   }
 
   if (negotiatedDetails->mCodecs.empty()) {
     JSEP_SET_ERROR("Failed to negotiate codec details for all codecs");
     return NS_ERROR_INVALID_ARG;
   }
 
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -1,19 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "CSFLog.h"
 #include "nspr.h"
 #include "plstr.h"
 
-// For rtcp-fb constants
-#include "ccsdp.h"
-
 #include "VideoConduit.h"
 #include "AudioConduit.h"
 #include "nsThreadUtils.h"
 #include "LoadManager.h"
 #include "YuvStamper.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
@@ -22,16 +19,23 @@
 #include "webrtc/common_video/interface/native_handle.h"
 #include "webrtc/video_engine/include/vie_errors.h"
 #include "browser_logging/WebRtcLog.h"
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidJNIWrapper.h"
 #endif
 
+// for ntohs
+#ifdef _MSC_VER
+#include "Winsock2.h"
+#else
+#include <netinet/in.h>
+#endif
+
 #include <algorithm>
 #include <math.h>
 
 #define DEFAULT_VIDEO_MAX_FRAMERATE 30
 
 namespace mozilla {
 
 static const char* logTag ="WebrtcVideoSessionConduit";
--- a/media/webrtc/signaling/src/sdp/SdpAttribute.h
+++ b/media/webrtc/signaling/src/sdp/SdpAttribute.h
@@ -938,21 +938,23 @@ public:
     virtual void Serialize(std::ostream& os) const = 0;
 
     SdpRtpmapAttributeList::CodecType codec_type;
   };
 
   class H264Parameters : public Parameters
   {
   public:
+    static const uint32_t kDefaultProfileLevelId = 0x420010;
+
     H264Parameters()
         : Parameters(SdpRtpmapAttributeList::kH264),
           packetization_mode(0),
           level_asymmetry_allowed(false),
-          profile_level_id(0),
+          profile_level_id(kDefaultProfileLevelId),
           max_mbps(0),
           max_fs(0),
           max_cpb(0),
           max_dpb(0),
           max_br(0)
     {
       memset(sprop_parameter_sets, 0, sizeof(sprop_parameter_sets));
     }
deleted file mode 100644
--- a/media/webrtc/signaling/src/sdp/sipcc/ccsdp.c
+++ /dev/null
@@ -1,335 +0,0 @@
-/* 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 "sdp.h"
-#include "ccapi.h"
-
-int ccsdpAttrGetFmtpInst(void *sdp_ptr, uint16_t level, uint16_t payload_num)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-  if ( sdpp->dest_sdp == NULL ) {
-    return 0;
-  }
-  return sdp_find_fmtp_inst(sdpp->dest_sdp, level, payload_num);
-}
-
-const char* ccsdpAttrGetFmtpParamSets(void *sdp_ptr, uint16_t level,
-                                            uint8_t cap_num, uint16_t inst_num)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->dest_sdp == NULL ) {
-    return NULL;
-  }
-  return sdp_attr_get_fmtp_param_sets(sdpp->dest_sdp, level, cap_num, inst_num);
-}
-
-sdp_result_e ccsdpAttrGetFmtpPackMode(void *sdp_ptr, uint16_t level,
-                         uint8_t cap_num, uint16_t inst_num, uint16_t *val)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->dest_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_get_fmtp_pack_mode(sdpp->dest_sdp, level, cap_num, inst_num, val);
-}
-
-sdp_result_e ccsdpAttrGetFmtpLevelAsymmetryAllowed(void *sdp_ptr, uint16_t level,
-                         uint8_t cap_num, uint16_t inst_num, uint16_t *val)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->dest_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_get_fmtp_level_asymmetry_allowed(sdpp->dest_sdp, level, cap_num, inst_num, val);
-}
-
-const char* ccsdpAttrGetFmtpProfileLevelId (void *sdp_ptr, uint16_t level,
-                                          uint8_t cap_num, uint16_t inst_num)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->dest_sdp == NULL ) {
-    return NULL;
-  }
-  return sdp_attr_get_fmtp_profile_id(sdpp->dest_sdp, level, cap_num, inst_num);
-}
-
-
-
-sdp_result_e ccsdpAttrGetFmtpMaxMbps (void *sdp_ptr, uint16_t level,
-                                uint8_t cap_num, uint16_t inst_num, uint32_t *val)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->dest_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_get_fmtp_max_mbps(sdpp->dest_sdp, level, cap_num, inst_num, val);
-}
-
-sdp_result_e ccsdpAttrGetFmtpMaxFs (void *sdp_ptr, uint16_t level,
-                             uint8_t cap_num, uint16_t inst_num, uint32_t *val)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->dest_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_get_fmtp_max_fs(sdpp->dest_sdp, level, cap_num, inst_num, val);
-}
-
-sdp_result_e ccsdpAttrGetFmtpMaxCpb (void *sdp_ptr, uint16_t level,
-                                 uint8_t cap_num, uint16_t inst_num, uint32_t *val)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->dest_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_get_fmtp_max_cpb(sdpp->dest_sdp, level, cap_num, inst_num, val);
-}
-
-sdp_result_e ccsdpAttrGetFmtpMaxBr (void *sdp_ptr, uint16_t level,
-                             uint8_t cap_num, uint16_t inst_num, uint32_t* val)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->dest_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_get_fmtp_max_br(sdpp->dest_sdp, level, cap_num, inst_num, val);
-}
-
-int ccsdpGetBandwidthValue (void *sdp_ptr, uint16_t level, uint16_t inst_num)
-
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->dest_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_get_bw_value(sdpp->dest_sdp, level, inst_num);
-}
-
-sdp_result_e ccsdpAttrGetFmtpMaxDpb (void *sdp_ptr, uint16_t level,
-                               uint8_t cap_num, uint16_t inst_num, uint32_t *val)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->dest_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_get_fmtp_max_dpb(sdpp->dest_sdp, level, cap_num, inst_num, val);
-}
-
-sdp_result_e ccsdpAddNewAttr (void *sdp_ptr, uint16_t level, uint8_t cap_num,
-                               sdp_attr_e attr_type, uint16_t *inst_num)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_add_new_attr(sdpp->src_sdp, level, cap_num, attr_type, inst_num);
-}
-
-sdp_result_e ccsdpAttrSetFmtpPayloadType (void *sdp_ptr, uint16_t level,
-                              uint8_t cap_num, uint16_t inst_num, uint16_t payload_num)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_payload_type(sdpp->src_sdp, level, cap_num, inst_num, payload_num);
-}
-
-
-sdp_result_e ccsdpAttrSetFmtpPackMode (void *sdp_ptr, uint16_t level,
-                                          uint8_t cap_num, uint16_t inst_num, uint16_t pack_mode)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_pack_mode(sdpp->src_sdp, level, cap_num, inst_num, pack_mode);
-}
-
-sdp_result_e ccsdpAttrSetFmtpLevelAsymmetryAllowed (void *sdp_ptr, uint16_t level,
-                                          uint8_t cap_num, uint16_t inst_num, uint16_t asym_allowed)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_level_asymmetry_allowed(sdpp->src_sdp, level, cap_num, inst_num, asym_allowed);
-}
-
-
-sdp_result_e ccsdpAttrSetFmtpProfileLevelId (void *sdp_ptr, uint16_t level,
-                               uint8_t cap_num, uint16_t inst_num, const char *profile_level_id)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_profile_level_id(sdpp->src_sdp, level, cap_num, inst_num, profile_level_id);
-}
-
-
-sdp_result_e ccsdpAttrSetFmtpParameterSets (void *sdp_ptr, uint16_t level,
-                                     uint8_t cap_num, uint16_t inst_num, const char *parameter_sets)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_parameter_sets(sdpp->src_sdp, level, cap_num, inst_num, parameter_sets);
-}
-
-
-
-sdp_result_e ccsdpAttrSetFmtpMaxBr (void *sdp_ptr, uint16_t level,
-                              uint8_t cap_num, uint16_t inst_num, uint32_t max_br)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_max_br(sdpp->src_sdp, level, cap_num, inst_num, max_br);
-}
-
-
-sdp_result_e ccsdpAttrSetFmtpMaxMbps (void *sdp_ptr, uint16_t level,
-                              uint8_t cap_num, uint16_t inst_num, uint32_t max_mbps)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_max_mbps(sdpp->src_sdp, level, cap_num, inst_num, max_mbps);
-}
-
-sdp_result_e ccsdpAttrSetFmtpMaxFs (void *sdp_ptr, uint16_t level,
-                        uint8_t cap_num, uint16_t inst_num, uint32_t max_fs)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_max_fs(sdpp->src_sdp, level, cap_num, inst_num, max_fs);
-}
-
-sdp_result_e ccsdpAttrSetFmtpMaxCpb (void *sdp_ptr, uint16_t level,
-                            uint8_t cap_num, uint16_t inst_num, uint32_t max_cpb)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_max_cpb(sdpp->src_sdp, level, cap_num, inst_num, max_cpb);
-}
-
-sdp_result_e ccsdpAttrSetFmtpMaxDbp (void *sdp_ptr, uint16_t level,
-                                  uint8_t cap_num, uint16_t inst_num, uint32_t max_dpb)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_max_dpb(sdpp->src_sdp, level, cap_num, inst_num, max_dpb);
-}
-
-
-sdp_result_e ccsdpAttrSetFmtpQcif  (void *sdp_ptr, uint16_t level,
-                             uint8_t cap_num, uint16_t inst_num, uint16_t qcif)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_qcif(sdpp->src_sdp, level, cap_num, inst_num, qcif);
-}
-
-sdp_result_e ccsdpAttrSetFmtpSqcif  (void *sdp_ptr, uint16_t level,
-                            uint8_t cap_num, uint16_t inst_num, uint16_t sqcif)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_attr_set_fmtp_sqcif(sdpp->src_sdp, level, cap_num, inst_num, sqcif);
-}
-
-sdp_result_e ccsdpAddNewBandwidthLine (void *sdp_ptr, uint16_t level, sdp_bw_modifier_e bw_modifier, uint16_t *inst_num)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_add_new_bw_line(sdpp->src_sdp, level, bw_modifier, inst_num);
-}
-
-
-sdp_result_e ccsdpSetBandwidth (void *sdp_ptr, uint16_t level, uint16_t inst_num,
-                         sdp_bw_modifier_e bw_modifier, uint32_t bw_val)
-{
-  cc_sdp_t *sdpp = sdp_ptr;
-
-  if ( sdpp->src_sdp == NULL ) {
-    return SDP_INVALID_PARAMETER;
-  }
-  return sdp_set_bw(sdpp->src_sdp, level, inst_num, bw_modifier, bw_val);
-}
-
-const char * ccsdpCodecName(rtp_ptype ptype)
-{
-  switch (ptype)
- {
-    case RTP_NONE:       return "NONE";
-    case RTP_PCMU:       return "PCMU";
-    case RTP_CELP:       return "CELP";
-    case RTP_G726:       return "G726";
-    case RTP_GSM:        return "GSM";
-    case RTP_G723:       return "G723";
-    case RTP_DVI4:       return "DVI4";
-    case RTP_DVI4_II:    return "DVI4_II";
-    case RTP_LPC:        return "LPC";
-    case RTP_PCMA:       return "PCMA";
-    case RTP_G722:       return "G722";
-    case RTP_G728:       return "G728";
-    case RTP_G729:       return "G729";
-    case RTP_JPEG:       return "JPEG";
-    case RTP_NV:         return "NV";
-    case RTP_H261:       return "H261";
-    case RTP_H264_P0:    return "H264_P0";
-    case RTP_H264_P1:    return "H264_P1";
-    case RTP_AVT:        return "AVT";
-    case RTP_L16:        return "L16";
-    case RTP_H263:       return "H263";
-    case RTP_ILBC:       return "iLBC";
-    case RTP_OPUS:       return "OPUS";
-    case RTP_VP8:        return "VP8";
-    case RTP_VP9:        return "VP9";
-    case RTP_I420:       return "I420";
-    /* case RTP_ISAC:       return "ISAC"; */
-  }
-  return "UNKNOWN";
-}
-
--- a/media/webrtc/signaling/src/sdp/sipcc/ccsdp.h
+++ b/media/webrtc/signaling/src/sdp/sipcc/ccsdp.h
@@ -1,53 +1,12 @@
 /* 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/. */
 
-/**
- *  Skip this file if not doing video on the device.
- *
- *  The ccsdp_xxx api's provide a means to query and populate the SDP attributes
- *  for video m lines. These Api are not needed if we are not supporting video on
- *  the platform. For audio the stack will populate the appropriate attributes
- *
- *
- *  These API's can be invoked from the vcmCheckAttrs() and vcmPopulateAttrs()
- *  methods to populate or extract the value of specific attributes in the video SDP.
- *  These api require an handle to the SDP that is passed in the above methods.
- * <pre>
- * sdp_handle     The SDP handle
- * level       The level the attribute is defined.  Can be either
- *             SDP_SESSION_LEVEL or 0-n specifying a media line level.
- * inst_num    The instance number of the attribute.  Multiple instances
- *             of a particular attribute may exist at each level and so
- *             the inst_num determines the particular attribute at that
- *             level that should be accessed.  Note that this is the
- *             instance number of the specified type of attribute, not the
- *             overall attribute number at the level.  Also note that the
- *             instance number is 1-based.  For example:
- *             v=0
- *             o=mhandley 2890844526 2890842807 IN IP4 126.16.64.4
- *             s=SDP Seminar
- *             c=IN IP4 10.1.0.2
- *             t=0 0
- *             m=audio 1234 RTP/AVP 0 101 102
- *             a=foo 1
- *             a=foo 2
- *             a=bar 1   # This is instance 1 of attribute bar.
- *             a=foo 3   # This is instance 3 of attribute foo.
- * cap_num     Almost all of the attributes may be defined as X-cpar
- *             parameters (with the exception of X-sqn, X-cap, and X-cpar).
- *             If the cap_num is set to zero, then the attribute is not
- *             an X-cpar parameter attribute.  If the cap_num is any other
- *             value, it specifies the capability number that the X-cpar
- *             attribute is specified for.
- * </pre>
- */
-
 #ifndef __CCSDP_H__
 #define __CCSDP_H__
 
 #include "cpr_types.h"
 #include "ccsdp_rtcp_fb.h"
 
 #define SIPSDP_ILBC_MODE20 20
 
@@ -80,48 +39,16 @@ typedef enum rtp_ptype_
     RTP_ILBC         = 116, /* used only to make an offer */
     RTP_OPUS         = 109,
     RTP_VP8          = 120,
     RTP_VP9          = 121,
     RTP_I420         = 124,
     RTP_ISAC         = 124
 } rtp_ptype;
 
-/**
- * IANA-registered static payload types for the RTP/AVP profile.
- * See http://www.iana.org/assignments/rtp-parameters/rtp-parameters.xml
- */
-typedef enum static_rtp_ptype_
-{
-    STATIC_RTP_AVP_PCMU_8000_1          = 0,
-    STATIC_RTP_AVP_GSM_8000_1           = 3,
-    STATIC_RTP_AVP_G723_8000_1          = 4,
-    STATIC_RTP_AVP_DVI4_8000_1          = 5,
-    STATIC_RTP_AVP_DVI4_16000_1         = 6,
-    STATIC_RTP_AVP_LPC_8000_1           = 7,
-    STATIC_RTP_AVP_PCMA_8000_1          = 8,
-    STATIC_RTP_AVP_G722_8000_1          = 9,
-    STATIC_RTP_AVP_L16_44100_2          = 10,
-    STATIC_RTP_AVP_L16_44100_1          = 11,
-    STATIC_RTP_AVP_QCELP_8000_1         = 12,
-    STATIC_RTP_AVP_CN_8000_1            = 13,
-    STATIC_RTP_AVP_MPA_90000_1          = 14,
-    STATIC_RTP_AVP_G728_8000_1          = 15,
-    STATIC_RTP_AVP_DVI4_11025_1         = 16,
-    STATIC_RTP_AVP_DVI4_22050_1         = 17,
-    STATIC_RTP_AVP_G729_8000_1          = 18,
-    STATIC_RTP_AVP_CELB_90000_1         = 25,
-    STATIC_RTP_AVP_JPEG_90000_1         = 26,
-    STATIC_RTP_AVP_NV_90000_1           = 28,
-    STATIC_RTP_AVP_H261_90000_1         = 31,
-    STATIC_RTP_AVP_MPV_90000_1          = 32,
-    STATIC_RTP_AVP_MP2T_90000_1         = 33,
-    STATIC_RTP_AVP_H263_90000_1         = 34
-} static_rtp_ptype;
-
 typedef struct {
     const char *name;
     int         value;
 } ccsdp_key_table_entry_t;
 
 typedef enum max_coded_audio_bandwidth_ {
     opus_nb  = 0,    /* Narrowband */
     opus_mb  = 1,    /* Mediumband */
@@ -255,442 +182,20 @@ typedef enum {
     SDP_ATTR_MSID_SEMANTIC,
     SDP_ATTR_BUNDLE_ONLY,
     SDP_ATTR_END_OF_CANDIDATES,
     SDP_ATTR_ICE_OPTIONS,
     SDP_ATTR_SSRC,
     SDP_MAX_ATTR_TYPES,
     SDP_ATTR_INVALID
 } sdp_attr_e;
-/* This is here so that it can be used in the VcmSIPCCBinding interface */
+
 typedef enum {
     SDP_SETUP_NOT_FOUND = -1,
     SDP_SETUP_ACTIVE = 0,
     SDP_SETUP_PASSIVE,
     SDP_SETUP_ACTPASS,
     SDP_SETUP_HOLDCONN,
     SDP_MAX_SETUP,
     SDP_SETUP_UNKNOWN
 } sdp_setup_type_e;
 
-/**
- * Returns the inst_num value for the specified FTMP attr
- *
- * @param[in]  sdp_handle     The SDP handle
- * @param[in]  level       The level to check for the attribute.
- *
- * @return     inst_num    The attribute instance number to check, or -1 for failure
- */
-
-int ccsdpAttrGetFmtpInst(void *sdp_handle, uint16_t level, uint16_t payload_num);
-
-/**
- * Gets the value of the fmtp attribute- parameter-sets parameter for H.264 codec
- *
- * @param[in]  sdp_handle     The SDP handle
- * @param[in]  level       The level to check for the attribute.
- * @param[in]  cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in]  inst_num    The attribute instance number to check.
- *
- * @return      parameter-sets value.
- */
-
-const char* ccsdpAttrGetFmtpParamSets(void *sdp_handle, uint16_t level,
-                                            uint8_t cap_num, uint16_t inst_num);
-
-/**
- * Gets the value of the fmtp attribute- packetization-mode parameter for H.264 codec
- *
- * @param[in]  sdp_handle     The SDP handle
- * @param[in]  level       The level to check for the attribute.
- * @param[in]  cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in]  inst_num    The attribute instance number to check.
- * @param[out] *val        packetization-mode value in the range 0 - 2.
- *
- * @return     sdp_result_e         SDP_SUCCESS = SUCCESS
- */
-sdp_result_e ccsdpAttrGetFmtpPackMode(void *sdp_handle, uint16_t level,
-                         uint8_t cap_num, uint16_t inst_num, uint16_t *val);
-/**
- * Gets the value of the fmtp attribute- level asymmetry allowed parameter for H.264 codec
- *
- * @param[in]  sdp_handle     The SDP handle
- * @param[in]  level       The level to check for the attribute.
- * @param[in]  cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in]  inst_num    The attribute instance number to check.
- * @param[out] *val        level-asymmetry-allowed param value in the range 0 - 1.
- *
- * @return     sdp_result_e         SDP_SUCCESS = SUCCESS
- */
-sdp_result_e ccsdpAttrGetFmtpLevelAsymmetryAllowed(void *sdp_handle, uint16_t level,
-                         uint8_t cap_num, uint16_t inst_num, uint16_t *val);
-
-
-/**
- * Gets the value of the fmtp attribute- profile-level-id parameter for H.264 codec
- *
- * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
- * @param[in]  level       The level to check for the attribute.
- * @param[in]  cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in]  inst_num    The attribute instance number to check.
- *
- * @return   char *      profile-level-id value.
- */
-const char* ccsdpAttrGetFmtpProfileLevelId (void *sdp_handle, uint16_t level,
-                                          uint8_t cap_num, uint16_t inst_num);
-
-/**
- * Gets the value of the fmtp attribute- max-mbps parameter for H.264 codec
- *
- * @param[in] sdp_handle     The SDP handle
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the
- *                          attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[out] *val        max-mbps value.
- *
- * @return     sdp_result_e         SDP_SUCCESS
- */
-
-sdp_result_e ccsdpAttrGetFmtpMaxMbps (void *sdp_handle, uint16_t level,
-                                uint8_t cap_num, uint16_t inst_num, uint32_t *val);
-
-/**
- * Gets the value of the fmtp attribute- max-fs parameter for H.264 codec
- *
- * @param[in] sdp_handle     The SDP handle
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the
- *                          attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[out] *val        max-fs value.
- *
- * @return     sdp_result_e         SDP_SUCCESS
- */
-sdp_result_e ccsdpAttrGetFmtpMaxFs (void *sdp_handle, uint16_t level,
-                             uint8_t cap_num, uint16_t inst_num, uint32_t *val);
-
-/**
- * Gets the value of the fmtp attribute- max-cpb parameter for H.264 codec
- * @param[in] sdp_handle     The SDP handle
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param [out] *val      max-cpb value.
- *
- * @return     sdp_result_e         SDP_SUCCESS
- */
-sdp_result_e ccsdpAttrGetFmtpMaxCpb (void *sdp_handle, uint16_t level,
-                                 uint8_t cap_num, uint16_t inst_num, uint32_t *val);
-
-/**
- * Gets the value of the fmtp attribute- max-br parameter for H.264 codec
- *
- * @param[in] sdp_handle     The SDP handle
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param [out] *val      max-br value.
- *
- * @return     sdp_result_e         SDP_SUCCESS
- */
-sdp_result_e ccsdpAttrGetFmtpMaxBr (void *sdp_handle, uint16_t level,
-                             uint8_t cap_num, uint16_t inst_num, uint32_t* val);
-
-/**
- * Returns the bandwidth value parameter from the b= line.
- *
- * @param[in] sdp_handle     The SDP handle
- * @param[in] level       The level from which to get the bw value.
- * @param[in] inst_num    instance number of bw line at the level. The first
- *                          instance has a inst_num of 1 and so on.
- *
- * @return     A valid numerical bw value or SDP_INVALID_VALUE(-2).
- */
-int ccsdpGetBandwidthValue (void *sdp_handle, uint16_t level, uint16_t inst_num);
-
-/**
- * Add a new attribute of the specified type at the given level and capability
- * level or base attribute if cap_num is zero.
- *
- * @param[in] sdp_handle     The SDP handle
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] attr_type   The type of attribute to add.
- * @param[in] inst_num    Pointer to a uint16_t in which to return the instance number of the newly added attribute.
- *
- * @return     sdp_result_e
- *              SDP_SUCCESS            Attribute was added successfully.
- *              SDP_NO_RESOURCE        No memory avail for new attribute.
- *              SDP_INVALID_PARAMETER  Specified media line is not defined.
- */
-sdp_result_e ccsdpAddNewAttr (void *sdp_handle, uint16_t level, uint8_t cap_num,
-                               sdp_attr_e attr_type, uint16_t *inst_num);
-
-/**
- * Gets the value of the fmtp attribute- max-dpb parameter for H.264 codec
- *
- * @param[in] sdp_handle     The SDP handle
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[out] *val       max-dpb value.
- *
- * @return     sdp_result_e
- *              SDP_SUCCESS            Attribute was added successfully.
- */
-
-sdp_result_e ccsdpAttrGetFmtpMaxDpb (void *sdp_handle, uint16_t level,
-                               uint8_t cap_num, uint16_t inst_num, uint32_t *val);
-
-
-/**
- * Sets the value of the fmtp attribute payload type parameter for the given attribute.
- *
- * @param[in] sdp_handle     The SDP handle
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[in] payload_num New payload type value.
- *
- * @return     SDP_SUCCESS            Attribute was added successfully.
- *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
- */
-sdp_result_e ccsdpAttrSetFmtpPayloadType (void *sdp_handle, uint16_t level,
-                              uint8_t cap_num, uint16_t inst_num, uint16_t payload_num);
-
-/**
- * Sets the value of the packetization mode attribute parameter for the given attribute.
- *
- * @param[in] sdp_handle     The SDP handle returned by sdp_init_description.
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[in] pack_mode   Packetization mode value
- *
- * @return     SDP_SUCCESS            Attribute was added successfully.
- *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
- */
-
-sdp_result_e ccsdpAttrSetFmtpPackMode (void *sdp_handle, uint16_t level,
-                                          uint8_t cap_num, uint16_t inst_num, uint16_t pack_mode);
-/**
- * Sets the value of the level-asymmetry-allowed attribute parameter for the given attribute.
- *
- * @param[in] sdp_handle     The SDP handle returned by sdp_init_description.
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[in] level_asymmetry_allowed   level asymmetry allowed value (0 or 1).
- *
- * @return     SDP_SUCCESS            Attribute was added successfully.
- *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
- */
-
-sdp_result_e ccsdpAttrSetFmtpLevelAsymmetryAllowed (void *sdp_handle, uint16_t level,
-                                          uint8_t cap_num, uint16_t inst_num, uint16_t level_asymmetry_allowed);
-
-/**
- * Sets the value of the profile-level-id parameter for the given attribute.
- *
- * @param[in] sdp_handle     The SDP handle returned by sdp_init_description.
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[in] profile_level_id profile_level_id to be set
- *
- * @return     SDP_SUCCESS            Attribute was added successfully.
- *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
- */
-
-sdp_result_e ccsdpAttrSetFmtpProfileLevelId (void *sdp_handle, uint16_t level,
-                               uint8_t cap_num, uint16_t inst_num, const char *profile_level_id);
-
-/**
- * Sets the value of the profile-level-id parameter for the given attribute.
- *
- * @param[in] sdp_handle     The SDP handle returned by sdp_init_description.
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[in] parameter_sets parameter_sets to be set
- *
- * @return     SDP_SUCCESS            Attribute was added successfully.
- *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
- */
-
-sdp_result_e ccsdpAttrSetFmtpParameterSets (void *sdp_handle, uint16_t level,
-                                     uint8_t cap_num, uint16_t inst_num, const char *parameter_sets);
-
-/**
- * Sets the value of the max-br parameter for the given attribute.
- *
- * @param[in] sdp_handle     The SDP handle returned by sdp_init_description.
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[in] max_br    max_br value to be set
- *
- * @return     SDP_SUCCESS            Attribute was added successfully.
- *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
- */
-
-
-sdp_result_e ccsdpAttrSetFmtpMaxBr (void *sdp_handle, uint16_t level,
-                              uint8_t cap_num, uint16_t inst_num, uint32_t max_br);
-
-/**
- * Sets the value of the fmtp attribute- max-mbps parameter for H.264 codec
- *
- * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[in] max_mbps    value of max_mbps to be set
- *
- * @return     SDP_SUCCESS            Attribute was added successfully.
- *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
- */
-
-sdp_result_e ccsdpAttrSetFmtpMaxMbps (void *sdp_handle, uint16_t level,
-                              uint8_t cap_num, uint16_t inst_num, uint32_t max_mbps);
-
-/**
- * Sets the value of the fmtp attribute- max-fs parameter for H.264 codec
- * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[in] max_fs      max_fs value to be set
- *
- * @return     SDP_SUCCESS            Attribute was added successfully.
- *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
- */
-sdp_result_e ccsdpAttrSetFmtpMaxFs (void *sdp_handle, uint16_t level,
-                        uint8_t cap_num, uint16_t inst_num, uint32_t max_fs);
-/**
- * Sets the value of the fmtp attribute- max-cbp parameter for H.264 codec
- * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[in] max_cpb      max_cbp value to be set
- *
- * @return     SDP_SUCCESS            Attribute was added successfully.
- *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
- */
-
-sdp_result_e ccsdpAttrSetFmtpMaxCpb (void *sdp_handle, uint16_t level,
-                            uint8_t cap_num, uint16_t inst_num, uint32_t max_cpb);
-/**
- * Sets the value of the fmtp attribute- max-dbp parameter for H.264 codec
- * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
- * @param[in] level       The level to check for the attribute.
- * @param[in] cap_num     The capability number associated with the attribute if any.  If none, should be zero.
- * @param[in] inst_num    The attribute instance number to check.
- * @param[in] max_dpb      max_dbp value to be set
- *
- * @return     SDP_SUCCESS            Attribute was added successfully.
- *             SDP_INVALID_PARAMETER  Specified attribute is not defined.
- */
-
-sdp_result_e ccsdpAttrSetFmtpMaxDbp (void *sdp_handle, uint16_t level,
-                                  uint8_t cap_num, uint16_t inst_num, uint32_t max_dpb);
-
-
-/**
- * Sets the value of the fmtp attribute qcif parameter for the given attribute.
- * @param[in]  sdp_handle     The SDP handle returned by sdp_init_description.
- * @par