merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 27 Nov 2015 11:08:41 +0100
changeset 308566 47b49b0d32360fab04b11ff9120970979c426911
parent 308502 ad985ade8a2e5e58123f0a774605d273834c3ba1 (current diff)
parent 308565 48139ac411625d1ee8b21c51770b917502dba1d9 (diff)
child 308567 7883e81f3c305078353ca27a6b1adb8c769d5904
child 308570 bf0d6a7b6fa1374e52560dce73111e9de835100d
child 308601 bb512bf5a0669afa0d8158daf906a4223f4ba6ce
child 308652 2c0df58d9443dec00c684b00b111dfaf96613997
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.0a1
first release with
nightly linux32
47b49b0d3236 / 45.0a1 / 20151127030231 / files
nightly linux64
47b49b0d3236 / 45.0a1 / 20151127030231 / files
nightly mac
47b49b0d3236 / 45.0a1 / 20151127030231 / files
nightly win32
47b49b0d3236 / 45.0a1 / 20151127030231 / files
nightly win64
47b49b0d3236 / 45.0a1 / 20151127030231 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
testing/marionette/command.js
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/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/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/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 */
-        int32_t length=srcLength;
-        char16_t ch;
-
-        i=0;
-        do {
-          ch=*src++;
-          if (!IsBidiControl((uint32_t)ch)) {
-            ++i;
-          }
-        } while(--length>0);
-        src-=srcLength;
-      }
-      destSize=i;
-
-    /* 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_PREV_CHAR(src, 0, srcLength, c);
-        if(options&NSBIDI_KEEP_BASE_COMBINING) {
-        /* collect modifier letters for this base character */
-          while(srcLength>0 && GetBidiCat(c) == eCharType_DirNonSpacingMark) {
-            UTF_PREV_CHAR(src, 0, srcLength, c);
-          }
-        }
-
-        if(options&NSBIDI_REMOVE_BIDI_CONTROLS && IsBidiControl(c)) {
-        /* do not copy this Bidi control character */
-          continue;
-        }
-
-      /* copy this "user character" */
-        j=srcLength;
-        if(options&NSBIDI_DO_MIRRORING) {
-          /* mirror only the base character */
-          c = GetMirroredChar(c);
-
-          int32_t k=0;
-          UTF_APPEND_CHAR_UNSAFE(dest, k, c);
-          dest+=k;
-          j+=k;
-        }
-        while(j<i) {
-          *dest++=src[j++];
-        }
-      } while(srcLength>0);
-      break;
-  } /* end of switch */
-  return destSize;
-}
-
-nsresult nsBidi::WriteReverse(const char16_t *aSrc, int32_t aSrcLength, char16_t *aDest, uint16_t aOptions, int32_t *aDestSize)
-{
-  if( aSrc==nullptr || aSrcLength<0 ||
-      aDest==nullptr
-    ) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  /* do input and output overlap? */
-  if( aSrc>=aDest && aSrc<aDest+aSrcLength ||
-      aDest>=aSrc && aDest<aSrc+aSrcLength
-    ) {
-    return NS_ERROR_INVALID_ARG;
-  }
-
-  if(aSrcLength>0) {
-    *aDestSize = doWriteReverse(aSrc, aSrcLength, aDest, aOptions);
-  }
-  return NS_OK;
-}
-#endif // FULL_BIDI_ENGINE
--- a/layout/base/nsBidi.h
+++ b/layout/base/nsBidi.h
@@ -44,24 +44,16 @@
  */
 
 /**
  * nsBidiLevel is the type of the level values in this
  * Bidi implementation.
  * It holds an embedding level and indicates the visual direction
  * by its bit 0 (even/odd value).<p>
  *
- * It can also hold non-level values for the
- * <code>aParaLevel</code> and <code>aEmbeddingLevels</code>
- * arguments of <code>SetPara</code>; there:
- * <ul>
- * <li>bit 7 of an <code>aEmbeddingLevels[]</code>
- * value indicates whether the using application is
- * specifying the level of a character to <i>override</i> whatever the
- * Bidi implementation would resolve it to.</li>
  * <li><code>aParaLevel</code> can be set to the
  * pseudo-level values <code>NSBIDI_DEFAULT_LTR</code>
  * and <code>NSBIDI_DEFAULT_RTL</code>.</li></ul>
  *
  * @see nsBidi::SetPara
  *
  * <p>The related constants are not real, valid level values.
  * <code>NSBIDI_DEFAULT_XXX</code> can be used to specify
@@ -122,74 +114,38 @@ enum nsBidiDirection {
   /** All left-to-right text This is a 0 value. */
   NSBIDI_LTR,
   /** All right-to-left text This is a 1 value. */
   NSBIDI_RTL,
   /** Mixed-directional text. */
   NSBIDI_MIXED
 };
 
-typedef enum nsBidiDirection nsBidiDirection;
-
 /* miscellaneous definitions ------------------------------------------------ */
-/** option flags for WriteReverse() */
-/**
- * option bit for WriteReverse():
- * keep combining characters after their base characters in RTL runs
- *
- * @see WriteReverse
- */
-#define NSBIDI_KEEP_BASE_COMBINING       1
-
-/**
- * option bit for WriteReverse():
- * replace characters with the "mirrored" property in RTL runs
- * by their mirror-image mappings
- *
- * @see WriteReverse
- */
-#define NSBIDI_DO_MIRRORING              2
-
-/**
- * option bit for WriteReverse():
- * remove Bidi control characters
- *
- * @see WriteReverse
- */
-#define NSBIDI_REMOVE_BIDI_CONTROLS      8
 
 /* helper macros for each allocated array member */
-#define GETDIRPROPSMEMORY(length) \
-                                  GetMemory((void **)&mDirPropsMemory, &mDirPropsSize, \
-                                  mMayAllocateText, (length))
+#define GETDIRPROPSMEMORY(length) nsBidi::GetMemory((void **)&mDirPropsMemory, \
+                                                    &mDirPropsSize, \
+                                                    (length))
 
-#define GETLEVELSMEMORY(length) \
-                                GetMemory((void **)&mLevelsMemory, &mLevelsSize, \
-                                mMayAllocateText, (length))
-
-#define GETRUNSMEMORY(length) \
-                              GetMemory((void **)&mRunsMemory, &mRunsSize, \
-                              mMayAllocateRuns, (length)*sizeof(Run))
+#define GETLEVELSMEMORY(length) nsBidi::GetMemory((void **)&mLevelsMemory, \
+                                                  &mLevelsSize, \
+                                                  (length))
 
-/* additional macros used by constructor - always allow allocation */
-#define GETINITIALDIRPROPSMEMORY(length) \
-                                         GetMemory((void **)&mDirPropsMemory, &mDirPropsSize, \
-                                         true, (length))
+#define GETRUNSMEMORY(length) nsBidi::GetMemory((void **)&mRunsMemory, \
+                                                &mRunsSize, \
+                                                (length)*sizeof(Run))
 
-#define GETINITIALLEVELSMEMORY(length) \
-                                       GetMemory((void **)&mLevelsMemory, &mLevelsSize, \
-                                       true, (length))
+#define GETISOLATESMEMORY(length) nsBidi::GetMemory((void **)&mIsolatesMemory, \
+                                                    &mIsolatesSize, \
+                                                    (length)*sizeof(Isolate))
 
-#define GETINITIALRUNSMEMORY(length) \
-                                     GetMemory((void **)&mRunsMemory, &mRunsSize, \
-                                     true, (length)*sizeof(Run))
-
-#define GETINITIALISOLATESMEMORY(length) \
-                                     GetMemory((void **)&mIsolatesMemory, &mIsolatesSize, \
-                                     true, (length)*sizeof(Isolate))
+#define GETOPENINGSMEMORY(length) nsBidi::GetMemory((void **)&mOpeningsMemory, \
+                                                    &mOpeningsSize, \
+                                                    (length)*sizeof(Opening))
 
 /*
  * Sometimes, bit values are more appropriate
  * to deal with directionality properties.
  * Abbreviations in these macro names refer to names
  * used in the Bidi algorithm.
  */
 typedef uint8_t DirProp;
@@ -229,33 +185,27 @@ typedef uint8_t DirProp;
 #define MASK_EMBEDDING (DIRPROP_FLAG(NSM)|MASK_POSSIBLE_N)
 
 /* the dirProp's L and R are defined to 0 and 1 values in nsCharType */
 #define GET_LR_FROM_LEVEL(level) ((DirProp)((level)&1))
 
 #define IS_DEFAULT_LEVEL(level) (((level)&0xfe)==0xfe)
 
 /*
- * The following bit is ORed to the property of directional control
- * characters which are ignored: unmatched PDF or PDI; LRx, RLx or FSI
- * which would exceed the maximum explicit bidi level.
- */
-#define IGNORE_CC 0x40
-
-#define PURE_DIRPROP(prop) ((prop)&~IGNORE_CC)
-
-/*
  * The following bit is used for the directional isolate status.
  * Stack entries corresponding to isolate sequences are greater than ISOLATE.
  */
 #define ISOLATE 0x0100
 
 /* number of isolate entries allocated initially without malloc */
 #define SIMPLE_ISOLATES_SIZE 5
 
+/* number of isolate run entries for paired brackets allocated initially without malloc */
+#define SIMPLE_OPENINGS_COUNT 8
+
 /* handle surrogate pairs --------------------------------------------------- */
 
 #define IS_FIRST_SURROGATE(uchar) (((uchar)&0xfc00)==0xd800)
 #define IS_SECOND_SURROGATE(uchar) (((uchar)&0xfc00)==0xdc00)
 
 /* get the UTF-32 value directly from the surrogate pseudo-characters */
 #define SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000)
 #define GET_UTF_32(first, second) (((first)<<10UL)+(second)-SURROGATE_OFFSET)
@@ -385,16 +335,42 @@ typedef uint8_t DirProp;
 #define UTF_APPEND_CHAR(s, i, length, c)             UTF_APPEND_CHAR_SAFE(s, i, length, c)
 
 struct Isolate {
   int32_t start1;
   int16_t stateImp;
   int16_t state;
 };
 
+// For bracket matching
+
+#define FOUND_L DIRPROP_FLAG(L)
+#define FOUND_R DIRPROP_FLAG(R)
+
+struct Opening {
+  int32_t position;                   /* position of opening bracket */
+  int32_t match;                      /* matching char or -position of closing bracket */
+  int32_t contextPos;                 /* position of last strong char found before opening */
+  uint16_t flags;                     /* bits for L or R/AL found within the pair */
+  DirProp contextDir;                 /* L or R according to last strong char before opening */
+  uint8_t filler;                     /* to complete a nice multiple of 4 chars */
+};
+
+struct IsoRun {
+  int32_t  contextPos;                /* position of char determining context */
+  uint16_t start;                     /* index of first opening entry for this run */
+  uint16_t limit;                     /* index after last opening entry for this run */
+  nsBidiLevel level;                  /* level of this run */
+  DirProp lastStrong;                 /* bidi class of last strong char found in this run */
+  DirProp lastBase;                   /* bidi class of last base char found in this run */
+  DirProp contextDir;                 /* L or R to use as context for following openings */
+};
+
+class nsBidi;
+
 /* Run structure for reordering --------------------------------------------- */
 
 typedef struct Run {
   int32_t logicalStart;  /* first character of the run; b31 indicates even/odd level */
   int32_t visualLimit;   /* last visual position of the run +1 */
 } Run;
 
 /* in a Run, logicalStart will get this bit set if the run level is odd */
@@ -500,38 +476,18 @@ public:
    *      If the function shall determine the paragraph level from the text,
    *      then <code>aParaLevel</code> can be set to
    *      either <code>NSBIDI_DEFAULT_LTR</code>
    *      or <code>NSBIDI_DEFAULT_RTL</code>;
    *      if there is no strongly typed character, then
    *      the desired default is used (0 for LTR or 1 for RTL).
    *      Any other value between 0 and <code>NSBIDI_MAX_EXPLICIT_LEVEL</code> is also valid,
    *      with odd levels indicating RTL.
-   *
-   * @param aEmbeddingLevels (in) may be used to preset the embedding and override levels,
-   *      ignoring characters like LRE and PDF in the text.
-   *      A level overrides the directional property of its corresponding
-   *      (same index) character if the level has the
-   *      <code>NSBIDI_LEVEL_OVERRIDE</code> bit set.<p>
-   *      Except for that bit, it must be
-   *      <code>aParaLevel<=aEmbeddingLevels[]<=NSBIDI_MAX_EXPLICIT_LEVEL</code>.<p>
-   *      <strong>Caution: </strong>A copy of this pointer, not of the levels,
-   *      will be stored in the <code>nsBidi</code> object;
-   *      the <code>aEmbeddingLevels</code> array must not be
-   *      deallocated before the <code>nsBidi</code> object is destroyed or reused,
-   *      and the <code>aEmbeddingLevels</code>
-   *      should not be modified to avoid unexpected results on subsequent Bidi operations.
-   *      However, the <code>SetPara</code> and
-   *      <code>SetLine</code> functions may modify some or all of the levels.<p>
-   *      After the <code>nsBidi</code> object is reused or destroyed, the caller
-   *      must take care of the deallocation of the <code>aEmbeddingLevels</code> array.<p>
-   *      <strong>The <code>aEmbeddingLevels</code> array must be
-   *      at least <code>aLength</code> long.</strong>
    */
-  nsresult SetPara(const char16_t *aText, int32_t aLength, nsBidiLevel aParaLevel, nsBidiLevel *aEmbeddingLevels);
+  nsresult SetPara(const char16_t *aText, int32_t aLength, nsBidiLevel aParaLevel);
 
   /**
    * Get the directionality of the text.
    *
    * @param aDirection receives a <code>NSBIDI_XXX</code> value that indicates if the entire text
    *       represented by this object is unidirectional,
    *       and which direction, or if it is mixed-directional.
    *
@@ -543,83 +499,16 @@ public:
    * Get the paragraph level of the text.
    *
    * @param aParaLevel receives a <code>NSBIDI_XXX</code> value indicating the paragraph level
    *
    * @see nsBidiLevel
    */
   nsresult GetParaLevel(nsBidiLevel* aParaLevel);
 
-#ifdef FULL_BIDI_ENGINE
-  /**
-   * <code>SetLine</code> sets an <code>nsBidi</code> to
-   * contain the reordering information, especially the resolved levels,
-   * for all the characters in a line of text. This line of text is
-   * specified by referring to an <code>nsBidi</code> object representing
-   * this information for a paragraph of text, and by specifying
-   * a range of indexes in this paragraph.<p>
-   * In the new line object, the indexes will range from 0 to <code>aLimit-aStart</code>.<p>
-   *
-   * This is used after calling <code>SetPara</code>
-   * for a paragraph, and after line-breaking on that paragraph.
-   * It is not necessary if the paragraph is treated as a single line.<p>
-   *
-   * After line-breaking, rules (L1) and (L2) for the treatment of
-   * trailing WS and for reordering are performed on
-   * an <code>nsBidi</code> object that represents a line.<p>
-   *
-   * <strong>Important:</strong> the line <code>nsBidi</code> object shares data with
-   * <code>aParaBidi</code>.
-   * You must destroy or reuse this object before <code>aParaBidi</code>.
-   * In other words, you must destroy or reuse the <code>nsBidi</code> object for a line
-   * before the object for its parent paragraph.
-   *
-   * @param aParaBidi is the parent paragraph object.
-   *
-   * @param aStart is the line's first index into the paragraph text.
-   *
-   * @param aLimit is just behind the line's last index into the paragraph text
-   *      (its last index +1).<br>
-   *      It must be <code>0<=aStart<=aLimit<=</code>paragraph length.
-   *
-   * @see SetPara
-   */
-  nsresult SetLine(const nsBidi* aParaBidi, int32_t aStart, int32_t aLimit);
-
-  /**
-   * Get the length of the text.
-   *
-   * @param aLength receives the length of the text that the nsBidi object was created for.
-   */
-  nsresult GetLength(int32_t* aLength);
-
-  /**
-   * Get the level for one character.
-   *
-   * @param aCharIndex the index of a character.
-   *
-   * @param aLevel receives the level for the character at aCharIndex.
-   *
-   * @see nsBidiLevel
-   */
-  nsresult GetLevelAt(int32_t aCharIndex,  nsBidiLevel* aLevel);
-
-  /**
-   * Get an array of levels for each character.<p>
-   *
-   * Note that this function may allocate memory under some
-   * circumstances, unlike <code>GetLevelAt</code>.
-   *
-   * @param aLevels receives a pointer to the levels array for the text,
-   *       or <code>nullptr</code> if an error occurs.
-   *
-   * @see nsBidiLevel
-   */
-  nsresult GetLevels(nsBidiLevel** aLevels);
-#endif // FULL_BIDI_ENGINE
   /**
    * Get the bidirectional type for one character.
    *
    * @param aCharIndex the index of a character.
    *
    * @param aType receives the bidirectional type of the character at aCharIndex.
    */
   nsresult GetCharTypeAt(int32_t aCharIndex,  nsCharType* aType);
@@ -702,102 +591,16 @@ public:
    * @endcode
    *
    * Note that in right-to-left runs, code like this places
    * modifier letters before base characters and second surrogates
    * before first ones.
    */
   nsresult GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart, int32_t* aLength, nsBidiDirection* aDirection);
 
-#ifdef FULL_BIDI_ENGINE
-  /**
-   * Get the visual position from a logical text position.
-   * If such a mapping is used many times on the same
-   * <code>nsBidi</code> object, then calling
-   * <code>GetLogicalMap</code> is more efficient.<p>
-   *
-   * Note that in right-to-left runs, this mapping places
-   * modifier letters before base characters and second surrogates
-   * before first ones.
-   *
-   * @param aLogicalIndex is the index of a character in the text.
-   *
-   * @param aVisualIndex will receive the visual position of this character.
-   *
-   * @see GetLogicalMap
-   * @see GetLogicalIndex
-   */
-  nsresult GetVisualIndex(int32_t aLogicalIndex, int32_t* aVisualIndex);
-
-  /**
-   * Get the logical text position from a visual position.
-   * If such a mapping is used many times on the same
-   * <code>nsBidi</code> object, then calling
-   * <code>GetVisualMap</code> is more efficient.<p>
-   *
-   * This is the inverse function to <code>GetVisualIndex</code>.
-   *
-   * @param aVisualIndex is the visual position of a character.
-   *
-   * @param aLogicalIndex will receive the index of this character in the text.
-   *
-   * @see GetVisualMap
-   * @see GetVisualIndex
-   */
-  nsresult GetLogicalIndex(int32_t aVisualIndex, int32_t* aLogicalIndex);
-
-  /**
-   * Get a logical-to-visual index map (array) for the characters in the nsBidi
-   * (paragraph or line) object.
-   *
-   * @param aIndexMap is a pointer to an array of <code>GetLength</code>
-   *      indexes which will reflect the reordering of the characters.
-   *      The array does not need to be initialized.<p>
-   *      The index map will result in <code>aIndexMap[aLogicalIndex]==aVisualIndex</code>.<p>
-   *