Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 27 Nov 2015 11:28:17 +0100
changeset 310440 bb512bf5a0669afa0d8158daf906a4223f4ba6ce
parent 310439 d1667f3f465389b4a5c75e5832756238fde49aa3 (current diff)
parent 310405 47b49b0d32360fab04b11ff9120970979c426911 (diff)
child 310441 556ab9e3521ba6b53393e09a203b06c811a8ec7b
push id1040
push userraliiev@mozilla.com
push dateMon, 29 Feb 2016 17:11:22 +0000
treeherdermozilla-release@8c3167321162 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone45.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 mozilla-central to fx-team
testing/web-platform/meta/dom/nodes/MutationObserver-document.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/register-wait-forever-in-install-worker.https.html.ini
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -2156,17 +2156,17 @@ DocAccessible::MoveChild(Accessible* aCh
   MaybeNotifyOfValueChange(parent);
   FireDelayedEvent(reorderEvent);
 }
 
 void
 DocAccessible::PutChildrenBack(nsTArray<RefPtr<Accessible> >* aChildren,
                                uint32_t aStartIdx)
 {
-  nsTArray<Accessible*> containers;
+  nsTArray<RefPtr<Accessible> > containers;
   for (auto idx = aStartIdx; idx < aChildren->Length(); idx++) {
     Accessible* child = aChildren->ElementAt(idx);
 
     // If the child is in the tree then remove it from the owner.
     if (child->IsInDocument()) {
       Accessible* owner = child->Parent();
       if (!owner) {
         NS_ERROR("Cannot put the child back. No parent, a broken tree.");
@@ -2193,17 +2193,21 @@ DocAccessible::PutChildrenBack(nsTArray<
         containers.IndexOf(container) == nsTArray<Accessible*>::NoIndex) {
       containers.AppendElement(container);
     }
   }
 
   // And put it back where it belongs to.
   aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
   for (uint32_t idx = 0; idx < containers.Length(); idx++) {
-    UpdateTreeOnInsertion(containers[idx]);
+    NS_ASSERTION(containers[idx]->IsInDocument(),
+                 "A container has been destroyed.");
+    if (containers[idx]->IsInDocument()) {
+      UpdateTreeOnInsertion(containers[idx]);
+    }
   }
 }
 
 void
 DocAccessible::CacheChildrenInSubtree(Accessible* aRoot,
                                       Accessible** aFocusedAcc)
 {
   // If the accessible is focused then report a focus event after all related
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/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="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9a58f2e395da17c252f61f28900b5b09aeb813bd"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- 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="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9a58f2e395da17c252f61f28900b5b09aeb813bd"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/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 git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gecko and Gaia -->
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <!-- Gonk-specific things and forks -->
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
   <project name="android-development" path="development" remote="b2g" revision="2bdf22305b523af644e1891b4ddfd9229336d0ce"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- 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="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <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="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9a58f2e395da17c252f61f28900b5b09aeb813bd"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5de6856fad82857028f9f059f50680a9bea5b75c"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
--- 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="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9a58f2e395da17c252f61f28900b5b09aeb813bd"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5de6856fad82857028f9f059f50680a9bea5b75c"/>
@@ -127,15 +127,15 @@
   <default remote="caf" revision="refs/tags/android-4.4.2_r1" sync-j="4"/>
   <!-- Emulator specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="72ffdf71c68a96309212eb13d63560d66db14c9e"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="58800ecb50e4e41cfb0a36cb43c82b73fb3612e5"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="3e85c4683c121530c1c3a48c696a569bf5f587e2"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="b2f83825411be614e8f7ec75fc731fc9c67a7078"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="f37bd545063039e30a92f2550ae78c0e6e4e2d08"/>
   <project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="0c6a6547cd1fd302fa2b0f6e375654df36bf0ec4"/>
-  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="29cbaa03a380ab69d47c476dd433059f7680837c"/>
+  <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d3ab8090c5c2ac77429575131c4718d96bfb93cc"/>
   <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="5f4b68c799927b6e078f987b12722c3a6ccd4a45"/>
   <project name="platform/development" path="development" revision="5968ff4e13e0d696ad8d972281fc27ae5a12829b"/>
   <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="0951179277915335251c5e11d242e4e1a8c2236f"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
 </manifest>
--- 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="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9a58f2e395da17c252f61f28900b5b09aeb813bd"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5de6856fad82857028f9f059f50680a9bea5b75c"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/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 git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gecko and Gaia -->
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <!-- Gonk-specific things and forks -->
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
   <project name="android-development" path="development" remote="b2g" revision="2bdf22305b523af644e1891b4ddfd9229336d0ce"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- 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="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9a58f2e395da17c252f61f28900b5b09aeb813bd"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "86959c405348d27ba5686956ae3a8ffc274d3db8", 
+        "git_revision": "ee6d8625c9d76de2f6614c87bb82b301bc37c7a9", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "eae17e983597fd71c6a396ed63031bb552753f41", 
+    "revision": "553d04c277d58581b55701f1978b82b257990a7e", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4-kk/sources.xml
+++ b/b2g/config/nexus-4-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="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9a58f2e395da17c252f61f28900b5b09aeb813bd"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -13,17 +13,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <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="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9a58f2e395da17c252f61f28900b5b09aeb813bd"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5de6856fad82857028f9f059f50680a9bea5b75c"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
--- 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="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="86959c405348d27ba5686956ae3a8ffc274d3db8"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee6d8625c9d76de2f6614c87bb82b301bc37c7a9"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9a58f2e395da17c252f61f28900b5b09aeb813bd"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/browser/base/content/browser-gestureSupport.js
+++ b/browser/base/content/browser-gestureSupport.js
@@ -641,38 +641,43 @@ var gHistorySwipeAnimation = {
       // from 0.
       if (this._direction == "horizontal" || this._lastSwipeDir != "") {
         gBrowser.stop();
         this._lastSwipeDir = "RELOAD"; // just ensure that != ""
         this._canGoBack = this.canGoBack();
         this._canGoForward = this.canGoForward();
         this._handleFastSwiping();
       }
+      this.updateAnimation(0);
     }
     else {
-      this._startingIndex = gBrowser.webNavigation.sessionHistory.index;
-      this._historyIndex = this._startingIndex;
-      this._canGoBack = this.canGoBack();
-      this._canGoForward = this.canGoForward();
-      if (this.active) {
-        this._addBoxes();
-        this._takeSnapshot();
-        this._installPrevAndNextSnapshots();
-        this._lastSwipeDir = "";
+      // Get the session history from SessionStore.
+      let updateSessionHistory = sessionHistory => {
+        this._startingIndex = sessionHistory.index;
+        this._historyIndex = this._startingIndex;
+        this._canGoBack = this.canGoBack();
+        this._canGoForward = this.canGoForward();
+        if (this.active) {
+          this._addBoxes();
+          this._takeSnapshot();
+          this._installPrevAndNextSnapshots();
+          this._lastSwipeDir = "";
+        }
+        this.updateAnimation(0);
       }
+      SessionStore.getSessionHistory(gBrowser.selectedTab, updateSessionHistory);
     }
-    this.updateAnimation(0);
   },
 
   /**
    * Stops the swipe animation.
    */
   stopAnimation: function HSA_stopAnimation() {
     gHistorySwipeAnimation._removeBoxes();
-    this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
+    this._historyIndex = this._getCurrentHistoryIndex();
   },
 
   /**
    * Updates the animation between two pages in history.
    *
    * @param aVal
    *        A floating point value that represents the progress of the
    *        swipe gesture.
@@ -721,16 +726,20 @@ var gHistorySwipeAnimation = {
         this._positionBox(this._nextBox, offset + aVal);
       } else {
         this._prevBox.collapsed = true;
         this._positionBox(this._curBox, aVal / dampValue);
       }
     }
   },
 
+  _getCurrentHistoryIndex: function() {
+    return SessionStore.getSessionHistory(gBrowser.selectedTab).index;
+  },
+
   /**
    * Event handler for events relevant to the history swipe animation.
    *
    * @param aEvent
    *        An event to process.
    */
   handleEvent: function HSA_handleEvent(aEvent) {
     let browser = gBrowser.selectedBrowser;
@@ -816,32 +825,36 @@ var gHistorySwipeAnimation = {
   },
 
   /**
    * Used to notify the history swipe animation that the OS sent a swipe end
    * event and that we should navigate to the page that the user swiped to, if
    * any. This will also result in the animation overlay to be torn down.
    */
   swipeEndEventReceived: function HSA_swipeEndEventReceived() {
-    if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex)
-      this._navigateToHistoryIndex();
-    else
-      this.stopAnimation();
+    // Update the session history before continuing.
+    let updateSessionHistory = sessionHistory => {
+      if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex)
+        this._navigateToHistoryIndex();
+      else
+        this.stopAnimation();
+    }
+    SessionStore.getSessionHistory(gBrowser.selectedTab, updateSessionHistory);
   },
 
   /**
    * Checks whether a particular index exists in the browser history or not.
    *
    * @param aIndex
    *        The index to check for availability for in the history.
    * @return true if the index exists in the browser history, false otherwise.
    */
   _doesIndexExistInHistory: function HSA__doesIndexExistInHistory(aIndex) {
     try {
-      gBrowser.webNavigation.sessionHistory.getEntryAtIndex(aIndex, false);
+      return SessionStore.getSessionHistory(gBrowser.selectedTab).entries[aIndex] != null;
     }
     catch(ex) {
       return false;
     }
     return true;
   },
 
   /**
@@ -954,21 +967,17 @@ var gHistorySwipeAnimation = {
 
   /**
    * Verifies that we're ready to take snapshots based on the global pref and
    * the current index in history.
    *
    * @return true if we're ready to take snapshots, false otherwise.
    */
   _readyToTakeSnapshots: function HSA__readyToTakeSnapshots() {
-    if ((this._maxSnapshots < 1) ||
-        (gBrowser.webNavigation.sessionHistory.index < 0)) {
-      return false;
-    }
-    return true;
+    return (this._maxSnapshots >= 1 && this._getCurrentHistoryIndex() >= 0);
   },
 
   /**
    * Takes a snapshot of the page the browser is currently on.
    */
   _takeSnapshot: function HSA__takeSnapshot() {
     if (!this._readyToTakeSnapshots()) {
       return;
@@ -1021,17 +1030,17 @@ var gHistorySwipeAnimation = {
    * snapshot in the list.
    *
    * @param aCanvas
    *        The snapshot to add to the list and compress.
    */
   _assignSnapshotToCurrentBrowser:
   function HSA__assignSnapshotToCurrentBrowser(aCanvas) {
     let browser = gBrowser.selectedBrowser;
-    let currIndex = browser.webNavigation.sessionHistory.index;
+    let currIndex = this._getCurrentHistoryIndex();
 
     this._removeTrackedSnapshot(currIndex, browser);
     this._addSnapshotRefToArray(currIndex, browser);
 
     if (!("snapshots" in browser))
       browser.snapshots = [];
     let snapshots = browser.snapshots;
     // Temporarily store the canvas as the compressed snapshot.
@@ -1054,17 +1063,17 @@ var gHistorySwipeAnimation = {
       // there's nothing to compress.
       return;
     }
 
     TelemetryStopwatch.start("FX_GESTURE_COMPRESS_SNAPSHOT_OF_PAGE");
     try {
       let browser = gBrowser.selectedBrowser;
       let snapshots = browser.snapshots;
-      let currIndex = browser.webNavigation.sessionHistory.index;
+      let currIndex = _getCurrentHistoryIndex();
 
       // Kick off snapshot compression.
       let canvas = snapshots[currIndex].image;
       canvas.toBlob(function(aBlob) {
           if (snapshots[currIndex]) {
             snapshots[currIndex].image = aBlob;
           }
         }, "image/png"
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -3372,27 +3372,26 @@ var E10SUINotification = {
   },
 };
 
 #else // E10S_TESTING_ONLY
 
 var E10SAccessibilityCheck = {
   init: function() {
     Services.obs.addObserver(this, "a11y-init-or-shutdown", true);
-    if (Services.appinfo.accessibilityIsBlacklistedForE10S) {
+    if (Services.appinfo.accessibilityEnabled) {
       this._showE10sAccessibilityWarning();
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
 
   observe: function(subject, topic, data) {
     if (topic == "a11y-init-or-shutdown"
-        && data == "1" &&
-        Services.appinfo.accessibilityIsBlacklistedForE10S) {
+        && data == "1") {
       this._showE10sAccessibilityWarning();
     }
   },
 
   _warnedAboutAccessibility: false,
 
   _showE10sAccessibilityWarning: function() {
     try {
--- a/configure.in
+++ b/configure.in
@@ -319,27 +319,26 @@ if test -n "$gonkdir" ; then
     AC_DEFINE(HAVE_SYS_UIO_H)
     AC_DEFINE(HAVE_PTHREADS)
     MOZ_CHROME_FILE_FORMAT=omni
     direct_nspr_config=1
     android_cxx_stl=mozstlport
 else
     if test "$COMPILE_ENVIRONMENT"; then
         MOZ_ANDROID_NDK
-    else
-        AC_DEFINE(ANDROID)
     fi # COMPILE_ENVIRONMENT
 
     case "$target" in
     *-android*|*-linuxandroid*)
         if test -z "$ANDROID_PACKAGE_NAME" ; then
             ANDROID_PACKAGE_NAME='org.mozilla.$(MOZ_APP_NAME)'
         fi
         MOZ_CHROME_FILE_FORMAT=omni
         ZLIB_DIR=yes
+        AC_DEFINE(ANDROID)
         ;;
     *-linux*)
         AC_PATH_PROG(OBJCOPY,objcopy)
         ;;
     esac
 fi
 
 case "$target" in
@@ -4213,16 +4212,17 @@ cairo-uikit)
     AC_DEFINE(MOZ_WIDGET_UIKIT)
     LDFLAGS="$LDFLAGS -framework UIKit -lobjc"
     TK_CFLAGS="-DNO_X11"
     TK_LIBS='-Wl,-framework,Foundation -Wl,-framework,CoreFoundation -Wl,-framework,CoreGraphics -Wl,-framework,CoreText -Wl,-framework,AVFoundation -Wl,-framework,AudioToolbox -Wl,-framework,CoreMedia -Wl,-framework,CoreVideo -Wl,-framework,OpenGLES -Wl,-framework,QuartzCore'
     CFLAGS="$CFLAGS $TK_CFLAGS"
     CXXFLAGS="$CXXFLAGS $TK_CFLAGS"
     MOZ_USER_DIR="Mozilla"
     MOZ_FS_LAYOUT=bundle
+    AC_DEFINE(MOZ_SINGLE_PROCESS_APZ)
     ;;
 
 cairo-android)
     AC_DEFINE(MOZ_WIDGET_ANDROID)
     MOZ_WIDGET_TOOLKIT=android
     MOZ_PDF_PRINTING=1
     MOZ_INSTRUMENT_EVENT_LOOP=1
     ;;
@@ -4796,18 +4796,21 @@ fi
 dnl ========================================================
 dnl = Enable the C++ async pan/zoom code instead of the Java version
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(android-apz,
 [  --enable-android-apz      Switch to C++ pan/zoom code],
     MOZ_ANDROID_APZ=1,
     MOZ_ANDROID_APZ=)
 if test -n "$MOZ_ANDROID_APZ"; then
-     dnl Do this if defined in confvars.sh
-     AC_DEFINE(MOZ_ANDROID_APZ)
+    dnl Do this if defined in confvars.sh
+    AC_DEFINE(MOZ_ANDROID_APZ)
+    if test -z "$MOZ_B2GDROID"; then
+      AC_DEFINE(MOZ_SINGLE_PROCESS_APZ)
+    fi
 fi
 
 dnl ========================================================
 dnl = Disable WebSMS backend
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(websms-backend,
 [  --disable-websms-backend
                            Disable WebSMS backend],
--- a/devtools/client/framework/gDevTools.jsm
+++ b/devtools/client/framework/gDevTools.jsm
@@ -824,17 +824,17 @@ var gDevToolsBrowser = {
     gDevToolsBrowser._trackedBrowserWindows.add(win);
     gDevToolsBrowser._addAllToolsToMenu(win.document);
 
     if (this._isFirebugInstalled()) {
       let broadcaster = win.document.getElementById("devtoolsMenuBroadcaster_DevToolbox");
       broadcaster.removeAttribute("key");
     }
 
-    let tabContainer = win.document.getElementById("tabbrowser-tabs");
+    let tabContainer = win.gBrowser.tabContainer;
     tabContainer.addEventListener("TabSelect", this, false);
     tabContainer.addEventListener("TabOpen", this, false);
     tabContainer.addEventListener("TabClose", this, false);
     tabContainer.addEventListener("TabPinned", this, false);
     tabContainer.addEventListener("TabUnpinned", this, false);
   },
 
   /**
@@ -1019,30 +1019,27 @@ var gDevToolsBrowser = {
           ref = doc.getElementById("appmenu_devtools_separator");
         }
 
         if (ref) {
           amp.insertBefore(elements.appmenuitem, ref);
         }
       }
 
-      let mp = doc.getElementById("menuWebDeveloperPopup");
-      if (mp) {
-        let ref;
+      let ref;
 
-        if (prevDef != null) {
-          let menuitem = doc.getElementById("menuitem_" + prevDef.id);
-          ref = menuitem && menuitem.nextSibling ? menuitem.nextSibling : null;
-        } else {
-          ref = doc.getElementById("menu_devtools_separator");
-        }
+      if (prevDef) {
+        let menuitem = doc.getElementById("menuitem_" + prevDef.id);
+        ref = menuitem && menuitem.nextSibling ? menuitem.nextSibling : null;
+      } else {
+        ref = doc.getElementById("menu_devtools_separator");
+      }
 
-        if (ref) {
-          mp.insertBefore(elements.menuitem, ref);
-        }
+      if (ref) {
+        ref.parentNode.insertBefore(elements.menuitem, ref);
       }
     }
 
     if (toolDefinition.id === "jsdebugger") {
       gDevToolsBrowser.setSlowScriptDebugHandler();
     }
   },
 
@@ -1082,25 +1079,25 @@ var gDevToolsBrowser = {
     let mcs = doc.getElementById("mainCommandSet");
     mcs.appendChild(fragCommands);
 
     this.attachKeybindingsToBrowser(doc, fragKeys);
 
     let mbs = doc.getElementById("mainBroadcasterSet");
     mbs.appendChild(fragBroadcasters);
 
-    let amp = doc.getElementById("appmenu_webDeveloper_popup");
-    if (amp) {
-      let amps = doc.getElementById("appmenu_devtools_separator");
-      amp.insertBefore(fragAppMenuItems, amps);
+    let amps = doc.getElementById("appmenu_devtools_separator");
+    if (amps) {
+      amps.parentNode.insertBefore(fragAppMenuItems, amps);
     }
 
-    let mp = doc.getElementById("menuWebDeveloperPopup");
     let mps = doc.getElementById("menu_devtools_separator");
-    mp.insertBefore(fragMenuItems, mps);
+    if (mps) {
+      mps.parentNode.insertBefore(fragMenuItems, mps);
+    }
   },
 
   /**
    * Add a menu entry for a tool definition
    *
    * @param {string} toolDefinition
    *        Tool definition of the tool to add a menu entry.
    * @param {XULDocument} doc
@@ -1251,17 +1248,17 @@ var gDevToolsBrowser = {
 
     // Destroy toolboxes for closed window
     for (let [target, toolbox] of gDevTools._toolboxes) {
       if (toolbox.frame && toolbox.frame.ownerDocument.defaultView == win) {
         toolbox.destroy();
       }
     }
 
-    let tabContainer = win.document.getElementById("tabbrowser-tabs");
+    let tabContainer = win.gBrowser.tabContainer;
     tabContainer.removeEventListener("TabSelect", this, false);
     tabContainer.removeEventListener("TabOpen", this, false);
     tabContainer.removeEventListener("TabClose", this, false);
     tabContainer.removeEventListener("TabPinned", this, false);
     tabContainer.removeEventListener("TabUnpinned", this, false);
   },
 
   handleEvent: function(event) {
@@ -1270,17 +1267,17 @@ var gDevToolsBrowser = {
       case "TabClose":
       case "TabPinned":
       case "TabUnpinned":
         let open = 0;
         let pinned = 0;
 
         for (let win of this._trackedBrowserWindows) {
           let tabContainer = win.gBrowser.tabContainer;
-          let numPinnedTabs = tabContainer.tabbrowser._numPinnedTabs;
+          let numPinnedTabs = win.gBrowser._numPinnedTabs || 0;
           let numTabs = tabContainer.itemCount - numPinnedTabs;
 
           open += numTabs;
           pinned += numPinnedTabs;
         }
 
         this._tabStats.histOpen.push(open);
         this._tabStats.histPinned.push(pinned);
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -1084,16 +1084,20 @@ TabActor.prototype = {
   },
 
   onListFrames: function BTA_onListFrames(aRequest) {
     let windows = this._docShellsToWindows(this.docShells);
     return { frames: windows };
   },
 
   onListWorkers: function BTA_onListWorkers(aRequest) {
+    if (!this.attached) {
+      return { error: "wrongState" };
+    }
+
     if (this._workerActorList === null) {
       this._workerActorList = new WorkerActorList({
         type: Ci.nsIWorkerDebugger.TYPE_DEDICATED,
         window: this.window
       });
     }
 
     return this._workerActorList.getList().then((actors) => {
--- a/devtools/server/actors/worker.js
+++ b/devtools/server/actors/worker.js
@@ -141,74 +141,82 @@ function WorkerActorList(options) {
   this._onListChanged = null;
   this._mustNotify = false;
   this.onRegister = this.onRegister.bind(this);
   this.onUnregister = this.onUnregister.bind(this);
 }
 
 WorkerActorList.prototype = {
   getList: function () {
+    // Create a set of debuggers.
     let dbgs = new Set();
     let e = wdm.getWorkerDebuggerEnumerator();
     while (e.hasMoreElements()) {
       let dbg = e.getNext().QueryInterface(Ci.nsIWorkerDebugger);
       if (matchWorkerDebugger(dbg, this._options)) {
         dbgs.add(dbg);
       }
     }
 
+    // Delete each actor for which we don't have a debugger.
     for (let [dbg, ] of this._actors) {
       if (!dbgs.has(dbg)) {
         this._actors.delete(dbg);
       }
     }
 
+    // Create an actor for each debugger for which we don't have one.
     for (let dbg of dbgs) {
       if (!this._actors.has(dbg)) {
         this._actors.set(dbg, new WorkerActor(dbg));
       }
     }
 
     let actors = [];
     for (let [, actor] of this._actors) {
       actors.push(actor);
     }
 
-    this._mustNotify = true;
-    this._checkListening();
+    if (!this._mustNotify) {
+      if (this._onListChanged !== null) {
+        wdm.addListener(this);
+      }
+      this._mustNotify = true;
+    }
 
     return Promise.resolve(actors);
   },
 
   get onListChanged() {
     return this._onListChanged;
   },
 
   set onListChanged(onListChanged) {
     if (typeof onListChanged !== "function" && onListChanged !== null) {
       throw new Error("onListChanged must be either a function or null.");
     }
 
+    if (this._mustNotify) {
+      if (this._onListChanged === null && onListChanged !== null) {
+        wdm.addListener(this);
+      }
+      if (this._onListChanged !== null && onListChanged === null) {
+        wdm.removeListener(this);
+      }
+    }
     this._onListChanged = onListChanged;
-    this._checkListening();
-  },
-
-  _checkListening: function () {
-    if (this._onListChanged !== null && this._mustNotify) {
-      wdm.addListener(this);
-    } else {
-      wdm.removeListener(this);
-    }
   },
 
   _notifyListChanged: function () {
-    if (this._mustNotify) {
-      this._onListChanged();
-      this._mustNotify = false;
-    }
+     this._onListChanged();
+
+     if (this._onListChanged !== null) {
+       wdm.removeListener(this);
+     }
+     this._mustNotify = false;
   },
 
   onRegister: function (dbg) {
     if (matchWorkerDebugger(dbg, this._options)) {
       this._notifyListChanged();
     }
   },
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7653,19 +7653,21 @@ nsContentUtils::GetButtonsFlagForButton(
   }
 }
 
 LayoutDeviceIntPoint
 nsContentUtils::ToWidgetPoint(const CSSPoint& aPoint,
                               const nsPoint& aOffset,
                               nsPresContext* aPresContext)
 {
-  return LayoutDeviceIntPoint::FromAppUnitsRounded(
-    CSSPoint::ToAppUnits(aPoint) + aOffset,
-    aPresContext->AppUnitsPerDevPixel());
+  nsPoint point = CSSPoint::ToAppUnits(aPoint) + aOffset;
+#if defined(MOZ_SINGLE_PROCESS_APZ)
+  point = point.ApplyResolution(aPresContext->PresShell()->GetCumulativeScaleResolution());
+#endif
+  return LayoutDeviceIntPoint::FromAppUnitsRounded(point, aPresContext->AppUnitsPerDevPixel());
 }
 
 nsView*
 nsContentUtils::GetViewToDispatchEvent(nsPresContext* presContext,
                                        nsIPresShell** presShell)
 {
   if (presContext && presShell) {
     *presShell = presContext->PresShell();
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -220,16 +220,17 @@
 #include "mozilla/dom/PopupBlockedEvent.h"
 #include "mozilla/dom/PrimitiveConversions.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
+#include "mozilla/dom/ServiceWorkerRegistration.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
 #endif
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesis.h"
 #endif
 
@@ -1571,16 +1572,18 @@ nsGlobalWindow::CleanUp()
   mAudioContexts.Clear();
 
   if (mIdleTimer) {
     mIdleTimer->Cancel();
     mIdleTimer = nullptr;
   }
 
   DisableTimeChangeNotifications();
+
+  mServiceWorkerRegistrationTable.Clear();
 }
 
 void
 nsGlobalWindow::ClearControllers()
 {
   if (mControllers) {
     uint32_t count;
     mControllers->GetControllerCount(&count);
@@ -1783,16 +1786,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
 
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerRegistrationTable)
+
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
 #endif
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
 
@@ -1854,16 +1859,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
 
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorkerRegistrationTable)
+
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
 #endif
 
   if (tmp->mOuterWindow) {
     static_cast<nsGlobalWindow*>(tmp->mOuterWindow.get())->MaybeClearInnerWindow(tmp);
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mOuterWindow)
   }
@@ -10227,16 +10234,34 @@ nsGlobalWindow::GetCaches(ErrorResult& a
                                                      storageBlocked,
                                                      forceTrustedOrigin, aRv);
   }
 
   RefPtr<CacheStorage> ref = mCacheStorage;
   return ref.forget();
 }
 
+already_AddRefed<ServiceWorkerRegistrationMainThread>
+nsPIDOMWindow::GetServiceWorkerRegistration(const nsAString& aScope)
+{
+  RefPtr<ServiceWorkerRegistrationMainThread> registration;
+  if (!mServiceWorkerRegistrationTable.Get(aScope,
+                                           getter_AddRefs(registration))) {
+    registration = new ServiceWorkerRegistrationMainThread(this, aScope);
+    mServiceWorkerRegistrationTable.Put(aScope, registration);
+  }
+  return registration.forget();
+}
+
+void
+nsPIDOMWindow::InvalidateServiceWorkerRegistration(const nsAString& aScope)
+{
+  mServiceWorkerRegistrationTable.Remove(aScope);
+}
+
 void
 nsGlobalWindow::FireOfflineStatusEventIfChanged()
 {
   if (!IsCurrentInnerWindow())
     return;
 
   bool isOffline = NS_IsOffline() || NS_IsAppOffline(GetPrincipal());
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -10,16 +10,17 @@
 
 #include "nsIDOMWindow.h"
 
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "mozilla/dom/EventTarget.h"
 #include "js/TypeDecls.h"
+#include "nsRefPtrHashtable.h"
 
 #define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed"
 #define DOM_WINDOW_FROZEN_TOPIC "dom-window-frozen"
 #define DOM_WINDOW_THAWED_TOPIC "dom-window-thawed"
 
 class nsIArray;
 class nsIContent;
 class nsICSSDeclaration;
@@ -32,16 +33,17 @@ class nsPerformance;
 class nsPIWindowRoot;
 class nsXBLPrototypeHandler;
 struct nsTimeout;
 
 namespace mozilla {
 namespace dom {
 class AudioContext;
 class Element;
+class ServiceWorkerRegistrationMainThread;
 } // namespace dom
 namespace gfx {
 class VRHMDInfo;
 } // namespace gfx
 } // namespace mozilla
 
 // Popup control state enum. The values in this enum must go from most
 // permissive to least permissive so that it's safe to push state in
@@ -213,16 +215,20 @@ public:
   }
 
   bool GetServiceWorkersTestingEnabled()
   {
     MOZ_ASSERT(IsOuterWindow());
     return mServiceWorkersTestingEnabled;
   }
 
+  already_AddRefed<mozilla::dom::ServiceWorkerRegistrationMainThread>
+    GetServiceWorkerRegistration(const nsAString& aScope);
+  void InvalidateServiceWorkerRegistration(const nsAString& aScope);
+
 protected:
   // Lazily instantiate an about:blank document if necessary, and if
   // we have what it takes to do so.
   void MaybeCreateDoc();
 
   float GetAudioGlobalVolumeInternal(float aVolume);
   void RefreshMediaElements();
 
@@ -850,16 +856,21 @@ protected:
   // This reference is used by the subclass nsGlobalWindow, and cleared in it's
   // DetachFromDocShell() method. This method is called by nsDocShell::Destroy(),
   // which is called before the nsDocShell is destroyed.
   nsIDocShell* MOZ_NON_OWNING_REF mDocShell;  // Weak Reference
 
   // mPerformance is only used on inner windows.
   RefPtr<nsPerformance>       mPerformance;
 
+  typedef nsRefPtrHashtable<nsStringHashKey,
+                            mozilla::dom::ServiceWorkerRegistrationMainThread>
+          ServiceWorkerRegistrationTable;
+  ServiceWorkerRegistrationTable mServiceWorkerRegistrationTable;
+
   uint32_t               mModalStateDepth;
 
   // These variables are only used on inner windows.
   nsTimeout             *mRunningTimeout;
 
   uint32_t               mMutationBits;
 
   bool                   mIsDocumentLoaded;
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -964,16 +964,21 @@ nsScriptLoader::ProcessRequest(nsScriptL
     mCurrentParserInsertedScript = aRequest->mElement;
   }
 
   FireScriptAvailable(NS_OK, aRequest);
 
   // The window may have gone away by this point, in which case there's no point
   // in trying to run the script.
   nsCOMPtr<nsIDocument> master = mDocument->MasterDocument();
+  {
+    // Try to perform a microtask checkpoint
+    nsAutoMicroTask mt;
+  }
+
   nsPIDOMWindow *pwin = master->GetInnerWindow();
   bool runScript = !!pwin;
   if (runScript) {
     nsContentUtils::DispatchTrustedEvent(scriptElem->OwnerDoc(),
                                          scriptElem,
                                          NS_LITERAL_STRING("beforescriptexecute"),
                                          true, true, &runScript);
   }
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -672,16 +672,17 @@ skip-if = os == "mac" # fails intermitte
 [test_bug737612.html]
 [test_bug738108.html]
 [test_bug744830.html]
 [test_bug749367.html]
 [test_bug753278.html]
 [test_bug761120.html]
 [test_bug782342.html]
 [test_bug787778.html]
+[test_bug789315.html]
 [test_bug789856.html]
 [test_bug804395.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #bug 901343, specialpowers.wrap issue createsystemxhr
 [test_bug809003.html]
 [test_bug810494.html]
 [test_bug811701.html]
 [test_bug811701.xhtml]
 [test_bug813919.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug789315.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=789315
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 789315</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+    <script type="text/javascript">
+      (function() {
+        const observerConfig = {
+          childList: true,
+        };
+
+        var observer = new MutationObserver(onMutations);
+        observer.observe(document.head, observerConfig);
+
+        function onMutations(mutations) {
+          for (var i in mutations) {
+            var mutation = mutations[i];
+            for (var j in mutation.addedNodes) {
+              var addedNode = mutation.addedNodes[j];
+              addedNode.mutationObserverHasNotified = true;
+            }
+          }
+        }
+
+      })();
+    </script>
+
+    <link id="testnode" rel="localization" href="dummy"></link>
+
+    <script type="text/javascript">
+      var testNode = document.getElementById("testnode");
+      ok(testNode.mutationObserverHasNotified);
+    </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=789315">Mozilla Bug 789315</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/bluetooth/common/webapi/BluetoothMapRequestHandle.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothMapRequestHandle.cpp
@@ -61,30 +61,18 @@ BluetoothMapRequestHandle::ReplyToFolder
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
 
   BluetoothService* bs = BluetoothService::Get();
   if (!bs) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
-  if (XRE_GetProcessType() == GeckoProcessType_Default) {
-    // In-process reply
-    bs->ReplyToMapFolderListing(aMasId, aFolderlists,
-      new BluetoothVoidReplyRunnable(nullptr, promise));
-  } else {
-    ContentChild *cc = ContentChild::GetSingleton();
-    if (!cc) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
-    }
-
-    bs->ReplyToMapFolderListing(aMasId, aFolderlists,
-      new BluetoothVoidReplyRunnable(nullptr, promise));
-  }
+  bs->ReplyToMapFolderListing(aMasId, aFolderlists,
+    new BluetoothVoidReplyRunnable(nullptr, promise));
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 BluetoothMapRequestHandle::ReplyToMessagesListing(long aMasId,
                                                   Blob& aBlob,
                                                   bool aNewMessage,
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -922,29 +922,32 @@ Event::GetScreenCoords(nsPresContext* aP
         aEvent->mClass != eTouchEventClass &&
         aEvent->mClass != eDragEventClass &&
         aEvent->mClass != eSimpleGestureEventClass)) {
     return CSSIntPoint(0, 0);
   }
 
   // Doing a straight conversion from LayoutDeviceIntPoint to CSSIntPoint
   // seem incorrect, but it is needed to maintain legacy functionality.
-  if (!aPresContext) {
+  WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent();
+  if (!aPresContext || !(guiEvent && guiEvent->widget)) {
     return CSSIntPoint(aPoint.x, aPoint.y);
   }
 
-  LayoutDeviceIntPoint offset = aPoint;
+  nsPoint pt =
+    LayoutDevicePixel::ToAppUnits(aPoint, aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
 
-  WidgetGUIEvent* guiEvent = aEvent->AsGUIEvent();
-  if (guiEvent && guiEvent->widget) {
-    offset += guiEvent->widget->WidgetToScreenOffset();
+#if defined(MOZ_SINGLE_PROCESS_APZ)
+  if (aPresContext->PresShell()) {
+    pt = pt.RemoveResolution(aPresContext->PresShell()->GetCumulativeScaleResolution());
   }
+#endif
 
-  nsPoint pt =
-    LayoutDevicePixel::ToAppUnits(offset, aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
+  pt += LayoutDevicePixel::ToAppUnits(guiEvent->widget->WidgetToScreenOffset(),
+                                      aPresContext->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom());
 
   return CSSPixel::FromAppUnitsRounded(pt);
 }
 
 // static
 CSSIntPoint
 Event::GetPageCoords(nsPresContext* aPresContext,
                      WidgetEvent* aEvent,
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -16,21 +16,29 @@ interface nsIURI;
 interface nsIServiceWorkerUnregisterCallback : nsISupports
 {
   // aState is true if the unregistration succeded.
   // It's false if this ServiceWorkerRegistration doesn't exist.
   void unregisterSucceeded(in bool aState);
   void unregisterFailed();
 };
 
-[scriptable, builtinclass, uuid(1a1e71dd-0f78-4e2e-a2db-a946fe02cddf)]
+interface nsIWorkerDebugger;
+
+[scriptable, builtinclass, uuid(76e357ed-208d-4e4c-9165-1c4059707879)]
 interface nsIServiceWorkerInfo : nsISupports
 {
   readonly attribute DOMString scriptSpec;
   readonly attribute DOMString cacheName;
+
+  readonly attribute nsIWorkerDebugger debugger;
+
+  void attachDebugger();
+
+  void detachDebugger();
 };
 
 [scriptable, uuid(87e63548-d440-4b8a-b158-65ad1de0211E)]
 interface nsIServiceWorkerRegistrationInfoListener : nsISupports
 {
   void onChange();
 };
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2802,32 +2802,30 @@ ContentChild::RecvOnAppThemeChanged()
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     if (os) {
         os->NotifyObservers(nullptr, "app-theme-changed", nullptr);
     }
     return true;
 }
 
 bool
-ContentChild::RecvStartProfiler(const uint32_t& aEntries,
-                                const double& aInterval,
-                                nsTArray<nsCString>&& aFeatures,
-                                nsTArray<nsCString>&& aThreadNameFilters)
+ContentChild::RecvStartProfiler(const ProfilerInitParams& params)
 {
     nsTArray<const char*> featureArray;
-    for (size_t i = 0; i < aFeatures.Length(); ++i) {
-        featureArray.AppendElement(aFeatures[i].get());
+    for (size_t i = 0; i < params.features().Length(); ++i) {
+        featureArray.AppendElement(params.features()[i].get());
     }
 
     nsTArray<const char*> threadNameFilterArray;
-    for (size_t i = 0; i < aThreadNameFilters.Length(); ++i) {
-        threadNameFilterArray.AppendElement(aThreadNameFilters[i].get());
+    for (size_t i = 0; i < params.threadFilters().Length(); ++i) {
+        threadNameFilterArray.AppendElement(params.threadFilters()[i].get());
     }
 
-    profiler_start(aEntries, aInterval, featureArray.Elements(), featureArray.Length(),
+    profiler_start(params.entries(), params.interval(),
+                   featureArray.Elements(), featureArray.Length(),
                    threadNameFilterArray.Elements(), threadNameFilterArray.Length());
 
     return true;
 }
 
 bool
 ContentChild::RecvStopProfiler()
 {
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -419,20 +419,17 @@ public:
     virtual bool RecvOnAppThemeChanged() override;
 
     virtual bool RecvAssociatePluginId(const uint32_t& aPluginId,
                                        const base::ProcessId& aProcessId) override;
     virtual bool RecvLoadPluginResult(const uint32_t& aPluginId,
                                       const bool& aResult) override;
     virtual bool RecvUpdateWindow(const uintptr_t& aChildId) override;
 
-    virtual bool RecvStartProfiler(const uint32_t& aEntries,
-                                   const double& aInterval,
-                                   nsTArray<nsCString>&& aFeatures,
-                                   nsTArray<nsCString>&& aThreadNameFilters) override;
+    virtual bool RecvStartProfiler(const ProfilerInitParams& params) override;
     virtual bool RecvPauseProfiler(const bool& aPause) override;
     virtual bool RecvStopProfiler() override;
     virtual bool RecvGatherProfile() override;
     virtual bool RecvDomainSetChanged(const uint32_t& aSetType, const uint32_t& aChangeType,
                                       const OptionalURIParams& aDomain) override;
     virtual bool RecvShutdown() override;
 
     virtual bool
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1552,16 +1552,31 @@ ContentParent::Init()
 
 #ifdef ACCESSIBILITY
     // If accessibility is running in chrome process then start it in content
     // process.
     if (nsIPresShell::IsAccessibilityActive()) {
         Unused << SendActivateA11y();
     }
 #endif
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
+    bool profilerActive = false;
+    DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+    if (profilerActive) {
+        nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
+        rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+        StartProfiler(currentProfilerParams);
+    }
+#endif
 }
 
 void
 ContentParent::ForwardKnownInfo()
 {
     MOZ_ASSERT(mMetamorphosed);
     if (!mMetamorphosed) {
         return;
@@ -3270,23 +3285,17 @@ ContentParent::Observe(nsISupports* aSub
     }
 #endif
     else if (!strcmp(aTopic, "app-theme-changed")) {
         Unused << SendOnAppThemeChanged();
     }
 #ifdef MOZ_ENABLE_PROFILER_SPS
     else if (!strcmp(aTopic, "profiler-started")) {
         nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
-        uint32_t entries;
-        double interval;
-        params->GetEntries(&entries);
-        params->GetInterval(&interval);
-        const nsTArray<nsCString>& features = params->GetFeatures();
-        const nsTArray<nsCString>& threadFilterNames = params->GetThreadFilterNames();
-        Unused << SendStartProfiler(entries, interval, features, threadFilterNames);
+        StartProfiler(params);
     }
     else if (!strcmp(aTopic, "profiler-stopped")) {
         Unused << SendStopProfiler();
     }
     else if (!strcmp(aTopic, "profiler-paused")) {
         Unused << SendPauseProfiler(true);
     }
     else if (!strcmp(aTopic, "profiler-resumed")) {
@@ -5704,16 +5713,34 @@ ContentParent::RecvGetAndroidSystemInfo(
   nsSystemInfo::GetAndroidSystemInfo(aInfo);
   return true;
 #else
   MOZ_CRASH("wrong platform!");
   return false;
 #endif
 }
 
+void
+ContentParent::StartProfiler(nsIProfilerStartParams* aParams)
+{
+    if (NS_WARN_IF(!aParams)) {
+        return;
+    }
+
+    ProfilerInitParams ipcParams;
+
+    ipcParams.enabled() = true;
+    aParams->GetEntries(&ipcParams.entries());
+    aParams->GetInterval(&ipcParams.interval());
+    ipcParams.features() = aParams->GetFeatures();
+    ipcParams.threadFilters() = aParams->GetThreadFilterNames();
+
+    Unused << SendStartProfiler(ipcParams);
+}
+
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
 NS_IMETHODIMP
 ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData) {
     mozilla::Unused << mParent->SendNotifyIdleObserver(mObserver,
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -930,16 +930,17 @@ private:
                                       const uint32_t& aDropEffect) override;
 
     virtual bool RecvGetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration* aConfig) override;
 
     virtual bool RecvGamepadListenerAdded() override;
     virtual bool RecvGamepadListenerRemoved() override;
     virtual bool RecvProfile(const nsCString& aProfile) override;
     virtual bool RecvGetGraphicsDeviceInitData(DeviceInitData* aOut) override;
+    void StartProfiler(nsIProfilerStartParams* aParams);
 
     virtual bool RecvGetDeviceStorageLocation(const nsString& aType,
                                               nsString* aPath) override;
 
     virtual bool RecvGetAndroidSystemInfo(AndroidSystemInfo* aInfo) override;
 
     // If you add strong pointers to cycle collected objects here, be sure to
     // release these objects in ShutDownProcess.  See the comment there for more
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -60,16 +60,17 @@ include InputStreamParams;
 include PTabContext;
 include URIParams;
 include PluginTypes;
 include ProtocolTypes;
 include PBackgroundSharedTypes;
 include PContentPermission;
 include BrowserConfiguration;
 include GraphicsMessages;
+include ProfilerTypes;
 
 // Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
 // are put into different UnifiedProtocolsXX.cpp files.
 // XXX Remove this once bug 1069073 is fixed
 include "mozilla/dom/PContentBridgeParent.h";
 
 using GeoPosition from "nsGeoPositionIPCSerialiser.h";
 
@@ -646,18 +647,17 @@ child:
      * PluginModuleContentParent that the PluginModuleChromeParent's async
      * init has completed.
      */
     async LoadPluginResult(uint32_t aPluginId, bool aResult);
 
     /**
      * Control the Gecko Profiler in the child process.
      */
-    async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures,
-                        nsCString[] aThreadNameFilters);
+    async StartProfiler(ProfilerInitParams params);
     async StopProfiler();
     async PauseProfiler(bool aPause);
 
     async GatherProfile();
 
     InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action);
 
     EndDragSession(bool aDoneDrag, bool aUserCancelled);
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -13,16 +13,17 @@
 #include "nsServiceManagerUtils.h"
 #include "MainThreadUtils.h"
 #include "mozilla/EMEUtils.h"
 #include "nsIConsoleService.h"
 #include "prenv.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/CDMCallbackProxy.h"
 #include "MediaData.h"
+#include "nsPrintfCString.h"
 
 namespace mozilla {
 
 CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
   : mKeys(aKeys)
   , mKeySystem(aKeySystem)
   , mCDM(nullptr)
   , mDecryptionJobCount(0)
@@ -36,16 +37,17 @@ CDMProxy::~CDMProxy()
 {
   MOZ_COUNT_DTOR(CDMProxy);
 }
 
 void
 CDMProxy::Init(PromiseId aPromiseId,
                const nsAString& aOrigin,
                const nsAString& aTopLevelOrigin,
+               const nsAString& aGMPName,
                bool aInPrivateBrowsing)
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
 
   EME_LOG("CDMProxy::Init (%s, %s) %s",
           NS_ConvertUTF16toUTF8(aOrigin).get(),
           NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
@@ -63,20 +65,27 @@ CDMProxy::Init(PromiseId aPromiseId,
     mps->GetThread(getter_AddRefs(mGMPThread));
     if (!mGMPThread) {
       RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                     NS_LITERAL_CSTRING("Couldn't get GMP thread CDMProxy::Init"));
       return;
     }
   }
 
+  if (aGMPName.IsEmpty()) {
+    RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+      nsPrintfCString("Unknown GMP for keysystem '%s'", NS_ConvertUTF16toUTF8(mKeySystem).get()));
+    return;
+  }
+
   nsAutoPtr<InitData> data(new InitData());
   data->mPromiseId = aPromiseId;
   data->mOrigin = aOrigin;
   data->mTopLevelOrigin = aTopLevelOrigin;
+  data->mGMPName = aGMPName;
   data->mInPrivateBrowsing = aInPrivateBrowsing;
   nsCOMPtr<nsIRunnable> task(
     NS_NewRunnableMethodWithArg<nsAutoPtr<InitData>>(this,
                                                      &CDMProxy::gmp_Init,
                                                      Move(data)));
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
@@ -171,16 +180,17 @@ CDMProxy::gmp_Init(nsAutoPtr<InitData>&&
 
   // Make a copy before we transfer ownership of aData to the
   // gmp_InitGetGMPDecryptorCallback.
   InitData data(*aData);
   UniquePtr<GetNodeIdCallback> callback(
     new gmp_InitGetGMPDecryptorCallback(this, Move(aData)));
   nsresult rv = mps->GetNodeId(data.mOrigin,
                                data.mTopLevelOrigin,
+                               data.mGMPName,
                                data.mInPrivateBrowsing,
                                Move(callback));
   if (NS_FAILED(rv)) {
     RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Call to GetNodeId() failed early"));
   }
 }
 
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -52,16 +52,17 @@ public:
   CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem);
 
   // Main thread only.
   // Loads the CDM corresponding to mKeySystem.
   // Calls MediaKeys::OnCDMCreated() when the CDM is created.
   void Init(PromiseId aPromiseId,
             const nsAString& aOrigin,
             const nsAString& aTopLevelOrigin,
+            const nsAString& aGMPName,
             bool aInPrivateBrowsing);
 
   // Main thread only.
   // Uses the CDM to create a key session.
   // Calls MediaKeys::OnSessionActivated() when session is created.
   // Assumes ownership of (Move()s) aInitData's contents.
   void CreateSession(uint32_t aCreateSessionToken,
                      dom::SessionType aSessionType,
@@ -181,16 +182,17 @@ public:
 private:
   friend class gmp_InitDoneCallback;
   friend class gmp_InitGetGMPDecryptorCallback;
 
   struct InitData {
     uint32_t mPromiseId;
     nsAutoString mOrigin;
     nsAutoString mTopLevelOrigin;
+    nsString mGMPName;
     bool mInPrivateBrowsing;
   };
 
   // GMP thread only.
   void gmp_Init(nsAutoPtr<InitData>&& aData);
   void gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData);
   void gmp_InitGetGMPDecryptor(nsresult aResult,
                                const nsACString& aNodeId,
--- a/dom/media/eme/EMEUtils.cpp
+++ b/dom/media/eme/EMEUtils.cpp
@@ -136,9 +136,22 @@ CopyArrayBufferViewOrArrayBufferData(con
   ArrayData data = GetArrayBufferViewOrArrayBufferData(aBufferOrView);
   aOutData.Clear();
   if (!data.IsValid()) {
     return;
   }
   aOutData.AppendElements(data.mData, data.mLength);
 }
 
+nsString
+KeySystemToGMPName(const nsAString& aKeySystem)
+{
+  if (aKeySystem.EqualsLiteral("com.adobe.primetime")) {
+    return NS_LITERAL_STRING("gmp-eme-adobe");
+  }
+  if (aKeySystem.EqualsLiteral("org.w3.clearkey")) {
+    return NS_LITERAL_STRING("gmp-clearkey");
+  }
+  MOZ_ASSERT(false, "We should only call this for known GMPs");
+  return EmptyString();
+}
+
 } // namespace mozilla
--- a/dom/media/eme/EMEUtils.h
+++ b/dom/media/eme/EMEUtils.h
@@ -99,11 +99,14 @@ struct ArrayData {
 // while the ArrayData is live, as then all bets about the data not changing
 // are off! No calls into JS, no calls into JS-implemented WebIDL or XPIDL,
 // nothing. Beware!
 //
 // Only call this on a properly initialized ArrayBufferViewOrArrayBuffer.
 ArrayData
 GetArrayBufferViewOrArrayBufferData(const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView);
 
+nsString
+KeySystemToGMPName(const nsAString& aKeySystem);
+
 } // namespace mozilla
 
 #endif // EME_LOG_H_
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -438,16 +438,19 @@ GetSupportedConfig(mozIGeckoMediaPluginS
 {
   MediaKeySystemConfiguration config;
   config.mLabel = aCandidate.mLabel;
   if (aCandidate.mInitDataTypes.WasPassed()) {
     nsTArray<nsString> initDataTypes;
     for (const nsString& candidate : aCandidate.mInitDataTypes.Value()) {
       if (candidate.EqualsLiteral("cenc")) {
         initDataTypes.AppendElement(candidate);
+      } else if (candidate.EqualsLiteral("keyids") &&
+                 aKeySystem.EqualsLiteral("org.w3.clearkey")) {
+        initDataTypes.AppendElement(candidate);
       }
     }
     if (initDataTypes.IsEmpty()) {
       return false;
     }
     config.mInitDataTypes.Construct();
     config.mInitDataTypes.Value().Assign(initDataTypes);
   }
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -363,16 +363,17 @@ MediaKeys::Init(ErrorResult& aRv)
   // here, and hold a self-reference until that promise is resolved or
   // rejected.
   MOZ_ASSERT(!mCreatePromiseId, "Should only be created once!");
   mCreatePromiseId = StorePromise(promise);
   AddRef();
   mProxy->Init(mCreatePromiseId,
                origin,
                topLevelOrigin,
+               KeySystemToGMPName(mKeySystem),
                inPrivateBrowsing);
 
   return promise.forget();
 }
 
 void
 MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const uint32_t aPluginId)
 {
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -1063,13 +1063,19 @@ GMPParent::Bridge(GMPServiceParent* aGMP
 {
   if (NS_FAILED(PGMPContent::Bridge(aGMPServiceParent, this))) {
     return false;
   }
   ++mGMPContentChildCount;
   return true;
 }
 
+nsString
+GMPParent::GetPluginBaseName() const
+{
+  return NS_LITERAL_STRING("gmp-") + mName;
+}
+
 } // namespace gmp
 } // namespace mozilla
 
 #undef LOG
 #undef LOGD
--- a/dom/media/gmp/GMPParent.h
+++ b/dom/media/gmp/GMPParent.h
@@ -119,16 +119,17 @@ public:
 
   // Specifies that a GMP can only work with the specified NodeIds.
   void SetNodeId(const nsACString& aNodeId);
   const nsACString& GetNodeId() const { return mNodeId; }
 
   const nsCString& GetDisplayName() const;
   const nsCString& GetVersion() const;
   const uint32_t GetPluginId() const;
+  nsString GetPluginBaseName() const;
 
   // Returns true if a plugin can be or is being used across multiple NodeIds.
   bool CanBeSharedCrossNodeIds() const;
 
   // A GMP can be used from a NodeId if it's already been set to work with
   // that NodeId, or if it's not been set to work with any NodeId and has
   // not yet been loaded (i.e. it's not shared across NodeIds).
   bool CanBeUsedFrom(const nsACString& aNodeId) const;
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -132,56 +132,61 @@ GeckoMediaPluginServiceChild::GetPluginV
   aOutVersion = version;
   return ok ? NS_OK : NS_ERROR_FAILURE;
 }
 
 class GetNodeIdDone : public GetServiceChildCallback
 {
 public:
   GetNodeIdDone(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
+                const nsAString& aGMPName,
                 bool aInPrivateBrowsing, UniquePtr<GetNodeIdCallback>&& aCallback)
     : mOrigin(aOrigin),
       mTopLevelOrigin(aTopLevelOrigin),
+      mGMPName(aGMPName),
       mInPrivateBrowsing(aInPrivateBrowsing),
       mCallback(Move(aCallback))
   {
   }
 
   virtual void Done(GMPServiceChild* aGMPServiceChild)
   {
     if (!aGMPServiceChild) {
       mCallback->Done(NS_ERROR_FAILURE, EmptyCString());
       return;
     }
 
     nsCString outId;
     if (!aGMPServiceChild->SendGetGMPNodeId(mOrigin, mTopLevelOrigin,
+                                            mGMPName,
                                             mInPrivateBrowsing, &outId)) {
       mCallback->Done(NS_ERROR_FAILURE, EmptyCString());
       return;
     }
 
     mCallback->Done(NS_OK, outId);
   }
 
 private:
   nsString mOrigin;
   nsString mTopLevelOrigin;
+  nsString mGMPName;
   bool mInPrivateBrowsing;
   UniquePtr<GetNodeIdCallback> mCallback;
 };
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceChild::GetNodeId(const nsAString& aOrigin,
                                         const nsAString& aTopLevelOrigin,
+                                        const nsAString& aGMPName,
                                         bool aInPrivateBrowsing,
                                         UniquePtr<GetNodeIdCallback>&& aCallback)
 {
   UniquePtr<GetServiceChildCallback> callback(
-    new GetNodeIdDone(aOrigin, aTopLevelOrigin, aInPrivateBrowsing, Move(aCallback)));
+    new GetNodeIdDone(aOrigin, aTopLevelOrigin, aGMPName, aInPrivateBrowsing, Move(aCallback)));
   GetServiceChild(Move(callback));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceChild::UpdateTrialCreateState(const nsAString& aKeySystem,
                                                      uint32_t aState)
 {
--- a/dom/media/gmp/GMPServiceChild.h
+++ b/dom/media/gmp/GMPServiceChild.h
@@ -42,16 +42,17 @@ public:
   static already_AddRefed<GeckoMediaPluginServiceChild> GetSingleton();
 
   NS_IMETHOD GetPluginVersionForAPI(const nsACString& aAPI,
                                     nsTArray<nsCString>* aTags,
                                     bool* aHasPlugin,
                                     nsACString& aOutVersion) override;
   NS_IMETHOD GetNodeId(const nsAString& aOrigin,
                        const nsAString& aTopLevelOrigin,
+                       const nsAString& aGMPName,
                        bool aInPrivateBrowsingMode,
                        UniquePtr<GetNodeIdCallback>&& aCallback) override;
   NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem,
                                     uint32_t aState) override;
 
   NS_DECL_NSIOBSERVER
 
   void SetServiceChild(UniquePtr<GMPServiceChild>&& aServiceChild);
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -158,58 +158,79 @@ CloneAndAppend(nsIFile* aFile, const nsA
   rv = f->Append(aDir);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return nullptr;
   }
   return f.forget();
 }
 
 static void
-MoveAndOverwrite(nsIFile* aOldStorageDir,
-                 nsIFile* aNewStorageDir,
+MoveAndOverwrite(nsIFile* aOldParentDir,
+                 nsIFile* aNewParentDir,
                  const nsAString& aSubDir)
 {
   nsresult rv;
 
-  nsCOMPtr<nsIFile> srcDir(CloneAndAppend(aOldStorageDir, aSubDir));
+  nsCOMPtr<nsIFile> srcDir(CloneAndAppend(aOldParentDir, aSubDir));
   if (NS_WARN_IF(!srcDir)) {
     return;
   }
 
   if (!FileExists(srcDir)) {
     // No sub-directory to be migrated.
     return;
   }
 
-  nsCOMPtr<nsIFile> dstDir(CloneAndAppend(aNewStorageDir, aSubDir));
+  // Ensure destination parent directory exists.
+  rv = aNewParentDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  nsCOMPtr<nsIFile> dstDir(CloneAndAppend(aNewParentDir, aSubDir));
   if (FileExists(dstDir)) {
     // We must have migrated before already, and then ran an old version
     // of Gecko again which created storage at the old location. Overwrite
     // the previously migrated storage.
     rv = dstDir->Remove(true);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       // MoveTo will fail.
       return;
     }
   }
 
-  rv = srcDir->MoveTo(aNewStorageDir, EmptyString());
+  rv = srcDir->MoveTo(aNewParentDir, EmptyString());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 }
 
 static void
 MigratePreGecko42StorageDir(nsIFile* aOldStorageDir,
                             nsIFile* aNewStorageDir)
 {
   MoveAndOverwrite(aOldStorageDir, aNewStorageDir, NS_LITERAL_STRING("id"));
   MoveAndOverwrite(aOldStorageDir, aNewStorageDir, NS_LITERAL_STRING("storage"));
 }
 
+static void
+MigratePreGecko45StorageDir(nsIFile* aStorageDirBase)
+{
+  nsCOMPtr<nsIFile> adobeStorageDir(CloneAndAppend(aStorageDirBase, NS_LITERAL_STRING("gmp-eme-adobe")));
+  if (NS_WARN_IF(!adobeStorageDir)) {
+    return;
+  }
+
+  // The base storage dir in pre-45 contained "id" and "storage" subdirs.
+  // We assume all storage in the base storage dir that aren't known to GMP
+  // storage are records for the Adobe GMP.
+  MoveAndOverwrite(aStorageDirBase, adobeStorageDir, NS_LITERAL_STRING("id"));
+  MoveAndOverwrite(aStorageDirBase, adobeStorageDir, NS_LITERAL_STRING("storage"));
+}
+
 static nsresult
 GMPPlatformString(nsAString& aOutPlatform)
 {
   // Append the OS and arch so that we don't reuse the storage if the profile is
   // copied or used under a different bit-ness, or copied to another platform.
   nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
   if (!runtime) {
     return NS_ERROR_FAILURE;
@@ -286,21 +307,29 @@ GeckoMediaPluginServiceParent::InitStora
     return rv;
   }
 
   rv = mStorageBaseDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
   if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_FILE_ALREADY_EXISTS)) {
     return rv;
   }
 
-  // Prior to 42, GMP storage was stored in $profile/gmp/. After 42, it's
-  // stored in $profile/gmp/$platform/. So we must migrate any old records
+  // Prior to 42, GMP storage was stored in $profileDir/gmp/. After 42, it's
+  // stored in $profileDir/gmp/$platform/. So we must migrate any old records
   // from the old location to the new location, for forwards compatibility.
   MigratePreGecko42StorageDir(gmpDirWithoutPlatform, mStorageBaseDir);
 
+  // Prior to 45, GMP storage was not separated by plugin. In 45 and after,
+  // it's stored in $profile/gmp/$platform/$gmpName. So we must migrate old
+  // records from the old location to the new location, for forwards
+  // compatibility. We assume all directories in the base storage dir that
+  // aren't known to GMP storage are records for the Adobe GMP, since it
+  // was first.
+  MigratePreGecko45StorageDir(mStorageBaseDir);
+
   return GeckoMediaPluginService::Init();
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceParent::Observe(nsISupports* aSubject,
                                        const char* aTopic,
                                        const char16_t* aSomeData)
 {
@@ -727,17 +756,17 @@ GeckoMediaPluginServiceParent::PathRunna
 {
   if (mOperation == ADD) {
     mService->AddOnGMPThread(mPath);
   } else {
     mService->RemoveOnGMPThread(mPath,
                                 mOperation == REMOVE_AND_DELETE_FROM_DISK,
                                 mDefer);
   }
-#ifndef MOZ_WIDGET_GONK // Bug 1214967: disabled on B2G due to inscrutable test failures.
+#ifndef MOZ_WIDGET_GONK // Bug 1214967: disabled on B2G due to inscrutable test failures.
   // For e10s, we must fire a notification so that all ContentParents notify
   // their children to update the codecs that the GMPDecoderModule can use.
   NS_DispatchToMainThread(new NotifyObserversTask("gmp-changed"), NS_DISPATCH_NORMAL);
   // For non-e10s, and for decoding in the chrome process, must update GMP
   // PDM's codecs list directly.
   NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
     GMPDecoderModule::UpdateUsableCodecs();
   }));
@@ -1207,16 +1236,17 @@ GeckoMediaPluginServiceParent::IsPersist
   NS_ENSURE_ARG(aOutAllowed);
   *aOutAllowed = mPersistentStorageAllowed.Get(aNodeId);
   return NS_OK;
 }
 
 nsresult
 GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
                                          const nsAString& aTopLevelOrigin,
+                                         const nsAString& aGMPName,
                                          bool aInPrivateBrowsing,
                                          nsACString& aOutId)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: (%s, %s), %s", __CLASS__, __FUNCTION__,
        NS_ConvertUTF16toUTF8(aOrigin).get(),
        NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
        (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing")));
@@ -1239,58 +1269,70 @@ GeckoMediaPluginServiceParent::GetNodeId
     mPersistentStorageAllowed.Put(salt, false);
     return NS_OK;
   }
 
   const uint32_t hash = AddToHash(HashString(aOrigin),
                                   HashString(aTopLevelOrigin));
 
   if (aInPrivateBrowsing) {
-    // For PB mode, we store the node id, indexed by the origin pair,
-    // so that if the same origin pair is opened in this session, it gets
-    // the same node id.
+    // For PB mode, we store the node id, indexed by the origin pair and GMP name,
+    // so that if the same origin pair is opened for the same GMP in this session,
+    // it gets the same node id.
+    const uint32_t pbHash = AddToHash(HashString(aGMPName), hash);
     nsCString* salt = nullptr;
-    if (!(salt = mTempNodeIds.Get(hash))) {
+    if (!(salt = mTempNodeIds.Get(pbHash))) {
       // No salt stored, generate and temporarily store some for this id.
       nsAutoCString newSalt;
       rv = GenerateRandomPathName(newSalt, NodeIdSaltLength);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       salt = new nsCString(newSalt);
-      mTempNodeIds.Put(hash, salt);
+      mTempNodeIds.Put(pbHash, salt);
       mPersistentStorageAllowed.Put(*salt, false);
     }
     aOutId = *salt;
     return NS_OK;
   }
 
   // Otherwise, try to see if we've previously generated and stored salt
   // for this origin pair.
-  nsCOMPtr<nsIFile> path; // $profileDir/gmp/
+  nsCOMPtr<nsIFile> path; // $profileDir/gmp/$platform/
   rv = GetStorageDir(getter_AddRefs(path));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  rv = path->Append(aGMPName);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // $profileDir/gmp/$platform/$gmpName/
+  rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
+  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  // $profileDir/gmp/id/
+  // $profileDir/gmp/$platform/$gmpName/id/
   rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
   if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsAutoCString hashStr;
   hashStr.AppendInt((int64_t)hash);
 
-  // $profileDir/gmp/id/$hash
+  // $profileDir/gmp/$platform/$gmpName/id/$hash
   rv = path->AppendNative(hashStr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = path->Create(nsIFile::DIRECTORY_TYPE, 0700);
   if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -1317,31 +1359,31 @@ GeckoMediaPluginServiceParent::GetNodeId
     // No stored salt for this origin. Generate salt, and store it and
     // the origin on disk.
     nsresult rv = GenerateRandomPathName(salt, NodeIdSaltLength);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     MOZ_ASSERT(salt.Length() == NodeIdSaltLength);
 
-    // $profileDir/gmp/id/$hash/salt
+    // $profileDir/gmp/$platform/$gmpName/id/$hash/salt
     rv = WriteToFile(path, NS_LITERAL_CSTRING("salt"), salt);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    // $profileDir/gmp/id/$hash/origin
+    // $profileDir/gmp/$platform/$gmpName/id/$hash/origin
     rv = WriteToFile(path,
                      NS_LITERAL_CSTRING("origin"),
                      NS_ConvertUTF16toUTF8(aOrigin));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    // $profileDir/gmp/id/$hash/topLevelOrigin
+    // $profileDir/gmp/$platform/$gmpName/id/$hash/topLevelOrigin
     rv = WriteToFile(path,
                      NS_LITERAL_CSTRING("topLevelOrigin"),
                      NS_ConvertUTF16toUTF8(aTopLevelOrigin));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
   } else {
@@ -1355,21 +1397,22 @@ GeckoMediaPluginServiceParent::GetNodeId
   mPersistentStorageAllowed.Put(salt, true);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceParent::GetNodeId(const nsAString& aOrigin,
                                          const nsAString& aTopLevelOrigin,
+                                         const nsAString& aGMPName,
                                          bool aInPrivateBrowsing,
                                          UniquePtr<GetNodeIdCallback>&& aCallback)
 {
   nsCString nodeId;
-  nsresult rv = GetNodeId(aOrigin, aTopLevelOrigin, aInPrivateBrowsing, nodeId);
+  nsresult rv = GetNodeId(aOrigin, aTopLevelOrigin, aGMPName, aInPrivateBrowsing, nodeId);
   aCallback->Done(rv, nodeId);
   return rv;
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceParent::UpdateTrialCreateState(const nsAString& aKeySystem,
                                                       uint32_t aState)
 {
@@ -1476,99 +1519,80 @@ struct NodeFilter {
   }
 private:
   const nsTArray<nsCString>& mNodeIDs;
 };
 
 void
 GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(DirectoryFilter& aFilter)
 {
-  nsresult rv;
+  // $profileDir/gmp/$platform/
   nsCOMPtr<nsIFile> path;
-
-  // $profileDir/gmp/
-  rv = GetStorageDir(getter_AddRefs(path));
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  // $profileDir/gmp/id/
-  rv = path->AppendNative(NS_LITERAL_CSTRING("id"));
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  // Iterate all sub-folders of $profileDir/gmp/id/
-  nsCOMPtr<nsISimpleEnumerator> iter;
-  rv = path->GetDirectoryEntries(getter_AddRefs(iter));
+  nsresult rv = GetStorageDir(getter_AddRefs(path));
   if (NS_FAILED(rv)) {
     return;
   }
 
-  bool hasMore = false;
-  nsTArray<nsCString> nodeIDsToClear;
-  while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
-    nsCOMPtr<nsISupports> supports;
-    rv = iter->GetNext(getter_AddRefs(supports));
-    if (NS_FAILED(rv)) {
-      continue;
-    }
+  // Iterate all sub-folders of $profileDir/gmp/$platform/, i.e. the dirs in which
+  // specific GMPs store their data.
+  DirectoryEnumerator iter(path, DirectoryEnumerator::DirsOnly);
+  for (nsCOMPtr<nsIFile> pluginDir; (pluginDir = iter.Next()) != nullptr;) {
+    ClearNodeIdAndPlugin(pluginDir, aFilter);
+  }
+}
 
-    // $profileDir/gmp/id/$hash
-    nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
-    if (NS_FAILED(rv)) {
-      continue;
-    }
+void
+GeckoMediaPluginServiceParent::ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir,
+                                                    DirectoryFilter& aFilter)
+{
+  // $profileDir/gmp/$platform/$gmpName/id/
+  nsCOMPtr<nsIFile> path = CloneAndAppend(aPluginStorageDir, NS_LITERAL_STRING("id"));
+  if (!path) {
+    return;
+  }
 
-    // Skip non-directory files.
-    bool isDirectory = false;
-    rv = dirEntry->IsDirectory(&isDirectory);
-    if (NS_FAILED(rv) || !isDirectory) {
-      continue;
-    }
-
+  // Iterate all sub-folders of $profileDir/gmp/$platform/$gmpName/id/
+  nsTArray<nsCString> nodeIDsToClear;
+  DirectoryEnumerator iter(path, DirectoryEnumerator::DirsOnly);
+  for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
+    // dirEntry is the hash of origins, i.e.:
+    // $profileDir/gmp/$platform/$gmpName/id/$originHash/
     if (!aFilter(dirEntry)) {
       continue;
     }
-
     nsAutoCString salt;
     if (NS_SUCCEEDED(ReadSalt(dirEntry, salt))) {
       // Keep node IDs to clear data/plugins associated with them later.
       nodeIDsToClear.AppendElement(salt);
       // Also remove node IDs from the table.
       mPersistentStorageAllowed.Remove(salt);
     }
     // Now we can remove the directory for the origin pair.
     if (NS_FAILED(dirEntry->Remove(true))) {
       NS_WARNING("Failed to delete the directory for the origin pair");
     }
   }
 
-  // Kill plugins that have node IDs to be cleared.
+  // Kill plugin instances that have node IDs being cleared.
   KillPlugins(mPlugins, mMutex, NodeFilter(nodeIDsToClear));
 
-  // Clear all matching $profileDir/gmp/storage/$nodeId/
-  rv = GetStorageDir(getter_AddRefs(path));
-  if (NS_FAILED(rv)) {
+  // Clear all storage in $profileDir/gmp/$platform/$gmpName/storage/$nodeId/
+  path = CloneAndAppend(aPluginStorageDir, NS_LITERAL_STRING("storage"));
+  if (!path) {
     return;
   }
 
-  rv = path->AppendNative(NS_LITERAL_CSTRING("storage"));
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  for (size_t i = 0; i < nodeIDsToClear.Length(); i++) {
+  for (const nsCString& nodeId : nodeIDsToClear) {
     nsCOMPtr<nsIFile> dirEntry;
-    rv = path->Clone(getter_AddRefs(dirEntry));
+    nsresult rv = path->Clone(getter_AddRefs(dirEntry));
     if (NS_FAILED(rv)) {
       continue;
     }
 
-    rv = dirEntry->AppendNative(nodeIDsToClear[i]);
+    rv = dirEntry->AppendNative(nodeId);
     if (NS_FAILED(rv)) {
       continue;
     }
 
     if (NS_FAILED(DeleteDir(dirEntry))) {
       NS_WARNING("Failed to delete GMP storage directory for the node");
     }
   }
@@ -1593,91 +1617,68 @@ GeckoMediaPluginServiceParent::ForgetThi
 }
 
 void
 GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread(PRTime aSince)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s: since=%lld", __CLASS__, __FUNCTION__, (int64_t)aSince));
 
-  nsCOMPtr<nsIFile> storagePath;
-  nsCOMPtr<nsIFile> temp;
-  if (NS_SUCCEEDED(GetStorageDir(getter_AddRefs(temp))) &&
-      NS_SUCCEEDED(temp->AppendNative(NS_LITERAL_CSTRING("storage")))) {
-    storagePath = temp.forget();
-  }
-
   struct MTimeFilter : public DirectoryFilter {
-    explicit MTimeFilter(PRTime aSince, already_AddRefed<nsIFile> aPath)
-      : mSince(aSince), mStoragePath(aPath) {}
+    explicit MTimeFilter(PRTime aSince)
+      : mSince(aSince) {}
 
     // Return true if any files under aPath is modified after |mSince|.
     bool IsModifiedAfter(nsIFile* aPath) {
       PRTime lastModified;
       nsresult rv = aPath->GetLastModifiedTime(&lastModified);
       if (NS_SUCCEEDED(rv) && lastModified >= mSince) {
         return true;
       }
-      // Check sub-directories recursively
-      nsCOMPtr<nsISimpleEnumerator> iter;
-      rv = aPath->GetDirectoryEntries(getter_AddRefs(iter));
-      if (NS_FAILED(rv)) {
-        return false;
-      }
-
-      bool hasMore = false;
-      while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
-        nsCOMPtr<nsISupports> supports;
-        rv = iter->GetNext(getter_AddRefs(supports));
-        if (NS_FAILED(rv)) {
-          continue;
-        }
-
-        nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv));
-        if (NS_FAILED(rv)) {
-          continue;
-        }
-
-        if (IsModifiedAfter(path)) {
+      DirectoryEnumerator iter(aPath, DirectoryEnumerator::FilesAndDirs);
+      for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
+        if (IsModifiedAfter(dirEntry)) {
           return true;
         }
       }
       return false;
     }
 
-    // |aPath| is $profileDir/gmp/id/$hash
+    // |aPath| is $profileDir/gmp/$platform/$gmpName/id/$originHash/
     virtual bool operator()(nsIFile* aPath) {
       if (IsModifiedAfter(aPath)) {
         return true;
       }
 
       nsAutoCString salt;
-      nsresult rv = ReadSalt(aPath, salt);
-      if (NS_FAILED(rv)) {
+      if (NS_FAILED(ReadSalt(aPath, salt))) {
         return false;
       }
 
-      // $profileDir/gmp/storage/
-      if (!mStoragePath) {
+      // $profileDir/gmp/$platform/$gmpName/id/
+      nsCOMPtr<nsIFile> idDir;
+      if (NS_FAILED(aPath->GetParent(getter_AddRefs(idDir)))) {
         return false;
       }
-      // $profileDir/gmp/storage/$nodeId/
-      nsCOMPtr<nsIFile> path;
-      rv = mStoragePath->Clone(getter_AddRefs(path));
-      if (NS_FAILED(rv)) {
+      // $profileDir/gmp/$platform/$gmpName/
+      nsCOMPtr<nsIFile> temp;
+      if (NS_FAILED(idDir->GetParent(getter_AddRefs(temp)))) {
         return false;
       }
 
-      rv = path->AppendNative(salt);
-      return NS_SUCCEEDED(rv) && IsModifiedAfter(path);
+      // $profileDir/gmp/$platform/$gmpName/storage/
+      if (NS_FAILED(temp->Append(NS_LITERAL_STRING("storage")))) {
+        return false;
+      }
+      // $profileDir/gmp/$platform/$gmpName/storage/$originSalt
+      return NS_SUCCEEDED(temp->AppendNative(salt)) && IsModifiedAfter(temp);
     }
   private:
     const PRTime mSince;
-    const nsCOMPtr<nsIFile> mStoragePath;
-  } filter(aSince, storagePath.forget());
+  } filter(aSince);
 
   ClearNodeIdAndPlugin(filter);
 
   NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL);
 }
 
 NS_IMETHODIMP
 GeckoMediaPluginServiceParent::ForgetThisSite(const nsAString& aSite)
@@ -1696,17 +1697,17 @@ void
 GeckoMediaPluginServiceParent::ClearStorage()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
   LOGD(("%s::%s", __CLASS__, __FUNCTION__));
 
   // Kill plugins with valid nodeIDs.
   KillPlugins(mPlugins, mMutex, &IsNodeIdValid);
 
-  nsCOMPtr<nsIFile> path; // $profileDir/gmp/
+  nsCOMPtr<nsIFile> path; // $profileDir/gmp/$platform/
   nsresult rv = GetStorageDir(getter_AddRefs(path));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   if (NS_FAILED(DeleteDir(path))) {
     NS_WARNING("Failed to delete GMP storage directory");
   }
@@ -1741,20 +1742,21 @@ GMPServiceParent::RecvLoadGMP(const nsCS
   *aPluginId = gmp->GetPluginId();
 
   return aAlreadyBridgedTo.Contains(*aId) || gmp->Bridge(this);
 }
 
 bool
 GMPServiceParent::RecvGetGMPNodeId(const nsString& aOrigin,
                                    const nsString& aTopLevelOrigin,
+                                   const nsString& aGMPName,
                                    const bool& aInPrivateBrowsing,
                                    nsCString* aID)
 {
-  nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin,
+  nsresult rv = mService->GetNodeId(aOrigin, aTopLevelOrigin, aGMPName,
                                     aInPrivateBrowsing, *aID);
   return NS_SUCCEEDED(rv);
 }
 
 bool
 GMPServiceParent::RecvUpdateGMPTrialCreateState(const nsString& aKeySystem,
                                                 const uint32_t& aState)
 {
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -36,16 +36,17 @@ public:
 
   // mozIGeckoMediaPluginService
   NS_IMETHOD GetPluginVersionForAPI(const nsACString& aAPI,
                                     nsTArray<nsCString>* aTags,
                                     bool* aHasPlugin,
                                     nsACString& aOutVersion) override;
   NS_IMETHOD GetNodeId(const nsAString& aOrigin,
                        const nsAString& aTopLevelOrigin,
+                       const nsAString& aGMPName,
                        bool aInPrivateBrowsingMode,
                        UniquePtr<GetNodeIdCallback>&& aCallback) override;
   NS_IMETHOD UpdateTrialCreateState(const nsAString& aKeySystem,
                                     uint32_t aState) override;
 
   NS_DECL_MOZIGECKOMEDIAPLUGINCHROMESERVICE
   NS_DECL_NSIOBSERVER
 
@@ -68,16 +69,17 @@ private:
                                 const nsCString& aAPI,
                                 const nsTArray<nsCString>& aTags);
   GMPParent* FindPluginForAPIFrom(size_t aSearchStartIndex,
                                   const nsCString& aAPI,
                                   const nsTArray<nsCString>& aTags,
                                   size_t* aOutPluginIndex);
 
   nsresult GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
+                     const nsAString& aGMPName,
                      bool aInPrivateBrowsing, nsACString& aOutId);
 
   void UnloadPlugins();
   void CrashPlugins();
   void NotifySyncShutdownComplete();
   void NotifyAsyncShutdownComplete();
 
   void LoadFromEnvironment();
@@ -90,17 +92,18 @@ private:
 
   nsresult SetAsyncShutdownTimeout();
 
   struct DirectoryFilter {
     virtual bool operator()(nsIFile* aPath) = 0;
     ~DirectoryFilter() {}
   };
   void ClearNodeIdAndPlugin(DirectoryFilter& aFilter);
-
+  void ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir,
+                            DirectoryFilter& aFilter);
   void ForgetThisSiteOnGMPThread(const nsACString& aOrigin);
   void ClearRecentHistoryOnGMPThread(PRTime aSince);
 
 protected:
   friend class GMPParent;
   void ReAddOnGMPThread(const RefPtr<GMPParent>& aOld);
   void PluginTerminated(const RefPtr<GMPParent>& aOld);
   virtual void InitializePlugins() override;
@@ -210,16 +213,17 @@ public:
                            const nsCString& aApi,
                            nsTArray<nsCString>&& aTags,
                            nsTArray<ProcessId>&& aAlreadyBridgedTo,
                            base::ProcessId* aID,
                            nsCString* aDisplayName,
                            uint32_t* aPluginId) override;
   virtual bool RecvGetGMPNodeId(const nsString& aOrigin,
                                 const nsString& aTopLevelOrigin,
+                                const nsString& aGMPName,
                                 const bool& aInPrivateBrowsing,
                                 nsCString* aID) override;
   static bool RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
                                             nsTArray<nsCString>&& aTags,
                                             bool* aHasPlugin,
                                             nsCString* aVersion);
   virtual bool RecvUpdateGMPTrialCreateState(const nsString& aKeySystem,
                                              const uint32_t& aState) override;
--- a/dom/media/gmp/GMPStorageParent.cpp
+++ b/dom/media/gmp/GMPStorageParent.cpp
@@ -27,20 +27,22 @@ namespace mozilla {
 
 extern LogModule* GetGMPLog();
 
 #define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
 #define LOG(level, msg) MOZ_LOG(GetGMPLog(), (level), msg)
 
 namespace gmp {
 
-// We store the records in files in the profile dir.
-// $profileDir/gmp/storage/$nodeId/
+// We store the records for a given GMP as files in the profile dir.
+// $profileDir/gmp/$platform/$gmpName/storage/$nodeId/
 static nsresult
-GetGMPStorageDir(nsIFile** aTempDir, const nsCString& aNodeId)
+GetGMPStorageDir(nsIFile** aTempDir,
+                 const nsString& aGMPName,
+                 const nsCString& aNodeId)
 {
   if (NS_WARN_IF(!aTempDir)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsCOMPtr<mozIGeckoMediaPluginChromeService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (NS_WARN_IF(!mps)) {
@@ -48,16 +50,26 @@ GetGMPStorageDir(nsIFile** aTempDir, con
   }
 
   nsCOMPtr<nsIFile> tmpFile;
   nsresult rv = mps->GetStorageDir(getter_AddRefs(tmpFile));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  rv = tmpFile->Append(aGMPName);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
+  if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   rv = tmpFile->AppendNative(NS_LITERAL_CSTRING("storage"));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = tmpFile->Create(nsIFile::DIRECTORY_TYPE, 0700);
   if (rv != NS_ERROR_FILE_ALREADY_EXISTS && NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -82,18 +94,20 @@ GetGMPStorageDir(nsIFile** aTempDir, con
 // the profile directory. The record name is a hash of the filename,
 // and we resolve hash collisions by just adding 1 to the hash code.
 // The format of records on disk is:
 //   4 byte, uint32_t $recordNameLength, in little-endian byte order,
 //   record name (i.e. $recordNameLength bytes, no null terminator)
 //   record bytes (entire remainder of file)
 class GMPDiskStorage : public GMPStorage {
 public:
-  explicit GMPDiskStorage(const nsCString& aNodeId)
+  explicit GMPDiskStorage(const nsCString& aNodeId,
+                          const nsString& aGMPName)
     : mNodeId(aNodeId)
+    , mGMPName(aGMPName)
   {
   }
 
   ~GMPDiskStorage() {
     // Close all open file handles.
     for (auto iter = mRecords.ConstIter(); !iter.Done(); iter.Next()) {
       Record* record = iter.UserData();
       if (record->mFileDesc) {
@@ -101,39 +115,23 @@ public:
         record->mFileDesc = nullptr;
       }
     }
   }
 
   nsresult Init() {
     // Build our index of records on disk.
     nsCOMPtr<nsIFile> storageDir;
-    nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mNodeId);
+    nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return NS_ERROR_FAILURE;
     }
 
-    nsCOMPtr<nsISimpleEnumerator> iter;
-    rv = storageDir->GetDirectoryEntries(getter_AddRefs(iter));
-    if (NS_FAILED(rv)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    bool hasMore;
-    while (NS_SUCCEEDED(iter->HasMoreElements(&hasMore)) && hasMore) {
-      nsCOMPtr<nsISupports> supports;
-      rv = iter->GetNext(getter_AddRefs(supports));
-      if (NS_FAILED(rv)) {
-        continue;
-      }
-      nsCOMPtr<nsIFile> dirEntry(do_QueryInterface(supports, &rv));
-      if (NS_FAILED(rv)) {
-        continue;
-      }
-
+    DirectoryEnumerator iter(storageDir, DirectoryEnumerator::FilesAndDirs);
+    for (nsCOMPtr<nsIFile> dirEntry; (dirEntry = iter.Next()) != nullptr;) {
       PRFileDesc* fd = nullptr;
       if (NS_FAILED(dirEntry->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) {
         continue;
       }
       int32_t recordLength = 0;
       nsCString recordName;
       nsresult err = ReadRecordMetadata(fd, recordLength, recordName);
       PR_Close(fd);
@@ -329,17 +327,17 @@ private:
 
   // We store records in a file which is a hash of the record name.
   // If there is a hash collision, we just keep adding 1 to the hash
   // code, until we find a free slot.
   nsresult GetUnusedFilename(const nsACString& aRecordName,
                              nsString& aOutFilename)
   {
     nsCOMPtr<nsIFile> storageDir;
-    nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mNodeId);
+    nsresult rv = GetGMPStorageDir(getter_AddRefs(storageDir), mGMPName, mNodeId);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     uint64_t recordNameHash = HashString(PromiseFlatCString(aRecordName).get());
     for (int i = 0; i < 1000000; i++) {
       nsCOMPtr<nsIFile> f;
       rv = storageDir->Clone(getter_AddRefs(f));
@@ -374,17 +372,17 @@ private:
 
   nsresult OpenStorageFile(const nsAString& aFileLeafName,
                            const OpenFileMode aMode,
                            PRFileDesc** aOutFD)
   {
     MOZ_ASSERT(aOutFD);
 
     nsCOMPtr<nsIFile> f;
-    nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mNodeId);
+    nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     f->Append(aFileLeafName);
 
     auto mode = PR_RDWR | PR_CREATE_FILE;
     if (aMode == Truncate) {
       mode |= PR_TRUNCATE;
@@ -451,17 +449,17 @@ private:
     }
 
     return NS_OK;
   }
 
   nsresult RemoveStorageFile(const nsString& aFilename)
   {
     nsCOMPtr<nsIFile> f;
-    nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mNodeId);
+    nsresult rv = GetGMPStorageDir(getter_AddRefs(f), mGMPName, mNodeId);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     rv = f->Append(aFilename);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     return f->Remove(/* bool recursive= */ false);
@@ -480,16 +478,17 @@ private:
     nsString mFilename;
     nsCString mRecordName;
     PRFileDesc* mFileDesc;
   };
 
   // Hash record name to record data.
   nsClassHashtable<nsCStringHashKey, Record> mRecords;
   const nsAutoCString mNodeId;
+  const nsString mGMPName;
 };
 
 class GMPMemoryStorage : public GMPStorage {
 public:
   GMPErr Open(const nsCString& aRecordName) override
   {
     MOZ_ASSERT(!IsOpen(aRecordName));
 
@@ -587,17 +586,18 @@ GMPStorageParent::Init()
     return NS_ERROR_FAILURE;
   }
 
   bool persistent = false;
   if (NS_WARN_IF(NS_FAILED(mps->IsPersistentStorageAllowed(mNodeId, &persistent)))) {
     return NS_ERROR_FAILURE;
   }
   if (persistent) {
-    UniquePtr<GMPDiskStorage> storage = MakeUnique<GMPDiskStorage>(mNodeId);
+    UniquePtr<GMPDiskStorage> storage =
+      MakeUnique<GMPDiskStorage>(mNodeId, mPlugin->GetPluginBaseName());
     if (NS_FAILED(storage->Init())) {
       NS_WARNING("Failed to initialize on disk GMP storage");
       return NS_ERROR_FAILURE;
     }
     mStorage = Move(storage);
   } else {
     mStorage = MakeUnique<GMPMemoryStorage>();
   }
--- a/dom/media/gmp/GMPUtils.cpp
+++ b/dom/media/gmp/GMPUtils.cpp
@@ -6,16 +6,17 @@
 
 #include "GMPUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIFile.h"
 #include "nsCOMPtr.h"
 #include "nsLiteralString.h"
 #include "nsCRTGlue.h"
 #include "mozilla/Base64.h"
+#include "nsISimpleEnumerator.h"
 
 namespace mozilla {
 
 bool
 GetEMEVoucherPath(nsIFile** aPath)
 {
   nsCOMPtr<nsIFile> path;
   NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(path));
@@ -66,9 +67,46 @@ ToBase64(const nsTArray<uint8_t>& aBytes
 
 bool
 FileExists(nsIFile* aFile)
 {
   bool exists = false;
   return aFile && NS_SUCCEEDED(aFile->Exists(&exists)) && exists;
 }
 
+DirectoryEnumerator::DirectoryEnumerator(nsIFile* aPath, Mode aMode)
+  : mMode(aMode)
+{
+  aPath->GetDirectoryEntries(getter_AddRefs(mIter));
+}
+
+already_AddRefed<nsIFile>
+DirectoryEnumerator::Next()
+{
+  if (!mIter) {
+    return nullptr;
+  }
+  bool hasMore = false;
+  while (NS_SUCCEEDED(mIter->HasMoreElements(&hasMore)) && hasMore) {
+    nsCOMPtr<nsISupports> supports;
+    nsresult rv = mIter->GetNext(getter_AddRefs(supports));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    nsCOMPtr<nsIFile> path(do_QueryInterface(supports, &rv));
+    if (NS_FAILED(rv)) {
+      continue;
+    }
+
+    if (mMode == DirsOnly) {
+      bool isDirectory = false;
+      rv = path->IsDirectory(&isDirectory);
+      if (NS_FAILED(rv) || !isDirectory) {
+        continue;
+      }
+    }
+    return path.forget();
+  }
+  return nullptr;
+}
+
 } // namespace mozilla
--- a/dom/media/gmp/GMPUtils.h
+++ b/dom/media/gmp/GMPUtils.h
@@ -3,19 +3,21 @@
  * 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 GMPUtils_h_
 #define GMPUtils_h_
 
 #include "mozilla/UniquePtr.h"
 #include "nsTArray.h"
+#include "nsCOMPtr.h"
 
 class nsIFile;
 class nsCString;
+class nsISimpleEnumerator;
 
 namespace mozilla {
 
 template<typename T>
 struct DestroyPolicy
 {
   void operator()(T* aGMPObject) const {
     aGMPObject->Destroy();
@@ -35,11 +37,29 @@ SplitAt(const char* aDelims,
         nsTArray<nsCString>& aOutTokens);
 
 nsCString
 ToBase64(const nsTArray<uint8_t>& aBytes);
 
 bool
 FileExists(nsIFile* aFile);
 
+// Enumerate directory entries for a specified path.
+class DirectoryEnumerator {
+public:
+
+  enum Mode {
+    DirsOnly, // Enumeration only includes directories.
+    FilesAndDirs // Enumeration includes directories and non-directory files.
+  };
+
+  DirectoryEnumerator(nsIFile* aPath, Mode aMode);
+
+  already_AddRefed<nsIFile> Next();
+
+private:
+  Mode mMode;
+  nsCOMPtr<nsISimpleEnumerator> mIter;
+};
+
 } // namespace mozilla
 
 #endif
--- a/dom/media/gmp/PGMPService.ipdl
+++ b/dom/media/gmp/PGMPService.ipdl
@@ -14,16 +14,17 @@ sync protocol PGMPService
 {
   parent spawns PGMP as child;
 
 parent:
   sync LoadGMP(nsCString nodeId, nsCString api, nsCString[] tags,
                ProcessId[] alreadyBridgedTo)
     returns (ProcessId id, nsCString displayName, uint32_t pluginId);
   sync GetGMPNodeId(nsString origin, nsString topLevelOrigin,
+                    nsString gmpName,
                     bool inPrivateBrowsing)
     returns (nsCString id);
 
   async UpdateGMPTrialCreateState(nsString keySystem, uint32_t status);
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/mozIGeckoMediaPluginService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginService.idl
@@ -47,17 +47,17 @@ public:
 
 [ptr] native TagArray(nsTArray<nsCString>);
 native GetGMPDecryptorCallback(mozilla::UniquePtr<GetGMPDecryptorCallback>&&);
 native GetGMPAudioDecoderCallback(mozilla::UniquePtr<GetGMPAudioDecoderCallback>&&);
 native GetGMPVideoDecoderCallback(mozilla::UniquePtr<GetGMPVideoDecoderCallback>&&);
 native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>&&);
 native GetNodeIdCallback(mozilla::UniquePtr<GetNodeIdCallback>&&);
 
-[scriptable, uuid(661492d6-726b-4ba0-8e6e-14bfaf2b62e4)]
+[scriptable, uuid(b5492915-2f0e-4973-9f91-a6fe61ac4749)]
 interface mozIGeckoMediaPluginService : nsISupports
 {
 
   /**
    * The GMP thread. Callable from any thread.
    */
   readonly attribute nsIThread thread;
 
@@ -140,16 +140,17 @@ interface mozIGeckoMediaPluginService : 
                        in GetGMPDecryptorCallback callback);
 
   /**
    * Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.
    */
   [noscript]
   void getNodeId(in AString origin,
                  in AString topLevelOrigin,
+                 in AString gmpName,
                  in bool inPrivateBrowsingMode,
                  in GetNodeIdCallback callback);
 
   /**
    * Stores the result of trying to create a decoder for the given keysystem.
    */
   [noscript]
   void updateTrialCreateState(in AString keySystem, in uint32_t status);
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -268,34 +268,41 @@ EnumerateDir(nsIFile* aPath, T&& aDirIte
     }
 
     aDirIter(entry);
   }
   return NS_OK;
 }
 
 /**
- * Enumerate files under $profileDir/gmp/$aDir/ (non-recursive).
+ * Enumerate files under $profileDir/gmp/$platform/gmp-fake/$aDir/ (non-recursive).
  */
 template<typename T>
 static nsresult
 EnumerateGMPStorageDir(const nsACString& aDir, T&& aDirIter)
 {
   RefPtr<GeckoMediaPluginServiceParent> service =
     GeckoMediaPluginServiceParent::GetSingleton();
   MOZ_ASSERT(service);
 
-  // $profileDir/gmp/
+  // $profileDir/gmp/$platform/
   nsCOMPtr<nsIFile> path;
   nsresult rv = service->GetStorageDir(getter_AddRefs(path));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  // $profileDir/gmp/$aDir/
+
+  // $profileDir/gmp/$platform/gmp-fake/
+  rv = path->Append(NS_LITERAL_STRING("gmp-fake"));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // $profileDir/gmp/$platform/gmp-fake/$aDir/
   rv = path->AppendNative(aDir);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   return EnumerateDir(path, aDirIter);
 }
 
@@ -464,16 +471,17 @@ GetNodeId(const nsAString& aOrigin,
   nsCString nodeId;
   nsresult result;
   UniquePtr<GetNodeIdCallback> callback(new TestGetNodeIdCallback(nodeId,
                                                                   result));
   // We rely on the fact that the GetNodeId implementation for
   // GeckoMediaPluginServiceParent is synchronous.
   nsresult rv = service->GetNodeId(aOrigin,
                                    aTopLevelOrigin,
+                                   NS_LITERAL_STRING("gmp-fake"),
                                    aInPBMode,
                                    Move(callback));
   EXPECT_TRUE(NS_SUCCEEDED(rv) && NS_SUCCEEDED(result));
   return nodeId;
 }
 
 static bool
 IsGMPStorageIsEmpty()
@@ -815,20 +823,20 @@ class GMPStorageTest : public GMPDecrypt
 
     rv = EnumerateGMPStorageDir(
         NS_LITERAL_CSTRING("storage"), StorageVerifier(aSiteInfo));
     EXPECT_TRUE(NS_SUCCEEDED(rv));
   }
 
   /**
    * 1. Generate some storage data.
-   * 2. Find the max mtime |t| in $profileDir/gmp/id/.
+   * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/id/.
    * 3. Pass |t| to clear recent history.
-   * 4. Check if all directories in $profileDir/gmp/id/ and
-   *    $profileDir/gmp/storage are removed.
+   * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and
+   *    $profileDir/gmp/$platform/gmp-fake/storage are removed.
    */
   void TestClearRecentHistory1() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory1_Clear);
@@ -837,20 +845,20 @@ class GMPStorageTest : public GMPDecrypt
     CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
                     NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
 }
 
   /**
    * 1. Generate some storage data.
-   * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
+   * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/storage/.
    * 3. Pass |t| to clear recent history.
-   * 4. Check if all directories in $profileDir/gmp/id/ and
-   *    $profileDir/gmp/storage are removed.
+   * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and
+   *    $profileDir/gmp/$platform/gmp-fake/storage are removed.
    */
   void TestClearRecentHistory2() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory2_Clear);
@@ -859,20 +867,20 @@ class GMPStorageTest : public GMPDecrypt
     CreateDecryptor(NS_LITERAL_STRING("http://example1.com"),
                     NS_LITERAL_STRING("http://example2.com"),
                     false,
                     NS_LITERAL_CSTRING("test-storage"));
   }
 
   /**
    * 1. Generate some storage data.
-   * 2. Find the max mtime |t| in $profileDir/gmp/storage/.
+   * 2. Find the max mtime |t| in $profileDir/gmp/$platform/gmp-fake/storage/.
    * 3. Pass |t+1| to clear recent history.
-   * 4. Check if all directories in $profileDir/gmp/id/ and
-   *    $profileDir/gmp/storage remain unchanged.
+   * 4. Check if all directories in $profileDir/gmp/$platform/gmp-fake/id/ and
+   *    $profileDir/gmp/$platform/gmp-fake/storage remain unchanged.
    */
   void TestClearRecentHistory3() {
     AssertIsOnGMPThread();
     EXPECT_TRUE(IsGMPStorageIsEmpty());
 
     // Generate storage data for some site.
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &GMPStorageTest::TestClearRecentHistory3_Clear);
@@ -943,39 +951,39 @@ class GMPStorageTest : public GMPDecrypt
   private:
     int mCount;
   };
 
   void TestClearRecentHistory_CheckEmpty() {
     FileCounter c1;
     nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), c1);
     EXPECT_TRUE(NS_SUCCEEDED(rv));
-    // There should be no files under $profileDir/gmp/id/
+    // There should be no files under $profileDir/gmp/$platform/gmp-fake/id/
     EXPECT_EQ(c1.GetCount(), 0);
 
     FileCounter c2;
     rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), c2);
     EXPECT_TRUE(NS_SUCCEEDED(rv));
-    // There should be no files under $profileDir/gmp/storage/
+    // There should be no files under $profileDir/gmp/$platform/gmp-fake/storage/
     EXPECT_EQ(c2.GetCount(), 0);
 
     SetFinished();
   }
 
   void TestClearRecentHistory_CheckNonEmpty() {
     FileCounter c1;
     nsresult rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), c1);
     EXPECT_TRUE(NS_SUCCEEDED(rv));
-    // There should be one directory under $profileDir/gmp/id/
+    // There should be one directory under $profileDir/gmp/$platform/gmp-fake/id/
     EXPECT_EQ(c1.GetCount(), 1);
 
     FileCounter c2;
     rv = EnumerateGMPStorageDir(NS_LITERAL_CSTRING("storage"), c2);
     EXPECT_TRUE(NS_SUCCEEDED(rv));
-    // There should be one directory under $profileDir/gmp/storage/
+    // There should be one directory under $profileDir/gmp/$platform/gmp-fake/storage/
     EXPECT_EQ(c2.GetCount(), 1);
 
     SetFinished();
   }
 
   void TestCrossOriginStorage() {
     EXPECT_TRUE(!mDecryptor);
 
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -614,16 +614,18 @@ skip-if = (toolkit == 'android' && proce
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_dormant_playback.html]
 skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'gonk')
 [test_eme_session_callable_value.html]
 [test_eme_canvas_blocked.html]
 skip-if = toolkit == 'android' # bug 1149374
+[test_eme_key_ids_initdata.html]
+skip-if = toolkit == 'android' # bug 1149374
 [test_eme_non_mse_fails.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_request_notifications.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_persistent_sessions.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_playback.html]
 skip-if = toolkit == 'android' || toolkit == 'gonk' # android: bug 1149374; gonk: bug 1193351
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_key_ids_initdata.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var tests = [
+  {
+    name: "One keyId",
+    initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"]}',
+    expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"],"type":"temporary"}',
+    sessionType: 'temporary',
+    expectPass: true,
+  },
+  {
+    name: "Two keyIds",
+    initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A", "0DdtU9od-Bh5L3xbv0Xf_A"]}',
+    expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A","0DdtU9od-Bh5L3xbv0Xf_A"],"type":"temporary"}',
+    sessionType: 'temporary',
+    expectPass: true,
+  },
+  {
+    name: "Two keyIds, temporary session",
+    initData: '{"type":"temporary", "kids":["LwVHf8JLtPrv2GUXFW2v_A", "0DdtU9od-Bh5L3xbv0Xf_A"]}',
+    expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A","0DdtU9od-Bh5L3xbv0Xf_A"],"type":"temporary"}',
+    sessionType: 'temporary',
+    expectPass: true,
+  },
+  {
+    name: "Two keyIds, persistent session, type before kids",
+    initData: '{"type":"persistent", "kids":["LwVHf8JLtPrv2GUXFW2v_A", "0DdtU9od-Bh5L3xbv0Xf_A"]}',
+    expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A","0DdtU9od-Bh5L3xbv0Xf_A"],"type":"persistent"}',
+    sessionType: 'persistent',
+    expectPass: true,
+  },
+  {
+    name: "Invalid keyId",
+    initData: '{"kids":["0"]}',
+    sessionType: 'temporary',
+    expectPass: false,
+  },
+  {
+    name: "Empty keyId",
+    initData: '{"kids":[""]}',
+    sessionType: 'temporary',
+    expectPass: false,
+  },
+  {
+    name: "SessionType in license doesn't match MediaKeySession's sessionType",
+    initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"]}',
+    sessionType: 'persistent',
+    expectPass: false,
+  },
+  {
+    name: "One valid and one invalid kid",
+    initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A", "invalid"]}',
+    expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"],"type":"temporary"}',
+    sessionType: 'temporary',
+    expectPass: true,
+  },
+  {
+    name: "Invalid initData",
+    initData: 'invalid initData',
+    sessionType: 'temporary',
+    expectPass: false,
+  },
+];
+
+function Test(test) {
+  return new Promise(function(resolve, reject) {
+    navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{initDataTypes: ['keyids']}]).then(
+      (access) => access.createMediaKeys()
+      ).then(
+        (mediaKeys) => {
+          var session = mediaKeys.createSession(test.sessionType);
+          var initData = new TextEncoder().encode(test.initData);
+          session.addEventListener("message", function(event) {
+            is(event.messageType, "license-request", "'" + test.name + "' MediaKeyMessage type should be license-request.");
+            var text = new TextDecoder().decode(event.message);
+            is(text, test.expectedRequest, "'" + test.name + "' got expected response.");
+            is(text == test.expectedRequest, test.expectPass,
+               "'" + test.name + "' expected to " + (test.expectPass ? "pass" : "fail"));
+            resolve();
+          });
+          return session.generateRequest('keyids', initData);
+        }
+      ).catch((x) => {
+        ok(!test.expectPass, "'" + test.name + "' expected to fail.");
+        resolve();
+      });
+  });
+}
+
+function beginTest() {
+  Promise.all(tests.map(Test)).then(function() { SimpleTest.finish(); });
+}
+
+SimpleTest.waitForExplicitFinish();
+SetupEMEPref(beginTest);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PPluginInstance;
 include protocol PPluginScriptableObject;
 include protocol PCrashReporter;
 include protocol PContent;
+include ProfilerTypes;
 
 using NPError from "npapi.h";
 using NPNVariable from "npapi.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h";
 using struct nsID from "nsID.h";
 
 namespace mozilla {
@@ -88,18 +89,17 @@ child:
   async SetParentHangTimeout(uint32_t seconds);
 
   intr PCrashReporter()
     returns (NativeThreadId tid, uint32_t processType);
 
   /**
    * Control the Gecko Profiler in the plugin process.
    */
-  async StartProfiler(uint32_t aEntries, double aInterval, nsCString[] aFeatures,
-                      nsCString[] aThreadNameFilters);
+  async StartProfiler(ProfilerInitParams params);
   async StopProfiler();
 
   async GatherProfile();
 
   async SettingChanged(PluginSettings settings);
 
 parent:
   async NP_InitializeResult(NPError aError);
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -2519,32 +2519,30 @@ PluginModuleChild::RecvProcessNativeEven
 #ifdef MOZ_WIDGET_COCOA
 void
 PluginModuleChild::ProcessNativeEvents() {
     CallProcessSomeEvents();    
 }
 #endif
 
 bool
-PluginModuleChild::RecvStartProfiler(const uint32_t& aEntries,
-                                     const double& aInterval,
-                                     nsTArray<nsCString>&& aFeatures,
-                                     nsTArray<nsCString>&& aThreadNameFilters)
+PluginModuleChild::RecvStartProfiler(const ProfilerInitParams& params)
 {
     nsTArray<const char*> featureArray;
-    for (size_t i = 0; i < aFeatures.Length(); ++i) {
-        featureArray.AppendElement(aFeatures[i].get());
+    for (size_t i = 0; i < params.features().Length(); ++i) {
+        featureArray.AppendElement(params.features()[i].get());
     }
 
     nsTArray<const char*> threadNameFilterArray;
-    for (size_t i = 0; i < aThreadNameFilters.Length(); ++i) {
-        threadNameFilterArray.AppendElement(aThreadNameFilters[i].get());
+    for (size_t i = 0; i < params.threadFilters().Length(); ++i) {
+        threadNameFilterArray.AppendElement(params.threadFilters()[i].get());
     }
 
-    profiler_start(aEntries, aInterval, featureArray.Elements(), featureArray.Length(),
+    profiler_start(params.entries(), params.interval(),
+                   featureArray.Elements(), featureArray.Length(),
                    threadNameFilterArray.Elements(), threadNameFilterArray.Length());
 
     return true;
 }
 
 bool
 PluginModuleChild::RecvStopProfiler()
 {
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -142,20 +142,17 @@ protected:
     virtual void
     ActorDestroy(ActorDestroyReason why) override;
 
     MOZ_NORETURN void QuickExit();
 
     virtual bool
     RecvProcessNativeEventsInInterruptCall() override;
 
-    virtual bool RecvStartProfiler(const uint32_t& aEntries,
-                                   const double& aInterval,
-                                   nsTArray<nsCString>&& aFeatures,
-                                   nsTArray<nsCString>&& aThreadNameFilters) override;
+    virtual bool RecvStartProfiler(const ProfilerInitParams& params) override;
     virtual bool RecvStopProfiler() override;
     virtual bool RecvGatherProfile() override;
 
 public:
     explicit PluginModuleChild(bool aIsChrome);
     virtual ~PluginModuleChild();
 
     bool CommonInit(base::ProcessId aParentPid,
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -3145,17 +3145,25 @@ PluginProfilerObserver::Observe(nsISuppo
     if (!strcmp(aTopic, "profiler-started")) {
         nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
         uint32_t entries;
         double interval;
         params->GetEntries(&entries);
         params->GetInterval(&interval);
         const nsTArray<nsCString>& features = params->GetFeatures();
         const nsTArray<nsCString>& threadFilterNames = params->GetThreadFilterNames();
-        Unused << mPmp->SendStartProfiler(entries, interval, features, threadFilterNames);
+
+        ProfilerInitParams ipcParams;
+        ipcParams.enabled() = true;
+        ipcParams.entries() = entries;
+        ipcParams.interval() = interval;
+        ipcParams.features() = features;
+        ipcParams.threadFilters() = threadFilterNames;
+
+        Unused << mPmp->SendStartProfiler(ipcParams);
     } else if (!strcmp(aTopic, "profiler-stopped")) {
         Unused << mPmp->SendStopProfiler();
     } else if (!strcmp(aTopic, "profiler-subprocess-gather")) {
         RefPtr<ProfileGatherer> gatherer = static_cast<ProfileGatherer*>(aSubject);
         mPmp->GatherAsyncProfile(gatherer);
     } else if (!strcmp(aTopic, "profiler-subprocess")) {
         nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
         mPmp->GatheredAsyncProfile(pse);
--- a/dom/push/PushService.jsm
+++ b/dom/push/PushService.jsm
@@ -273,17 +273,17 @@ this.PushService = {
       case "perm-changed":
         this._onPermissionChange(aSubject, aData).catch(error => {
           console.error("onPermissionChange: Error updating registrations:",
             error);
         })
         break;
 
       case "clear-origin-data":
-        this._clearOriginData(data).catch(error => {
+        this._clearOriginData(aData).catch(error => {
           console.error("clearOriginData: Error clearing origin data:", error);
         });
         break;
     }
   },
 
   _clearOriginData: function(data) {
     console.log("clearOriginData()");
--- a/dom/telephony/test/marionette/manifest.ini
+++ b/dom/telephony/test/marionette/manifest.ini
@@ -43,16 +43,17 @@ disabled = Bug 1214537
 [test_mmi_change_pin.js]
 [test_mmi_change_pin2.js]
 [test_mmi_clip.js]
 [test_mmi_clir.js]
 [test_mmi_imei.js]
 [test_mmi_unlock_puk.js]
 [test_mmi_unlock_puk2.js]
 [test_mmi_ussd.js]
+skip-if = android_version < '19'
 [test_modem_switch_tech.js]
 [test_multiple_hold.js]
 [test_outgoing_already_held.js]
 [test_outgoing_answer_hangup_oncallschanged.js]
 [test_outgoing_answer_radio_off.js]
 [test_outgoing_auto_hold.js]
 [test_outgoing_badNumber.js]
 [test_outgoing_busy.js]
--- a/dom/telephony/test/marionette/test_mmi_ussd.js
+++ b/dom/telephony/test/marionette/test_mmi_ussd.js
@@ -3,21 +3,19 @@
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = "head.js";
 
 function testUSSD() {
   log("Test *#1234# ...");
 
   return gSendMMI("*#1234#").then(aResult => {
-    // Since emulator doesn't support sending USSD, so we expect the result is
-    // always failed.
-    ok(!aResult.success, "Check success");
+    ok(aResult.success, "Check success");
     is(aResult.serviceCode, "scUssd", "Check serviceCode");
-    is(aResult.statusMessage, "RequestNotSupported", "Check statusMessage");
+    is(aResult.statusMessage, "", "Check statusMessage");
     is(aResult.additionalInformation, undefined, "No additional information");
   });
 }
 
 // Start test
 startTest(function() {
   return testUSSD()
     .catch(error => ok(false, "Promise reject: " + error))
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -128,128 +128,176 @@ struct ServiceWorkerManager::Registratio
   nsTArray<nsCString> mOrderedScopes;
 
   // Scope to registration.
   // The scope should be a fully qualified valid URL.
   nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mInfos;
 
   // Maps scopes to job queues.
   nsClassHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
-
-  nsDataHashtable<nsCStringHashKey, bool> mSetOfScopesBeingUpdated;
 };
 
 struct ServiceWorkerManager::PendingOperation final
 {
   nsCOMPtr<nsIRunnable> mRunnable;
 
   ServiceWorkerJobQueue* mQueue;
   RefPtr<ServiceWorkerJob> mJob;
 
   ServiceWorkerRegistrationData mRegistration;
 };
 
 class ServiceWorkerJob : public nsISupports
 {
+  friend class ServiceWorkerJobQueue;
+
+public:
+  NS_DECL_ISUPPORTS
+
+  enum Type
+  {
+    RegisterJob,
+    UpdateJob,
+    InstallJob,
+    UnregisterJob
+  };
+
+  virtual void Start() = 0;
+
+  bool
+  IsRegisterOrInstallJob() const
+  {
+    return mJobType == RegisterJob || mJobType == UpdateJob ||
+      mJobType == InstallJob;
+  }
+
 protected:
   // The queue keeps the jobs alive, so they can hold a rawptr back to the
   // queue.
   ServiceWorkerJobQueue* mQueue;
 
-public:
-  NS_DECL_ISUPPORTS
-
-  virtual void Start() = 0;
-
-  virtual bool
-  IsRegisterJob() const { return false; }
-
-protected:
-  explicit ServiceWorkerJob(ServiceWorkerJobQueue* aQueue)
+  Type mJobType;
+
+  explicit ServiceWorkerJob(ServiceWorkerJobQueue* aQueue, Type aJobType)
     : mQueue(aQueue)
+    , mJobType(aJobType)
   {}
 
   virtual ~ServiceWorkerJob()
   {}
 
   void
   Done(nsresult aStatus);
 };
 
 class ServiceWorkerJobQueue final
 {
   friend class ServiceWorkerJob;
 
-  nsTArray<RefPtr<ServiceWorkerJob>> mJobs;
+  struct QueueData final
+  {
+    QueueData()
+      : mPopping(false)
+    { }
+
+    ~QueueData()
+    {
+      if (!mJobs.IsEmpty()) {
+        NS_WARNING("Pending/running jobs still around on shutdown!");
+      }
+    }
+
+    nsTArray<RefPtr<ServiceWorkerJob>> mJobs;
+    bool mPopping;
+  };
+
   const nsCString mOriginAttributesSuffix;
-  bool mPopping;
+  QueueData mRegistrationJobQueue;
+  QueueData mInstallationJobQueue;
 
 public:
   explicit ServiceWorkerJobQueue(const nsACString& aScopeKey)
     : mOriginAttributesSuffix(aScopeKey)
-    , mPopping(false)
   {}
 
   ~ServiceWorkerJobQueue()
-  {
-    if (!mJobs.IsEmpty()) {
-      NS_WARNING("Pending/running jobs still around on shutdown!");
-    }
-  }
+  { }
 
   void
   Append(ServiceWorkerJob* aJob)
   {
     MOZ_ASSERT(aJob);
-    MOZ_ASSERT(!mJobs.Contains(aJob));
-    bool wasEmpty = mJobs.IsEmpty();
-    mJobs.AppendElement(aJob);
+    QueueData& queue = GetQueue(aJob->mJobType);
+    MOZ_ASSERT(!queue.mJobs.Contains(aJob));
+
+    bool wasEmpty = queue.mJobs.IsEmpty();
+    queue.mJobs.AppendElement(aJob);
     if (wasEmpty) {
       aJob->Start();
     }
   }
 
   void
   CancelJobs();
 
-  // Only used by HandleError, keep it that way!
-  ServiceWorkerJob*
-  Peek()
-  {
-    if (mJobs.IsEmpty()) {
-      return nullptr;
-    }
-    return mJobs[0];
-  }
-
 private:
   void
-  Pop()
+  CancelJobs(QueueData& aQueue);
+
+  // Internal helper function used to assign jobs to the correct queue.
+  QueueData&
+  GetQueue(ServiceWorkerJob::Type aType)
   {
-    MOZ_ASSERT(!mPopping,
+    switch (aType) {
+    case ServiceWorkerJob::Type::RegisterJob:
+    case ServiceWorkerJob::Type::UpdateJob:
+    case ServiceWorkerJob::Type::UnregisterJob:
+      return mRegistrationJobQueue;
+    case ServiceWorkerJob::Type::InstallJob:
+      return mInstallationJobQueue;
+    default:
+      MOZ_CRASH("Invalid job queue type.");
+      return mRegistrationJobQueue;
+    }
+  }
+
+  bool
+  IsEmpty()
+  {
+    return mRegistrationJobQueue.mJobs.IsEmpty() &&
+      mInstallationJobQueue.mJobs.IsEmpty();
+  }
+
+  void
+  Pop(QueueData& aQueue)
+  {
+    MOZ_ASSERT(!aQueue.mPopping,
                "Pop() called recursively, did you write a job which calls Done() synchronously from Start()?");
-    AutoRestore<bool> savePopping(mPopping);
-    mPopping = true;
-    MOZ_ASSERT(!mJobs.IsEmpty());
-    mJobs.RemoveElementAt(0);
-    if (!mJobs.IsEmpty()) {
-      mJobs[0]->Start();
-    } else {
+
+    AutoRestore<bool> savePopping(aQueue.mPopping);
+    aQueue.mPopping = true;
+    MOZ_ASSERT(!aQueue.mJobs.IsEmpty());
+    aQueue.mJobs.RemoveElementAt(0);
+    if (!aQueue.mJobs.IsEmpty()) {
+      aQueue.mJobs[0]->Start();
+    } else if (IsEmpty()) {
       RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
       MOZ_ASSERT(swm);
       swm->MaybeRemoveRegistrationInfo(mOriginAttributesSuffix);
     }
   }
 
   void
   Done(ServiceWorkerJob* aJob)
   {
-    MOZ_ASSERT(!mJobs.IsEmpty());
-    MOZ_ASSERT(mJobs[0] == aJob);
-    Pop();
+    MOZ_ASSERT(aJob);
+    QueueData& queue = GetQueue(aJob->mJobType);
+    MOZ_ASSERT(!queue.mJobs.IsEmpty());
+    MOZ_ASSERT(queue.mJobs[0] == aJob);
+    Pop(queue);
   }
 };
 
 namespace {
 
 nsresult
 PopulateRegistrationData(nsIPrincipal* aPrincipal,
                          const ServiceWorkerRegistrationInfo* aRegistration,
@@ -370,16 +418,17 @@ ServiceWorkerRegistrationInfo::Clear()
 }
 
 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope,
                                                              nsIPrincipal* aPrincipal)
   : mControlledDocumentsCounter(0)
   , mScope(aScope)
   , mPrincipal(aPrincipal)
   , mLastUpdateCheckTime(0)
+  , mUpdating(false)
   , mPendingUninstall(false)
 {}
 
 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
 {
   if (IsControllingDocuments()) {
     NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
   }
@@ -551,24 +600,24 @@ protected:
   {}
 
 public:
   virtual void ContinueAfterWorkerEvent(bool aSuccess) = 0;
 };
 
 NS_IMPL_ISUPPORTS0(ContinueLifecycleTask);
 
-class ServiceWorkerRegisterJob;
+class ServiceWorkerInstallJob;
 
 class ContinueInstallTask final : public ContinueLifecycleTask
 {
-  RefPtr<ServiceWorkerRegisterJob> mJob;
+  RefPtr<ServiceWorkerInstallJob> mJob;
 
 public:
-  explicit ContinueInstallTask(ServiceWorkerRegisterJob* aJob)
+  explicit ContinueInstallTask(ServiceWorkerInstallJob* aJob)
     : mJob(aJob)
   {}
 
   void ContinueAfterWorkerEvent(bool aSuccess) override;
 };
 
 class ContinueActivateTask final : public ContinueLifecycleTask
 {
@@ -626,38 +675,45 @@ public:
     : mWindow(aWindow)
     , mPromise(aPromise)
   {}
 
   void
   UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override
   {
     RefPtr<ServiceWorkerRegistrationMainThread> swr =
-      new ServiceWorkerRegistrationMainThread(mWindow,
-                                              NS_ConvertUTF8toUTF16(aInfo->mScope));
+      mWindow->GetServiceWorkerRegistration(NS_ConvertUTF8toUTF16(aInfo->mScope));
     mPromise->MaybeResolve(swr);
   }
 
   void
   UpdateFailed(ErrorResult& aStatus) override
   {
     mPromise->MaybeReject(aStatus);
   }
 };
 
-class ContinueUpdateRunnable final : public nsRunnable
+class ContinueUpdateRunnable final : public LifeCycleEventCallback
 {
   nsMainThreadPtrHandle<nsISupports> mJob;
+  bool mScriptEvaluationResult;
 public:
   explicit ContinueUpdateRunnable(const nsMainThreadPtrHandle<nsISupports> aJob)
     : mJob(aJob)
+    , mScriptEvaluationResult(false)
   {
     AssertIsOnMainThread();
   }
 
+  void
+  SetResult(bool aResult)
+  {
+    mScriptEvaluationResult = aResult;
+  }
+
   NS_IMETHOD Run();
 };
 
 namespace {
 
 /**
  * The spec mandates slightly different behaviors for computing the scope
  * prefix string in case a Service-Worker-Allowed header is specified versus
@@ -850,118 +906,323 @@ public:
 
 private:
   ~PropagateRemoveAllRunnable()
   {}
 };
 
 } // namespace
 
-class ServiceWorkerRegisterJob final : public ServiceWorkerJob,
+class ServiceWorkerJobBase : public ServiceWorkerJob
+{
+public:
+  ServiceWorkerJobBase(ServiceWorkerJobQueue* aQueue,
+                       ServiceWorkerJob::Type aJobType,
+                       ServiceWorkerUpdateFinishCallback* aCallback)
+    : ServiceWorkerJobBase(aQueue, aJobType, aCallback, nullptr, nullptr)
+  { }
+
+  ServiceWorkerJobBase(ServiceWorkerJobQueue* aQueue,
+                       ServiceWorkerJob::Type aJobType,
+                       ServiceWorkerUpdateFinishCallback* aCallback,
+                       ServiceWorkerRegistrationInfo* aRegistration,
+                       ServiceWorkerInfo* aServiceWorkerInfo)
+    : ServiceWorkerJob(aQueue, aJobType)
+    , mCallback(aCallback)
+    , mCanceled(false)
+    , mRegistration(aRegistration)
+    , mUpdateAndInstallInfo(aServiceWorkerInfo)
+  {
+    AssertIsOnMainThread();
+  }
+
+  void
+  Cancel()
+  {
+    mQueue = nullptr;
+    mCanceled = true;
+  }
+
+protected:
+  RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
+  bool mCanceled;
+  RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
+  RefPtr<ServiceWorkerInfo> mUpdateAndInstallInfo;
+
+  ~ServiceWorkerJobBase()
+  { }
+
+  // This MUST only be called when the job is still performing actions related
+  // to registration or update. After the spec resolves the update promise, use
+  // Done() with the failure code instead.
+  // Callers MUST hold a strong ref before calling this!
+  void
+  Fail(ErrorResult& aRv)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(mRegistration);
+
+    // With cancellation support, we may only be running with one reference
+    // from another object like a stream loader or something.
+    RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
+
+    // Save off the plain error code to pass to Done() where its logged to
+    // stderr as a warning.
+    nsresult origStatus = static_cast<nsresult>(aRv.ErrorCodeAsInt());
+
+    // Ensure that we only surface SecurityErr or TypeErr to script.
+    if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
+                        !aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR)) {
+
+      // Remove the old error code so we can replace it with a TypeError.
+      aRv.SuppressException();
+
+      NS_ConvertUTF8toUTF16 scriptSpec(mRegistration->mScriptSpec);
+      NS_ConvertUTF8toUTF16 scope(mRegistration->mScope);
+
+      // Throw the type error with a generic error message.
+      aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
+    }
+
+    if (mCallback) {
+      mCallback->UpdateFailed(aRv);
+      mCallback = nullptr;
+    }
+    // In case the callback does not consume the exception
+    aRv.SuppressException();
+
+    mUpdateAndInstallInfo = nullptr;
+    if (mRegistration->mInstallingWorker) {
+      nsresult rv = serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal,
+                                                         mRegistration->mInstallingWorker->CacheName());
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to purge the installing worker cache.");
+      }
+    }
+
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    swm->MaybeRemoveRegistration(mRegistration);
+    // Ensures that the job can't do anything useful from this point on.
+    mRegistration = nullptr;
+    Done(origStatus);
+  }
+
+  void
+  Fail(nsresult aRv)
+  {
+    ErrorResult rv(aRv);
+    Fail(rv);
+  }
+
+  void
+  Succeed()
+  {
+    AssertIsOnMainThread();
+    // We don't have a callback for soft updates.
+    if (mCallback) {
+      mCallback->UpdateSucceeded(mRegistration);
+      mCallback = nullptr;
+    }
+  }
+};
+
+class ServiceWorkerInstallJob final : public ServiceWorkerJobBase
+{
+  friend class ContinueInstallTask;
+
+public:
+  ServiceWorkerInstallJob(ServiceWorkerJobQueue* aQueue,
+                          ServiceWorkerUpdateFinishCallback* aCallback,
+                          ServiceWorkerRegistrationInfo* aRegistration,
+                          ServiceWorkerInfo* aServiceWorkerInfo)
+    : ServiceWorkerJobBase(aQueue, Type::InstallJob, aCallback,
+                           aRegistration, aServiceWorkerInfo)
+  {
+    MOZ_ASSERT(aRegistration);
+  }
+
+  void
+  Start()
+  {
+    AssertIsOnMainThread();
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableMethod(this, &ServiceWorkerInstallJob::Install);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+  }
+
+  void
+  Install()
+  {
+    RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
+    if (mCanceled) {
+      return Fail(NS_ERROR_DOM_ABORT_ERR);
+    }
+    MOZ_ASSERT(mRegistration);
+
+    // Begin [[Install]] atomic step 3.
+    if (mRegistration->mInstallingWorker) {
+      mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
+      mRegistration->mInstallingWorker->WorkerPrivate()->TerminateWorker();
+    }
+
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
+                                                   WhichServiceWorker::INSTALLING_WORKER);
+
+    mRegistration->mInstallingWorker = mUpdateAndInstallInfo.forget();
+    mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
+    mRegistration->NotifyListenersOnChange();
+
+    Succeed();
+
+    // The job should NOT call fail from this point on.
+
+    // Step 8 "Queue a task..." for updatefound.
+    nsCOMPtr<nsIRunnable> upr =
+      NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(
+        swm,
+        &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
+        mRegistration);
+
+    NS_DispatchToMainThread(upr);
+
+    // Call ContinueAfterInstallEvent(false) on main thread if the SW
+    // script fails to load.
+    nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool>
+      (this, &ServiceWorkerInstallJob::ContinueAfterInstallEvent, false);
+
+    nsMainThreadPtrHandle<ContinueLifecycleTask> installTask(
+      new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
+    RefPtr<LifeCycleEventCallback> callback = new ContinueLifecycleRunnable(installTask);
+
+    // This triggers Step 4.7 "Queue a task to run the following substeps..."
+    // which sends the install event to the worker.
+    ServiceWorkerPrivate* workerPrivate =
+      mRegistration->mInstallingWorker->WorkerPrivate();
+    nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"),
+                                                    callback, failRunnable);
+
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      ContinueAfterInstallEvent(false /* aSuccess */);
+    }
+  }
+
+  void
+  ContinueAfterInstallEvent(bool aInstallEventSuccess)
+  {
+    if (mCanceled) {
+      return Done(NS_ERROR_DOM_ABORT_ERR);
+    }
+
+    if (!mRegistration->mInstallingWorker) {
+      NS_WARNING("mInstallingWorker was null.");
+      return Done(NS_ERROR_DOM_ABORT_ERR);
+    }
+
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+
+    // "If installFailed is true"
+    if (NS_WARN_IF(!aInstallEventSuccess)) {
+      mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
+      mRegistration->mInstallingWorker = nullptr;
+      swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
+                                                     WhichServiceWorker::INSTALLING_WORKER);
+      swm->MaybeRemoveRegistration(mRegistration);
+      return Done(NS_ERROR_DOM_ABORT_ERR);
+    }
+
+    // "If registration's waiting worker is not null"
+    if (mRegistration->mWaitingWorker) {
+      mRegistration->mWaitingWorker->WorkerPrivate()->TerminateWorker();
+      mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
+
+      nsresult rv =
+        serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal,
+                                             mRegistration->mWaitingWorker->CacheName());
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to purge the old waiting cache.");
+      }
+    }
+
+    mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget();
+    mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
+    mRegistration->NotifyListenersOnChange();
+    swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
+                                                   WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
+
+    // "If registration's waiting worker's skip waiting flag is set"
+    if (mRegistration->mWaitingWorker->SkipWaitingFlag()) {
+      mRegistration->PurgeActiveWorker();
+    }
+
+    Done(NS_OK);
+    // Activate() is invoked out of band of atomic.
+    mRegistration->TryToActivate();
+  }
+};
+
+class ServiceWorkerRegisterJob final : public ServiceWorkerJobBase,
                                        public serviceWorkerScriptCache::CompareCallback
 {
-  friend class ContinueInstallTask;
+  friend class ContinueUpdateRunnable;
 
   nsCString mScope;
   nsCString mScriptSpec;
-  RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
-  nsTArray<RefPtr<ServiceWorkerUpdateFinishCallback>> mCallbacks;
   nsCOMPtr<nsIPrincipal> mPrincipal;
-  RefPtr<ServiceWorkerInfo> mUpdateAndInstallInfo;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
 
   ~ServiceWorkerRegisterJob()
-  {}
-
-  enum
-  {
-    REGISTER_JOB = 0,
-    UPDATE_JOB = 1,
-  } mJobType;
-
-  bool mCanceled;
+  { }
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // [[Register]]
   ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
                            const nsCString& aScope,
                            const nsCString& aScriptSpec,
                            ServiceWorkerUpdateFinishCallback* aCallback,
                            nsIPrincipal* aPrincipal,
                            nsILoadGroup* aLoadGroup)
-    : ServiceWorkerJob(aQueue)
+    : ServiceWorkerJobBase(aQueue, Type::RegisterJob, aCallback)
     , mScope(aScope)
     , mScriptSpec(aScriptSpec)
     , mPrincipal(aPrincipal)
     , mLoadGroup(aLoadGroup)
-    , mJobType(REGISTER_JOB)
-    , mCanceled(false)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(mLoadGroup);
     MOZ_ASSERT(aCallback);
-
-    mCallbacks.AppendElement(aCallback);
   }
 
   // [[Update]]
   ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
                            ServiceWorkerRegistrationInfo* aRegistration,
                            ServiceWorkerUpdateFinishCallback* aCallback)
-    : ServiceWorkerJob(aQueue)
-    , mRegistration(aRegistration)
-    , mJobType(UPDATE_JOB)
-    , mCanceled(false)
+    : ServiceWorkerJobBase(aQueue, Type::UpdateJob, aCallback,
+                           aRegistration, nullptr)
   {
     AssertIsOnMainThread();
-    MOZ_ASSERT(aCallback);
-
-    mCallbacks.AppendElement(aCallback);
-  }
-
-  bool
-  IsRegisterJob() const override
-  {
-    return true;
-  }
-
-  void
-  AppendCallback(ServiceWorkerUpdateFinishCallback* aCallback)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aCallback);
-    MOZ_ASSERT(!mCallbacks.Contains(aCallback));
-
-    mCallbacks.AppendElement(aCallback);
-  }
-
-  void
-  Cancel()
-  {
-    mQueue = nullptr;
-    mCanceled = true;
   }
 
   void
   Start() override
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(!mCanceled);
 
     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     if (!swm->HasBackgroundActor()) {
       nsCOMPtr<nsIRunnable> runnable =
         NS_NewRunnableMethod(this, &ServiceWorkerRegisterJob::Start);
       swm->AppendPendingOperation(runnable);
       return;
     }
 
-    if (mJobType == REGISTER_JOB) {
+    if (mJobType == RegisterJob) {
       mRegistration = swm->GetRegistration(mPrincipal, mScope);
 
       if (mRegistration) {
         mRegistration->mPendingUninstall = false;
         RefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
         if (newest && mScriptSpec.Equals(newest->ScriptSpec()) &&
             mScriptSpec.Equals(mRegistration->mScriptSpec)) {
           swm->StoreRegistration(mPrincipal, mRegistration);
@@ -980,21 +1241,17 @@ public:
       } else {
         mRegistration = swm->CreateNewRegistration(mScope, mPrincipal);
       }
 
       mRegistration->mScriptSpec = mScriptSpec;
       mRegistration->NotifyListenersOnChange();
       swm->StoreRegistration(mPrincipal, mRegistration);
     } else {
-      MOZ_ASSERT(mJobType == UPDATE_JOB);
-      MOZ_ASSERT(mRegistration);
-      MOZ_ASSERT(mRegistration->mUpdateJob == nullptr);
-
-      mRegistration->mUpdateJob = this;
+      MOZ_ASSERT(mJobType == UpdateJob);
     }
 
     Update();
   }
 
   void
   ComparisonResult(nsresult aStatus, bool aInCacheAndEqual,
                    const nsAString& aNewCacheName,
@@ -1066,242 +1323,95 @@ public:
       return Fail(NS_ERROR_FAILURE);
     }
 
     ServiceWorkerManager::RegistrationDataPerPrincipal* data;
     if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
       return Fail(NS_ERROR_FAILURE);
     }
 
-    nsAutoString cacheName;
-    // We have to create a ServiceWorker here simply to ensure there are no
-    // errors. Ideally we should just pass this worker on to ContinueInstall.
-    MOZ_ASSERT(!data->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
-    data->mSetOfScopesBeingUpdated.Put(mRegistration->mScope, true);
-
-    // Call FailScopeUpdate on main thread if the SW script load fails below.
-    nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs
-      <StorensRefPtrPassByPtr<ServiceWorkerManager>, nsCString>
-      (this, &ServiceWorkerRegisterJob::FailScopeUpdate, swm, scopeKey);
-
     MOZ_ASSERT(!mUpdateAndInstallInfo);
     mUpdateAndInstallInfo =
       new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec,
                             aNewCacheName);
 
     RefPtr<ServiceWorkerJob> upcasted = this;
     nsMainThreadPtrHandle<nsISupports> handle(
         new nsMainThreadPtrHolder<nsISupports>(upcasted));
-    RefPtr<nsRunnable> callback = new ContinueUpdateRunnable(handle);
+    RefPtr<LifeCycleEventCallback> callback = new ContinueUpdateRunnable(handle);
 
     ServiceWorkerPrivate* workerPrivate =
       mUpdateAndInstallInfo->WorkerPrivate();
-    rv = workerPrivate->ContinueOnSuccessfulScriptEvaluation(callback);
+    rv = workerPrivate->CheckScriptEvaluation(callback);
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return FailScopeUpdate(swm, scopeKey);
+      Fail(NS_ERROR_DOM_ABORT_ERR);
     }
   }
 
+private:
+  // This will perform steps 27 and 28 from [[Update]]
+  // Remove the job from the registration queue and invoke [[Install]]
   void
-  FailScopeUpdate(ServiceWorkerManager* aSwm, const nsACString& aScopeKey)
-  {
-    AssertIsOnMainThread();
-    MOZ_ASSERT(aSwm);
-    ServiceWorkerManager::RegistrationDataPerPrincipal* data;
-    if (aSwm->mRegistrationInfos.Get(aScopeKey, &data)) {
-      data->mSetOfScopesBeingUpdated.Remove(aScopeKey);
-    }
-    Fail(NS_ERROR_DOM_ABORT_ERR);
-  }
-
-  // This MUST only be called when the job is still performing actions related
-  // to registration or update. After the spec resolves the update promise, use
-  // Done() with the failure code instead.
-  // Callers MUST hold a strong ref before calling this!
-  void
-  Fail(ErrorResult& aRv)
+  ContinueInstall(bool aScriptEvaluationResult)
   {
     AssertIsOnMainThread();
-    MOZ_ASSERT(mCallbacks.Length());
-
-    // With cancellation support, we may only be running with one reference
-    // from another object like a stream loader or something.
-    RefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
-
-    // Save off the plain error code to pass to Done() where its logged to
-    // stderr as a warning.
-    nsresult origStatus = static_cast<nsresult>(aRv.ErrorCodeAsInt());
-
-    // Ensure that we only surface SecurityErr or TypeErr to script.
-    if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
-                        !aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR)) {
-
-      // Remove the old error code so we can replace it with a TypeError.
-      aRv.SuppressException();
-
-      // Depending on how the job was created and where we are in the
-      // state machine the spec and scope may be stored in different ways.
-      // Extract the current scope and script spec.
-      nsString scriptSpec;
-      nsString scope;
-      if (mRegistration) {
-        CopyUTF8toUTF16(mRegistration->mScriptSpec, scriptSpec);
-        CopyUTF8toUTF16(mRegistration->mScope, scope);
-      } else {
-        CopyUTF8toUTF16(mScriptSpec, scriptSpec);
-        CopyUTF8toUTF16(mScope, scope);
-      }
-
-      // Throw the type error with a generic error message.
-      aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
-    }
-
-    for (uint32_t i = 1; i < mCallbacks.Length(); ++i) {
-      ErrorResult rv;
-      aRv.CloneTo(rv);
-      mCallbacks[i]->UpdateFailed(rv);
-      rv.SuppressException();
-    }
-
-    mCallbacks[0]->UpdateFailed(aRv);
-
-    // In case the callback does not consume the exception
-    aRv.SuppressException();
-
-    mUpdateAndInstallInfo = nullptr;
-    if (mRegistration->mInstallingWorker) {
-      nsresult rv = serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal,
-                                                         mRegistration->mInstallingWorker->CacheName());
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Failed to purge the installing worker cache.");
-      }
-    }
-
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    swm->MaybeRemoveRegistration(mRegistration);
-    // Ensures that the job can't do anything useful from this point on.
-    mRegistration->mUpdateJob = nullptr;
-    mRegistration = nullptr;
-    Done(origStatus);
-  }
-
-  void
-  Fail(nsresult aRv)
-  {
-    ErrorResult rv(aRv);
-    Fail(rv);
-  }
-
-  // Public so our error handling code can continue with a successful worker.
-  void
-  ContinueInstall()
-  {
-    // mRegistration will be null if we have already Fail()ed.
-    if (!mRegistration) {
-      return;
-    }
-
-    // Even if we are canceled, ensure integrity of mSetOfScopesBeingUpdated
-    // first.
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-
-    nsAutoCString scopeKey;
-    nsresult rv = swm->PrincipalToScopeKey(mRegistration->mPrincipal, scopeKey);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return Fail(NS_ERROR_FAILURE);
-    }
-
-    ServiceWorkerManager::RegistrationDataPerPrincipal* data;
-    if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
-      return Fail(NS_ERROR_FAILURE);
-    }
-
-    MOZ_ASSERT(data->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
-    data->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
-    // This is effectively the end of Step 4.3 of the [[Update]] algorithm.
-    // The invocation of [[Install]] is not part of the atomic block.
+    MOZ_ASSERT(mRegistration);
+    mRegistration->mUpdating = false;
 
     RefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
     if (mCanceled) {
       return Fail(NS_ERROR_DOM_ABORT_ERR);
     }
 
-    // Begin [[Install]] atomic step 4.
-    if (mRegistration->mInstallingWorker) {
-      mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
-      mRegistration->mInstallingWorker->WorkerPrivate()->TerminateWorker();
+    if (NS_WARN_IF(!aScriptEvaluationResult)) {
+      ErrorResult error;
+
+      NS_ConvertUTF8toUTF16 scriptSpec(mRegistration->mScriptSpec);
+      NS_ConvertUTF8toUTF16 scope(mRegistration->mScope);
+      error.ThrowTypeError<MSG_SW_SCRIPT_THREW>(scriptSpec, scope);
+      return Fail(error);
     }
 
-    swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
-                                                   WhichServiceWorker::INSTALLING_WORKER);
-
-    mRegistration->mInstallingWorker = mUpdateAndInstallInfo.forget();
-    mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
-    mRegistration->NotifyListenersOnChange();
-
-    Succeed();
-    // The job should NOT call fail from this point on.
-
-    // Step 4.6 "Queue a task..." for updatefound.
-    nsCOMPtr<nsIRunnable> upr =
-      NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(
-        swm,
-        &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
-        mRegistration);
-
-    NS_DispatchToMainThread(upr);
-
-    // Call ContinueAfterInstallEvent(false) on main thread if the SW
-    // script fails to load.
-    nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool>
-      (this, &ServiceWorkerRegisterJob::ContinueAfterInstallEvent, false);
-
-    nsMainThreadPtrHandle<ContinueLifecycleTask> installTask(
-      new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
-    RefPtr<LifeCycleEventCallback> callback = new ContinueLifecycleRunnable(installTask);
-
-    // This triggers Step 4.7 "Queue a task to run the following substeps..."
-    // which sends the install event to the worker.
-    ServiceWorkerPrivate* workerPrivate =
-      mRegistration->mInstallingWorker->WorkerPrivate();
-    rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"),
-                                           callback, failRunnable);
-
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      ContinueAfterInstallEvent(false /* aSuccess */);
-    }
-  }
-
-private:
+    RefPtr<ServiceWorkerInstallJob> job =
+      new ServiceWorkerInstallJob(mQueue, mCallback,
+                                  mRegistration, mUpdateAndInstallInfo);
+    mQueue->Append(job);
+    Done(NS_OK);
+  }
+
   void
   Update()
   {
+    AssertIsOnMainThread();
+
     // Since Update() is called synchronously from Start(), we can assert this.
     MOZ_ASSERT(!mCanceled);
     MOZ_ASSERT(mRegistration);
     nsCOMPtr<nsIRunnable> r =
       NS_NewRunnableMethod(this, &ServiceWorkerRegisterJob::ContinueUpdate);
     NS_DispatchToMainThread(r);
+
+    mRegistration->mUpdating = true;
   }
 
   // Aspects of (actually the whole algorithm) of [[Update]] after
   // "Run the following steps in parallel."
   void
   ContinueUpdate()
   {
     AssertIsOnMainThread();
     RefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
     if (mCanceled) {
       return Fail(NS_ERROR_DOM_ABORT_ERR);
     }
 
     if (mRegistration->mInstallingWorker) {
       mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
-      // This will terminate the installing worker thread.
+      mRegistration->mInstallingWorker->WorkerPrivate()->TerminateWorker();
       mRegistration->mInstallingWorker = nullptr;
     }
 
     RefPtr<ServiceWorkerInfo> workerInfo = mRegistration->Newest();
     nsAutoString cacheName;
 
     // 9.2.20 If newestWorker is not null, and newestWorker's script url is
     // equal to registration's registering script url and response is a
@@ -1315,123 +1425,67 @@ private:
                                         NS_ConvertUTF8toUTF16(mRegistration->mScriptSpec),
                                         this, mLoadGroup);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return Fail(rv);
     }
   }
 
   void
-  Succeed()
+  Done(nsresult aStatus)
   {
     AssertIsOnMainThread();
-    MOZ_ASSERT(mCallbacks.Length());
-
-    for (uint32_t i = 0; i < mCallbacks.Length(); ++i) {
-      mCallbacks[i]->UpdateSucceeded(mRegistration);
-    }
-    mCallbacks.Clear();
-  }
-
-  void
-  ContinueAfterInstallEvent(bool aInstallEventSuccess)
-  {
-    if (mCanceled) {
-      return Done(NS_ERROR_DOM_ABORT_ERR);
-    }
-
-    if (!mRegistration->mInstallingWorker) {
-      NS_WARNING("mInstallingWorker was null.");
-      return Done(NS_ERROR_DOM_ABORT_ERR);
-    }
-
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-
-    // "If installFailed is true"
-    if (NS_WARN_IF(!aInstallEventSuccess)) {
-      mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
-      mRegistration->mInstallingWorker = nullptr;
-      swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
-                                                     WhichServiceWorker::INSTALLING_WORKER);
-      swm->MaybeRemoveRegistration(mRegistration);
-      return Done(NS_ERROR_DOM_ABORT_ERR);
+
+    if (mRegistration) {
+      mRegistration->mUpdating = false;
     }
 
-    // "If registration's waiting worker is not null"
-    if (mRegistration->mWaitingWorker) {
-      mRegistration->mWaitingWorker->WorkerPrivate()->TerminateWorker();
-      mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
-
-      nsresult rv = serviceWorkerScriptCache::PurgeCache(mRegistration->mPrincipal,
-                                                         mRegistration->mWaitingWorker->CacheName());
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Failed to purge the old waiting cache.");
-      }
-    }
-
-    mRegistration->mWaitingWorker = mRegistration->mInstallingWorker.forget();
-    mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
-    mRegistration->NotifyListenersOnChange();
-    swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
-                                                   WhichServiceWorker::INSTALLING_WORKER | WhichServiceWorker::WAITING_WORKER);
-
-    // "If registration's waiting worker's skip waiting flag is set"
-    if (mRegistration->mWaitingWorker->SkipWaitingFlag()) {
-      mRegistration->PurgeActiveWorker();
-    }
-
-    Done(NS_OK);
-    // Activate() is invoked out of band of atomic.
-    mRegistration->TryToActivate();
-  }
-
-  void
-  Done(nsresult aStatus)
-  {
     ServiceWorkerJob::Done(aStatus);
-
-    if (mJobType == UPDATE_JOB && mRegistration) {
-      MOZ_ASSERT(NS_IsMainThread());
-      MOZ_ASSERT(mRegistration->mUpdateJob);
-      mRegistration->mUpdateJob = nullptr;
-    }
   }
 };
 
 NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerRegisterJob, ServiceWorkerJob);
 
 void
 ServiceWorkerJobQueue::CancelJobs()
 {
-  if (mJobs.IsEmpty()) {
+  // The order doesn't matter. Cancel() just sets a flag on these jobs.
+  CancelJobs(mRegistrationJobQueue);
+  CancelJobs(mInstallationJobQueue);
+}
+
+void
+ServiceWorkerJobQueue::CancelJobs(QueueData& aQueue)
+{
+  if (aQueue.mJobs.IsEmpty()) {
     return;
   }
 
   // We have to treat the first job specially. It is the running job and needs
   // to be notified correctly.
-  RefPtr<ServiceWorkerJob> runningJob = mJobs[0];
+  RefPtr<ServiceWorkerJob> runningJob = aQueue.mJobs[0];
   // We can just let an Unregister job run to completion.
-  if (runningJob->IsRegisterJob()) {
-    ServiceWorkerRegisterJob* job = static_cast<ServiceWorkerRegisterJob*>(runningJob.get());
+  if (runningJob->IsRegisterOrInstallJob()) {
+    ServiceWorkerJobBase* job = static_cast<ServiceWorkerJobBase*>(runningJob.get());
     job->Cancel();
   }
 
   // Get rid of everything. Non-main thread objects may still be holding a ref
   // to the running register job. Since we called Cancel() on it, the job's
   // main thread functions will just exit.
-  mJobs.Clear();
+  aQueue.mJobs.Clear();
 }
 
 NS_IMETHODIMP
 ContinueUpdateRunnable::Run()
 {
   AssertIsOnMainThread();
   RefPtr<ServiceWorkerJob> job = static_cast<ServiceWorkerJob*>(mJob.get());
   RefPtr<ServiceWorkerRegisterJob> upjob = static_cast<ServiceWorkerRegisterJob*>(job.get());
-  upjob->ContinueInstall();
+  upjob->ContinueInstall(mScriptEvaluationResult);
   return NS_OK;
 }
 
 void
 ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess)
 {
   // This does not start the job immediately if there are other jobs in the
   // queue, which captures the "atomic" behaviour we want.
@@ -1800,17 +1854,17 @@ public:
 
       rv = principal->CheckMayLoad(scopeURI, true /* report */,
                                    false /* allowIfInheritsPrincipal */);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         continue;
       }
 
       RefPtr<ServiceWorkerRegistrationMainThread> swr =
-        new ServiceWorkerRegistrationMainThread(mWindow, scope);
+        mWindow->GetServiceWorkerRegistration(scope);
 
       array.AppendElement(swr);
     }
 
     mPromise->MaybeResolve(array);
     return NS_OK;
   }
 };
@@ -1907,17 +1961,17 @@ public:
 
     if (!registration) {
       mPromise->MaybeResolve(JS::UndefinedHandleValue);
       return NS_OK;
     }
 
     NS_ConvertUTF8toUTF16 scope(registration->mScope);
     RefPtr<ServiceWorkerRegistrationMainThread> swr =
-      new ServiceWorkerRegistrationMainThread(mWindow, scope);
+      mWindow->GetServiceWorkerRegistration(scope);
     mPromise->MaybeResolve(swr);
 
     return NS_OK;
   }
 };
 
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
@@ -2169,17 +2223,17 @@ ServiceWorkerManager::CheckReadyPromise(
   MOZ_ASSERT(principal);
 
   RefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(principal, aURI);
 
   if (registration && registration->mActiveWorker) {
     NS_ConvertUTF8toUTF16 scope(registration->mScope);
     RefPtr<ServiceWorkerRegistrationMainThread> swr =
-      new ServiceWorkerRegistrationMainThread(aWindow, scope);
+      aWindow->GetServiceWorkerRegistration(scope);
     aPromise->MaybeResolve(swr);
     return true;
   }
 
   return false;
 }
 
 ServiceWorkerInfo*
@@ -2227,17 +2281,17 @@ class ServiceWorkerUnregisterJob final :
   ~ServiceWorkerUnregisterJob()
   {}
 
 public:
   ServiceWorkerUnregisterJob(ServiceWorkerJobQueue* aQueue,
                              const nsACString& aScope,
                              nsIServiceWorkerUnregisterCallback* aCallback,
                              nsIPrincipal* aPrincipal)
-    : ServiceWorkerJob(aQueue)
+    : ServiceWorkerJob(aQueue, Type::UnregisterJob)
     , mScope(aScope)
     , mCallback(aCallback)
     , mPrincipal(aPrincipal)
   {
     AssertIsOnMainThread();
   }
 
   void
@@ -2582,38 +2636,16 @@ ServiceWorkerManager::HandleError(JSCont
     return;
   }
 
   ServiceWorkerManager::RegistrationDataPerPrincipal* data;
   if (NS_WARN_IF(!mRegistrationInfos.Get(scopeKey, &data))) {
     return;
   }
 
-  // If this is a failure, we may need to cancel an in-progress registration.
-  if (!JSREPORT_IS_WARNING(aFlags) &&
-      data->mSetOfScopesBeingUpdated.Contains(aScope)) {
-
-    data->mSetOfScopesBeingUpdated.Remove(aScope);
-
-    ServiceWorkerJobQueue* queue = data->mJobQueues.Get(aScope);
-    MOZ_ASSERT(queue);
-
-    ServiceWorkerJob* job = queue->Peek();
-    if (job) {
-      MOZ_ASSERT(job->IsRegisterJob());
-      RefPtr<ServiceWorkerRegisterJob> regJob =
-        static_cast<ServiceWorkerRegisterJob*>(job);
-
-      ErrorResult rv;
-      NS_ConvertUTF8toUTF16 scope(aScope);
-      rv.ThrowTypeError<MSG_SW_SCRIPT_THREW>(aWorkerURL, scope);
-      regJob->Fail(rv);
-    }
-  }
-
   // Always report any uncaught exceptions or errors to the console of
   // each client.
   ReportToAllClients(aScope, aMessage, aFilename, aLine, aLineNumber,
                      aColumnNumber, aFlags);
 }
 
 void
 ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
@@ -2659,33 +2691,16 @@ void
 ServiceWorkerRegistrationInfo::NotifyListenersOnChange()
 {
   nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners(mListeners);
   for (size_t index = 0; index < listeners.Length(); ++index) {
     listeners[index]->OnChange();
   }
 }
 
-bool
-ServiceWorkerRegistrationInfo::IsUpdating() const
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  return mUpdateJob != nullptr;
-}
-
-void
-ServiceWorkerRegistrationInfo::AppendUpdateCallback(ServiceWorkerUpdateFinishCallback* aCallback)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aCallback);
-  MOZ_ASSERT(mUpdateJob);
-
-  mUpdateJob->AppendCallback(aCallback);
-}
-
 void
 ServiceWorkerManager::LoadRegistration(
                              const ServiceWorkerRegistrationData& aRegistration)
 {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(aRegistration.principal());
@@ -3010,16 +3025,17 @@ ServiceWorkerManager::RemoveScopeAndRegi
   RefPtr<ServiceWorkerRegistrationInfo> info;
   data->mInfos.Get(aRegistration->mScope, getter_AddRefs(info));
 
   data->mInfos.Remove(aRegistration->mScope);
   data->mOrderedScopes.RemoveElement(aRegistration->mScope);
   swm->NotifyListenersOnUnregister(info);
 
   swm->MaybeRemoveRegistrationInfo(scopeKey);
+  swm->NotifyServiceWorkerRegistrationRemoved(aRegistration);
 }
 
 void
 ServiceWorkerManager::MaybeRemoveRegistrationInfo(const nsACString& aScopeKey)
 {
   RegistrationDataPerPrincipal* data;
   if (!mRegistrationInfos.Get(aScopeKey, &data)) {
     return;
@@ -3522,65 +3538,44 @@ ServiceWorkerManager::InvalidateServiceW
 
     if (utf8Scope.Equals(aRegistration->mScope)) {
       target->InvalidateWorkers(aWhichOnes);
     }
   }
 }
 
 void
-ServiceWorkerManager::SoftUpdate(nsIPrincipal* aPrincipal,
-                                 const nsACString& aScope,
-                                 ServiceWorkerUpdateFinishCallback* aCallback)
+ServiceWorkerManager::NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration)
 {
-  MOZ_ASSERT(aPrincipal);
-
-  nsAutoCString scopeKey;
-  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  SoftUpdate(scopeKey, aScope, aCallback);
+  AssertIsOnMainThread();
+  nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
+  while (it.HasMore()) {
+    RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
+    nsAutoString regScope;
+    target->GetScope(regScope);
+    MOZ_ASSERT(!regScope.IsEmpty());
+
+    NS_ConvertUTF16toUTF8 utf8Scope(regScope);
+
+    if (utf8Scope.Equals(aRegistration->mScope)) {
+      target->RegistrationRemoved();
+    }
+  }
 }
 
 void
-ServiceWorkerManager::SoftUpdate(const PrincipalOriginAttributes& aOriginAttributes,
-                                 const nsACString& aScope,
-                                 ServiceWorkerUpdateFinishCallback* aCallback)
+ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes,
+                                 const nsACString& aScope)
 {
+  AssertIsOnMainThread();
   nsAutoCString scopeKey;
   aOriginAttributes.CreateSuffix(scopeKey);
-  SoftUpdate(scopeKey, aScope, aCallback);
-}
-
-namespace {
-
-// Empty callback.  Only use when you really want to ignore errors.
-class EmptyUpdateFinishCallback final : public ServiceWorkerUpdateFinishCallback
-{
-public:
-  void
-  UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override
-  {}
-
-  void
-  UpdateFailed(ErrorResult& aStatus) override
-  {}
-};
-
-} // anonymous namespace
-
-void
-ServiceWorkerManager::SoftUpdate(const nsACString& aScopeKey,
-                                 const nsACString& aScope,
-                                 ServiceWorkerUpdateFinishCallback* aCallback)
-{
+
   RefPtr<ServiceWorkerRegistrationInfo> registration =
-    GetRegistration(aScopeKey, aScope);
+    GetRegistration(scopeKey, aScope);
   if (NS_WARN_IF(!registration)) {
     return;
   }
 
   // "If registration's uninstalling flag is set, abort these steps."
   if (registration->mPendingUninstall) {
     return;
   }
@@ -3596,38 +3591,81 @@ ServiceWorkerManager::SoftUpdate(const n
   RefPtr<ServiceWorkerInfo> newest = registration->Newest();
   if (!newest) {
     return;
   }
 
   // "Set registration's registering script url to newestWorker's script url."
   registration->mScriptSpec = newest->ScriptSpec();
 
+  // "If the registration queue for registration is empty, invoke Update algorithm,
+  // or its equivalent, with client, registration as its argument."
+  // TODO(catalinb): We don't implement the force bypass cache flag.
+  // See: https://github.com/slightlyoff/ServiceWorker/issues/759
+  if (!registration->mUpdating) {
+    ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(scopeKey, aScope);
+    MOZ_ASSERT(queue);
+
+    RefPtr<ServiceWorkerRegisterJob> job =
+      new ServiceWorkerRegisterJob(queue, registration, nullptr);
+    queue->Append(job);
+  }
+}
+
+void
+ServiceWorkerManager::Update(nsIPrincipal* aPrincipal,
+                             const nsACString& aScope,
+                             ServiceWorkerUpdateFinishCallback* aCallback)
+{
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aCallback);
+
+  nsAutoCString scopeKey;
+  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  RefPtr<ServiceWorkerRegistrationInfo> registration =
+    GetRegistration(scopeKey, aScope);
+  if (NS_WARN_IF(!registration)) {
+    return;
+  }
+
+  // "If registration's uninstalling flag is set, abort these steps."
+  if (registration->mPendingUninstall) {
+    return;
+  }
+
+  // "Let newestWorker be the result of running Get Newest Worker algorithm
+  // passing registration as its argument.
+  // If newestWorker is null, return a promise rejected with "InvalidStateError"
+  RefPtr<ServiceWorkerInfo> newest = registration->Newest();
+  if (!newest) {
+    ErrorResult error(NS_ERROR_DOM_INVALID_STATE_ERR);
+    aCallback->UpdateFailed(error);
+
+    // In case the callback does not consume the exception
+    error.SuppressException();
+
+    return;
+  }
+
+  // "Set registration's registering script url to newestWorker's script url."
+  registration->mScriptSpec = newest->ScriptSpec();
+
   ServiceWorkerJobQueue* queue =
-    GetOrCreateJobQueue(aScopeKey, aScope);
+    GetOrCreateJobQueue(scopeKey, aScope);
   MOZ_ASSERT(queue);
 
-  RefPtr<ServiceWorkerUpdateFinishCallback> cb(aCallback);
-  if (!cb) {
-    cb = new EmptyUpdateFinishCallback();
-  }
-
   // "Invoke Update algorithm, or its equivalent, with client, registration as
   // its argument."
-  if (registration->IsUpdating()) {
-    // This is used to reduce burst of update events. If there is an update
-    // job in queue when we try to create a new one, drop current one and
-    // merge the callback function to existing update job.
-    // See. https://github.com/slightlyoff/ServiceWorker/issues/759
-    registration->AppendUpdateCallback(cb);
-  } else {
-    RefPtr<ServiceWorkerRegisterJob> job =
-      new ServiceWorkerRegisterJob(queue, registration, cb);
-    queue->Append(job);
-  }
+  RefPtr<ServiceWorkerRegisterJob> job =
+    new ServiceWorkerRegisterJob(queue, registration, aCallback);
+  queue->Append(job);
 }
 
 namespace {
 
 static void
 FireControllerChangeOnDocument(nsIDocument* aDocument)
 {
   AssertIsOnMainThread();
@@ -3753,30 +3791,31 @@ ServiceWorkerManager::ClaimClients(nsIPr
 
 nsresult
 ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal,
                                          const nsCString& aScope,
                                          uint64_t aServiceWorkerID)
 {
   RefPtr<ServiceWorkerRegistrationInfo> registration =
     GetRegistration(aPrincipal, aScope);
-  if (!registration) {
+  if (NS_WARN_IF(!registration)) {
     return NS_ERROR_FAILURE;
   }
 
   if (registration->mInstallingWorker &&
       (registration->mInstallingWorker->ID() == aServiceWorkerID)) {
     registration->mInstallingWorker->SetSkipWaitingFlag();
   } else if (registration->mWaitingWorker &&
              (registration->mWaitingWorker->ID() == aServiceWorkerID)) {
     registration->mWaitingWorker->SetSkipWaitingFlag();
     if (registration->mWaitingWorker->State() == ServiceWorkerState::Installed) {
       registration->TryToActivate();
     }
   } else {
+    NS_WARNING("Failed to set skipWaiting flag, no matching worker.");
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 void
 ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
@@ -4528,16 +4567,38 @@ ServiceWorkerInfo::GetScriptSpec(nsAStri
 NS_IMETHODIMP
 ServiceWorkerInfo::GetCacheName(nsAString& aCacheName)
 {
   AssertIsOnMainThread();
   aCacheName = mCacheName;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+ServiceWorkerInfo::GetDebugger(nsIWorkerDebugger** aResult)
+{
+  if (NS_WARN_IF(!aResult)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return mServiceWorkerPrivate->GetDebugger(aResult);
+}
+
+NS_IMETHODIMP
+ServiceWorkerInfo::AttachDebugger()
+{
+  return mServiceWorkerPrivate->AttachDebugger();
+}
+
+NS_IMETHODIMP
+ServiceWorkerInfo::DetachDebugger()
+{
+  return mServiceWorkerPrivate->DetachDebugger();
+}
+
 void
 ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker)
 {
   MOZ_ASSERT(aWorker);
 #ifdef DEBUG
   nsAutoString workerURL;
   aWorker->GetScriptURL(workerURL);
   MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec)));
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -42,21 +42,19 @@ namespace dom {
 class ServiceWorkerRegistrationListener;
 
 namespace workers {
 
 class ServiceWorker;
 class ServiceWorkerClientInfo;
 class ServiceWorkerInfo;
 class ServiceWorkerJob;
-class ServiceWorkerRegisterJob;
 class ServiceWorkerJobQueue;
 class ServiceWorkerManagerChild;
 class ServiceWorkerPrivate;
-class ServiceWorkerUpdateFinishCallback;
 
 class ServiceWorkerRegistrationInfo final
   : public nsIServiceWorkerRegistrationInfo
 {
   uint32_t mControlledDocumentsCounter;
 
   virtual ~ServiceWorkerRegistrationInfo();
 
@@ -74,17 +72,21 @@ public:
   RefPtr<ServiceWorkerInfo> mActiveWorker;
   RefPtr<ServiceWorkerInfo> mWaitingWorker;
   RefPtr<ServiceWorkerInfo> mInstallingWorker;
 
   nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> mListeners;
 
   uint64_t mLastUpdateCheckTime;
 
-  RefPtr<ServiceWorkerRegisterJob> mUpdateJob;
+  // According to the spec, Soft Update shouldn't queue an update job
+  // if the registration queue is not empty. Because our job queue
+  // works slightly different, we use a flag to determine if the registration
+  // is already updating.
+  bool mUpdating;
 
   // When unregister() is called on a registration, it is not immediately
   // removed since documents may be controlled. It is marked as
   // pendingUninstall and when all controlling documents go away, removed.
   bool mPendingUninstall;
 
   ServiceWorkerRegistrationInfo(const nsACString& aScope,
                                 nsIPrincipal* aPrincipal);
@@ -144,22 +146,16 @@ public:
   void
   RefreshLastUpdateCheckTime();
 
   bool
   IsLastUpdateCheckTimeOverOneDay() const;
 
   void
   NotifyListenersOnChange();
-
-  bool
-  IsUpdating() const;
-
-  void
-  AppendUpdateCallback(ServiceWorkerUpdateFinishCallback* aCallback);
 };
 
 class ServiceWorkerUpdateFinishCallback
 {
 protected:
   virtual ~ServiceWorkerUpdateFinishCallback()
   {}
 
@@ -311,17 +307,19 @@ class ServiceWorkerManager final
   : public nsIServiceWorkerManager
   , public nsIIPCBackgroundChildCreateCallback
   , public nsIObserver
 {
   friend class GetReadyPromiseRunnable;
   friend class GetRegistrationsRunnable;
   friend class GetRegistrationRunnable;
   friend class ServiceWorkerJobQueue;
+  friend class ServiceWorkerInstallJob;
   friend class ServiceWorkerRegisterJob;
+  friend class ServiceWorkerJobBase;
   friend class ServiceWorkerRegistrationInfo;
   friend class ServiceWorkerUnregisterJob;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERMANAGER
   NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
   NS_DECL_NSIOBSERVER
@@ -368,24 +366,23 @@ public:
                     ErrorResult& aRv);
 
   void
   DispatchPreparedFetchEvent(nsIInterceptedChannel* aChannel,
                              nsIRunnable* aPreparedRunnable,
                              ErrorResult& aRv);
 
   void
-  SoftUpdate(nsIPrincipal* aPrincipal,
-             const nsACString& aScope,
-             ServiceWorkerUpdateFinishCallback* aCallback = nullptr);
+  Update(nsIPrincipal* aPrincipal,
+         const nsACString& aScope,
+         ServiceWorkerUpdateFinishCallback* aCallback);
 
   void
-  SoftUpdate(const PrincipalOriginAttributes& aOriginAttributes,
-             const nsACString& aScope,
-             ServiceWorkerUpdateFinishCallback* aCallback = nullptr);
+  SoftUpdate(const OriginAttributes& aOriginAttributes,
+             const nsACString& aScope);
 
   void
   PropagateSoftUpdate(const PrincipalOriginAttributes& aOriginAttributes,
                       const nsAString& aScope);
 
   void
   PropagateRemove(const nsACString& aHost);
 
@@ -484,21 +481,16 @@ private:
 
   ServiceWorkerJobQueue*
   GetOrCreateJobQueue(const nsACString& aOriginSuffix,
                       const nsACString& aScope);
 
   void
   MaybeRemoveRegistrationInfo(const nsACString& aScopeKey);
 
-  void
-  SoftUpdate(const nsACString& aScopeKey,
-             const nsACString& aScope,
-             ServiceWorkerUpdateFinishCallback* aCallback = nullptr);
-
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetRegistration(const nsACString& aScopeKey,
                   const nsACString& aScope) const;
 
   void
   AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
@@ -521,16 +513,19 @@ private:
   ServiceWorkerInfo*
   GetActiveWorkerInfoForDocument(nsIDocument* aDocument);
 
   void
   InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
                                             WhichServiceWorker aWhichOnes);
 
   void
+  NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
+
+  void
   StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                             nsIDocument* aDoc);
 
   void
   StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration);
 
   already_AddRefed<ServiceWorkerRegistrationInfo>
   GetServiceWorkerRegistrationInfo(nsPIDOMWindow* aWindow);
--- a/dom/workers/ServiceWorkerManagerChild.cpp
+++ b/dom/workers/ServiceWorkerManagerChild.cpp
@@ -37,17 +37,17 @@ ServiceWorkerManagerChild::RecvNotifySof
 {
   if (mShuttingDown) {
     return true;
   }
 
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
 
-  swm->SoftUpdate(aOriginAttributes, NS_ConvertUTF16toUTF8(aScope), nullptr);
+  swm->SoftUpdate(aOriginAttributes, NS_ConvertUTF16toUTF8(aScope));
   return true;
 }
 
 bool
 ServiceWorkerManagerChild::RecvNotifyUnregister(const PrincipalInfo& aPrincipalInfo,
                                                 const nsString& aScope)
 {
   if (mShuttingDown) {
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -56,16 +56,17 @@ private:
   RefPtr<ServiceWorkerPrivate> mPrivate;
 };
 
 NS_IMPL_ISUPPORTS0(KeepAliveToken)
 
 ServiceWorkerPrivate::ServiceWorkerPrivate(ServiceWorkerInfo* aInfo)
   : mInfo(aInfo)
   , mIsPushWorker(false)
+  , mDebuggerCount(0)
   , mTokenCount(0)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aInfo);
 
   mIdleWorkerTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
   MOZ_ASSERT(mIdleWorkerTimer);
 }
@@ -98,56 +99,74 @@ ServiceWorkerPrivate::SendMessageEvent(J
   return rv.StealNSResult();
 }
 
 namespace {
 
 class CheckScriptEvaluationWithCallback final : public WorkerRunnable
 {
   nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
-  RefPtr<nsRunnable> mCallback;
+  RefPtr<LifeCycleEventCallback> mCallback;
+  DebugOnly<bool> mDone;
 
 public:
   CheckScriptEvaluationWithCallback(WorkerPrivate* aWorkerPrivate,
                                     KeepAliveToken* aKeepAliveToken,
-                                    nsRunnable* aCallback)
+                                    LifeCycleEventCallback* aCallback)
     : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
     , mKeepAliveToken(new nsMainThreadPtrHolder<KeepAliveToken>(aKeepAliveToken))
     , mCallback(aCallback)
+    , mDone(false)
   {
     AssertIsOnMainThread();
   }
 
+  ~CheckScriptEvaluationWithCallback()
+  {
+    MOZ_ASSERT(mDone);
+  }
+
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
-    if (aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
-      nsresult rv = NS_DispatchToMainThread(mCallback);
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Failed to dispatch CheckScriptEvaluation callback.");
-      }
-    }
+    Done(aWorkerPrivate->WorkerScriptExecutedSuccessfully());
 
     return true;
   }
+
+  NS_IMETHOD
+  Cancel() override
+  {
+    Done(false);
+    return WorkerRunnable::Cancel();
+  }
+
+private:
+  void
+  Done(bool aResult)
+  {
+    mDone = true;
+    mCallback->SetResult(aResult);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(mCallback)));
+  }
 };
 
 } // anonymous namespace
 
 nsresult
-ServiceWorkerPrivate::ContinueOnSuccessfulScriptEvaluation(nsRunnable* aCallback)
+ServiceWorkerPrivate::CheckScriptEvaluation(LifeCycleEventCallback* aCallback)
 {
   nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   MOZ_ASSERT(mKeepAliveToken);
   RefPtr<WorkerRunnable> r = new CheckScriptEvaluationWithCallback(mWorkerPrivate,
-                                                                     mKeepAliveToken,
-                                                                     aCallback);
+                                                                   mKeepAliveToken,
+                                                                   aCallback);
   AutoJSAPI jsapi;
   jsapi.Init();
   if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
@@ -347,78 +366,151 @@ public:
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     MOZ_ASSERT(aWorkerPrivate);
     return DispatchLifecycleEvent(aCx, aWorkerPrivate);
   }
 
+  NS_IMETHOD
+  Cancel() override
+  {
+    mCallback->SetResult(false);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(mCallback)));
+
+    return WorkerRunnable::Cancel();
+  }
+
 private:
   bool
   DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 
 };
 
 /*
- * Used to handle ExtendableEvent::waitUntil() and proceed with
- * installation/activation.
+ * Used to handle ExtendableEvent::waitUntil() and catch abnormal worker
+ * termination during the execution of life cycle events. It is responsible
+ * with advancing the job queue for install/activate tasks.
  */
-class LifecycleEventPromiseHandler final : public PromiseNativeHandler
+class LifeCycleEventWatcher final : public PromiseNativeHandler,
+                                    public WorkerFeature
 {
+  WorkerPrivate* mWorkerPrivate;
   RefPtr<LifeCycleEventCallback> mCallback;
+  bool mDone;
 
-  virtual
-  ~LifecycleEventPromiseHandler()
-  { }
+  ~LifeCycleEventWatcher()
+  {
+    if (mDone) {
+      return;
+    }
+
+    MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate);
+    // XXXcatalinb: If all the promises passed to waitUntil go out of scope,
+    // the resulting Promise.all will be cycle collected and it will drop its
+    // native handlers (including this object). Instead of waiting for a timeout
+    // we report the failure now.
+    JSContext* cx = mWorkerPrivate->GetJSContext();
+    ReportResult(cx, false);
+  }
 
 public:
   NS_DECL_ISUPPORTS
 
-  explicit LifecycleEventPromiseHandler(LifeCycleEventCallback* aCallback)
-    : mCallback(aCallback)
+  LifeCycleEventWatcher(WorkerPrivate* aWorkerPrivate,
+                        LifeCycleEventCallback* aCallback)
+    : mWorkerPrivate(aWorkerPrivate)
+    , mCallback(aCallback)
+    , mDone(false)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+    aWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+  bool
+  Init()
   {
-    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(mWorkerPrivate);
+    mWorkerPrivate->AssertIsOnWorkerThread();
+    JSContext* cx = mWorkerPrivate->GetJSContext();
+
+    // We need to listen for worker termination in case the event handler
+    // never completes or never resolves the waitUntil promise. There are
+    // two possible scenarios:
+    // 1. The keepAlive token expires and the worker is terminated, in which
+    //    case the registration/update promise will be rejected
+    // 2. A new service worker is registered which will terminate the current
+    //    installing worker.
+    if (NS_WARN_IF(!mWorkerPrivate->AddFeature(cx, this))) {
+      NS_WARNING("LifeCycleEventWatcher failed to add feature.");
+      ReportResult(cx, false);
+      return false;
+    }
+
+    return true;
+  }
+
+  bool
+  Notify(JSContext* aCx, Status aStatus) override
+  {
+    if (aStatus < Terminating) {
+      return true;
+    }
+
+    MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate);
+    ReportResult(aCx, false);
+
+    return true;
+  }
+
+  void
+  ReportResult(JSContext* aCx, bool aResult)
+  {
+    mWorkerPrivate->AssertIsOnWorkerThread();
+
+    if (mDone) {
+      return;
+    }
+    mDone = true;
+
+    mCallback->SetResult(aResult);
+    nsresult rv = NS_DispatchToMainThread(mCallback);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      NS_RUNTIMEABORT("Failed to dispatch life cycle event handler.");
+    }
+
+    mWorkerPrivate->RemoveFeature(aCx, this);
   }
 
   void
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
-    workerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate);
+    mWorkerPrivate->AssertIsOnWorkerThread();
 
-    mCallback->SetResult(true);
-    nsresult rv = NS_DispatchToMainThread(mCallback);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      NS_RUNTIMEABORT("Failed to dispatch life cycle event handler.");
-    }
+    ReportResult(aCx, true);
   }
 
   void
   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
-    workerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(GetCurrentThreadWorkerPrivate() == mWorkerPrivate);
+    mWorkerPrivate->AssertIsOnWorkerThread();
 
-    mCallback->SetResult(false);
-    nsresult rv = NS_DispatchToMainThread(mCallback);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      NS_RUNTIMEABORT("Failed to dispatch life cycle event handler.");
-    }
+    ReportResult(aCx, false);
 
     // Note, all WaitUntil() rejections are reported to client consoles
     // by the WaitUntilHandler in ServiceWorkerEvents.  This ensures that
     // errors in non-lifecycle events like FetchEvent and PushEvent are
     // reported properly.
   }
 };
 
-NS_IMPL_ISUPPORTS0(LifecycleEventPromiseHandler)
+NS_IMPL_ISUPPORTS0(LifeCycleEventWatcher)
 
 bool
 LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx,
                                                      WorkerPrivate* aWorkerPrivate)
 {
   aWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
 
@@ -431,26 +523,33 @@ LifecycleEventWorkerRunnable::DispatchLi
     init.mCancelable = false;
     event = ExtendableEvent::Constructor(target, mEventName, init);
   } else {
     MOZ_CRASH("Unexpected lifecycle event");
   }
 
   event->SetTrusted(true);
 
+  // It is important to initialize the watcher before actually dispatching
+  // the event in order to catch worker termination while the event handler
+  // is still executing. This can happen with infinite loops, for example.
+  RefPtr<LifeCycleEventWatcher> watcher =
+    new LifeCycleEventWatcher(aWorkerPrivate, mCallback);
+
+  if (!watcher->Init()) {
+    return true;
+  }
+
   RefPtr<Promise> waitUntil;
   DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(),
                                        event, getter_AddRefs(waitUntil));
   if (waitUntil) {
-    RefPtr<LifecycleEventPromiseHandler> handler =
-      new LifecycleEventPromiseHandler(mCallback);
-    waitUntil->AppendNativeHandler(handler);
+    waitUntil->AppendNativeHandler(watcher);
   } else {
-    mCallback->SetResult(false);
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(mCallback)));
+    watcher->ReportResult(aCx, false);
   }
 
   return true;
 }
 
 } // anonymous namespace
 
 nsresult
@@ -1326,17 +1425,17 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede
   // XXXcatalinb: We need to have a separate load group that's linked to
   // an existing tab child to pass security checks on b2g.
   // This should be fixed in bug 1125961, but for now we enforce updating
   // the overriden load group when intercepting a fetch.
   MOZ_ASSERT_IF(aWhy == FetchEvent, aLoadGroup);
 
   if (mWorkerPrivate) {
     mWorkerPrivate->UpdateOverridenLoadGroup(aLoadGroup);
-    ResetIdleTimeout(aWhy);
+    RenewKeepAliveToken(aWhy);
 
     return NS_OK;
   }
 
   // Sanity check: mSupportsArray should be empty if we're about to
   // spin up a new worker.
   MOZ_ASSERT(mSupportsArray.IsEmpty());
 
@@ -1406,17 +1505,17 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede
                                               scriptSpec,
                                               false, WorkerTypeService,
                                               mInfo->Scope(), &info, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
   mIsPushWorker = false;
-  ResetIdleTimeout(aWhy);
+  RenewKeepAliveToken(aWhy);
 
   return NS_OK;
 }
 
 void
 ServiceWorkerPrivate::StoreISupports(nsISupports* aSupports)
 {
   AssertIsOnMainThread();
@@ -1472,17 +1571,17 @@ ServiceWorkerPrivate::NoteDeadServiceWor
   mInfo = nullptr;
   TerminateWorker();
 }
 
 void
 ServiceWorkerPrivate::NoteStoppedControllingDocuments()
 {
   AssertIsOnMainThread();
-  if (mIsPushWorker) {
+  if (mIsPushWorker || mDebuggerCount) {
     return;
   }
 
   TerminateWorker();
 }
 
 void
 ServiceWorkerPrivate::Activated()
@@ -1502,16 +1601,78 @@ ServiceWorkerPrivate::Activated()
     AutoJSAPI jsapi;
     jsapi.Init();
     if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
       NS_WARNING("Failed to dispatch pending functional event!");
     }
   }
 }
 
+nsresult
+ServiceWorkerPrivate::GetDebugger(nsIWorkerDebugger** aResult)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aResult);
+
+  if (!mDebuggerCount) {
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(mWorkerPrivate);
+
+  nsCOMPtr<nsIWorkerDebugger> debugger = do_QueryInterface(mWorkerPrivate->Debugger());
+  debugger.forget(aResult);
+
+  return NS_OK;
+}
+
+nsresult
+ServiceWorkerPrivate::AttachDebugger()
+{
+  AssertIsOnMainThread();
+
+  // When the first debugger attaches to a worker, we spawn a worker if needed,
+  // and cancel the idle timeout. The idle timeout should not be reset until
+  // the last debugger detached from the worker.
+  if (!mDebuggerCount) {
+    nsresult rv = SpawnWorkerIfNeeded(AttachEvent, nullptr);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mIdleWorkerTimer->Cancel();
+  }
+
+  ++mDebuggerCount;
+
+  return NS_OK;
+}
+
+nsresult
+ServiceWorkerPrivate::DetachDebugger()
+{
+  AssertIsOnMainThread();
+
+  if (!mDebuggerCount) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  --mDebuggerCount;
+
+  // When the last debugger detaches from a worker, we either reset the idle
+  // timeout, or terminate the worker if there are no more active tokens.
+  if (!mDebuggerCount) {
+    if (mTokenCount) {
+      ResetIdleTimeout();
+    } else {
+      TerminateWorker();
+    }
+  }
+
+  return NS_OK;
+}
+
 /* static */ void
 ServiceWorkerPrivate::NoteIdleWorkerCallback(nsITimer* aTimer, void* aPrivate)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrivate);
 
   RefPtr<ServiceWorkerPrivate> swp = static_cast<ServiceWorkerPrivate*>(aPrivate);
 
@@ -1545,34 +1706,46 @@ ServiceWorkerPrivate::TerminateWorkerCal
 
   MOZ_ASSERT(aTimer == serviceWorkerPrivate->mIdleWorkerTimer,
       "Invalid timer!");
 
   serviceWorkerPrivate->TerminateWorker();
 }
 
 void
-ServiceWorkerPrivate::ResetIdleTimeout(WakeUpReason aWhy)
+ServiceWorkerPrivate::RenewKeepAliveToken(WakeUpReason aWhy)
 {
-  // We should have an active worker if we're reseting the idle timeout
+  // We should have an active worker if we're renewing the keep alive token.
   MOZ_ASSERT(mWorkerPrivate);
 
   if (aWhy == PushEvent || aWhy == PushSubscriptionChangeEvent) {
     mIsPushWorker = true;
   }
 
+  // If there is at least one debugger attached to the worker, the idle worker
+  // timeout was canceled when the first debugger attached to the worker. It
+  // should not be reset until the last debugger detaches from the worker.
+  if (!mDebuggerCount) {
+    ResetIdleTimeout();
+  }
+
+  if (!mKeepAliveToken) {
+    mKeepAliveToken = new KeepAliveToken(this);
+  }
+}
+
+void
+ServiceWorkerPrivate::ResetIdleTimeout()
+{
   uint32_t timeout = Preferences::GetInt("dom.serviceWorkers.idle_timeout");
   DebugOnly<nsresult> rv =
     mIdleWorkerTimer->InitWithFuncCallback(ServiceWorkerPrivate::NoteIdleWorkerCallback,
                                            this, timeout,
                                            nsITimer::TYPE_ONE_SHOT);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
-  if (!mKeepAliveToken) {
-    mKeepAliveToken = new KeepAliveToken(this);
-  }
 }
 
 void
 ServiceWorkerPrivate::AddToken()
 {
   AssertIsOnMainThread();
   ++mTokenCount;
 }
--- a/dom/workers/ServiceWorkerPrivate.h
+++ b/dom/workers/ServiceWorkerPrivate.h
@@ -67,21 +67,19 @@ public:
   explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo);
 
   nsresult
   SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                    const Optional<Sequence<JS::Value>>& aTransferable,
                    UniquePtr<ServiceWorkerClientInfo>&& aClientInfo);
 
   // This is used to validate the worker script and continue the installation
-  // process. Note that the callback is dispatched to the main thread
-  // ONLY if the evaluation was successful. Failure is handled by the JS
-  // exception handler which will call ServiceWorkerManager::HandleError.
+  // process.
   nsresult
-  ContinueOnSuccessfulScriptEvaluation(nsRunnable* aCallback);
+  CheckScriptEvaluation(LifeCycleEventCallback* aCallback);
 
   nsresult
   SendLifeCycleEvent(const nsAString& aEventType,
                      LifeCycleEventCallback* aCallback,
                      nsIRunnable* aLoadFailure);
 
   nsresult
   SendPushEvent(const Maybe<nsTArray<uint8_t>>& aData,
@@ -132,35 +130,48 @@ public:
   GetWorkerPrivate() const
   {
     return mWorkerPrivate;
   }
 
   void
   Activated();
 
+  nsresult
+  GetDebugger(nsIWorkerDebugger** aResult);
+
+  nsresult
+  AttachDebugger();
+
+  nsresult
+  DetachDebugger();
+
 private:
   enum WakeUpReason {
     FetchEvent = 0,
     PushEvent,
     PushSubscriptionChangeEvent,
     MessageEvent,
     NotificationClickEvent,
-    LifeCycleEvent
+    LifeCycleEvent,
+    AttachEvent
   };
 
   // Timer callbacks
   static void
   NoteIdleWorkerCallback(nsITimer* aTimer, void* aPrivate);
 
   static void
   TerminateWorkerCallback(nsITimer* aTimer, void *aPrivate);
 
   void
-  ResetIdleTimeout(WakeUpReason aWhy);
+  RenewKeepAliveToken(WakeUpReason aWhy);
+
+  void
+  ResetIdleTimeout();
 
   void
   AddToken();
 
   void
   ReleaseToken();
 
   // |aLoadFailedRunnable| is a runnable dispatched to the main thread
@@ -188,16 +199,18 @@ private:
   // woken up. The flag is reset to false every time a new WorkerPrivate
   // is created.
   bool mIsPushWorker;
 
   // We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the
   // worker a grace period after each event.
   RefPtr<KeepAliveToken> mKeepAliveToken;
 
+  uint64_t mDebuggerCount;
+
   uint64_t mTokenCount;
 
   // Meant for keeping objects alive while handling requests from the worker
   // on the main thread. Access to this array is provided through
   // |StoreISupports| and |RemoveISupports|. Note that the array is also
   // cleared whenever the worker is terminated.
   nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
 
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -245,34 +245,46 @@ ServiceWorkerRegistrationMainThread::Inv
 
   if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) {
     mWaitingWorker = nullptr;
   }
 
   if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) {
     mActiveWorker = nullptr;
   }
+
+}
+
+void
+ServiceWorkerRegistrationMainThread::RegistrationRemoved()
+{
+  // If the registration is being removed completely, remove it from the
+  // window registration hash table so that a new registration would get a new
+  // wrapper JS object.
+  nsCOMPtr<nsPIDOMWindow> window = GetOwner();
+  if (window) {
+    window->InvalidateServiceWorkerRegistration(mScope);
+  }
 }
 
 namespace {
 
 void
 UpdateInternal(nsIPrincipal* aPrincipal,
                const nsAString& aScope,
                ServiceWorkerUpdateFinishCallback* aCallback)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aCallback);
 
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
 
-  // The spec defines ServiceWorkerRegistration.update() exactly as Soft Update.
-  swm->SoftUpdate(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback);
+  swm->Update(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback);
 }
 
 class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
 {
   RefPtr<Promise> mPromise;
 
   ~MainThreadUpdateCallback()
   { }
@@ -388,24 +400,32 @@ public:
   {}
 
   NS_IMETHOD
   Run() override
   {
     AssertIsOnMainThread();
     ErrorResult result;
 
-    MutexAutoLock lock(mPromiseProxy->Lock());
-    if (mPromiseProxy->CleanedUp()) {
-      return NS_OK;
+    nsCOMPtr<nsIPrincipal> principal;
+    // UpdateInternal may try to reject the promise synchronously leading
+    // to a deadlock.
+    {
+      MutexAutoLock lock(mPromiseProxy->Lock());
+      if (mPromiseProxy->CleanedUp()) {
+        return NS_OK;
+      }
+
+      principal = mPromiseProxy->GetWorkerPrivate()->GetPrincipal();
     }
+    MOZ_ASSERT(principal);
 
     RefPtr<WorkerThreadUpdateCallback> cb =
       new WorkerThreadUpdateCallback(mPromiseProxy);
-    UpdateInternal(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope, cb);
+    UpdateInternal(principal, mScope, cb);
     return NS_OK;
   }
 
 private:
   ~UpdateRunnable()
   {}
 
   RefPtr<PromiseWorkerProxy> mPromiseProxy;
@@ -853,16 +873,22 @@ public:
   void
   InvalidateWorkers(WhichServiceWorker aWhichOnes) override
   {
     AssertIsOnMainThread();
     // FIXME(nsm);
   }
 
   void
+  RegistrationRemoved() override
+  {
+    AssertIsOnMainThread();
+  }
+
+  void
   GetScope(nsAString& aScope) const override
   {
     aScope = mScope;
   }
 
   ServiceWorkerRegistrationWorkerThread*
   GetRegistration() const
   {
--- a/dom/workers/ServiceWorkerRegistration.h
+++ b/dom/workers/ServiceWorkerRegistration.h
@@ -61,16 +61,19 @@ public:
 
   virtual void
   UpdateFound() = 0;
 
   virtual void
   InvalidateWorkers(WhichServiceWorker aWhichOnes) = 0;
 
   virtual void
+  RegistrationRemoved() = 0;
+
+  virtual void
   GetScope(nsAString& aScope) const = 0;
 };
 
 class ServiceWorkerRegistrationBase : public DOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
@@ -101,19 +104,16 @@ protected:
 class ServiceWorkerRegistrationMainThread final : public ServiceWorkerRegistrationBase,
                                                   public ServiceWorkerRegistrationListener
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationMainThread,
                                            ServiceWorkerRegistrationBase)
 
-  ServiceWorkerRegistrationMainThread(nsPIDOMWindow* aWindow,
-                                      const nsAString& aScope);
-
   already_AddRefed<Promise>
   Update(ErrorResult& aRv);
 
   already_AddRefed<Promise>
   Unregister(ErrorResult& aRv);
 
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -150,22 +150,28 @@ public:
   // ServiceWorkerRegistrationListener
   void
   UpdateFound() override;
 
   void
   InvalidateWorkers(WhichServiceWorker aWhichOnes) override;
 
   void
+  RegistrationRemoved() override;
+
+  void
   GetScope(nsAString& aScope) const override
   {
     aScope = mScope;
   }
 
 private:
+  friend nsPIDOMWindow;
+  ServiceWorkerRegistrationMainThread(nsPIDOMWindow* aWindow,
+                                      const nsAString& aScope);
   ~ServiceWorkerRegistrationMainThread();
 
   already_AddRefed<workers::ServiceWorker>
   GetWorkerReference(WhichServiceWorker aWhichOne);
 
   void
   StartListeningForEvents();
 
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -588,17 +588,19 @@ public:
     swm->SetSkipWaitingFlag(workerPrivate->GetPrincipal(), mScope,
                             workerPrivate->ServiceWorkerID());
 
     RefPtr<SkipWaitingResultRunnable> runnable =
       new SkipWaitingResultRunnable(workerPrivate, mPromiseProxy);
 
     AutoJSAPI jsapi;
     jsapi.Init();
-    runnable->Dispatch(jsapi.cx());
+    if (!runnable->Dispatch(jsapi.cx())) {
+      NS_WARNING("Failed to dispatch SkipWaitingResultRunnable to the worker.");
+    }
     return NS_OK;
   }
 };
 
 } // namespace
 
 already_AddRefed<Promise>
 ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv)
--- a/dom/workers/test/serviceworkers/chrome.ini
+++ b/dom/workers/test/serviceworkers/chrome.ini
@@ -1,17 +1,19 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   app/*
   app2/*
   chrome_helpers.js
+  serviceworkerinfo_iframe.html
   serviceworkermanager_iframe.html
   serviceworkerregistrationinfo_iframe.html
   worker.js
   worker2.js
 
 [test_aboutserviceworkers.html]
 skip-if = true #bug 1193319
 [test_app_installation.html]
 [test_privateBrowsing.html]
+[test_serviceworkerinfo.xul]
 [test_serviceworkermanager.xul]
 [test_serviceworkerregistrationinfo.xul]
--- a/dom/workers/test/serviceworkers/chrome_helpers.js
+++ b/dom/workers/test/serviceworkers/chrome_helpers.js
@@ -52,8 +52,23 @@ function waitForServiceWorkerRegistratio
           callback();
         }
         resolve(callback ? callback() : undefined);
       }
     };
     registration.addListener(listener);
   });
 }
+
+function waitForServiceWorkerShutdown() {
+  return new Promise(function (resolve) {
+    let observer = {
+      observe: function (subject, topic, data) {
+        if (topic !== "service-worker-shutdown") {
+          return;
+        }
+        SpecialPowers.removeObserver(observer, "service-worker-shutdown");
+        resolve();
+      }
+    };
+    SpecialPowers.addObserver(observer, "service-worker-shutdown", false);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/serviceworkerinfo_iframe.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <script>
+      window.onmessage = function (event) {
+        if (event.data !== "register") {
+          return;
+        }
+        var promise = navigator.serviceWorker.register("worker.js");
+        window.onmessage = function (event) {
+          if (event.data !== "unregister") {
+            return;
+          }
+          promise.then(function (registration) {
+            registration.unregister();
+          });
+          window.onmessage = null;
+        };
+      };
+    </script>
+  </head>
+  <body>
+    This is a test page.
+  </body>
+<html>
--- a/dom/workers/test/serviceworkers/test_install_event.html
+++ b/dom/workers/test/serviceworkers/test_install_event.html
@@ -19,23 +19,36 @@
     var p = navigator.serviceWorker.register("worker.js", { scope: "./install_event" });
     return p;
   }
 
   function nextRegister(reg) {
     ok(reg instanceof ServiceWorkerRegistration, "reg should be a ServiceWorkerRegistration");
     var p = navigator.serviceWorker.register("install_event_worker.js", { scope: "./install_event" });
     return p.then(function(swr) {
-      ok(reg.scope === swr.scope, "Scope for registrations should match.");
-      return new Promise(function(resolve, reject) {
+      ok(reg === swr, "register should resolve to the same registration object");
+      var update_found_promise = new Promise(function(resolve, reject) {
         swr.addEventListener('updatefound', function(e) {
           ok(true, "Received onupdatefound");
           resolve();
         });
       });
+
+      var worker_activating = new Promise(function(res, reject) {
+        ok(swr.installing instanceof ServiceWorker, "There should be an installing worker if promise resolves.");
+        ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'");
+        swr.installing.onstatechange = function(e) {
+          if (e.target.state == "activating") {
+            e.target.onstatechange = null;
+            res();
+          }
+        }
+      });
+
+      return Promise.all([update_found_promise, worker_activating]);
     }, function(e) {
       ok(false, "Unexpected Error in nextRegister! " + e);
     });
   }
 
   function installError() {
     // Silence worker errors so they don't cause the test to fail.
     window.onerror = function(e) {}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_serviceworkerinfo.xul
@@ -0,0 +1,101 @@
+<?xml version="1.0"?>
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for ServiceWorkerInfo"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="test();">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript" src="chrome_helpers.js"/>
+  <script type="application/javascript">
+  <![CDATA[
+
+    let IFRAME_URL = EXAMPLE_URL + "serviceworkerinfo_iframe.html";
+
+    function wait_for_active_worker(registration) {
+      ok(registration, "Registration is valid.");
+      return new Promise(function(res, rej) {
+        if (registration.activeWorker) {
+          res(registration);
+          return;
+        }
+        let listener = {
+          onChange: function() {
+            if (registration.activeWorker) {
+              registration.removeListener(listener);
+              res(registration);
+            }
+          }
+        }
+        registration.addListener(listener);
+      });
+    }
+
+    function test() {
+      SimpleTest.waitForExplicitFinish();
+
+      SpecialPowers.pushPrefEnv({'set': [
+        ["dom.serviceWorkers.enabled", true],
+        ["dom.serviceWorkers.idle_extended_timeout", 1000000],
+        ["dom.serviceWorkers.idle_timeout", 0],
+        ["dom.serviceWorkers.testing.enabled", true],
+      ]}, function () {
+        Task.spawn(function *() {
+          let iframe = $("iframe");
+          let promise = new Promise(function (resolve) {
+            iframe.onload = function () {
+              resolve();
+            };
+          });
+          iframe.src = IFRAME_URL;
+          yield promise;
+
+          info("Check that a service worker eventually shuts down.");
+          promise = Promise.all([
+            waitForRegister(EXAMPLE_URL),
+            waitForServiceWorkerShutdown()
+          ]);
+          iframe.contentWindow.postMessage("register", "*");
+          let [registration] = yield promise;
+
+          // Make sure the worker is active.
+          registration = yield wait_for_active_worker(registration);
+
+          let activeWorker = registration.activeWorker;
+          ok(activeWorker !== null, "Worker is not active!");
+          ok(activeWorker.debugger === null);
+
+          info("Attach a debugger to the service worker, and check that the " +
+               "service worker is restarted.");
+          activeWorker.attachDebugger();
+          ok(activeWorker.debugger !== null);
+
+          info("Detach the debugger from the service worker, and check that " +
+               "the service worker eventually shuts down again.");
+          promise = waitForServiceWorkerShutdown();
+          activeWorker.detachDebugger();
+          yield promise;
+          ok(activeWorker.debugger === null);
+
+          promise = waitForUnregister(EXAMPLE_URL);
+          iframe.contentWindow.postMessage("unregister", "*");
+          registration = yield promise;
+
+          SimpleTest.finish();
+        });
+      });
+    }
+
+  ]]>
+  </script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <p id="display"></p>
+    <div id="content" style="display:none;"></div>
+    <pre id="test"></pre>
+    <iframe id="iframe"></iframe>
+  </body>
+  <label id="test-result"/>
+</window>
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -417,44 +417,60 @@ APZCCallbackHelper::GetRootContentDocume
 CSSPoint
 APZCCallbackHelper::ApplyCallbackTransform(const CSSPoint& aInput,
                                            const ScrollableLayerGuid& aGuid)
 {
     CSSPoint input = aInput;
     if (aGuid.mScrollId == FrameMetrics::NULL_SCROLL_ID) {
         return input;
     }
+
     nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(aGuid.mScrollId);
     if (!content) {
         return input;
     }
 
+#if !defined(MOZ_SINGLE_PROCESS_APZ)
     // First, scale inversely by the root content document's pres shell
     // resolution to cancel the scale-to-resolution transform that the
     // compositor adds to the layer with the pres shell resolution. The points
     // sent to Gecko by APZ don't have this transform unapplied (unlike other
     // compositor-side transforms) because APZ doesn't know about it.
     if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) {
         input = input / shell->GetResolution();
     }
+#endif
 
-    // Now apply the callback-transform.
+    // Apply the callback-transform.
     // XXX: technically we need to walk all the way up the layer tree from the layer
     // represented by |aGuid.mScrollId| up to the root of the layer tree and apply
     // the input transforms at each level in turn. However, it is quite difficult
     // to do this given that the structure of the layer tree may be different from
     // the structure of the content tree. Also it may be impossible to do correctly
     // at this point because there are other CSS transforms and such interleaved in
     // between so applying the inputTransforms all in a row at the end may leave
     // some things transformed improperly. In practice we should rarely hit scenarios
     // where any of this matters, so I'm skipping it for now and just doing the single
     // transform for the layer that the input hit.
+
     void* property = content->GetProperty(nsGkAtoms::apzCallbackTransform);
     if (property) {
         CSSPoint delta = (*static_cast<CSSPoint*>(property));
+
+#if defined(MOZ_SINGLE_PROCESS_APZ)
+        // The delta is in root content document coordinate space while the
+        // aInput point is in root document coordinate space so convert the
+        // delta to root document space before adding it to the aInput point.
+        float resolution = 1.0f;
+        if (nsIPresShell* shell = GetRootContentDocumentPresShellForContent(content)) {
+            resolution = shell->GetResolution();
+        }
+        delta.x = delta.x * resolution;
+        delta.y = delta.y * resolution;
+#endif // MOZ_SINGLE_PROCESS_APZ
         input += delta;
     }
     return input;
 }
 
 LayoutDeviceIntPoint
 APZCCallbackHelper::ApplyCallbackTransform(const LayoutDeviceIntPoint& aPoint,
                                            const ScrollableLayerGuid& aGuid,
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -142,16 +142,26 @@ ChromeProcessController::HandleDoubleTap
   }
 
   nsCOMPtr<nsIDocument> document = GetRootContentDocument(aGuid.mScrollId);
   if (!document.get()) {
     return;
   }
 
   CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid);
+#if defined(MOZ_SINGLE_PROCESS_APZ)
+  // CalculateRectToZoomTo performs a hit test on the frame associated with the
+  // Root Content Document. Unfortunately that frame does not know about the
+  // resolution of the document and so we must remove it before calculating
+  // the zoomToRect.
+  nsIPresShell* presShell = document->GetShell();
+  const float resolution = presShell->ScaleToResolution() ? presShell->GetResolution () : 1.0f;
+  point.x = point.x / resolution;
+  point.y = point.y / resolution;
+#endif // MOZ_SINGLE_PROCESS_APZ
   CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
 
   uint32_t presShellId;
   FrameMetrics::ViewID viewId;
   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
       document->GetDocumentElement(), &presShellId, &viewId)) {
     mAPZCTreeManager->ZoomToRect(
       ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId), zoomToRect);
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -123,16 +123,26 @@ BasicLayerManager::PushGroupForLayer(gfx
       group.mMaskSurface = GetMaskForLayer(aLayer, &group.mMaskTransform);
       return group;
     }
   }
 
   Matrix maskTransform;
   RefPtr<SourceSurface> maskSurf = GetMaskForLayer(aLayer, &maskTransform);
 
+  if (maskSurf) {
+    // The returned transform will transform the mask to device space on the
+    // destination. Since the User->Device space transform will be applied
+    // to the mask by PopGroupAndBlend we need to adjust the transform to
+    // transform the mask to user space.
+    Matrix currentTransform = ToMatrix(group.mFinalTarget->CurrentMatrix());
+    currentTransform.Invert();
+    maskTransform = maskTransform * currentTransform;
+  }
+
   if (aLayer->CanUseOpaqueSurface() &&
       ((didCompleteClip && aRegion.GetNumRects() == 1) ||
        !aContext->CurrentMatrix().HasNonIntegerTranslation())) {
     // If the layer is opaque in its visible region we can push a gfxContentType::COLOR
     // group. We need to make sure that only pixels inside the layer's visible
     // region are copied back to the destination. Remember if we've already
     // clipped precisely to the visible region.
     group.mNeedsClipToVisibleRegion = !didCompleteClip || aRegion.GetNumRects() > 1;
@@ -167,18 +177,22 @@ BasicLayerManager::PopGroupForLayer(Push
 
   DrawTarget* dt = group.mFinalTarget->GetDrawTarget();
   RefPtr<DrawTarget> sourceDT = group.mGroupTarget->GetDrawTarget();
   group.mGroupTarget = nullptr;
 
   RefPtr<SourceSurface> src = sourceDT->Snapshot();
 
   if (group.mMaskSurface) {
-    dt->SetTransform(group.mMaskTransform * Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
-    dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)),
+    Point finalOffset = group.mFinalTarget->GetDeviceOffset();
+    dt->SetTransform(group.mMaskTransform * Matrix::Translation(-finalOffset));
+    Matrix surfTransform = group.mMaskTransform;
+    surfTransform.Invert();
+    dt->MaskSurface(SurfacePattern(src, ExtendMode::CLAMP, surfTransform *
+                                                           Matrix::Translation(group.mGroupOffset.x, group.mGroupOffset.y)),
                     group.mMaskSurface, Point(0, 0), DrawOptions(group.mOpacity, group.mOperator));
   } else {
     // For now this is required since our group offset is in device space of the final target,
     // context but that may still have its own device offset. Once PushGroup/PopGroup logic is
     // migrated to DrawTargets this can go as gfxContext::GetDeviceOffset will essentially
     // always become null.
     dt->SetTransform(Matrix::Translation(-group.mFinalTarget->GetDeviceOffset()));
     dt->DrawSurface(src, Rect(group.mGroupOffset.x, group.mGroupOffset.y, src->GetSize().width, src->GetSize().height),
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -1659,21 +1659,23 @@ CompositorParent::SetControllerForLayerT
                                                  aLayersId,
                                                  aController));
 }
 
 /*static*/ APZCTreeManager*
 CompositorParent::GetAPZCTreeManager(uint64_t aLayersId)
 {
   EnsureLayerTreeMapReady();
-  const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
-  if (state && state->mParent) {
-    return state->mParent->mApzcTreeManager;
+  MonitorAutoLock lock(*sIndirectLayerTreesLock);
+  LayerTreeMap::iterator cit = sIndirectLayerTrees.find(aLayersId);
+  if (sIndirectLayerTrees.end() == cit) {
+    return nullptr;
   }
-  return nullptr;
+  LayerTreeState* lts = &cit->second;
+  return (lts->mParent ? lts->mParent->mApzcTreeManager.get() : nullptr);
 }
 
 float
 CompositorParent::ComputeRenderIntegrity()
 {
   if (mLayerManager) {
     return mLayerManager->ComputeRenderIntegrity();
   }
--- a/gfx/src/nsPoint.h
+++ b/gfx/src/nsPoint.h
@@ -32,16 +32,21 @@ struct nsPoint : public mozilla::gfx::Ba
 
   /**
    * Return this point scaled to a different appunits per pixel (APP) ratio.
    * @param aFromAPP the APP to scale from
    * @param aToAPP the APP to scale to
    */
   MOZ_WARN_UNUSED_RESULT inline nsPoint
     ScaleToOtherAppUnits(int32_t aFromAPP, int32_t aToAPP) const;
+
+  MOZ_WARN_UNUSED_RESULT inline nsPoint
+    RemoveResolution(const float resolution) const;
+  MOZ_WARN_UNUSED_RESULT inline nsPoint
+    ApplyResolution(const float resolution) const;
 };
 
 inline nsPoint ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel);
 
 inline nsIntPoint
 nsPoint::ScaleToNearestPixels(float aXScale, float aYScale,
                               nscoord aAppUnitsPerPixel) const
 {
@@ -63,16 +68,38 @@ nsPoint::ScaleToOtherAppUnits(int32_t aF
     nsPoint point;
     point.x = NSToCoordRound(NSCoordScale(x, aFromAPP, aToAPP));
     point.y = NSToCoordRound(NSCoordScale(y, aFromAPP, aToAPP));
     return point;
   }
   return *this;
 }
 
+inline nsPoint
+nsPoint::RemoveResolution(const float resolution) const {
+  if (resolution != 1.0f) {
+    nsPoint point;
+    point.x = NSToCoordRound(NSCoordToFloat(x) / resolution);
+    point.y = NSToCoordRound(NSCoordToFloat(y) / resolution);
+    return point;
+  }
+  return *this;
+}
+
+inline nsPoint
+nsPoint::ApplyResolution(const float resolution) const {
+  if (resolution != 1.0f) {
+    nsPoint point;
+    point.x = NSToCoordRound(NSCoordToFloat(x) * resolution);
+    point.y = NSToCoordRound(NSCoordToFloat(y) * resolution);
+    return point;
+  }
+  return *this;
+}
+
 // app units are integer multiples of pixels, so no rounding needed
 inline nsPoint
 ToAppUnits(const nsIntPoint& aPoint, nscoord aAppUnitsPerPixel)
 {
   return nsPoint(NSIntPixelsToAppUnits(aPoint.x, aAppUnitsPerPixel),
                  NSIntPixelsToAppUnits(aPoint.y, aAppUnitsPerPixel));
 }
 
--- a/gfx/src/nsRect.h
+++ b/gfx/src/nsRect.h
@@ -166,16 +166,18 @@ struct nsRect :
   MOZ_WARN_UNUSED_RESULT inline mozilla::gfx::IntRect
   ToInsidePixels(nscoord aAppUnitsPerPixel) const;
 
   // This is here only to keep IPDL-generated code happy. DO NOT USE.
   bool operator==(const nsRect& aRect) const
   {
     return IsEqualEdges(aRect);
   }
+
+  MOZ_WARN_UNUSED_RESULT inline nsRect RemoveResolution(const float aResolution) const;
 };
 
 /*
  * App Unit/Pixel conversions
  */
 
 inline nsRect
 nsRect::ScaleToOtherAppUnitsRoundOut(int32_t aFromAPP, int32_t aToAPP) const
@@ -274,16 +276,35 @@ nsRect::ToOutsidePixels(nscoord aAppUnit
 }
 
 inline mozilla::gfx::IntRect
 nsRect::ToInsidePixels(nscoord aAppUnitsPerPixel) const
 {
   return ScaleToInsidePixels(1.0f, 1.0f, aAppUnitsPerPixel);
 }
 
+inline nsRect
+nsRect::RemoveResolution(const float aResolution) const
+{
+  MOZ_ASSERT(aResolution > 0.0f);
+  nsRect rect;
+  rect.x = NSToCoordRound(NSCoordToFloat(x) / aResolution);
+  rect.y = NSToCoordRound(NSCoordToFloat(y) / aResolution);
+  // A 1x1 rect indicates we are just hit testing a point, so pass down a 1x1
+  // rect as well instead of possibly rounding the width or height to zero.
+  if (width == 1 && height == 1) {
+    rect.width = rect.height = 1;
+  } else {
+    rect.width = NSToCoordCeil(NSCoordToFloat(width) / aResolution);
+    rect.height = NSToCoordCeil(NSCoordToFloat(height) / aResolution);
+  }
+
+  return rect;
+}
+
 const mozilla::gfx::IntRect& GetMaxSizedIntRect();
 
 // app units are integer multiples of pixels, so no rounding needed
 nsRect
 ToAppUnits(const mozilla::gfx::IntRect& aRect, nscoord aAppUnitsPerPixel);
 
 #ifdef DEBUG
 // Diagnostics
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -11,16 +11,17 @@
 #include "nsNetUtil.h"
 #include "nsIJARChannel.h"
 #include "nsIProtocolHandler.h"
 #include "nsIPrincipal.h"
 #include "nsIZipReader.h"
 #include "gfxFontConstants.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
+#include "mozilla/Telemetry.h"
 #include "mozilla/gfx/2D.h"
 #include "gfxPlatformFontList.h"
 
 #include "opentype-sanitiser.h"
 #include "ots-memory-stream.h"
 
 using namespace mozilla;
 
@@ -422,16 +423,18 @@ gfxUserFontEntry::LoadNextSrc()
                 fe->mFamilyName = mFamilyName;
                 // For src:local(), we don't care whether the request is from
                 // a private window as there's no issue of caching resources;
                 // local fonts are just available all the time.
                 StoreUserFontData(fe, false, nsString(), nullptr, 0,
                                   gfxUserFontData::kUnknownCompression);
                 mPlatformFontEntry = fe;
                 SetLoadState(STATUS_LOADED);
+                Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
+                                      currSrc.mSourceType + 1);
                 return;
             } else {
                 LOG(("userfonts (%p) [src %d] failed local: (%s) for (%s)\n",
                      mFontSet, mSrcIndex,
                      NS_ConvertUTF16toUTF8(currSrc.mLocalName).get(),
                      NS_ConvertUTF16toUTF8(mFamilyName).get()));
             }
         }
@@ -452,16 +455,24 @@ gfxUserFontEntry::LoadNextSrc()
                         gfxFontEntry* fe = gfxUserFontSet::
                             UserFontCache::GetFont(currSrc.mURI,
                                                    principal,
                                                    this,
                                                    mFontSet->GetPrivateBrowsing());
                         if (fe) {
                             mPlatformFontEntry = fe;
                             SetLoadState(STATUS_LOADED);
+                            if (LOG_ENABLED()) {
+                                nsAutoCString fontURI;
+                                currSrc.mURI->GetSpec(fontURI);
+                                LOG(("userfonts (%p) [src %d] "
+                                     "loaded uri from cache: (%s) for (%s)\n",
+                                     mFontSet, mSrcIndex, fontURI.get(),
+                                     NS_ConvertUTF16toUTF8(mFamilyName).get()));
+                            }
                             return;
                         }
                     }
 
                     // record the principal returned by CheckFontLoad,
                     // for use when creating a channel
                     // and when caching the loaded entry
                     mPrincipal = principal;
@@ -477,16 +488,18 @@ gfxUserFontEntry::LoadNextSrc()
 
                         // sync load font immediately
                         rv = mFontSet->SyncLoadFontData(this, &currSrc, buffer,
                                                         bufferLength);
 
                         if (NS_SUCCEEDED(rv) &&
                             LoadPlatformFont(buffer, bufferLength)) {
                             SetLoadState(STATUS_LOADED);
+                            Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
+                                                  currSrc.mSourceType + 1);
                             return;
                         } else {
                             mFontSet->LogMessage(this,
                                                  "font load failed",
                                                  nsIScriptError::errorFlag,
                                                  rv);
                         }
 
@@ -531,16 +544,18 @@ gfxUserFontEntry::LoadNextSrc()
             uint32_t bufferLength = 0;
 
             // sync load font immediately
             currSrc.mBuffer->TakeBuffer(buffer, bufferLength);
             if (buffer && LoadPlatformFont(buffer, bufferLength)) {
                 // LoadPlatformFont takes ownership of the buffer, so no need
                 // to free it here.
                 SetLoadState(STATUS_LOADED);
+                Telemetry::Accumulate(Telemetry::WEBFONT_SRCTYPE,
+                                      currSrc.mSourceType + 1);
                 return;
             } else {
                 mFontSet->LogMessage(this,
                                      "font load failed",
                                      nsIScriptError::errorFlag);
             }
         }
 
@@ -572,34 +587,47 @@ gfxUserFontEntry::LoadPlatformFont(const
                   mUserFontLoadState == STATUS_LOADING) &&
                  mFontDataLoadingState < LOADING_FAILED,
                  "attempting to load a font that has either completed or failed");
 
     gfxFontEntry* fe = nullptr;
 
     gfxUserFontType fontType =
         gfxFontUtils::DetermineFontDataType(aFontData, aLength);
+    Telemetry::Accumulate(Telemetry::WEBFONT_FONTTYPE, uint32_t(fontType));
 
     // Unwrap/decompress/sanitize or otherwise munge the downloaded data
     // to make a usable sfnt structure.
 
     // Because platform font activation code may replace the name table
     // in the font with a synthetic one, we save the original name so that
     // it can be reported via the nsIDOMFontFace API.
     nsAutoString originalFullName;
 
     // Call the OTS sanitizer; this will also decode WOFF to sfnt
     // if necessary. The original data in aFontData is left unchanged.
     uint32_t saneLen;
+    uint32_t fontCompressionRatio = 0;
     const uint8_t* saneData =
         SanitizeOpenTypeData(aFontData, aLength, saneLen, fontType);
     if (!saneData) {
         mFontSet->LogMessage(this, "rejected by sanitizer");
     }
     if (saneData) {
+        if (saneLen) {
+            fontCompressionRatio = uint32_t(100.0 * aLength / saneLen + 0.5);
+            if (fontType == GFX_USERFONT_WOFF ||
+                fontType == GFX_USERFONT_WOFF2) {
+                Telemetry::Accumulate(fontType == GFX_USERFONT_WOFF ?
+                                      Telemetry::WEBFONT_COMPRESSION_WOFF :
+                                      Telemetry::WEBFONT_COMPRESSION_WOFF2,
+                                      fontCompressionRatio);
+                }
+        }
+
         // The sanitizer ensures that we have a valid sfnt and a usable
         // name table, so this should never fail unless we're out of
         // memory, and GetFullNameFromSFNT is not directly exposed to
         // arbitrary/malicious data from the web.
         gfxFontUtils::GetFullNameFromSFNT(saneData, saneLen,
                                           originalFullName);
         // Here ownership of saneData is passed to the platform,
         // which will delete it when no longer required
@@ -636,21 +664,21 @@ gfxUserFontEntry::LoadPlatformFont(const
         fe->mFeatureSettings.AppendElements(mFeatureSettings);
         fe->mLanguageOverride = mLanguageOverride;
         fe->mFamilyName = mFamilyName;
         StoreUserFontData(fe, mFontSet->GetPrivateBrowsing(), originalFullName,
                           &metadata, metaOrigLen, compression);
         if (LOG_ENABLED()) {
             nsAutoCString fontURI;
             mSrcList[mSrcIndex].mURI->GetSpec(fontURI);
-            LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) (%p) gen: %8.8x\n",
+            LOG(("userfonts (%p) [src %d] loaded uri: (%s) for (%s) "
+                 "(%p) gen: %8.8x compress: %d%%\n",
                  mFontSet, mSrcIndex, fontURI.get(),
                  NS_ConvertUTF16toUTF8(mFamilyName).get(),
-                 this,
-                 uint32_t(mFontSet->mGeneration)));
+                 this, uint32_t(mFontSet->mGeneration), fontCompressionRatio));
         }
         mPlatformFontEntry = fe;
         SetLoadState(STATUS_LOADED);
         gfxUserFontSet::UserFontCache::CacheFont(fe);
     } else {
         if (LOG_ENABLED()) {
             nsAutoCString fontURI;
             mSrcList[mSrcIndex].mURI->GetSpec(fontURI);
@@ -733,17 +761,20 @@ gfxUserFontEntry::FontDataDownloadComple
 void
 gfxUserFontEntry::GetUserFontSets(nsTArray<gfxUserFontSet*>& aResult)
 {
     aResult.Clear();
     aResult.AppendElement(mFontSet);
 }
 
 gfxUserFontSet::gfxUserFontSet()
-    : mFontFamilies(4), mLocalRulesUsed(false)
+    : mFontFamilies(4),
+      mLocalRulesUsed(false),
+      mDownloadCount(0),
+      mDownloadSize(0)
 {
     IncrementGeneration(true);
     gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList();
     if (fp) {
         fp->AddUserFontSet(this);
     }
 }
 
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -463,16 +463,25 @@ public:
     };
 
     void SetLocalRulesUsed() {
         mLocalRulesUsed = true;
     }
 
     static mozilla::LogModule* GetUserFontsLog();
 
+    // record statistics about font completion
+    virtual void RecordFontLoadDone(uint32_t aFontSize,
+                                    mozilla::TimeStamp aDoneTime) {}
+
+    void GetLoadStatistics(uint32_t& aLoadCount, uint64_t& aLoadSize) const {
+        aLoadCount = mDownloadCount;
+        aLoadSize = mDownloadSize;
+    }
+
 protected:
     // Protected destructor, to discourage deletion outside of Release():
     virtual ~gfxUserFontSet();
 
     // Return whether the font set is associated with a private-browsing tab.
     virtual bool GetPrivateBrowsing() = 0;
 
     // parse data for a data URL
@@ -508,16 +517,20 @@ protected:
     // font families defined by @font-face rules
     nsRefPtrHashtable<nsStringHashKey, gfxUserFontFamily> mFontFamilies;
 
     uint64_t        mGeneration;        // bumped on any font load change
     uint64_t        mRebuildGeneration; // only bumped on rebuilds
 
     // true when local names have been looked up, false otherwise
     bool mLocalRulesUsed;
+
+    // performance stats
+    uint32_t mDownloadCount;
+    uint64_t mDownloadSize;
 };
 
 // acts a placeholder until the real font is downloaded
 
 class gfxUserFontEntry : public gfxFontEntry {
     friend class gfxUserFontSet;
     friend class nsUserFontSet;
     friend class nsFontFaceLoader;
--- a/intl/unicharutil/tools/genUnicodePropertyData.pl
+++ b/intl/unicharutil/tools/genUnicodePropertyData.pl
@@ -14,16 +14,17 @@
 # (1) Download the current Unicode data files from
 #
 #         http://www.unicode.org/Public/UNIDATA/
 #
 #     NB: not all the files are actually needed; currently, we require
 #       - UnicodeData.txt
 #       - Scripts.txt
 #       - BidiMirroring.txt
+#       - BidiBrackets.txt
 #       - HangulSyllableType.txt
 #       - ReadMe.txt (to record version/date of the UCD)
 #       - Unihan_Variants.txt (from Unihan.zip)
 #     though this may change if we find a need for additional properties.
 #
 #     The Unicode data files listed above should be together in one directory.
 #
 #     We also require the file
@@ -329,28 +330,30 @@ my %verticalOrientationCode = (
   'Tr' => 3  #   Tr - Transformed typographically, with fallback to Rotated
 );
 
 # initialize default properties
 my @script;
 my @category;
 my @combining;
 my @mirror;
+my @pairedBracketType;
 my @hangul;
 my @casemap;
 my @xidmod;
 my @numericvalue;
 my @hanVariant;
 my @bidicategory;
 my @fullWidth;
 my @verticalOrientation;
 for (my $i = 0; $i < 0x110000; ++$i) {
     $script[$i] = $scriptCode{"UNKNOWN"};
     $category[$i] = $catCode{"UNASSIGNED"};
     $combining[$i] = 0;
+    $pairedBracketType[$i] = 0;
     $casemap[$i] = 0;
     $xidmod[$i] = $xidmodCode{"not-chars"};
     $numericvalue[$i] = -1;
     $hanVariant[$i] = 0;
     $bidicategory[$i] = $bidicategoryCode{"L"};
     $fullWidth[$i] = 0;
     $verticalOrientation[$i] = 1; # default for unlisted codepoints is 'R'
 }
@@ -518,23 +521,48 @@ while (<FH>) {
     chomp;
     push @versionInfo, $_;
     last if /Date:/;
 }
 while (<FH>) {
     s/#.*//;
     if (m/([0-9A-F]{4,6});\s*([0-9A-F]{4,6})/) {
         my $mirrorOffset = hex("0x$2") - hex("0x$1");
-	my $offsetIndex = first { $offsets[$_] eq $mirrorOffset } 0..$#offsets;
-	if ($offsetIndex == undef) {
+        my $offsetIndex = first { $offsets[$_] eq $mirrorOffset } 0..$#offsets;
+        if ($offsetIndex == undef) {
             die "too many offset codes\n" if scalar @offsets == 31;
             push @offsets, $mirrorOffset;
-	    $offsetIndex = $#offsets;
+            $offsetIndex = $#offsets;
         }
-	$mirror[hex "0x$1"] = $offsetIndex;
+        $mirror[hex "0x$1"] = $offsetIndex;
+    }
+}
+close FH;
+
+# read BidiBrackets.txt
+my %pairedBracketTypeCode = (
+  'N' => 0,
+  'O' => 1,
+  'C' => 2
+);
+open FH, "< $ARGV[1]/BidiBrackets.txt" or die "can't open UCD file BidiBrackets.txt\n";
+push @versionInfo, "";
+while (<FH>) {
+    chomp;
+    push @versionInfo, $_;
+    last if /Date:/;
+}
+while (<FH>) {
+    s/#.*//;
+    if (m/([0-9A-F]{4,6});\s*([0-9A-F]{4,6});\s*(.)/) {
+        my $mirroredChar = $offsets[$mirror[hex "0x$1"]] + hex "0x$1";
+        die "bidi bracket does not match mirrored char\n" unless $mirroredChar == hex "0x$2";
+        my $pbt = uc($3);
+        warn "unknown Bidi Bracket type" unless exists $pairedBracketTypeCode{$pbt};
+        $pairedBracketType[hex "0x$1"] = $pairedBracketTypeCode{$pbt};
     }
 }
 close FH;
 
 # read HangulSyllableType.txt
 my %hangulType = (
   'L'   => 0x01,
   'V'   => 0x02,
@@ -731,37 +759,37 @@ my $type = q/
 struct nsCharProps1 {
   unsigned char mMirrorOffsetIndex:5;
   unsigned char mHangulType:3;
   unsigned char mCombiningClass:8;
 };
 /;
 print DATA_TABLES "#ifndef ENABLE_INTL_API\n";
 &genTables("CharProp1", $type, "nsCharProps1", 11, 5, \&sprintCharProps1, 1, 2, 1);
-+print DATA_TABLES "#endif\n\n";
+print DATA_TABLES "#endif\n\n";
 
 sub sprintCharProps2
 {
   my $usv = shift;
   return sprintf("{%d,%d,%d,%d,%d,%d,%d},",
-                 $script[$usv], 0, $category[$usv],
+                 $script[$usv], $pairedBracketType[$usv], $category[$usv],
                  $bidicategory[$usv], $xidmod[$usv], $numericvalue[$usv],
                  $verticalOrientation[$usv]);
 }
-$type = q/
+$type = q|
 struct nsCharProps2 {
   unsigned char mScriptCode:8;
-  unsigned char mUnused:3;
+  unsigned char mPairedBracketType:3; // only 2 bits actually needed
   unsigned char mCategory:5;
   unsigned char mBidiCategory:5;
   unsigned char mXidmod:4;
   signed char   mNumericValue:5;
   unsigned char mVertOrient:2;
 };
-/;
+|;
 &genTables("CharProp2", $type, "nsCharProps2", 11, 5, \&sprintCharProps2, 16, 4, 1);
 
 print HEADER "#pragma pack()\n\n";
 
 sub sprintHanVariants
 {
   my $baseUsv = shift;
   my $varShift = 0;
--- a/intl/unicharutil/util/nsUnicodeProperties.cpp
+++ b/intl/unicharutil/util/nsUnicodeProperties.cpp
@@ -165,16 +165,36 @@ GetScriptTagForCode(int32_t aScriptCode)
 {
     // this will safely return 0 for negative script codes, too :)
     if (uint32_t(aScriptCode) > ArrayLength(sScriptCodeToTag)) {
         return 0;
     }
     return sScriptCodeToTag[aScriptCode];
 }
 
+PairedBracketType GetPairedBracketType(uint32_t aCh)
+{
+#if ENABLE_INTL_API
+  return PairedBracketType
+           (u_getIntPropertyValue(aCh, UCHAR_BIDI_PAIRED_BRACKET_TYPE));
+#else
+  return PairedBracketType(GetCharProps2(aCh).mPairedBracketType);
+#endif
+}
+
+uint32_t GetPairedBracket(uint32_t aCh)
+{
+#if ENABLE_INTL_API
+  return u_getBidiPairedBracket(aCh);
+#else
+  return GetPairedBracketType(aCh) != PAIRED_BRACKET_TYPE_NONE
+         ? GetMirroredChar(aCh) : aCh;
+#endif
+}
+
 static inline uint32_t
 GetCaseMapValue(uint32_t aCh)
 {
     if (aCh < UNICODE_BMP_LIMIT) {
         return sCaseMapValues[sCaseMapPages[0][aCh >> kCaseMapCharBits]]
                              [aCh & ((1 << kCaseMapCharBits) - 1)];
     }
     if (aCh < (kCaseMapMaxPlane + 1) * 0x10000) {
--- a/intl/unicharutil/util/nsUnicodeProperties.h
+++ b/intl/unicharutil/util/nsUnicodeProperties.h
@@ -52,16 +52,26 @@ enum VerticalOrientation {
   VERTICAL_ORIENTATION_Tu = 2,
   VERTICAL_ORIENTATION_Tr = 3
 };
 
 inline VerticalOrientation GetVerticalOrientation(uint32_t aCh) {
   return VerticalOrientation(GetCharProps2(aCh).mVertOrient);
 }
 
+/* This MUST match the values assigned by genUnicodePropertyData.pl! */
+enum PairedBracketType {
+  PAIRED_BRACKET_TYPE_NONE = 0,
+  PAIRED_BRACKET_TYPE_OPEN = 1,
+  PAIRED_BRACKET_TYPE_CLOSE = 2
+};
+
+PairedBracketType GetPairedBracketType(uint32_t aCh);
+uint32_t GetPairedBracket(uint32_t aCh);
+
 enum XidmodType {
   XIDMOD_RECOMMENDED,
   XIDMOD_INCLUSION,
   XIDMOD_UNCOMMON_USE,
   XIDMOD_TECHNICAL,
   XIDMOD_OBSOLETE,
   XIDMOD_ASPIRATIONAL,
   XIDMOD_LIMITED_USE,
--- a/intl/unicharutil/util/nsUnicodePropertyData.cpp
+++ b/intl/unicharutil/util/nsUnicodePropertyData.cpp
@@ -6,17 +6,17 @@
 
 /*
  * Derived from the Unicode Character Database by genUnicodePropertyData.pl
  *
  * For Unicode terms of use, see http://www.unicode.org/terms_of_use.html
  */
 
 /*
- * Created on Mon Sep  7 02:52:23 2015 from UCD data files with version info:
+ * Created on Tue Nov 17 07:34:16 2015 from UCD data files with version info:
  *
 
 # Date: 2015-06-16, 20:24:00 GMT [KW]
 #
 # Unicode Character Database
 # Copyright (c) 1991-2015 Unicode, Inc.
 # For terms of use, see http://www.unicode.org/terms_of_use.html
 #
@@ -33,16 +33,19 @@ Standard.
 
 
 # Scripts-8.0.0.txt
 # Date: 2015-03-11, 22:29:42 GMT [MD]
 
 # BidiMirroring-8.0.0.txt
 # Date: 2015-01-20, 18:30:00 GMT [KW, LI]
 
+# BidiBrackets-8.0.0.txt
+# Date: 2015-01-20, 19:00:00 GMT [AG, LI, KW]
+
 # HangulSyllableType-8.0.0.txt
 # Date: 2014-12-16, 23:07:45 GMT [MD]
 
 # File: xidmodifications.txt
 # Version: 8.0.0
 # Generated: 2015-05-17, 03:09:04 GMT
 
 #
@@ -406,19 +409,19 @@ static const uint16_t sCharProp2Pages[7]
   {320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,320,639},
   {110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110},
   {640,641,641,642,110,110,110,110,643,643,643,643,643,643,643,644,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110,110},
   {377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,377,645}
 };
 
 static const nsCharProps2 sCharProp2Values[646][32] = {
   {{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,8,8,-1,1},{0,0,0,7,8,-1,1},{0,0,0,8,8,-1,1},{0,0,0,9,8,-1,1},{0,0,0,7,8,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,7,12,-1,1},{0,0,0,7,12,-1,1},{0,0,0,7,12,-1,1},{0,0,0,8,12,-1,1}},
-  {{0,0,29,9,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,21,4,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,1,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,25,3,8,-1,1},{0,0,21,6,8,-1,1},{0,0,17,3,1,-1,1},{0,0,21,6,1,-1,1},{0,0,21,6,8,-1,1},{0,0,13,2,0,0,1},{0,0,13,2,0,1,1},{0,0,13,2,0,2,1},{0,0,13,2,0,3,1},{0,0,13,2,0,4,1},{0,0,13,2,0,5,1},{0,0,13,2,0,6,1},{0,0,13,2,0,7,1},{0,0,13,2,0,8,1},{0,0,13,2,0,9,1},{0,0,21,6,1,-1,1},{0,0,21,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,21,10,8,-1,1}},
-  {{0,0,21,10,8,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{0,0,22,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,24,10,8,-1,1},{0,0,16,10,0,-1,1}},
-  {{0,0,24,10,8,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{0,0,22,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,0,18,12,-1,1}},
+  {{0,0,29,9,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,21,4,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,1,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,25,3,8,-1,1},{0,0,21,6,8,-1,1},{0,0,17,3,1,-1,1},{0,0,21,6,1,-1,1},{0,0,21,6,8,-1,1},{0,0,13,2,0,0,1},{0,0,13,2,0,1,1},{0,0,13,2,0,2,1},{0,0,13,2,0,3,1},{0,0,13,2,0,4,1},{0,0,13,2,0,5,1},{0,0,13,2,0,6,1},{0,0,13,2,0,7,1},{0,0,13,2,0,8,1},{0,0,13,2,0,9,1},{0,0,21,6,1,-1,1},{0,0,21,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,21,10,8,-1,1}},
+  {{0,0,21,10,8,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{0,1,22,10,8,-1,1},{0,0,21,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,24,10,8,-1,1},{0,0,16,10,0,-1,1}},
+  {{0,0,24,10,8,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{0,1,22,10,8,-1,1},{0,0,25,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,0,18,12,-1,1}},
   {{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,7,8,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1},{0,0,0,18,12,-1,1}},
   {{0,0,29,6,9,-1,1},{0,0,21,10,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,26,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,24,10,9,-1,1},{0,0,26,10,8,-1,0},{25,0,7,0,9,-1,1},{0,0,20,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,1,18,10,-1,1},{0,0,26,10,8,-1,0},{0,0,24,10,9,-1,1},{0,0,26,4,8,-1,1},{0,0,25,4,8,-1,0},{0,0,15,2,9,2,1},{0,0,15,2,9,3,1},{0,0,24,10,9,-1,1},{0,0,5,0,9,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,1,-1,1},{0,0,24,10,9,-1,1},{0,0,15,2,9,1,1},{25,0,7,0,9,-1,1},{0,0,19,10,8,-1,1},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,21,10,8,-1,1}},
   {{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{0,0,25,10,8,-1,0},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1}},
   {{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{0,0,25,10,8,-1,0},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1}},
   {{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1}},
   {{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,9,-1,1},{25,0,5,0,9,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,9,-1,1}},
   {{25,0,5,0,9,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,11,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1}},
   {{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,9,0,0,-1,1},{25,0,5,0,0,-1,1},{25,0,5,0,9,-1,1}},
@@ -520,17 +523,17 @@ static const nsCharProps2 sCharProp2Valu
   {{61,0,2,0,12,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1}},
   {{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,12,17,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,9,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{0,0,23,4,8,-1,1}},
   {{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,7,0,0,-1,1},{38,0,6,0,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,12,17,0,-1,1},{38,0,21,0,8,-1,1},{38,0,13,0,0,0,1},{38,0,13,0,0,1,1},{38,0,13,0,0,2,1},{38,0,13,0,0,3,1},{38,0,13,0,0,4,1},{38,0,13,0,0,5,1},{38,0,13,0,0,6,1},{38,0,13,0,0,7,1},{38,0,13,0,0,8,1},{38,0,13,0,0,9,1},{38,0,21,0,8,-1,1},{38,0,21,0,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1}},
   {{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,12,17,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,9,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{61,0,2,0,12,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,6,0,0,-1,1},{61,0,2,0,12,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{24,0,12,17,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,13,0,0,0,1},{24,0,13,0,0,1,1},{24,0,13,0,0,2,1},{24,0,13,0,0,3,1},{24,0,13,0,0,4,1},{24,0,13,0,0,5,1},{24,0,13,0,0,6,1},{24,0,13,0,0,7,1},{24,0,13,0,0,8,1},{24,0,13,0,0,9,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{24,0,7,0,9,-1,1},{24,0,7,0,9,-1,1},{24,0,7,0,0,-1,1},{24,0,7,0,0,-1,1}},
   {{39,0,7,0,0,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,1,-1,1},{39,0,21,0,9,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,3,-1,1},{39,0,12,17,3,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1}},
-  {{39,0,13,0,0,0,1},{39,0,13,0,0,1,1},{39,0,13,0,0,2,1},{39,0,13,0,0,3,1},{39,0,13,0,0,4,1},{39,0,13,0,0,5,1},{39,0,13,0,0,6,1},{39,0,13,0,0,7,1},{39,0,13,0,0,8,1},{39,0,13,0,0,9,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,2,-1,1},{39,0,22,10,8,-1,1},{39,0,18,10,8,-1,1},{39,0,22,10,8,-1,1},{39,0,18,10,8,-1,1},{39,0,10,0,0,-1,1},{39,0,10,0,0,-1,1}},
+  {{39,0,13,0,0,0,1},{39,0,13,0,0,1,1},{39,0,13,0,0,2,1},{39,0,13,0,0,3,1},{39,0,13,0,0,4,1},{39,0,13,0,0,5,1},{39,0,13,0,0,6,1},{39,0,13,0,0,7,1},{39,0,13,0,0,8,1},{39,0,13,0,0,9,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,15,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,2,-1,1},{39,1,22,10,8,-1,1},{39,2,18,10,8,-1,1},{39,1,22,10,8,-1,1},{39,2,18,10,8,-1,1},{39,0,10,0,0,-1,1},{39,0,10,0,0,-1,1}},
   {{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1}},
   {{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,9,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,11,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,11,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,10,0,0,-1,1}},
   {{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,21,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,7,0,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{61,0,2,0,12,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1}},
   {{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,9,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{39,0,12,17,0,-1,1},{61,0,2,0,12,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1}},
   {{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,12,17,0,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{61,0,2,0,12,-1,1},{39,0,26,0,8,-1,1},{39,0,26,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{39,0,21,0,8,-1,1},{39,0,21,0,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1}},
   {{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,10,0,0,-1,1},{28,0,10,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,10,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,10,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,10,0,0,-1,1},{28,0,10,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,7,0,0,-1,1}},
   {{28,0,13,0,0,0,1},{28,0,13,0,0,1,1},{28,0,13,0,0,2,1},{28,0,13,0,0,3,1},{28,0,13,0,0,4,1},{28,0,13,0,0,5,1},{28,0,13,0,0,6,1},{28,0,13,0,0,7,1},{28,0,13,0,0,8,1},{28,0,13,0,0,9,1},{28,0,21,0,8,-1,1},{28,0,21,0,8,-1,1},{28,0,21,0,8,-1,1},{28,0,21,0,8,-1,1},{28,0,21,0,8,-1,1},{28,0,21,0,8,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,10,0,0,-1,1},{28,0,10,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,7,0,0,-1,1},{28,0,12,17,0,-1,1},{28,0,12,17,0,-1,1}},
@@ -551,17 +554,17 @@ static const nsCharProps2 sCharProp2Valu
   {{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{11,0,12,17,0,-1,1},{11,0,12,17,0,-1,1},{11,0,12,17,0,-1,1}},
   {{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,21,0,8,-1,1},{11,0,15,0,4,1,1},{11,0,15,0,4,2,1},{11,0,15,0,4,3,1},{11,0,15,0,4,4,1},{11,0,15,0,4,5,1},{11,0,15,0,4,6,1},{11,0,15,0,4,7,1},{11,0,15,0,4,8,1},{11,0,15,0,4,9,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{11,0,15,0,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{11,0,26,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1}},
   {{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{6,0,9,0,6,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{6,0,5,0,6,-1,1},{6,0,5,0,6,-1,1},{6,0,5,0,6,-1,1},{6,0,5,0,6,-1,1},{6,0,5,0,6,-1,1},{6,0,5,0,6,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{40,0,17,10,8,-1,1},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0}},
   {{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0}},
   {{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,21,0,8,-1,0},{40,0,21,0,8,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0},{40,0,7,0,5,-1,0}},
-  {{29,0,29,9,8,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,22,10,8,-1,1},{29,0,18,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
+  {{29,0,29,9,8,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,0,7,0,7,-1,1},{29,1,22,10,8,-1,1},{29,2,18,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1}},
   {{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{0,0,21,0,8,-1,1},{0,0,21,0,8,-1,1},{0,0,21,0,8,-1,1},{32,0,14,0,7,-1,1},{32,0,14,0,7,-1,1},{32,0,14,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{32,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,7,0,7,-1,1},{42,0,12,17,7,-1,1},{42,0,12,17,7,-1,1},{42,0,12,17,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,7,0,7,-1,1},{43,0,12,17,7,-1,1},{43,0,12,17,7,-1,1},{43,0,12,17,7,-1,1},{0,0,21,0,8,-1,1},{0,0,21,0,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,7,0,7,-1,1},{44,0,12,17,7,-1,1},{44,0,12,17,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{45,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{45,0,12,17,7,-1,1},{45,0,12,17,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1}},
   {{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,11,-1,1},{23,0,7,0,11,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,4,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,7,0,0,-1,1},{23,0,12,17,10,-1,1},{23,0,12,17,10,-1,1},{23,0,10,0,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,12,17,0,-1,1},{23,0,10,0,0,-1,1},{23,0,10,0,0,-1,1}},
@@ -616,35 +619,35 @@ static const nsCharProps2 sCharProp2Valu
   {{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,9,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,9,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,9,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,9,0,0,-1,1}},
   {{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1}},
   {{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,8,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,8,0,0,-1,1},{14,0,24,10,9,-1,1},{14,0,5,0,9,-1,1},{14,0,24,10,9,-1,1}},
   {{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,8,0,0,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{61,0,2,0,12,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1}},
   {{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,9,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,9,0,0,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{14,0,5,0,0,-1,1},{14,0,5,0,0,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,9,0,0,-1,1},{14,0,9,0,9,-1,1},{14,0,8,0,0,-1,1},{14,0,24,10,9,-1,1},{14,0,24,10,9,-1,1},{61,0,2,0,12,-1,1}},
   {{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,29,9,9,-1,1},{0,0,1,18,10,-1,1},{1,0,1,18,1,-1,1},{1,0,1,18,1,-1,1},{0,0,1,0,10,-1,1},{0,0,1,1,10,-1,1},{0,0,17,10,1,-1,1},{0,0,17,10,9,-1,1},{0,0,17,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,21,10,9,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,1,-1,1},{0,0,22,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,20,10,8,-1,1}},
   {{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,1,-1,1},{0,0,27,9,8,-1,1},{0,0,28,7,8,-1,1},{0,0,1,11,10,-1,1},{0,0,1,14,10,-1,1},{0,0,1,16,10,-1,1},{0,0,1,12,10,-1,1},{0,0,1,15,10,-1,1},{0,0,29,6,9,-1,1},{0,0,21,4,8,-1,0},{0,0,21,4,8,-1,0},{0,0,21,4,8,-1,1},{0,0,21,4,9,-1,1},{0,0,21,4,9,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,8,-1,1},{0,0,21,10,9,-1,1},{0,0,16,10,3,-1,1}},
-  {{0,0,16,10,3,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,1},{0,0,25,6,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,16,10,2,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,29,9,9,-1,1}},
-  {{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{61,0,2,0,12,-1,0},{0,0,1,19,10,-1,1},{0,0,1,20,10,-1,1},{0,0,1,21,10,-1,1},{0,0,1,22,10,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,15,2,9,0,1},{25,0,6,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{0,0,15,2,9,4,1},{0,0,15,2,9,5,1},{0,0,15,2,9,6,1},{0,0,15,2,9,7,1},{0,0,15,2,9,8,1},{0,0,15,2,9,9,1},{0,0,25,3,9,-1,1},{0,0,25,3,9,-1,1},{0,0,25,10,9,-1,1},{0,0,22,10,9,-1,1},{0,0,18,10,9,-1,1},{25,0,6,0,9,-1,1}},
-  {{0,0,15,2,9,0,1},{0,0,15,2,9,1,1},{0,0,15,2,9,2,1},{0,0,15,2,9,3,1},{0,0,15,2,9,4,1},{0,0,15,2,9,5,1},{0,0,15,2,9,6,1},{0,0,15,2,9,7,1},{0,0,15,2,9,8,1},{0,0,15,2,9,9,1},{0,0,25,3,9,-1,1},{0,0,25,3,9,-1,1},{0,0,25,10,9,-1,1},{0,0,22,10,9,-1,1},{0,0,18,10,9,-1,1},{61,0,2,0,12,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
+  {{0,0,16,10,3,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,1},{0,0,25,6,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,16,10,2,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,29,9,9,-1,1}},
+  {{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{0,0,1,18,10,-1,1},{61,0,2,0,12,-1,0},{0,0,1,19,10,-1,1},{0,0,1,20,10,-1,1},{0,0,1,21,10,-1,1},{0,0,1,22,10,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,1,18,11,-1,1},{0,0,15,2,9,0,1},{25,0,6,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{0,0,15,2,9,4,1},{0,0,15,2,9,5,1},{0,0,15,2,9,6,1},{0,0,15,2,9,7,1},{0,0,15,2,9,8,1},{0,0,15,2,9,9,1},{0,0,25,3,9,-1,1},{0,0,25,3,9,-1,1},{0,0,25,10,9,-1,1},{0,1,22,10,9,-1,1},{0,2,18,10,9,-1,1},{25,0,6,0,9,-1,1}},
+  {{0,0,15,2,9,0,1},{0,0,15,2,9,1,1},{0,0,15,2,9,2,1},{0,0,15,2,9,3,1},{0,0,15,2,9,4,1},{0,0,15,2,9,5,1},{0,0,15,2,9,6,1},{0,0,15,2,9,7,1},{0,0,15,2,9,8,1},{0,0,15,2,9,9,1},{0,0,25,3,9,-1,1},{0,0,25,3,9,-1,1},{0,0,25,10,9,-1,1},{0,1,22,10,9,-1,1},{0,2,18,10,9,-1,1},{61,0,2,0,12,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{25,0,6,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,9,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{0,0,23,4,8,-1,1},{61,0,2,4,12,-1,1}},
   {{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{61,0,2,4,12,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,11,17,8,-1,0},{1,0,11,17,8,-1,0},{1,0,11,17,8,-1,0}},
   {{1,0,11,17,8,-1,0},{1,0,12,17,7,-1,1},{1,0,11,17,8,-1,0},{1,0,11,17,8,-1,0},{1,0,11,17,8,-1,0},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{1,0,12,17,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{0,0,26,10,9,-1,0},{0,0,26,10,9,-1,0},{0,0,9,0,9,-1,1},{0,0,26,10,9,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,9,-1,0},{0,0,26,10,9,-1,0},{0,0,9,0,9,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,9,-1,0},{0,0,5,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,5,0,9,-1,1},{0,0,5,0,9,-1,0},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,5,0,9,-1,0},{0,0,26,10,8,-1,0},{0,0,9,0,9,-1,1},{0,0,26,10,9,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,3,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
   {{0,0,26,10,9,-1,0},{0,0,26,10,9,-1,0},{0,0,26,10,9,-1,0},{0,0,26,10,8,-1,0},{0,0,9,0,9,-1,1},{0,0,26,10,8,-1,0},{14,0,9,0,9,-1,1},{0,0,26,10,8,-1,0},{0,0,9,0,9,-1,1},{0,0,26,10,8,-1,0},{25,0,9,0,9,-1,1},{25,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,26,4,3,-1,0},{0,0,5,0,9,-1,1},{0,0,9,0,9,-1,1},{0,0,9,0,9,-1,1},{25,0,9,0,4,-1,1},{0,0,9,0,9,-1,1},{0,0,5,0,9,-1,1},{0,0,7,0,9,-1,0},{0,0,7,0,9,-1,0},{0,0,7,0,9,-1,0},{0,0,7,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,9,-1,0},{0,0,5,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,9,0,9,-1,0},{0,0,9,0,9,-1,0}},
   {{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,9,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,5,0,9,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{25,0,5,0,4,-1,1},{0,0,26,0,8,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0},{0,0,15,10,9,-1,0}},
   {{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0},{25,0,14,0,9,-1,0}},
   {{25,0,14,0,4,-1,0},{25,0,14,0,4,-1,0},{25,0,14,0,4,-1,0},{25,0,9,0,4,-1,0},{25,0,5,0,4,-1,0},{25,0,14,0,4,-1,0},{25,0,14,0,4,-1,0},{25,0,14,0,4,-1,0},{25,0,14,0,4,-1,0},{0,0,15,10,9,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}},
   {{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}},
   {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}},
   {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
   {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,3,8,-1,1},{0,0,25,4,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,1}},
   {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
   {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
-  {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
-  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,22,10,11,-1,3},{0,0,18,10,11,-1,3},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1}},
+  {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
+  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,1,22,10,11,-1,3},{0,2,18,10,11,-1,3},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1}},
   {{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1}},
   {{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,0,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
   {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,0,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
   {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
   {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
   {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}},
   {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
   {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}},
@@ -655,24 +658,24 @@ static const nsCharProps2 sCharProp2Valu
   {{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,26,0,9,-1,0},{0,0,15,10,9,0,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,1,0},{0,0,15,10,8,2,0},{0,0,15,10,8,3,0},{0,0,15,10,8,4,0},{0,0,15,10,8,5,0},{0,0,15,10,8,6,0},{0,0,15,10,8,7,0},{0,0,15,10,8,8,0},{0,0,15,10,8,9,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,0,0}},
   {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}},
   {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
   {{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
   {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,25,10,8,-1,0}},
   {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}},
   {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
   {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,0,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
-  {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,15,10,8,1,0},{0,0,15,10,8,2,0},{0,0,15,10,8,3,0},{0,0,15,10,8,4,0},{0,0,15,10,8,5,0},{0,0,15,10,8,6,0},{0,0,15,10,8,7,0},{0,0,15,10,8,8,0},{0,0,15,10,8,9,0},{0,0,15,10,8,-1,0}},
+  {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,15,10,8,1,0},{0,0,15,10,8,2,0},{0,0,15,10,8,3,0},{0,0,15,10,8,4,0},{0,0,15,10,8,5,0},{0,0,15,10,8,6,0},{0,0,15,10,8,7,0},{0,0,15,10,8,8,0},{0,0,15,10,8,9,0},{0,0,15,10,8,-1,0}},
   {{0,0,15,10,8,1,0},{0,0,15,10,8,2,0},{0,0,15,10,8,3,0},{0,0,15,10,8,4,0},{0,0,15,10,8,5,0},{0,0,15,10,8,6,0},{0,0,15,10,8,7,0},{0,0,15,10,8,8,0},{0,0,15,10,8,9,0},{0,0,15,10,8,-1,0},{0,0,15,10,8,1,0},{0,0,15,10,8,2,0},{0,0,15,10,8,3,0},{0,0,15,10,8,4,0},{0,0,15,10,8,5,0},{0,0,15,10,8,6,0},{0,0,15,10,8,7,0},{0,0,15,10,8,8,0},{0,0,15,10,8,9,0},{0,0,15,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}},
-  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
-  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
+  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
+  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
   {{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1},{46,0,26,0,8,-1,1}},
-  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
-  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
-  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
+  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
+  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
+  {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
   {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
   {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
   {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
   {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
   {{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1}},
   {{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,25,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}},
   {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}},
   {{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1},{0,0,26,10,8,-1,1}},
@@ -688,25 +691,25 @@ static const nsCharProps2 sCharProp2Valu
   {{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1}},
   {{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{12,0,5,0,4,-1,1},{61,0,2,0,12,-1,1},{12,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{12,0,5,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1}},
   {{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1}},
   {{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{57,0,7,0,5,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{57,0,6,0,9,-1,1},{57,0,21,0,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{57,0,12,17,5,-1,1}},
   {{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{11,0,7,0,0,-1,1},{61,0,2,0,12,-1,1}},
   {{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1},{8,0,12,17,4,-1,1}},
   {{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1}},
-  {{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1},{0,0,18,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,6,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1}},
+  {{0,0,20,10,8,-1,1},{0,0,19,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,1,22,10,8,-1,1},{0,2,18,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,6,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,21,10,8,-1,1}},
   {{0,0,17,10,8,-1,1},{0,0,21,10,8,-1,1},{0,0,22,10,8,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{61,0,2,0,12,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,9,-1,0}},
   {{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0}},
   {{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,8,-1,0},{17,0,26,10,9,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}},
   {{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0}},
   {{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{17,0,26,10,9,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}},
   {{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}},
-  {{0,0,29,9,9,-1,0},{0,0,21,10,8,-1,2},{0,0,21,10,8,-1,2},{0,0,21,10,8,-1,0},{0,0,26,10,8,-1,0},{17,0,6,0,0,-1,0},{0,0,7,0,0,-1,0},{17,0,14,0,0,-1,0},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,17,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,18,10,8,-1,3}},
+  {{0,0,29,9,9,-1,0},{0,0,21,10,8,-1,2},{0,0,21,10,8,-1,2},{0,0,21,10,8,-1,0},{0,0,26,10,8,-1,0},{17,0,6,0,0,-1,0},{0,0,7,0,0,-1,0},{17,0,14,0,0,-1,0},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,1,22,10,8,-1,3},{0,2,18,10,8,-1,3},{0,0,17,10,8,-1,3},{0,0,22,10,8,-1,3},{0,0,18,10,8,-1,3},{0,0,18,10,8,-1,3}},
   {{0,0,26,10,8,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{17,0,14,0,3,-1,0},{1,0,12,17,3,-1,0},{1,0,12,17,3,-1,0},{1,0,12,17,3,-1,0},{1,0,12,17,3,-1,0},{18,0,10,0,4,-1,0},{18,0,10,0,4,-1,0},{0,0,17,10,8,-1,3},{0,0,6,0,3,-1,0},{0,0,6,0,3,-1,0},{0,0,6,0,3,-1,0},{0,0,6,0,3,-1,0},{0,0,6,0,3,-1,0},{0,0,26,10,9,-1,0},{0,0,26,10,8,-1,0},{17,0,14,0,9,-1,0},{17,0,14,0,9,-1,0},{17,0,14,0,9,-1,0},{17,0,6,0,3,-1,0},{0,0,7,0,3,-1,0},{0,0,21,10,8,-1,0},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0}},
   {{61,0,2,0,12,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0}},
   {{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0}},
   {{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,0},{20,0,7,0,0,-1,2},{20,0,7,0,0,-1,2},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{1,0,12,17,0,-1,0},{1,0,12,17,0,-1,0},{0,0,24,10,9,-1,2},{0,0,24,10,9,-1,2},{20,0,6,0,0,-1,0},{20,0,6,0,0,-1,0},{20,0,7,0,9,-1,0}},
   {{0,0,17,10,1,-1,3},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0}},
   {{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0}},
   {{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,2},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{22,0,7,0,0,-1,0},{0,0,21,10,1,-1,0},{0,0,6,0,0,-1,3},{22,0,6,0,0,-1,0},{22,0,6,0,0,-1,0},{22,0,7,0,9,-1,0}},
   {{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0},{5,0,7,0,0,-1,0}},
@@ -801,23 +804,23 @@ static const nsCharProps2 sCharProp2Valu
   {{2,0,24,13,8,-1,1},{2,0,24,13,8,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1}},
   {{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{0,0,18,10,8,-1,1},{0,0,22,10,8,-1,1}},
   {{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1}},
   {{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1}},
   {{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,23,13,9,-1,1},{2,0,26,10,8,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1}},
   {{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{1,0,12,17,10,-1,1},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,21,10,9,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0}},
   {{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{1,0,12,17,3,-1,1},{8,0,12,17,3,-1,1},{8,0,12,17,3,-1,1},{0,0,21,10,9,-1,0},{0,0,17,10,9,-1,0},{0,0,17,10,9,-1,0},{0,0,16,10,9,-1,0},{0,0,16,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0}},
-  {{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,21,6,9,-1,2},{0,0,21,10,9,-1,2},{0,0,21,6,9,-1,2},{61,0,2,0,12,-1,0},{0,0,21,10,9,-1,0},{0,0,21,6,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,17,10,9,-1,1},{0,0,22,10,9,-1,3},{0,0,18,10,9,-1,3},{0,0,22,10,9,-1,3},{0,0,18,10,9,-1,3},{0,0,22,10,9,-1,3},{0,0,18,10,9,-1,3},{0,0,21,4,9,-1,0}},
+  {{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,21,10,8,-1,0},{0,0,21,10,8,-1,0},{0,0,22,10,9,-1,0},{0,0,18,10,9,-1,0},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,16,10,9,-1,1},{0,0,21,6,9,-1,2},{0,0,21,10,9,-1,2},{0,0,21,6,9,-1,2},{61,0,2,0,12,-1,0},{0,0,21,10,9,-1,0},{0,0,21,6,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,17,10,9,-1,1},{0,1,22,10,9,-1,3},{0,2,18,10,9,-1,3},{0,1,22,10,9,-1,3},{0,2,18,10,9,-1,3},{0,1,22,10,9,-1,3},{0,2,18,10,9,-1,3},{0,0,21,4,9,-1,0}},
   {{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,25,3,9,-1,0},{0,0,17,3,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{61,0,2,0,12,-1,0},{0,0,21,10,9,-1,0},{0,0,23,4,9,-1,0},{0,0,21,4,9,-1,0},{0,0,21,10,9,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,3,-1,1},{2,0,7,13,9,-1,1},{61,0,2,13,12,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1}},
   {{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{2,0,7,13,9,-1,1},{61,0,2,13,12,-1,1},{61,0,2,13,12,-1,1},{0,0,1,18,10,-1,1}},
-  {{61,0,2,0,12,-1,1},{0,0,21,10,9,-1,2},{0,0,21,10,9,-1,0},{0,0,21,4,9,-1,0},{0,0,23,4,9,-1,0},{0,0,21,4,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,0,22,10,9,-1,3},{0,0,18,10,9,-1,3},{0,0,21,10,9,-1,0},{0,0,25,3,9,-1,0},{0,0,21,6,9,-1,2},{0,0,17,3,9,-1,1},{0,0,21,6,9,-1,2},{0,0,21,6,9,-1,0},{0,0,13,2,9,0,0},{0,0,13,2,9,1,0},{0,0,13,2,9,2,0},{0,0,13,2,9,3,0},{0,0,13,2,9,4,0},{0,0,13,2,9,5,0},{0,0,13,2,9,6,0},{0,0,13,2,9,7,0},{0,0,13,2,9,8,0},{0,0,13,2,9,9,0},{0,0,21,6,9,-1,3},{0,0,21,10,9,-1,3},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,21,10,9,-1,2}},
-  {{0,0,21,10,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{0,0,22,10,9,-1,3},{0,0,21,10,9,-1,0},{0,0,18,10,9,-1,3},{0,0,24,10,9,-1,0},{0,0,16,10,9,-1,3}},
-  {{0,0,24,10,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{0,0,22,10,9,-1,3},{0,0,25,10,9,-1,3},{0,0,18,10,9,-1,3},{0,0,25,10,9,-1,3},{0,0,22,10,9,-1,3}},
-  {{0,0,18,10,9,-1,3},{0,0,21,10,9,-1,1},{0,0,22,10,9,-1,1},{0,0,18,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{0,0,6,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1}},
+  {{61,0,2,0,12,-1,1},{0,0,21,10,9,-1,2},{0,0,21,10,9,-1,0},{0,0,21,4,9,-1,0},{0,0,23,4,9,-1,0},{0,0,21,4,9,-1,0},{0,0,21,10,9,-1,0},{0,0,21,10,9,-1,0},{0,1,22,10,9,-1,3},{0,2,18,10,9,-1,3},{0,0,21,10,9,-1,0},{0,0,25,3,9,-1,0},{0,0,21,6,9,-1,2},{0,0,17,3,9,-1,1},{0,0,21,6,9,-1,2},{0,0,21,6,9,-1,0},{0,0,13,2,9,0,0},{0,0,13,2,9,1,0},{0,0,13,2,9,2,0},{0,0,13,2,9,3,0},{0,0,13,2,9,4,0},{0,0,13,2,9,5,0},{0,0,13,2,9,6,0},{0,0,13,2,9,7,0},{0,0,13,2,9,8,0},{0,0,13,2,9,9,0},{0,0,21,6,9,-1,3},{0,0,21,10,9,-1,3},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,21,10,9,-1,2}},
+  {{0,0,21,10,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{25,0,9,0,9,-1,0},{0,1,22,10,9,-1,3},{0,0,21,10,9,-1,0},{0,2,18,10,9,-1,3},{0,0,24,10,9,-1,0},{0,0,16,10,9,-1,3}},
+  {{0,0,24,10,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{25,0,5,0,9,-1,0},{0,1,22,10,9,-1,3},{0,0,25,10,9,-1,3},{0,2,18,10,9,-1,3},{0,0,25,10,9,-1,3},{0,1,22,10,9,-1,3}},
+  {{0,2,18,10,9,-1,3},{0,0,21,10,9,-1,1},{0,1,22,10,9,-1,1},{0,2,18,10,9,-1,1},{0,0,21,10,9,-1,1},{0,0,21,10,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{0,0,6,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1}},
   {{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{22,0,7,0,9,-1,1},{0,0,6,0,9,-1,1},{0,0,6,0,9,-1,1}},
   {{18,0,7,0,10,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{61,0,2,0,12,-1,1}},
   {{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{18,0,7,0,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{0,0,23,4,9,-1,0},{0,0,23,4,9,-1,0},{0,0,25,10,9,-1,0},{0,0,24,10,9,-1,3},{0,0,26,10,9,-1,0},{0,0,23,4,9,-1,0},{0,0,23,4,9,-1,0},{61,0,2,0,12,-1,0},{0,0,26,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,25,10,9,-1,1},{0,0,26,10,9,-1,1},{0,0,26,10,9,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{61,0,2,0,12,-1,0},{0,0,1,10,8,-1,1},{0,0,1,10,8,-1,1},{0,0,1,10,8,-1,1},{0,0,26,10,8,-1,0},{0,0,26,10,8,-1,0},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1}},
   {{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{51,0,7,0,7,-1,1}},
   {{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{61,0,2,0,12,-1,1},{61,0,2,0,12,-1,1}},
   {{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1},{51,0,7,0,7,-1,1}},
--- a/intl/unicharutil/util/nsUnicodeScriptCodes.h
+++ b/intl/unicharutil/util/nsUnicodeScriptCodes.h
@@ -6,17 +6,17 @@
 
 /*
  * Derived from the Unicode Character Database by genUnicodePropertyData.pl
  *
  * For Unicode terms of use, see http://www.unicode.org/terms_of_use.html
  */
 
 /*
- * Created on Thu Sep  3 14:42:28 2015 from UCD data files with version info:
+ * Created on Tue Nov 17 07:34:16 2015 from UCD data files with version info:
  *
 
 # Date: 2015-06-16, 20:24:00 GMT [KW]
 #
 # Unicode Character Database
 # Copyright (c) 1991-2015 Unicode, Inc.
 # For terms of use, see http://www.unicode.org/terms_of_use.html
 #
@@ -33,16 +33,19 @@ Standard.
 
 
 # Scripts-8.0.0.txt
 # Date: 2015-03-11, 22:29:42 GMT [MD]
 
 # BidiMirroring-8.0.0.txt
 # Date: 2015-01-20, 18:30:00 GMT [KW, LI]
 
+# BidiBrackets-8.0.0.txt
+# Date: 2015-01-20, 19:00:00 GMT [AG, LI, KW]
+
 # HangulSyllableType-8.0.0.txt
 # Date: 2014-12-16, 23:07:45 GMT [MD]
 
 # File: xidmodifications.txt
 # Version: 8.0.0
 # Generated: 2015-05-17, 03:09:04 GMT
 
 #
@@ -67,17 +70,17 @@ struct nsCharProps1 {
   unsigned char mHangulType:3;
   unsigned char mCombiningClass:8;
 };
 
 
 
 struct nsCharProps2 {
   unsigned char mScriptCode:8;
-  unsigned char mUnused:3;
+  unsigned char mPairedBracketType:3; // only 2 bits actually needed
   unsigned char mCategory:5;
   unsigned char mBidiCategory:5;
   unsigned char mXidmod:4;
   signed char   mNumericValue:5;
   unsigned char mVertOrient:2;
 };
 
 
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -172,16 +172,17 @@ class ModuleNamespaceObject : public Pro
         bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                  HandleId id, MutableHandleValue vp) const override;
         bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
                  HandleValue receiver, ObjectOpResult& result) const override;
 
         static const char family;
     };
 
+  public:
     static const ProxyHandler proxyHandler;
 };
 
 typedef Rooted<ModuleNamespaceObject*> RootedModuleNamespaceObject;
 typedef Handle<ModuleNamespaceObject*> HandleModuleNamespaceObject;
 
 struct FunctionDeclaration
 {
@@ -311,9 +312,16 @@ class MOZ_STACK_CLASS ModuleBuilder
 };
 
 JSObject* InitModuleClass(JSContext* cx, HandleObject obj);
 JSObject* InitImportEntryClass(JSContext* cx, HandleObject obj);
 JSObject* InitExportEntryClass(JSContext* cx, HandleObject obj);
 
 } // namespace js
 
+template<>
+inline bool
+JSObject::is<js::ModuleNamespaceObject>() const
+{
+    return js::IsDerivedProxyObject(this, &js::ModuleNamespaceObject::proxyHandler);
+}
+
 #endif /* builtin_ModuleObject_h */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/lib/dummyModuleResolveHook.js
@@ -0,0 +1,14 @@
+/* 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/. */
+
+// A dummy implementation of the module resolve hook used by module tests. This
+// implements the bare minimum necessary to allow modules to refer to each
+// other.
+
+let moduleRepo = {};
+setModuleResolveHook(function(module, specifier) {
+    if (specifier in moduleRepo)
+        return moduleRepo[specifier];
+    throw "Module '" + specifier + "' not found";
+});
--- a/js/src/jit-test/tests/modules/ambiguous-star-export.js
+++ b/js/src/jit-test/tests/modules/ambiguous-star-export.js
@@ -1,38 +1,31 @@
 // Test ambigious export * statements.
 
 "use strict";
 
 load(libdir + "asserts.js");
+load(libdir + "dummyModuleResolveHook.js");
 
 function checkModuleEval(source, result) {
     let m = parseModule(source);
     m.declarationInstantiation();
     assertEq(m.evaluation(), result);
 }
 
 function checkModuleSyntaxError(source) {
     let m = parseModule(source);
     assertThrowsInstanceOf(() => m.declarationInstantiation(), SyntaxError);
 }
 
-let moduleRepo = new Map();
-setModuleResolveHook(function(module, specifier) {
-    if (specifier in moduleRepo)
-        return moduleRepo[specifier];
-    throw "Module " + specifier + " not found";
-});
-
 let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;");
 let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
 let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
-let ms = [a, b, c];
-ms.map((m) => m.declarationInstantiation());
-ms.map((m) => m.evaluation(), moduleRepo.values());
+c.declarationInstantiation();
+c.evaluation();
 
 // Check importing/exporting non-ambiguous name works.
 checkModuleEval("import { a } from 'c'; a;", 1);
 checkModuleEval("export { a } from 'c';", undefined);
 
 // Check importing/exporting ambiguous name is a syntax error.
 checkModuleSyntaxError("import { b } from 'c';");
 checkModuleSyntaxError("export { b } from 'c';");
--- a/js/src/jit-test/tests/modules/bug1210391.js
+++ b/js/src/jit-test/tests/modules/bug1210391.js
@@ -1,11 +1,8 @@
-let moduleRepo = new Map();
-setModuleResolveHook(function(module, specifier) {
-        return moduleRepo[specifier];
-});
+load(libdir + "dummyModuleResolveHook.js");
 let a = moduleRepo['a'] = parseModule("export var a = 1; export var b = 2;");
 let b = moduleRepo['b'] = parseModule("export var b = 3; export var c = 4;");
 let c = moduleRepo['c'] = parseModule("export * from 'a'; export * from 'b';");
 let d = moduleRepo['d'] = parseModule("import { a } from 'c'; a;");
 d.declarationInstantiation();
 d.evaluation();
 
--- a/js/src/jit-test/tests/modules/debugger-frames.js
+++ b/js/src/jit-test/tests/modules/debugger-frames.js
@@ -52,21 +52,21 @@ dbg.onDebuggerStatement = function (fram
     // followed by the global.
     assertEq(env.parent.type, 'declarative');
     assertEq(env.parent.parent.type, 'object');
     assertEq(env.parent.parent.parent, null);
 };
 
 f = g2.eval(
 `
-    let moduleRepo = new Map();
+    let moduleRepo = {};
     setModuleResolveHook(function(module, specifier) {
         if (specifier in moduleRepo)
             return moduleRepo[specifier];
-        throw "Module " + specifier + " not found";
+        throw "Module '" + specifier + "' not found";
     });
 
     // Set up a module to import from.
     a = moduleRepo['a'] = parseModule(
     \`
         export var a = 1;
         export let b = 2;
         export const c = 3;
--- a/js/src/jit-test/tests/modules/import-namespace.js
+++ b/js/src/jit-test/tests/modules/import-namespace.js
@@ -1,21 +1,15 @@
 // Test importing module namespaces
 
 "use strict";
 
 load(libdir + "asserts.js");
 load(libdir + "iteration.js");
-
-let moduleRepo = new Map();
-setModuleResolveHook(function(module, specifier) {
-    if (specifier in moduleRepo)
-        return moduleRepo[specifier];
-    throw "Module " + specifier + " not found";
-});
+load(libdir + "dummyModuleResolveHook.js");
 
 function parseAndEvaluate(source) {
     let m = parseModule(source);
     m.declarationInstantiation();
     return m.evaluation();
 }
 
 function testHasNames(names, expected) {
--- a/js/src/jit-test/tests/modules/many-exports.js
+++ b/js/src/jit-test/tests/modules/many-exports.js
@@ -1,19 +1,14 @@
 // Test many exports.
 
+load(libdir + "dummyModuleResolveHook.js");
+
 const count = 1024;
 
-let moduleRepo = {};
-setModuleResolveHook(function(module, specifier) {
-    if (specifier in moduleRepo)
-        return moduleRepo[specifier];
-    throw "Module " + specifier + " not found";
-});
-
 let s = "";
 for (let i = 0; i < count; i++)
     s += "export let e" + i + " = " + (i * i) + ";\n";
 let a = moduleRepo['a'] = parseModule(s);
 
 let b = moduleRepo['b'] = parseModule("import * as ns from 'a'");
 
 b.declarationInstantiation();
--- a/js/src/jit-test/tests/modules/many-imports.js
+++ b/js/src/jit-test/tests/modules/many-imports.js
@@ -1,19 +1,14 @@
 // Test importing an import many times.
 
+load(libdir + "dummyModuleResolveHook.js");
+
 const count = 1024;
 
-let moduleRepo = {};
-setModuleResolveHook(function(module, specifier) {
-    if (specifier in moduleRepo)
-        return moduleRepo[specifier];
-    throw "Module " + specifier + " not found";
-});
-
 let a = moduleRepo['a'] = parseModule("export let a = 1;");
 
 let s = "";
 for (let i = 0; i < count; i++) {
     s += "import { a as i" + i + " } from 'a';\n";
     s += "assertEq(i" + i + ", 1);\n";
 }
 let b = moduleRepo['b'] = parseModule(s);
--- a/js/src/jit-test/tests/modules/many-namespace-imports.js
+++ b/js/src/jit-test/tests/modules/many-namespace-imports.js
@@ -1,19 +1,14 @@
 // Test importing a namespace many times.
 
+load(libdir + "dummyModuleResolveHook.js");
+
 const count = 1024;
 
-let moduleRepo = {};
-setModuleResolveHook(function(module, specifier) {
-    if (specifier in moduleRepo)
-        return moduleRepo[specifier];
-    throw "Module " + specifier + " not found";
-});
-
 let a = moduleRepo['a'] = parseModule("export let a = 1;");
 
 let s = "";
 for (let i = 0; i < count; i++) {
     s += "import * as ns" + i + " from 'a';\n";
     s += "assertEq(ns" + i + ".a, 1);\n";
 }
 let b = moduleRepo['b'] = parseModule(s);
--- a/js/src/jit-test/tests/modules/module-declaration-instantiation.js
+++ b/js/src/jit-test/tests/modules/module-declaration-instantiation.js
@@ -1,30 +1,25 @@
 // Exercise ModuleDeclarationInstantiation() operation.
 
+load(libdir + "dummyModuleResolveHook.js");
+
 function testModuleEnvironment(module, expected) {
     var actual = getModuleEnvironmentNames(module).sort();
     assertEq(actual.length, expected.length);
     for (var i = 0; i < actual.length; i++) {
         assertEq(actual[i], expected[i]);
     }
 }
 
 // Check the environment of an empty module.
 let m = parseModule("");
 m.declarationInstantiation();
 testModuleEnvironment(m, []);
 
-let moduleRepo = new Map();
-setModuleResolveHook(function(module, specifier) {
-    if (specifier in moduleRepo)
-        return moduleRepo[specifier];
-    throw "Module " + specifier + " not found";
-});
-
 let a = moduleRepo['a'] = parseModule("var x = 1; export { x };");
 let b = moduleRepo['b'] = parseModule("import { x as y } from 'a';");
 
 a.declarationInstantiation();
 b.declarationInstantiation();
 
 testModuleEnvironment(a, ['x']);
 testModuleEnvironment(b, ['y']);
--- a/js/src/jit-test/tests/modules/module-evaluation.js
+++ b/js/src/jit-test/tests/modules/module-evaluation.js
@@ -1,18 +1,12 @@
 // Exercise ModuleEvaluation() concrete method.
 
 load(libdir + "asserts.js");
-
-let moduleRepo = new Map();
-setModuleResolveHook(function(module, specifier) {
-    if (specifier in moduleRepo)
-        return moduleRepo[specifier];
-    throw "Module " + specifier + " not found";
-});
+load(libdir + "dummyModuleResolveHook.js");
 
 function parseAndEvaluate(source) {
     let m = parseModule(source);
     m.declarationInstantiation();
     return m.evaluation();
 }
 
 // Check the evaluation of an empty module succeeds.
--- a/js/src/jsprototypes.h
+++ b/js/src/jsprototypes.h
@@ -83,17 +83,17 @@
     real(Uint8Array,            23,     InitViaClassSpec,       TYPED_ARRAY_CLASP(Uint8)) \
     real(Int16Array,            24,     InitViaClassSpec,       TYPED_ARRAY_CLASP(Int16)) \
     real(Uint16Array,           25,     InitViaClassSpec,       TYPED_ARRAY_CLASP(Uint16)) \
     real(Int32Array,            26,     InitViaClassSpec,       TYPED_ARRAY_CLASP(Int32)) \
     real(Uint32Array,           27,     InitViaClassSpec,       TYPED_ARRAY_CLASP(Uint32)) \
     real(Float32Array,          28,     InitViaClassSpec,       TYPED_ARRAY_CLASP(Float32)) \
     real(Float64Array,          29,     InitViaClassSpec,       TYPED_ARRAY_CLASP(Float64)) \
     real(Uint8ClampedArray,     30,     InitViaClassSpec,       TYPED_ARRAY_CLASP(Uint8Clamped)) \
-    real(Proxy,                 31,     InitProxyClass,         OCLASP(Proxy)) \
+    real(Proxy,                 31,     InitProxyClass,         js::ProxyClassPtr) \
     real(WeakMap,               32,     InitWeakMapClass,       OCLASP(WeakMap)) \
     real(Map,                   33,     InitMapClass,           OCLASP(Map)) \
     real(Set,                   34,     InitSetClass,           OCLASP(Set)) \
     real(DataView,              35,     InitDataViewClass,      OCLASP(DataView)) \
     real(Symbol,                36,     InitSymbolClass,        OCLASP(Symbol)) \
 IF_SAB(real,imaginary)(SharedArrayBuffer,       37,     InitSharedArrayBufferClass, &js::SharedArrayBufferObject::protoClass) \
 IF_INTL(real,imaginary) (Intl,                  38,     InitIntlClass,          CLASP(Intl)) \
 IF_BDATA(real,imaginary)(TypedObject,           39,     InitTypedObjectModuleObject,   OCLASP(TypedObjectModule)) \
--- a/js/src/proxy/DeadObjectProxy.cpp
+++ b/js/src/proxy/DeadObjectProxy.cpp
@@ -146,15 +146,13 @@ DeadObjectProxy::regexp_toShared(JSConte
 {
     ReportDead(cx);
     return false;
 }
 
 const char DeadObjectProxy::family = 0;
 const DeadObjectProxy DeadObjectProxy::singleton;
 
-
 bool
 js::IsDeadProxyObject(JSObject* obj)
 {
-    return obj->is<ProxyObject>() &&
-           obj->as<ProxyObject>().handler() == &DeadObjectProxy::singleton;
+    return IsDerivedProxyObject(obj, &DeadObjectProxy::singleton);
 }
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -712,20 +712,20 @@ js::proxy_GetElements(JSContext* cx, Han
 }
 
 JSString*
 js::proxy_FunToString(JSContext* cx, HandleObject proxy, unsigned indent)
 {
     return Proxy::fun_toString(cx, proxy, indent);
 }
 
-const Class js::ProxyObject::class_ =
+const Class js::ProxyObject::proxyClass =
     PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_CACHED_PROTO(JSProto_Proxy));
 
-const Class* const js::ProxyClassPtr = &js::ProxyObject::class_;
+const Class* const js::ProxyClassPtr = &js::ProxyObject::proxyClass;
 
 JS_FRIEND_API(JSObject*)
 js::NewProxyObject(JSContext* cx, const BaseProxyHandler* handler, HandleValue priv, JSObject* proto_,
                    const ProxyOptions& options)
 {
     if (options.lazyProto()) {
         MOZ_ASSERT(!proto_);
         proto_ = TaggedProto::LazyProto;
@@ -733,17 +733,17 @@ js::NewProxyObject(JSContext* cx, const 
 
     return ProxyObject::New(cx, handler, priv, TaggedProto(proto_), options);
 }
 
 void
 ProxyObject::renew(JSContext* cx, const BaseProxyHandler* handler, Value priv)
 {
     MOZ_ASSERT_IF(IsCrossCompartmentWrapper(this), IsDeadProxyObject(this));
-    MOZ_ASSERT(getClass() == &ProxyObject::class_);
+    MOZ_ASSERT(getClass() == &ProxyObject::proxyClass);
     MOZ_ASSERT(!IsWindowProxy(this));
     MOZ_ASSERT(hasLazyPrototype());
 
     setHandler(handler);
     setCrossCompartmentPrivate(priv);
     setExtra(0, UndefinedValue());
     setExtra(1, UndefinedValue());
 }
--- a/js/src/vm/ProxyObject.h
+++ b/js/src/vm/ProxyObject.h
@@ -99,27 +99,35 @@ class ProxyObject : public JSObject
     static unsigned grayLinkExtraSlot(JSObject* obj);
 
     void renew(JSContext* cx, const BaseProxyHandler* handler, Value priv);
 
     static void trace(JSTracer* trc, JSObject* obj);
 
     void nuke(const BaseProxyHandler* handler);
 
-    static const Class class_;
+    // There is no class_ member to force specialization of JSObject::is<T>().
+    // The implementation in JSObject is incorrect for proxies since it doesn't
+    // take account of the handler type.
+    static const Class proxyClass;
 };
 
+bool IsDerivedProxyObject(const JSObject* obj, const js::BaseProxyHandler* handler);
+
 } // namespace js
 
-// Note: the following |JSObject::is<T>| methods are implemented in terms of
-// the Is*Proxy() friend API functions to ensure the implementations are tied
-// together.  The exception is |JSObject::is<js::OuterWindowProxyObject>()
-// const|, which uses the standard template definition, because there is no
-// IsOuterWindowProxy() function in the friend API.
-
 template<>
 inline bool
 JSObject::is<js::ProxyObject>() const
 {
+    // Note: this method is implemented in terms of the IsProxy() friend API
+    // functions to ensure the implementations are tied together.
+    // Note 2: this specialization isn't used for subclasses of ProxyObject
+    // which must supply their own implementation.
     return js::IsProxy(const_cast<JSObject*>(this));
 }
 
+inline bool
+js::IsDerivedProxyObject(const JSObject* obj, const js::BaseProxyHandler* handler) {
+    return obj->is<js::ProxyObject>() && obj->as<js::ProxyObject>().handler() == handler;
+}
+
 #endif /* vm_ProxyObject_h */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -2239,16 +2239,23 @@ class DebugScopeProxy : public BaseProxy
                  ObjectOpResult& result) const override
     {
         return result.fail(JSMSG_CANT_DELETE);
     }
 };
 
 } /* anonymous namespace */
 
+template<>
+bool
+JSObject::is<js::DebugScopeObject>() const
+{
+    return IsDerivedProxyObject(this, &DebugScopeProxy::singleton);
+}
+
 const char DebugScopeProxy::family = 0;
 const DebugScopeProxy DebugScopeProxy::singleton;
 
 /* static */ DebugScopeObject*
 DebugScopeObject::create(JSContext* cx, ScopeObject& scope, HandleObject enclosing)
 {
     MOZ_ASSERT(scope.compartment() == cx->compartment());
     MOZ_ASSERT(!enclosing->is<ScopeObject>());
@@ -2328,23 +2335,16 @@ DebugScopeObject::isOptimizedOut() const
         return !s.as<CallObject>().isForEval() &&
                !s.as<CallObject>().callee().needsCallObject() &&
                !maybeSnapshot();
     }
 
     return false;
 }
 
-bool
-js::IsDebugScopeSlow(ProxyObject* proxy)
-{
-    MOZ_ASSERT(proxy->hasClass(&ProxyObject::class_));
-    return proxy->handler() == &DebugScopeProxy::singleton;
-}
-
 /*****************************************************************************/
 
 DebugScopes::DebugScopes(JSContext* cx)
  : proxiedScopes(cx),
    missingScopes(cx->runtime()),
    liveScopes(cx->runtime())
 {}
 
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -1260,19 +1260,16 @@ class DebugScopes
     static void onPopCall(AbstractFramePtr frame, JSContext* cx);
     static void onPopBlock(JSContext* cx, const ScopeIter& si);
     static void onPopBlock(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc);
     static void onPopWith(AbstractFramePtr frame);
     static void onPopStrictEvalScope(AbstractFramePtr frame);
     static void onCompartmentUnsetIsDebuggee(JSCompartment* c);
 };
 
-extern bool
-IsDebugScopeSlow(ProxyObject* proxy);
-
 }  /* namespace js */
 
 template<>
 inline bool
 JSObject::is<js::NestedScopeObject>() const
 {
     return is<js::BlockObject>() ||
            is<js::StaticWithObject>() ||
@@ -1294,23 +1291,18 @@ JSObject::is<js::ScopeObject>() const
     return is<js::LexicalScopeBase>() ||
            is<js::DeclEnvObject>() ||
            is<js::NestedScopeObject>() ||
            is<js::RuntimeLexicalErrorObject>() ||
            is<js::NonSyntacticVariablesObject>();
 }
 
 template<>
-inline bool
-JSObject::is<js::DebugScopeObject>() const
-{
-    // Note: don't use is<ProxyObject>() here -- it also matches subclasses!
-    return hasClass(&js::ProxyObject::class_) &&
-           IsDebugScopeSlow(&const_cast<JSObject*>(this)->as<js::ProxyObject>());
-}
+bool
+JSObject::is<js::DebugScopeObject>() const;
 
 template<>
 inline bool
 JSObject::is<js::ClonedBlockObject>() const
 {
     return is<js::BlockObject>() && !!getProto();
 }
 
--- a/layout/base/nsBidi.cpp
+++ b/layout/base/nsBidi.cpp
@@ -36,28 +36,41 @@ enum {
     RLO = eCharType_RightToLeftOverride,
     PDF = eCharType_PopDirectionalFormat,
     NSM = eCharType_DirNonSpacingMark,
     BN =  eCharType_BoundaryNeutral,
     LRI = eCharType_LeftToRightIsolate,
     RLI = eCharType_RightToLeftIsolate,
     FSI = eCharType_FirstStrongIsolate,
     PDI = eCharType_PopDirectionalIsolate,
+    ENL,    /* EN after W7 */           /* 23 */
+    ENR,    /* EN not subject to W7 */  /* 24 */
     dirPropCount
 };
 
+#define IS_STRONG_TYPE(dirProp) ((dirProp) <= R || (dirProp) == AL)
+
 /* to avoid some conditional statements, use tiny constant arrays */
 static Flags flagLR[2]={ DIRPROP_FLAG(L), DIRPROP_FLAG(R) };
 static Flags flagE[2]={ DIRPROP_FLAG(LRE), DIRPROP_FLAG(RLE) };
 static Flags flagO[2]={ DIRPROP_FLAG(LRO), DIRPROP_FLAG(RLO) };
 
 #define DIRPROP_FLAG_LR(level) flagLR[(level)&1]
 #define DIRPROP_FLAG_E(level) flagE[(level)&1]
 #define DIRPROP_FLAG_O(level) flagO[(level)&1]
 
+#define NO_OVERRIDE(level)  ((level)&~NSBIDI_LEVEL_OVERRIDE)
+
+static inline uint8_t
+DirFromStrong(uint8_t aDirProp)
+{
+  MOZ_ASSERT(IS_STRONG_TYPE(aDirProp));
+  return aDirProp == L ? L : R;
+}
+
 /*
  * General implementation notes:
  *
  * Throughout the implementation, there are comments like (W2) that refer to
  * rules of the Bidi algorithm in its version 5, in this example to the second
  * rule of the resolution of weak types.
  *
  * For handling surrogate pairs, where two UChar's form one "abstract" (or UTF-32)
@@ -133,19 +146,16 @@ static Flags flagO[2]={ DIRPROP_FLAG(LRO
  * the flags variable.
  *
  * If there are no White Space types in the paragraph, then
  * (L1) is not necessary in AdjustWSLevels().
  */
 nsBidi::nsBidi()
 {
   Init();
-
-  mMayAllocateText=true;
-  mMayAllocateRuns=true;
 }
 
 nsBidi::~nsBidi()
 {
   Free();
 }
 
 void nsBidi::Init()
@@ -169,57 +179,49 @@ void nsBidi::Init()
   mLevels=nullptr;
   mRuns=nullptr;
   mIsolates=nullptr;
 
   mDirPropsMemory=nullptr;
   mLevelsMemory=nullptr;
   mRunsMemory=nullptr;
   mIsolatesMemory=nullptr;
-
-  mMayAllocateText=false;
-  mMayAllocateRuns=false;
 }
 
 /*
- * We are allowed to allocate memory if aMemory==nullptr or
- * aMayAllocate==true for each array that we need.
+ * We are allowed to allocate memory if aMemory==nullptr
+ * for each array that we need.
  * We also try to grow and shrink memory as needed if we
  * allocate it.
  *
  * Assume aSizeNeeded>0.
  * If *aMemory!=nullptr, then assume *aSize>0.
  *
  * ### this realloc() may unnecessarily copy the old data,
  * which we know we don't need any more;
  * is this the best way to do this??
  */
-bool nsBidi::GetMemory(void **aMemory, size_t *aSize, bool aMayAllocate, size_t aSizeNeeded)
+/*static*/
+bool
+nsBidi::GetMemory(void **aMemory, size_t *aSize, size_t aSizeNeeded)
 {
   /* check for existing memory */
   if(*aMemory==nullptr) {
     /* we need to allocate memory */
-    if(!aMayAllocate) {
-      return false;
+    *aMemory=malloc(aSizeNeeded);
+    if (*aMemory!=nullptr) {
+      *aSize=aSizeNeeded;
+      return true;
     } else {
-      *aMemory=malloc(aSizeNeeded);
-      if (*aMemory!=nullptr) {
-        *aSize=aSizeNeeded;
-        return true;
-      } else {
-        *aSize=0;
-        return false;
-      }
+      *aSize=0;
+      return false;
     }
   } else {
     /* there is some memory, is it enough or too much? */
-    if(aSizeNeeded>*aSize && !aMayAllocate) {
-      /* not enough memory, and we must not allocate */
-      return false;
-    } else if(aSizeNeeded!=*aSize && aMayAllocate) {
+    if(aSizeNeeded!=*aSize) {
       /* we may try to grow or shrink */
       void *memory=realloc(*aMemory, aSizeNeeded);
 
       if(memory!=nullptr) {
         *aMemory=memory;
         *aSize=aSizeNeeded;
         return true;
       } else {
@@ -243,17 +245,17 @@ void nsBidi::Free()
   mRunsMemory = nullptr;
   free(mIsolatesMemory);
   mIsolatesMemory = nullptr;
 }
 
 /* SetPara ------------------------------------------------------------ */
 
 nsresult nsBidi::SetPara(const char16_t *aText, int32_t aLength,
-                         nsBidiLevel aParaLevel, nsBidiLevel *aEmbeddingLevels)
+                         nsBidiLevel aParaLevel)
 {
   nsBidiDirection direction;
 
   /* check the argument values */
   if(aText==nullptr ||
      ((NSBIDI_MAX_EXPLICIT_LEVEL<aParaLevel) && !IS_DEFAULT_LEVEL(aParaLevel)) ||
      aLength<-1
     ) {
@@ -297,42 +299,32 @@ nsresult nsBidi::SetPara(const char16_t 
    */
   if(GETDIRPROPSMEMORY(aLength)) {
     mDirProps=mDirPropsMemory;
     GetDirProps(aText);
   } else {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  /* are explicit levels specified? */
-  if(aEmbeddingLevels==nullptr) {
-    /* no: determine explicit levels according to the (Xn) rules */\
-    if(GETLEVELSMEMORY(aLength)) {
-      mLevels=mLevelsMemory;
-      ResolveExplicitLevels(&direction);
-    } else {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
+  /* determine explicit levels according to the (Xn) rules */
+  if(GETLEVELSMEMORY(aLength)) {
+    mLevels=mLevelsMemory;
+    ResolveExplicitLevels(&direction, aText);
   } else {
-    /* set BN for all explicit codes, check that all levels are aParaLevel..NSBIDI_MAX_EXPLICIT_LEVEL */
-    mLevels=aEmbeddingLevels;
-    nsresult rv = CheckExplicitLevels(&direction);
-    if(NS_FAILED(rv)) {
-      return rv;
-    }
+    return NS_ERROR_OUT_OF_MEMORY;
   }
 
   /* allocate isolate memory */
   if (mIsolateCount <= SIMPLE_ISOLATES_SIZE) {
     mIsolates = mSimpleIsolates;
   } else {
     if (mIsolateCount * sizeof(Isolate) <= mIsolatesSize) {
       mIsolates = mIsolatesMemory;
     } else {
-      if (GETINITIALISOLATESMEMORY(mIsolateCount)) {
+      if (GETISOLATESMEMORY(mIsolateCount)) {
         mIsolates = mIsolatesMemory;
       } else {
         return NS_ERROR_OUT_OF_MEMORY;
       }
     }
   }
   mIsolateCount = -1;  /* current isolates stack entry == none */
 
@@ -363,17 +355,17 @@ nsresult nsBidi::SetPara(const char16_t 
        * then we can treat the entire paragraph as one run.
        * Otherwise, we need to perform the following rules on runs of
        * the text with the same embedding levels. (X10)
        * "Significant" explicit level codes are ones that actually
        * affect non-BN characters.
        * Examples for "insignificant" ones are empty embeddings
        * LRE-PDF, LRE-RLE-PDF-PDF, etc.
        */
-      if(aEmbeddingLevels==nullptr && !(mFlags&DIRPROP_FLAG_MULTI_RUNS)) {
+      if(!(mFlags&DIRPROP_FLAG_MULTI_RUNS)) {
         ResolveImplicitLevels(0, aLength,
                     GET_LR_FROM_LEVEL(mParaLevel),
                     GET_LR_FROM_LEVEL(mParaLevel));
       } else {
         /* sor, eor: start and end types of same-level-run */
         nsBidiLevel *levels=mLevels;
         int32_t start, limit=0;
         nsBidiLevel level, nextLevel;
@@ -580,16 +572,344 @@ void nsBidi::GetDirProps(const char16_t 
     stackLast--;
   }
 
   flags|=DIRPROP_FLAG_LR(mParaLevel);
 
   mFlags = flags;
 }
 
+/* Functions for handling paired brackets ----------------------------------- */
+
+/* In the mIsoRuns array, the first entry is used for text outside of any
+   isolate sequence.  Higher entries are used for each more deeply nested
+   isolate sequence.
+   mIsoRunLast is the index of the last used entry.
+   The mOpenings array is used to note the data of opening brackets not yet
+   matched by a closing bracket, or matched but still susceptible to change
+   level.
+   Each isoRun entry contains the index of the first and
+   one-after-last openings entries for pending opening brackets it
+   contains.  The next mOpenings entry to use is the one-after-last of the
+   most deeply nested isoRun entry.
+   mIsoRuns entries also contain their current embedding level and the bidi
+   class of the last-encountered strong character, since these will be needed
+   to resolve the level of paired brackets.  */
+
+nsBidi::BracketData::BracketData(const nsBidi *aBidi)
+{
+  mIsoRunLast = 0;
+  mIsoRuns[0].start = 0;
+  mIsoRuns[0].limit = 0;
+  mIsoRuns[0].level = aBidi->mParaLevel;
+  mIsoRuns[0].lastStrong = mIsoRuns[0].lastBase = mIsoRuns[0].contextDir =
+    GET_LR_FROM_LEVEL(aBidi->mParaLevel);
+  mIsoRuns[0].contextPos = 0;
+  mOpenings = mSimpleOpenings;
+  mOpeningsCount = SIMPLE_OPENINGS_COUNT;
+  mOpeningsMemory = nullptr;
+}
+
+nsBidi::BracketData::~BracketData()
+{
+  free(mOpeningsMemory);
+}
+
+/* LRE, LRO, RLE, RLO, PDF */
+void
+nsBidi::BracketData::ProcessBoundary(int32_t aLastDirControlCharPos,
+                                     nsBidiLevel aContextLevel,
+                                     nsBidiLevel aEmbeddingLevel,
+                                     const DirProp* aDirProps)
+{
+  IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast];
+  if (DIRPROP_FLAG(aDirProps[aLastDirControlCharPos]) & MASK_ISO) { /* after an isolate */
+    return;
+  }
+  if (NO_OVERRIDE(aEmbeddingLevel) > NO_OVERRIDE(aContextLevel)) {  /* not PDF */
+    aContextLevel = aEmbeddingLevel;
+  }
+  lastIsoRun.limit = lastIsoRun.start;
+  lastIsoRun.level = aEmbeddingLevel;
+  lastIsoRun.lastStrong = lastIsoRun.lastBase = lastIsoRun.contextDir =
+    GET_LR_FROM_LEVEL(aContextLevel);
+  lastIsoRun.contextPos = aLastDirControlCharPos;
+}
+
+/* LRI or RLI */
+void
+nsBidi::BracketData::ProcessLRI_RLI(nsBidiLevel aLevel)
+{
+  MOZ_ASSERT(mIsoRunLast <= NSBIDI_MAX_EXPLICIT_LEVEL);
+  IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast];
+  lastIsoRun.lastBase = O_N;
+  IsoRun& currIsoRun = mIsoRuns[++mIsoRunLast];
+  currIsoRun.start = currIsoRun.limit = lastIsoRun.limit;
+  currIsoRun.level = aLevel;
+  currIsoRun.lastStrong = currIsoRun.lastBase = currIsoRun.contextDir =
+    GET_LR_FROM_LEVEL(aLevel);
+  currIsoRun.contextPos = 0;
+}
+
+/* PDI */
+void
+nsBidi::BracketData::ProcessPDI()
+{
+  mIsoRuns[mIsoRunLast].lastBase = O_N;
+}
+
+/* newly found opening bracket: create an openings entry */
+bool                            /* return true if success */
+nsBidi::BracketData::AddOpening(char16_t aMatch, int32_t aPosition)
+{
+  IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast];
+  if (lastIsoRun.limit >= mOpeningsCount) {  /* no available new entry */
+    if (!GETOPENINGSMEMORY(lastIsoRun.limit * 2)) {
+      return false;
+    }
+    if (mOpenings == mSimpleOpenings) {
+      memcpy(mOpeningsMemory, mSimpleOpenings,
+             SIMPLE_OPENINGS_COUNT * sizeof(Opening));
+    }
+    mOpenings = mOpeningsMemory;     /* may have changed */
+    mOpeningsCount = mOpeningsSize / sizeof(Opening);
+  }
+  Opening& o = mOpenings[lastIsoRun.limit];
+  o.position = aPosition;
+  o.match = aMatch;
+  o.contextDir = lastIsoRun.contextDir;
+  o.contextPos = lastIsoRun.contextPos;
+  o.flags = 0;
+  lastIsoRun.limit++;
+  return true;
+}
+
+/* change N0c1 to N0c2 when a preceding bracket is assigned the embedding level */
+void
+nsBidi::BracketData::FixN0c(int32_t aOpeningIndex, int32_t aNewPropPosition,
+                            DirProp aNewProp, DirProp* aDirProps)
+{
+  /* This function calls itself recursively */
+  IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast];
+  for (int32_t k = aOpeningIndex + 1; k < lastIsoRun.limit; k++) {
+    Opening& o = mOpenings[k];
+    if (o.match >= 0) {     /* not an N0c match */
+      continue;
+    }
+    if (aNewPropPosition < o.contextPos) {
+      break;
+    }
+    int32_t openingPosition = o.position;
+    if (aNewPropPosition >= openingPosition) {
+      continue;
+    }
+    if (aNewProp == o.contextDir) {
+      break;
+    }
+    aDirProps[openingPosition] = aNewProp;
+    int32_t closingPosition = -(o.match);
+    aDirProps[closingPosition] = aNewProp;
+    o.match = 0;                    /* prevent further changes */
+    FixN0c(k, openingPosition, aNewProp, aDirProps);
+    FixN0c(k, closingPosition, aNewProp, aDirProps);
+  }
+}
+
+/* process closing bracket */
+DirProp              /* return L or R if N0b or N0c, ON if N0d */
+nsBidi::BracketData::ProcessClosing(int32_t aOpenIdx, int32_t aPosition,
+                                    DirProp* aDirProps)
+{
+  IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast];
+  Opening& o = mOpenings[aOpenIdx];
+  DirProp newProp;
+  DirProp direction = GET_LR_FROM_LEVEL(lastIsoRun.level);
+  bool stable = true; // assume stable until proved otherwise
+
+  /* The stable flag is set when brackets are paired and their
+     level is resolved and cannot be changed by what will be
+     found later in the source string.
+     An unstable match can occur only when applying N0c, where
+     the resolved level depends on the preceding context, and
+     this context may be affected by text occurring later.
+     Example: RTL paragraph containing:  abc[(latin) HEBREW]
+     When the closing parenthesis is encountered, it appears
+     that N0c1 must be applied since 'abc' sets an opposite
+     direction context and both parentheses receive level 2.
+     However, when the closing square bracket is processed,
+     N0b applies because of 'HEBREW' being included within the
+     brackets, thus the square brackets are treated like R and
+     receive level 1. However, this changes the preceding
+     context of the opening parenthesis, and it now appears
+     that N0c2 must be applied to the parentheses rather than
+     N0c1. */
+
+  if ((direction == 0 && o.flags & FOUND_L) ||
+      (direction == 1 && o.flags & FOUND_R)) { /* N0b */
+    newProp = direction;
+  } else if (o.flags & (FOUND_L|FOUND_R)) {    /* N0c */
+    /* it is stable if there is no containing pair or in
+       conditions too complicated and not worth checking */
+    stable = (aOpenIdx == lastIsoRun.start);
+    if (direction != o.contextDir) {
+      newProp = o.contextDir;           /* N0c1 */
+    } else {
+      newProp = direction;                     /* N0c2 */
+    }
+  } else {
+    /* forget this and any brackets nested within this pair */
+    lastIsoRun.limit = aOpenIdx;
+    return O_N;                                 /* N0d */
+  }
+  aDirProps[o.position] = newProp;
+  aDirProps[aPosition] = newProp;
+  /* Update nested N0c pairs that may be affected */
+  FixN0c(aOpenIdx, o.position, newProp, aDirProps);
+  if (stable) {
+    /* forget any brackets nested within this pair */
+    lastIsoRun.limit = aOpenIdx;
+  } else {
+    int32_t k;
+    o.match = -aPosition;
+    /* neutralize any unmatched opening between the current pair */
+    for (k = aOpenIdx + 1; k < lastIsoRun.limit; k++) {
+      Opening& oo = mOpenings[k];
+      if (oo.position > aPosition) {
+        break;
+      }
+      if (oo.match > 0) {
+        oo.match = 0;
+      }
+    }
+  }
+  return newProp;
+}
+
+static inline bool
+IsMatchingCloseBracket(char16_t aCh1, char16_t aCh2)
+{
+  // U+232A RIGHT-POINTING ANGLE BRACKET and U+3009 RIGHT ANGLE BRACKET
+  // are canonical equivalents, so we special-case them here.
+  return (aCh1 == aCh2) ||
+         (aCh1 == 0x232A && aCh2 == 0x3009) ||
+         (aCh2 == 0x232A && aCh1 == 0x3009);
+}
+
+/* Handle strong characters, digits and candidates for closing brackets. */
+/* Returns true if success. (The only failure mode is an OOM when trying
+   to allocate memory for the Openings array.) */
+bool
+nsBidi::BracketData::ProcessChar(int32_t aPosition, char16_t aCh,
+                                 DirProp* aDirProps, nsBidiLevel* aLevels)
+{
+  IsoRun& lastIsoRun = mIsoRuns[mIsoRunLast];
+  DirProp newProp;
+  DirProp dirProp = aDirProps[aPosition];
+  nsBidiLevel level = aLevels[aPosition];
+  if (dirProp == O_N) {
+    /* First see if it is a matching closing bracket. Hopefully, this is
+       more efficient than checking if it is a closing bracket at all */
+    for (int32_t idx = lastIsoRun.limit - 1; idx >= lastIsoRun.start; idx--) {
+      if (!IsMatchingCloseBracket(aCh, mOpenings[idx].match)) {
+        continue;
+      }
+      /* We have a match */
+      newProp = ProcessClosing(idx, aPosition, aDirProps);
+      if (newProp == O_N) {           /* N0d */
+        aCh = 0;        /* prevent handling as an opening */
+        break;
+      }
+      lastIsoRun.lastBase = O_N;
+      lastIsoRun.contextDir = newProp;
+      lastIsoRun.contextPos = aPosition;
+      if (level & NSBIDI_LEVEL_OVERRIDE) {    /* X4, X5 */
+        newProp = GET_LR_FROM_LEVEL(level);
+        lastIsoRun.lastStrong = newProp;
+        uint16_t flag = DIRPROP_FLAG(newProp);
+        for (int32_t i = lastIsoRun.start; i < idx; i++) {
+          mOpenings[i].flags |= flag;
+        }
+        /* matching brackets are not overridden by LRO/RLO */
+        aLevels[aPosition] &= ~NSBIDI_LEVEL_OVERRIDE;
+      }
+      /* matching brackets are not overridden by LRO/RLO */
+      aLevels[mOpenings[idx].position] &= ~NSBIDI_LEVEL_OVERRIDE;
+      return true;
+    }
+    /* We get here only if the ON character is not a matching closing
+       bracket or it is a case of N0d */
+    /* Now see if it is an opening bracket */
+    char16_t match = GetPairedBracket(aCh);
+    if (match != aCh &&                 /* has a matching char */
+        GetPairedBracketType(aCh) == PAIRED_BRACKET_TYPE_OPEN) { /* opening bracket */
+      if (!AddOpening(match, aPosition)) {
+        return false;
+      }
+    }
+  }
+  if (level & NSBIDI_LEVEL_OVERRIDE) {    /* X4, X5 */
+    newProp = GET_LR_FROM_LEVEL(level);
+    if (dirProp != S && dirProp != WS && dirProp != O_N) {
+      aDirProps[aPosition] = newProp;
+    }
+    lastIsoRun.lastBase = newProp;
+    lastIsoRun.lastStrong = newProp;
+    lastIsoRun.contextDir = newProp;
+    lastIsoRun.contextPos = aPosition;
+  } else if (IS_STRONG_TYPE(dirProp)) {
+    newProp = DirFromStrong(dirProp);
+    lastIsoRun.lastBase = dirProp;
+    lastIsoRun.lastStrong = dirProp;
+    lastIsoRun.contextDir = newProp;
+    lastIsoRun.contextPos = aPosition;
+  } else if (dirProp == EN) {
+    lastIsoRun.lastBase = EN;
+    if (lastIsoRun.lastStrong == L) {
+      newProp = L;                  /* W7 */
+      aDirProps[aPosition] = ENL;
+      lastIsoRun.contextDir = L;
+      lastIsoRun.contextPos = aPosition;
+    } else {
+      newProp = R;                  /* N0 */
+      if (lastIsoRun.lastStrong == AL) {
+        aDirProps[aPosition] = AN;  /* W2 */
+      } else {
+        aDirProps[aPosition] = ENR;
+      }
+      lastIsoRun.contextDir = R;
+      lastIsoRun.contextPos = aPosition;
+    }
+  } else if (dirProp == AN) {
+    newProp = R;                      /* N0 */
+    lastIsoRun.lastBase = AN;
+    lastIsoRun.contextDir = R;
+    lastIsoRun.contextPos = aPosition;
+  } else if (dirProp == NSM) {
+    /* if the last real char was ON, change NSM to ON so that it
+       will stay ON even if the last real char is a bracket which
+       may be changed to L or R */
+    newProp = lastIsoRun.lastBase;
+    if (newProp == O_N) {
+      aDirProps[aPosition] = newProp;
+    }
+  } else {
+    newProp = dirProp;
+    lastIsoRun.lastBase = dirProp;
+  }
+  if (IS_STRONG_TYPE(newProp)) {
+    uint16_t flag = DIRPROP_FLAG(DirFromStrong(newProp));
+    for (int32_t i = lastIsoRun.start; i < lastIsoRun.limit; i++) {
+      if (aPosition > mOpenings[i].position) {
+        mOpenings[i].flags |= flag;
+      }
+    }
+  }
+  return true;
+}
+
 /* perform (X1)..(X9) ------------------------------------------------------- */
 
 /*
  * Resolve the explicit levels as specified by explicit embedding codes.
  * Recalculate the flags to have them reflect the real properties
  * after taking the explicit embeddings into account.
  *
  * The Bidi algorithm is designed to result in the same behavior whether embedding
@@ -630,17 +950,17 @@ void nsBidi::GetDirProps(const char16_t 
  *
  * In order to have a correct push-pop semantics even in the case of overflows,
  * overflow counters and a valid isolate counter are used as described in UAX#9
  * section 3.3.2 "Explicit Levels and Direction".
  *
  * This implementation assumes that NSBIDI_MAX_EXPLICIT_LEVEL is odd.
  */
 
-void nsBidi::ResolveExplicitLevels(nsBidiDirection *aDirection)
+void nsBidi::ResolveExplicitLevels(nsBidiDirection *aDirection, const char16_t *aText)
 {
   DirProp *dirProps=mDirProps;
   nsBidiLevel *levels=mLevels;
 
   int32_t i=0, length=mLength;
   Flags flags=mFlags;       /* collect all directionalities in the text */
   DirProp dirProp;
   nsBidiLevel level=mParaLevel;
@@ -650,268 +970,237 @@ void nsBidi::ResolveExplicitLevels(nsBid
 
   /* determine if the text is mixed-directional or single-directional */
   direction=DirectionFromFlags(flags);
 
   /* we may not need to resolve any explicit levels */
   if(direction!=NSBIDI_MIXED) {
     /* not mixed directionality: levels don't matter - trailingWSStart will be 0 */
   } else if(!(flags&(MASK_EXPLICIT|MASK_ISO))) {
+    BracketData bracketData(this);
     /* no embeddings, set all levels to the paragraph level */
     for(i=0; i<length; ++i) {
       levels[i]=level;
+      if (dirProps[i] == BN) {
+        continue;
+      }
+      if (!bracketData.ProcessChar(i, aText[i], mDirProps, mLevels)) {
+        NS_WARNING("BracketData::ProcessChar failed, out of memory?");
+        // Ran out of memory for deeply-nested openings; give up and
+        // return LTR. This could presumably result in incorrect display,
+        // but in practice it won't happen except in some artificially-
+        // constructed torture test -- which is just as likely to die
+        // altogether with an OOM failure.
+        *aDirection = NSBIDI_LTR;
+        return;
+      }
     }
   } else {
     /* continue to perform (Xn) */
 
     /* (X1) level is set for all codes, embeddingLevel keeps track of the push/pop operations */
     /* both variables may carry the NSBIDI_LEVEL_OVERRIDE flag to indicate the override status */
     nsBidiLevel embeddingLevel = level, newLevel;
     nsBidiLevel previousLevel = level;     /* previous level for regular (not CC) characters */
+    int32_t lastDirControlCharPos = 0;     /* index of last effective LRx,RLx, PDx */
 
     uint16_t stack[NSBIDI_MAX_EXPLICIT_LEVEL + 2];   /* we never push anything >=NSBIDI_MAX_EXPLICIT_LEVEL
                                                         but we need one more entry as base */
     int32_t stackLast = 0;
     int32_t overflowIsolateCount = 0;
     int32_t overflowEmbeddingCount = 0;
     int32_t validIsolateCount = 0;
 
+    BracketData bracketData(this);
+
     stack[0] = level;
 
     /* recalculate the flags */
     flags=0;
 
     /* since we assume that this is a single paragraph, we ignore (X8) */
     for(i=0; i<length; ++i) {
       dirProp=dirProps[i];
       switch(dirProp) {
         case LRE:
         case RLE:
         case LRO:
         case RLO:
           /* (X2, X3, X4, X5) */
           flags |= DIRPROP_FLAG(BN);
+          levels[i] = previousLevel;
           if (dirProp == LRE || dirProp == LRO) {
             newLevel = (embeddingLevel + 2) & ~(NSBIDI_LEVEL_OVERRIDE | 1);    /* least greater even level */
           } else {
             newLevel = ((embeddingLevel & ~NSBIDI_LEVEL_OVERRIDE) + 1) | 1;    /* least greater odd level */
           }
           if(newLevel <= NSBIDI_MAX_EXPLICIT_LEVEL && overflowIsolateCount == 0 && overflowEmbeddingCount == 0) {
+            lastDirControlCharPos = i;
             embeddingLevel = newLevel;
             if (dirProp == LRO || dirProp == RLO) {
               embeddingLevel |= NSBIDI_LEVEL_OVERRIDE;
             }
             stackLast++;
             stack[stackLast] = embeddingLevel;
-            /* we don't need to set UBIDI_LEVEL_OVERRIDE off for LRE and RLE
+            /* we don't need to set NSBIDI_LEVEL_OVERRIDE off for LRE and RLE
                since this has already been done for newLevel which is
                the source for embeddingLevel.
              */
           } else {
-            dirProps[i] |= IGNORE_CC;
             if (overflowIsolateCount == 0) {
               overflowEmbeddingCount++;
             }
           }
           break;
 
         case PDF:
           /* (X7) */
           flags |= DIRPROP_FLAG(BN);
+          levels[i] = previousLevel;
           /* handle all the overflow cases first */
           if (overflowIsolateCount) {
-            dirProps[i] |= IGNORE_CC;
             break;
           }
           if (overflowEmbeddingCount) {
-            dirProps[i] |= IGNORE_CC;
             overflowEmbeddingCount--;
             break;
           }
           if (stackLast > 0 && stack[stackLast] < ISOLATE) {   /* not an isolate entry */
+            lastDirControlCharPos = i;
             stackLast--;
             embeddingLevel = stack[stackLast];
-          } else {
-            dirProps[i] |= IGNORE_CC;
           }
           break;
 
         case LRI:
         case RLI:
-          if (embeddingLevel != previousLevel) {
-            previousLevel = embeddingLevel;
+          flags |= DIRPROP_FLAG(O_N) | DIRPROP_FLAG_LR(embeddingLevel);
+          levels[i] = NO_OVERRIDE(embeddingLevel);
+          if (NO_OVERRIDE(embeddingLevel) != NO_OVERRIDE(previousLevel)) {
+            bracketData.ProcessBoundary(lastDirControlCharPos, previousLevel,
+                                        embeddingLevel, mDirProps);
+            flags |= DIRPROP_FLAG_MULTI_RUNS;
           }
+          previousLevel = embeddingLevel;
           /* (X5a, X5b) */
-          flags |= DIRPROP_FLAG(O_N) | DIRPROP_FLAG(BN) | DIRPROP_FLAG_LR(embeddingLevel);
-          level = embeddingLevel;
           if (dirProp == LRI) {
             newLevel = (embeddingLevel + 2) & ~(NSBIDI_LEVEL_OVERRIDE | 1); /* least greater even level */
           } else {
             newLevel = ((embeddingLevel & ~NSBIDI_LEVEL_OVERRIDE) + 1) | 1;  /* least greater odd level */
           }
           if (newLevel <= NSBIDI_MAX_EXPLICIT_LEVEL && overflowIsolateCount == 0 && overflowEmbeddingCount == 0) {
+            flags |= DIRPROP_FLAG(dirProp);
+            lastDirControlCharPos = i;
             previousLevel = embeddingLevel;
             validIsolateCount++;
             if (validIsolateCount > mIsolateCount) {
               mIsolateCount = validIsolateCount;
             }
             embeddingLevel = newLevel;
             stackLast++;
             stack[stackLast] = embeddingLevel + ISOLATE;
+            bracketData.ProcessLRI_RLI(embeddingLevel);
           } else {
-            dirProps[i] |= IGNORE_CC;
+            /* make it so that it is handled by AdjustWSLevels() */
+            dirProps[i] = WS;
             overflowIsolateCount++;
           }
           break;
 
         case PDI:
+          if (NO_OVERRIDE(embeddingLevel) != NO_OVERRIDE(previousLevel)) {
+            bracketData.ProcessBoundary(lastDirControlCharPos, previousLevel,
+                                        embeddingLevel, mDirProps);
+            flags |= DIRPROP_FLAG_MULTI_RUNS;
+          }
           /* (X6a) */
           if (overflowIsolateCount) {
-            dirProps[i] |= IGNORE_CC;
             overflowIsolateCount--;
+            /* make it so that it is handled by AdjustWSLevels() */
+            dirProps[i] = WS;
           } else if (validIsolateCount) {
+            flags |= DIRPROP_FLAG(PDI);
+            lastDirControlCharPos = i;
             overflowEmbeddingCount = 0;
             while (stack[stackLast] < ISOLATE) {
               /* pop embedding entries        */
               /* until the last isolate entry */
               stackLast--;
 
               // Since validIsolateCount is true, there must be an isolate entry
               // on the stack, so the stack is guaranteed to not be empty.
               // Still, to eliminate a warning from coverity, we use an assertion.
               MOZ_ASSERT(stackLast > 0);
             }
             stackLast--;  /* pop also the last isolate entry */
             MOZ_ASSERT(stackLast >= 0);  // For coverity
             validIsolateCount--;
+            bracketData.ProcessPDI();
           } else {
-            dirProps[i] |= IGNORE_CC;
+            /* make it so that it is handled by AdjustWSLevels() */
+            dirProps[i] = WS;
           }
           embeddingLevel = stack[stackLast] & ~ISOLATE;
-          previousLevel = level = embeddingLevel;
-          flags |= DIRPROP_FLAG(O_N) | DIRPROP_FLAG(BN) | DIRPROP_FLAG_LR(embeddingLevel);
+          flags |= DIRPROP_FLAG(O_N) | DIRPROP_FLAG_LR(embeddingLevel);
+          previousLevel = embeddingLevel;
+          levels[i] = NO_OVERRIDE(embeddingLevel);
           break;
 
         case B:
           /*
            * We do not expect to see a paragraph separator (B),
            */
           NS_NOTREACHED("Unexpected paragraph separator");
           break;
 
         case BN:
           /* BN, LRE, RLE, and PDF are supposed to be removed (X9) */
           /* they will get their levels set correctly in AdjustWSLevels() */
-          flags|=DIRPROP_FLAG(BN);
+          levels[i] = previousLevel;
+          flags |= DIRPROP_FLAG(BN);
           break;
 
         default:
           /* all other types get the "real" level */
-          level = embeddingLevel;
-          if(embeddingLevel != previousLevel) {
-            previousLevel = embeddingLevel;
+          if (NO_OVERRIDE(embeddingLevel) != NO_OVERRIDE(previousLevel)) {
+            bracketData.ProcessBoundary(lastDirControlCharPos, previousLevel,
+                                        embeddingLevel, mDirProps);
+            flags |= DIRPROP_FLAG_MULTI_RUNS;
+            if (embeddingLevel & NSBIDI_LEVEL_OVERRIDE) {
+              flags |= DIRPROP_FLAG_O(embeddingLevel);
+            } else {
+              flags |= DIRPROP_FLAG_E(embeddingLevel);
+            }
           }
-
-          if (level & NSBIDI_LEVEL_OVERRIDE) {
-            flags |= DIRPROP_FLAG_LR(level);
-          } else {
-            flags |= DIRPROP_FLAG(dirProp);
+          previousLevel = embeddingLevel;
+          levels[i] = embeddingLevel;
+          if (!bracketData.ProcessChar(i, aText[i], mDirProps, mLevels)) {
+            NS_WARNING("BracketData::ProcessChar failed, out of memory?");
+            *aDirection = NSBIDI_LTR;
+            return;
           }
+          flags |= DIRPROP_FLAG(dirProps[i]);
           break;
       }
-
-      /*
-       * We need to set reasonable levels even on BN codes and
-       * explicit codes because we will later look at same-level runs (X10).
-       */
-      levels[i]=level;
-      if (i > 0 && levels[i - 1] != level) {
-        flags |= DIRPROP_FLAG_MULTI_RUNS;
-        if (level & NSBIDI_LEVEL_OVERRIDE) {
-          flags |= DIRPROP_FLAG_O(level);
-        } else {
-          flags |= DIRPROP_FLAG_E(level);
-        }
-      }
-      if (DIRPROP_FLAG(dirProp) & MASK_ISO) {
-        level = embeddingLevel;
-      }
     }
 
     if(flags&MASK_EMBEDDING) {
       flags|=DIRPROP_FLAG_LR(mParaLevel);
     }
 
     /* subsequently, ignore the explicit codes and BN (X9) */
 
     /* again, determine if the text is mixed-directional or single-directional */
     mFlags=flags;
     direction=DirectionFromFlags(flags);
   }
 
   *aDirection = direction;
 }
 
-/*
- * Use a pre-specified embedding levels array:
- *
- * Adjust the directional properties for overrides (->LEVEL_OVERRIDE),
- * ignore all explicit codes (X9),
- * and check all the preset levels.
- *
- * Recalculate the flags to have them reflect the real properties
- * after taking the explicit embeddings into account.
- */
-nsresult nsBidi::CheckExplicitLevels(nsBidiDirection *aDirection)
-{
-  const DirProp *dirProps=mDirProps;
-  DirProp dirProp;
-  nsBidiLevel *levels=mLevels;
-  int32_t isolateCount = 0;
-
-  int32_t i, length=mLength;
-  Flags flags=0;  /* collect all directionalities in the text */
-  nsBidiLevel level, paraLevel=mParaLevel;
-  mIsolateCount = 0;
-
-  for(i=0; i<length; ++i) {
-    level=levels[i];
-    dirProp = dirProps[i];
-    if (dirProp == LRI || dirProp == RLI) {
-      isolateCount++;
-      if (isolateCount > mIsolateCount) {
-        mIsolateCount = isolateCount;
-      }
-    } else if (dirProp == PDI) {
-      isolateCount--;
-    }
-    if(level&NSBIDI_LEVEL_OVERRIDE) {
-      /* keep the override flag in levels[i] but adjust the flags */
-      level&=~NSBIDI_LEVEL_OVERRIDE;     /* make the range check below simpler */
-      flags|=DIRPROP_FLAG_O(level);
-    } else {
-      /* set the flags */
-      flags|=DIRPROP_FLAG_E(level)|DIRPROP_FLAG(dirProp);
-    }
-    if(level<paraLevel || NSBIDI_MAX_EXPLICIT_LEVEL<level) {
-      /* level out of bounds */
-      *aDirection = NSBIDI_LTR;
-      return NS_ERROR_INVALID_ARG;
-    }
-  }
-  if(flags&MASK_EMBEDDING) {
-    flags|=DIRPROP_FLAG_LR(mParaLevel);
-  }
-
-  /* determine if the text is mixed-directional or single-directional */
-  mFlags=flags;
-  *aDirection = DirectionFromFlags(flags);
-  return NS_OK;
-}
-
 /* determine if the text is mixed-directional or single-directional */
 nsBidiDirection nsBidi::DirectionFromFlags(Flags aFlags)
 {
   /* if the text contains AN and neutrals, then some neutrals may become RTL */
   if(!(aFlags&MASK_RTL || (aFlags&DIRPROP_FLAG(AN) && aFlags&MASK_POSSIBLE_N))) {
     return NSBIDI_LTR;
   } else if(!(aFlags&MASK_LTR)) {
     return NSBIDI_RTL;
@@ -1191,31 +1480,31 @@ void nsBidi::ResolveImplicitLevels(int32
   const DirProp *dirProps = mDirProps;
   DirProp dirProp;
   LevState levState;
   int32_t i, start1, start2;
   uint16_t oldStateImp, stateImp, actionImp;
   uint8_t gprop, resProp, cell;
 
   /* initialize for property and levels state tables */
-  levState.startON = -1;
   levState.runStart = aStart;
   levState.runLevel = mLevels[aStart];
   levState.pImpTab = impTab[levState.runLevel & 1];
   levState.pImpAct = impAct0;
 
   /* The isolates[] entries contain enough information to
      resume the bidi algorithm in the same state as it was
      when it was interrupted by an isolate sequence. */
-  if (dirProps[aStart] == PDI) {
+  if (dirProps[aStart] == PDI && mIsolateCount >= 0) {
     start1 = mIsolates[mIsolateCount].start1;
     stateImp = mIsolates[mIsolateCount].stateImp;
     levState.state = mIsolates[mIsolateCount].state;
     mIsolateCount--;
   } else {
+    levState.startON = -1;
     start1 = aStart;
     if (dirProps[aStart] == NSM) {
       stateImp = 1 + aSOR;
     } else {
       stateImp = 0;
     }
     levState.state = 0;
     ProcessPropertySeq(&levState, aSOR, aStart, aStart);
@@ -1228,17 +1517,17 @@ void nsBidi::ResolveImplicitLevels(int32
         dirProp = mDirProps[aLimit - 1];
         if (dirProp == LRI || dirProp == RLI) {
           break;  /* no forced closing for sequence ending with LRI/RLI */
         }
       }
       gprop = aEOR;
     } else {
       DirProp prop;
-      prop = PURE_DIRPROP(dirProps[i]);
+      prop = dirProps[i];
       gprop = groupProp[prop];
     }
     oldStateImp = stateImp;
     cell = impTabProps[oldStateImp][gprop];
     stateImp = GET_STATEPROPS(cell);      /* isolate the new state */
     actionImp = GET_ACTIONPROPS(cell);    /* isolate the action */
     if ((i == aLimit) && (actionImp == 0)) {
       /* there is an unprocessed sequence if its property == eor   */
@@ -1299,24 +1588,24 @@ void nsBidi::AdjustWSLevels()
 
   if(mFlags&MASK_WS) {
     nsBidiLevel paraLevel=mParaLevel;
     Flags flag;
 
     i=mTrailingWSStart;
     while(i>0) {
       /* reset a sequence of WS/BN before eop and B/S to the paragraph paraLevel */
-      while (i > 0 && DIRPROP_FLAG(PURE_DIRPROP(dirProps[--i])) & MASK_WS) {
+      while (i > 0 && DIRPROP_FLAG(dirProps[--i]) & MASK_WS) {
         levels[i]=paraLevel;
       }
 
       /* reset BN to the next character's paraLevel until B/S, which restarts above loop */
       /* here, i+1 is guaranteed to be <length */
       while(i>0) {
-        flag = DIRPROP_FLAG(PURE_DIRPROP(dirProps[--i]));
+        flag = DIRPROP_FLAG(dirProps[--i]);
         if(flag&MASK_BN_EXPLICIT) {
           levels[i]=levels[i+1];
         } else if(flag&MASK_B_S) {
           levels[i]=paraLevel;
           break;
         }
       }
     }
@@ -1329,262 +1618,16 @@ nsresult nsBidi::GetDirection(nsBidiDire
   return NS_OK;
 }
 
 nsresult nsBidi::GetParaLevel(nsBidiLevel* aParaLevel)
 {
   *aParaLevel = mParaLevel;
   return NS_OK;
 }
-#ifdef FULL_BIDI_ENGINE
-
-/* -------------------------------------------------------------------------- */
-
-nsresult nsBidi::GetLength(int32_t* aLength)
-{
-  *aLength = mLength;
-  return NS_OK;
-}
-
-/*
- * General remarks about the functions in this section:
- *
- * These functions deal with the aspects of potentially mixed-directional
- * text in a single paragraph or in a line of a single paragraph
- * which has already been processed according to
- * the Unicode 6.3 Bidi algorithm as defined in
- * http://www.unicode.org/unicode/reports/tr9/ , version 28,
- * also described in The Unicode Standard, Version 6.3.0 .
- *
- * This means that there is a nsBidi object with a levels
- * and a dirProps array.
- * paraLevel and direction are also set.
- * Only if the length of the text is zero, then levels==dirProps==nullptr.
- *
- * The overall directionality of the paragraph
- * or line is used to bypass the reordering steps if possible.
- * Even purely RTL text does not need reordering there because
- * the getLogical/VisualIndex() functions can compute the
- * index on the fly in such a case.
- *
- * The implementation of the access to same-level-runs and of the reordering
- * do attempt to provide better performance and less memory usage compared to
- * a direct implementation of especially rule (L2) with an array of
- * one (32-bit) integer per text character.
- *
- * Here, the levels array is scanned as soon as necessary, and a vector of
- * same-level-runs is created. Reordering then is done on this vector.
- * For each run of text positions that were resolved to the same level,
- * only 8 bytes are stored: the first text position of the run and the visual
- * position behind the run after reordering.
- * One sign bit is used to hold the directionality of the run.
- * This is inefficient if there are many very short runs. If the average run
- * length is <2, then this uses more memory.
- *
- * In a further attempt to save memory, the levels array is never changed
- * after all the resolution rules (Xn, Wn, Nn, In).
- * Many functions have to consider the field trailingWSStart:
- * if it is less than length, then there is an implicit trailing run
- * at the paraLevel,
- * which is not reflected in the levels array.
- * This allows a line nsBidi object to use the same levels array as
- * its paragraph parent object.
- *
- * When a nsBidi object is created for a line of a paragraph, then the
- * paragraph's levels and dirProps arrays are reused by way of setting
- * a pointer into them, not by copying. This again saves memory and forbids to
- * change the now shared levels for (L1).
- */
-nsresult nsBidi::SetLine(const nsBidi* aParaBidi, int32_t aStart, int32_t aLimit)
-{
-  nsBidi* pParent = (nsBidi*)aParaBidi;
-  int32_t length;
-
-  /* check the argument values */
-  if(pParent==nullptr) {
-    return NS_ERROR_INVALID_POINTER;
-  } else if(aStart < 0 || aStart >= aLimit || aLimit > pParent->mLength) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  /* set members from our aParaBidi parent */
-  length = mLength = aLimit - aStart;
-  mParaLevel=pParent->mParaLevel;
-
-  mRuns=nullptr;
-  mFlags=0;
-
-  mDirProps=pParent->mDirProps+aStart;
-  mLevels=pParent->mLevels+aStart;
-  mRunCount=-1;
-
-  if(pParent->mDirection!=NSBIDI_MIXED) {
-    /* the parent is already trivial */
-    mDirection=pParent->mDirection;
-
-    /*
-     * The parent's levels are all either
-     * implicitly or explicitly ==paraLevel;
-     * do the same here.
-     */
-    if(pParent->mTrailingWSStart<=aStart) {
-      mTrailingWSStart=0;
-    } else if(pParent->mTrailingWSStart<aLimit) {
-      mTrailingWSStart=pParent->mTrailingWSStart-aStart;
-    } else {
-      mTrailingWSStart=length;
-    }
-  } else {
-    const nsBidiLevel *levels=mLevels;
-    int32_t i, trailingWSStart;
-    nsBidiLevel level;
-
-    SetTrailingWSStart();
-    trailingWSStart=mTrailingWSStart;
-
-    /* recalculate pLineBidi->direction */
-    if(trailingWSStart==0) {
-      /* all levels are at paraLevel */
-      mDirection=(nsBidiDirection)(mParaLevel&1);
-   } else {
-      /* get the level of the first character */
-      level=levels[0]&1;
-
-      /* if there is anything of a different level, then the line is mixed */
-      if(trailingWSStart<length && (mParaLevel&1)!=level) {
-        /* the trailing WS is at paraLevel, which differs from levels[0] */
-        mDirection=NSBIDI_MIXED;
-      } else {
-        /* see if levels[1..trailingWSStart-1] have the same direction as levels[0] and paraLevel */
-        i=1;
-        for(;;) {
-          if(i==trailingWSStart) {
-            /* the direction values match those in level */
-            mDirection=(nsBidiDirection)level;
-            break;
-          } else if((levels[i]&1)!=level) {
-            mDirection=NSBIDI_MIXED;
-            break;
-          }
-          ++i;
-        }
-      }
-    }
-
-    switch(mDirection) {
-      case NSBIDI_LTR:
-        /* make sure paraLevel is even */
-        mParaLevel=(mParaLevel+1)&~1;
-
-        /* all levels are implicitly at paraLevel (important for GetLevels()) */
-        mTrailingWSStart=0;
-      break;
-      case NSBIDI_RTL:
-        /* make sure paraLevel is odd */
-        mParaLevel|=1;
-
-        /* all levels are implicitly at paraLevel (important for GetLevels()) */
-        mTrailingWSStart=0;
-        break;
-      default:
-        break;
-    }
-  }
-  return NS_OK;
-}
-
-/* handle trailing WS (L1) -------------------------------------------------- */
-
-/*
- * SetTrailingWSStart() sets the start index for a trailing
- * run of WS in the line. This is necessary because we do not modify
- * the paragraph's levels array that we just point into.
- * Using trailingWSStart is another form of performing (L1).
- *
- * To make subsequent operations easier, we also include the run
- * before the WS if it is at the paraLevel - we merge the two here.
- */
-void nsBidi::SetTrailingWSStart() {
-  /* mDirection!=NSBIDI_MIXED */
-
-  const DirProp *dirProps=mDirProps;
-  nsBidiLevel *levels=mLevels;
-  int32_t start=mLength;
-  nsBidiLevel paraLevel=mParaLevel;
-
-  /* go backwards across all WS, BN, explicit codes */
-  while(start>0 && DIRPROP_FLAG(dirProps[start-1])&MASK_WS) {
-    --start;
-  }
-
-  /* if the WS run can be merged with the previous run then do so here */
-  while(start>0 && levels[start-1]==paraLevel) {
-    --start;
-  }
-
-  mTrailingWSStart=start;
-}
-
-nsresult nsBidi::GetLevelAt(int32_t aCharIndex, nsBidiLevel* aLevel)
-{
-  /* return paraLevel if in the trailing WS run, otherwise the real level */
-  if(aCharIndex<0 || mLength<=aCharIndex) {
-    *aLevel = 0;
-  } else if(mDirection!=NSBIDI_MIXED || aCharIndex>=mTrailingWSStart) {
-    *aLevel = mParaLevel;
-  } else {
-    *aLevel = mLevels[aCharIndex];
-  }
-  return NS_OK;
-}
-
-nsresult nsBidi::GetLevels(nsBidiLevel** aLevels)
-{
-  int32_t start, length;
-
-  length = mLength;
-  if(length<=0) {
-    *aLevels = nullptr;
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  start = mTrailingWSStart;
-  if(start==length) {
-    /* the current levels array reflects the WS run */
-    *aLevels = mLevels;
-    return NS_OK;
-  }
-
-  /*
-   * After the previous if(), we know that the levels array
-   * has an implicit trailing WS run and therefore does not fully
-   * reflect itself all the levels.
-   * This must be a nsBidi object for a line, and
-   * we need to create a new levels array.
-   */
-
-  if(GETLEVELSMEMORY(length)) {
-    nsBidiLevel *levels=mLevelsMemory;
-
-    if(start>0 && levels!=mLevels) {
-      memcpy(levels, mLevels, start);
-    }
-    memset(levels+start, mParaLevel, length-start);
-
-    /* this new levels array is set for the line and reflects the WS run */
-    mTrailingWSStart=length;
-    *aLevels=mLevels=levels;
-    return NS_OK;
-  } else {
-    /* out of memory */
-    *aLevels = nullptr;
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-}
-#endif // FULL_BIDI_ENGINE
 
 nsresult nsBidi::GetCharTypeAt(int32_t aCharIndex, nsCharType* pType)
 {
   if(aCharIndex<0 || mLength<=aCharIndex) {
     return NS_ERROR_INVALID_ARG;
   }
   *pType = (nsCharType)mDirProps[aCharIndex];
   return NS_OK;
@@ -2033,459 +2076,8 @@ bool nsBidi::PrepareReorder(const nsBidi
   /* initialize the index map */
   for(start=aLength; start>0;) {
     --start;
     aIndexMap[start]=start;
   }
 
   return true;
 }
-
-#ifdef FULL_BIDI_ENGINE
-/* API functions for logical<->visual mapping ------------------------------- */
-
-nsresult nsBidi::GetVisualIndex(int32_t aLogicalIndex, int32_t* aVisualIndex) {
-  int32_t visualIndex = NSBIDI_MAP_NOWHERE;
-
-  if(aLogicalIndex<0 || mLength<=aLogicalIndex) {
-    return NS_ERROR_INVALID_ARG;
-  } else {
-    /* we can do the trivial cases without the runs array */
-    switch(mDirection) {
-    case NSBIDI_LTR:
-      *aVisualIndex = aLogicalIndex;
-      return NS_OK;
-    case NSBIDI_RTL:
-      *aVisualIndex = mLength-aLogicalIndex-1;
-      return NS_OK;
-    default:
-      if(mRunCount<0 && !GetRuns()) {
-        return NS_ERROR_OUT_OF_MEMORY;
-      } else {
-        Run *runs=mRuns;
-        int32_t i, visualStart=0, offset, length;
-
-        /* linear search for the run, search on the visual runs */
-        for (i = 0; i < mRunCount; ++i) {
-          length=runs[i].visualLimit-visualStart;
-          offset=aLogicalIndex-GET_INDEX(runs[i].logicalStart);
-          if(offset>=0 && offset<length) {
-            if(IS_EVEN_RUN(runs[i].logicalStart)) {
-              /* LTR */
-              visualIndex = visualStart + offset;
-            } else {
-              /* RTL */
-              visualIndex = visualStart + length - offset - 1;
-            }
-            break;
-          }
-          visualStart+=length;
-        }
-        if (i >= mRunCount) {
-          *aVisualIndex = NSBIDI_MAP_NOWHERE;
-          return NS_OK;
-        }
-      }
-    }
-  }
-
-  *aVisualIndex = visualIndex;
-  return NS_OK;
-}
-
-nsresult nsBidi::GetLogicalIndex(int32_t aVisualIndex, int32_t *aLogicalIndex)
-{
-  if(aVisualIndex<0 || mLength<=aVisualIndex) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  /* we can do the trivial cases without the runs array */
-  if (mDirection == NSBIDI_LTR) {
-    *aLogicalIndex = aVisualIndex;
-    return NS_OK;
-  } else if (mDirection == NSBIDI_RTL) {
-    *aLogicalIndex = mLength - aVisualIndex - 1;
-    return NS_OK;
-  }
-
-  if(mRunCount<0 && !GetRuns()) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  Run *runs=mRuns;
-  int32_t i, runCount=mRunCount, start;
-
-  if(runCount<=10) {
-    /* linear search for the run */
-    for(i=0; aVisualIndex>=runs[i].visualLimit; ++i) {}
-  } else {
-    /* binary search for the run */
-    int32_t start=0, limit=runCount;
-
-    /* the middle if() will guaranteed find the run, we don't need a loop limit */
-    for(;;) {
-      i=(start+limit)/2;
-      if(aVisualIndex>=runs[i].visualLimit) {
-        start=i+1;
-      } else if(i==0 || aVisualIndex>=runs[i-1].visualLimit) {
-        break;
-      } else {
-        limit=i;
-      }
-    }
-  }
-
-  start=runs[i].logicalStart;
-  if(IS_EVEN_RUN(start)) {
-    /* LTR */
-    /* the offset in runs[i] is aVisualIndex-runs[i-1].visualLimit */
-    if(i>0) {
-      aVisualIndex-=runs[i-1].visualLimit;
-    }
-    *aLogicalIndex = GET_INDEX(start)+aVisualIndex;
-    return NS_OK;
-  } else {
-    /* RTL */
-    *aLogicalIndex = GET_INDEX(start)+runs[i].visualLimit-aVisualIndex-1;
-    return NS_OK;
-  }
-}
-
-nsresult nsBidi::GetLogicalMap(int32_t *aIndexMap)
-{
-  nsresult rv;
-
-  /* CountRuns() checks for VALID_PARA_OR_LINE */
-  rv = CountRuns(nullptr);
-  if(NS_FAILED(rv)) {
-    return rv;
-  } else if(aIndexMap==nullptr) {
-    return NS_ERROR_INVALID_ARG;
-  } else {
-    /* fill a logical-to-visual index map using the runs[] */
-    int32_t visualStart, visualLimit, j;
-    int32_t logicalStart;
-    Run *runs = mRuns;
-    if (mLength <= 0) {
-      return NS_OK;
-    }
-
-    visualStart = 0;
-    for (j = 0; j < mRunCount; ++j) {
-      logicalStart = GET_INDEX(runs[j].logicalStart);
-      visualLimit = runs[j].visualLimit;
-      if (IS_EVEN_RUN(runs[j].logicalStart)) {
-        do { /* LTR */
-          aIndexMap[logicalStart++] = visualStart++;
-        } while (visualStart < visualLimit);
-      } else {
-        logicalStart += visualLimit-visualStart;  /* logicalLimit */
-        do { /* RTL */
-          aIndexMap[--logicalStart] = visualStart++;
-        } while (visualStart < visualLimit);
-      }
-      /* visualStart==visualLimit; */
-    }
-  }
-  return NS_OK;
-}
-
-nsresult nsBidi::GetVisualMap(int32_t *aIndexMap)
-{
-  int32_t* runCount=nullptr;
-  nsresult rv;
-
-  if(aIndexMap==nullptr) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  /* CountRuns() checks all of its and our arguments */
-  rv = CountRuns(runCount);
-  if(NS_FAILED(rv)) {
-    return rv;
-  } else {
-    /* fill a visual-to-logical index map using the runs[] */
-    Run *runs=mRuns, *runsLimit=runs+mRunCount;
-    int32_t logicalStart, visualStart, visualLimit;
-
-    visualStart=0;
-    for(; runs<runsLimit; ++runs) {
-      logicalStart=runs->logicalStart;
-      visualLimit=runs->visualLimit;
-      if(IS_EVEN_RUN(logicalStart)) {
-        do { /* LTR */
-          *aIndexMap++ = logicalStart++;
-        } while(++visualStart<visualLimit);
-      } else {
-        REMOVE_ODD_BIT(logicalStart);
-        logicalStart+=visualLimit-visualStart;  /* logicalLimit */
-        do { /* RTL */
-          *aIndexMap++ = --logicalStart;
-        } while(++visualStart<visualLimit);
-      }
-      /* visualStart==visualLimit; */
-    }
-    return NS_OK;
-  }
-}
-
-/* reorder a line based on a levels array (L2) ------------------------------ */
-
-nsresult nsBidi::ReorderLogical(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap)
-{
-  int32_t start, limit, sumOfSosEos;
-  nsBidiLevel minLevel, maxLevel;
-
-  if(aIndexMap==nullptr ||
-     !PrepareReorder(aLevels, aLength, aIndexMap, &minLevel, &maxLevel)) {
-    return NS_OK;
-  }
-
-  /* nothing to do? */
-  if(minLevel==maxLevel && (minLevel&1)==0) {
-    return NS_OK;
-  }
-
-  /* reorder only down to the lowest odd level */
-  minLevel|=1;
-
-  /* loop maxLevel..minLevel */
-  do {
-    start=0;
-
-    /* loop for all sequences of levels to reorder at the current maxLevel */
-    for(;;) {
-      /* look for a sequence of levels that are all at >=maxLevel */
-      /* look for the first index of such a sequence */
-      while(start<aLength && aLevels[start]<maxLevel) {
-        ++start;
-      }
-      if(start>=aLength) {
-        break;  /* no more such sequences */
-      }
-
-      /* look for the limit of such a sequence (the index behind it) */
-      for(limit=start; ++limit<aLength && aLevels[limit]>=maxLevel;) {}
-
-      /*
-       * sos=start of sequence, eos=end of sequence
-       *
-       * The closed (inclusive) interval from sos to eos includes all the logical
-       * and visual indexes within this sequence. They are logically and
-       * visually contiguous and in the same range.
-       *
-       * For each run, the new visual index=sos+eos-old visual index;
-       * we pre-add sos+eos into sumOfSosEos ->
-       * new visual index=sumOfSosEos-old visual index;
-       */
-      sumOfSosEos=start+limit-1;
-
-      /* reorder each index in the sequence */
-      do {
-        aIndexMap[start]=sumOfSosEos-aIndexMap[start];
-      } while(++start<limit);
-
-      /* start==limit */
-      if(limit==aLength) {
-        break;  /* no more such sequences */
-      } else {
-        start=limit+1;
-      }
-    }
-  } while(--maxLevel>=minLevel);
-
-  return NS_OK;
-}
-
-nsresult nsBidi::InvertMap(const int32_t *aSrcMap, int32_t *aDestMap, int32_t aLength)
-{
-  if(aSrcMap!=nullptr && aDestMap!=nullptr && aLength > 0) {
-    const int32_t *pi;
-    int32_t destLength = -1, count = 0;
-    /* find highest value and count positive indexes in srcMap */
-    pi = aSrcMap + aLength;
-    while (pi > aSrcMap) {
-      if (*--pi > destLength) {
-        destLength = *pi;
-      }
-      if (*pi >= 0) {
-        count++;
-      }
-    }
-    destLength++;  /* add 1 for origin 0 */
-    if (count < destLength) {
-      /* we must fill unmatched destMap entries with -1 */
-      memset(aDestMap, 0xFF, destLength * sizeof(int32_t));
-    }
-    pi = aSrcMap + aLength;
-    while (aLength > 0) {
-      if (*--pi >= 0) {
-        aDestMap[*pi] = --aLength;
-      } else {
-        --aLength;
-      }
-    }
-  }
-  return NS_OK;
-}
-
-int32_t nsBidi::doWriteReverse(const char16_t *src, int32_t srcLength,
-                               char16_t *dest, uint16_t options) {
-  /*
-   * RTL run -
-   *
-   * RTL runs need to be copied to the destination in reverse order
-   * of code points, not code units, to keep Unicode characters intact.
-   *
-   * The general strategy for this is to read the source text
-   * in backward order, collect all code units for a code point
-   * (and optionally following combining characters, see below),
-   * and copy all these code units in ascending order
-   * to the destination for this run.
-   *
-   * Several options request whether combining characters
-   * should be kept after their base characters,
-   * whether Bidi control characters should be removed, and
-   * whether characters should be replaced by their mirror-image
-   * equivalent Unicode characters.
-   */
-  int32_t i, j, destSize;
-  uint32_t c;
-
-  /* optimize for several combinations of options */
-  switch(options&(NSBIDI_REMOVE_BIDI_CONTROLS|NSBIDI_DO_MIRRORING|NSBIDI_KEEP_BASE_COMBINING)) {
-    case 0:
-        /*
-         * With none of the "complicated" options set, the destination
-         * run will have the same length as the source run,
-         * and there is no mirroring and no keeping combining characters
-         * with their base characters.
-         */
-      destSize=srcLength;
-
-    /* preserve character integrity */
-      do {
-      /* i is always after the last code unit known to need to be kept in this segment */
-        i=srcLength;
-
-      /* collect code units for one base character */
-        UTF_BACK_1(src, 0, srcLength);
-
-      /* copy this base character */
-        j=srcLength;
-        do {
-          *dest++=src[j++];
-        } while(j<i);
-      } while(srcLength>0);
-      break;
-    case NSBIDI_KEEP_BASE_COMBINING:
-    /*
-         * Here, too, the destination
-         * run will have the same length as the source run,
-         * and there is no mirroring.
-         * We do need to keep combining characters with their base characters.
-         */
-      destSize=srcLength;
-
-    /* preserve character integrity */
-      do {
-      /* i is always after the last code unit known to need to be kept in this segment */
-        i=srcLength;
-
-      /* collect code units and modifier letters for one base character */
-        do {
-          UTF_PREV_CHAR(src, 0, srcLength, c);
-        } while(srcLength>0 && GetBidiCat(c) == eCharType_DirNonSpacingMark);
-
-      /* copy this "user character" */
-        j=srcLength;
-        do {
-          *dest++=src[j++];
-        } while(j<i);
-      } while(srcLength>0);
-      break;
-    default:
-    /*
-         * With several "complicated" options set, this is the most
-         * general and the slowest copying of an RTL run.
-         * We will do mirroring, remove Bidi controls, and
-         * keep combining characters with their base characters
-         * as requested.
-         */
-      if(!(options&NSBIDI_REMOVE_BIDI_CONTROLS)) {
-        i=srcLength;
-      } else {
-      /* we need to find out the destination length of the run,
-               which will not include the Bidi control characters */