merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Tue, 26 Sep 2017 11:54:34 +0200
changeset 433627 bc56729898954e32d3a3731d03d178ed78924c33
parent 433596 6fb1f5fae37168c824b988733aac0d46499b6c11 (current diff)
parent 433626 5d1b6657fdb015ad795a6f720b7c28f11bf142d0 (diff)
child 433628 e3f4b5f57a29f11da2f697365d32163730862565
child 433669 422cd683ccaf40b0f469fb15e3c344bb3a06590e
child 433734 f41e4ba17689198d1f697af30ef3f519fa116105
push id8114
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 16:33:21 +0000
treeherdermozilla-beta@73e0d89a540f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.0a1
first release with
nightly linux32
bc5672989895 / 58.0a1 / 20170926100259 / files
nightly linux64
bc5672989895 / 58.0a1 / 20170926100259 / files
nightly mac
bc5672989895 / 58.0a1 / 20170926100259 / files
nightly win32
bc5672989895 / 58.0a1 / 20170926100259 / files
nightly win64
bc5672989895 / 58.0a1 / 20170926100259 / 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. r=merge a=merge MozReview-Commit-ID: 4W52wcX8xBm
browser/themes/linux/places/calendar.png
browser/themes/linux/places/query.png
browser/themes/osx/page-livemarks@2x.png
browser/themes/osx/places/history.png
browser/themes/osx/places/history@2x.png
browser/themes/osx/places/query.png
browser/themes/osx/places/query@2x.png
browser/themes/shared/icons/toolbar.svg
browser/themes/windows/livemark-folder.png
browser/themes/windows/places/calendar.png
browser/themes/windows/places/query.png
parser/html/nsHtml5Atom.cpp
parser/html/nsHtml5Atom.h
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2845,18 +2845,20 @@ var JawsScreenReaderVersionCheck = {
     }
   },
 
   onWindowsRestored() {
     Services.tm.dispatchToMainThread(() => this._checkVersionAndPrompt());
   },
 
   _checkVersionAndPrompt() {
-    // This executes a JAWS version check.
-    if (!Services.appinfo.shouldBlockIncompatJaws) {
+    // Make sure we only prompt for versions of JAWS we do not
+    // support and never prompt if e10s is disabled.
+    if (!Services.appinfo.shouldBlockIncompatJaws ||
+        !Services.appinfo.browserTabsRemoteAutostart) {
       return;
     }
 
     let win = RecentWindow.getMostRecentBrowserWindow();
     if (!win || !win.gBrowser || !win.gBrowser.selectedBrowser) {
       Services.console.logStringMessage(
           "Content access support for older versions of JAWS is disabled " +
           "due to compatibility issues with this version of Firefox.");
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -119,58 +119,16 @@ menuitem.bookmark-item {
   background-color: Highlight;
 }
 
 /* Bookmarks toolbar */
 #PlacesToolbarDropIndicator {
   list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
 }
 
-/* Bookmark items */
-.bookmark-item {
-  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
-}
-
-.bookmark-item[container] {
-  list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
-}
-
-.bookmark-item[container][livemark] {
-  list-style-image: url("chrome://browser/skin/feeds/feedIcon16.png");
-}
-
-.bookmark-item[container][livemark] .bookmark-item {
-  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-
-.bookmark-item[container][livemark] .bookmark-item[visited] {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-
-.bookmark-item[container][query] {
-  list-style-image: url("chrome://browser/skin/places/query.png");
-}
-
-.bookmark-item[query][tagContainer] {
-  list-style-image: url("chrome://browser/skin/places/tag.png");
-}
-
-.bookmark-item[query][dayContainer] {
-  list-style-image: url("chrome://browser/skin/places/calendar.png");
-}
-
-.bookmark-item[query][hostContainer] {
-  list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
-}
-
-.bookmark-item[query][hostContainer][open] {
-  list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
-}
-
 .bookmark-item[cutting] > .toolbarbutton-icon,
 .bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
   opacity: 0.5;
 }
 
 .bookmark-item[cutting] > .toolbarbutton-text,
 .bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
   opacity: 0.7;
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -36,24 +36,22 @@ browser.jar:
 * skin/classic/browser/newtab/newTab.css              (newtab/newTab.css)
   skin/classic/browser/notification-icons/geo-blocked.svg  (notification-icons/geo-blocked.svg)
   skin/classic/browser/notification-icons/geo-detailed.svg (notification-icons/geo-detailed.svg)
   skin/classic/browser/notification-icons/geo.svg          (notification-icons/geo.svg)
   skin/classic/browser/places/allBookmarks.png        (places/allBookmarks.png)
   skin/classic/browser/places/bookmarksMenu.png       (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png    (places/bookmarksToolbar.png)
   skin/classic/browser/places/bookmarks-menu-arrow.png           (places/bookmarks-menu-arrow.png)
-  skin/classic/browser/places/calendar.png            (places/calendar.png)
 * skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
   skin/classic/browser/places/livemark-item.png       (places/livemark-item.png)
   skin/classic/browser/places/starred48.png           (places/starred48.png)
 * skin/classic/browser/places/places.css              (places/places.css)
   skin/classic/browser/places/organizer.css           (places/organizer.css)
   skin/classic/browser/places/organizer.xml           (places/organizer.xml)
-  skin/classic/browser/places/query.png               (places/query.png)
   skin/classic/browser/places/tag.png                 (places/tag.png)
   skin/classic/browser/places/toolbarDropMarker.png   (places/toolbarDropMarker.png)
   skin/classic/browser/places/unsortedBookmarks.png   (places/unsortedBookmarks.png)
   skin/classic/browser/places/downloads.png           (places/downloads.png)
   skin/classic/browser/preferences/alwaysAsk.png      (preferences/alwaysAsk.png)
   skin/classic/browser/preferences/preferences.css    (preferences/preferences.css)
 * skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
 * skin/classic/browser/preferences/in-content/dialog.css      (preferences/in-content/dialog.css)
deleted file mode 100644
index f7128685c0dd4e836aab11cbe861c2fb5ec17fde..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 2420dee0e8a4eef6e2b8cf9245d03514d7e37933..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -181,89 +181,16 @@
 
 /* ----- BOOKMARK TOOLBAR ----- */
 
 #nav-bar-customization-target > #wrapper-personal-bookmarks > #personal-bookmarks {
   min-height: 32px;
   -moz-box-align: center;
 }
 
-/* ----- BOOKMARK BUTTONS ----- */
-
-.bookmark-item[container] {
-  list-style-image: url("chrome://global/skin/tree/folder.png");
-}
-
-.bookmark-item[container][livemark] {
-  list-style-image: url("chrome://browser/skin/page-livemarks.png");
-}
-
-.bookmark-item[container][livemark] .bookmark-item {
-  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-
-.bookmark-item[container][livemark] .bookmark-item[visited] {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-
-.bookmark-item[container][query] {
-  list-style-image: url("chrome://browser/skin/places/query.png");
-}
-
-.bookmark-item[query][tagContainer] {
-  list-style-image: url("chrome://browser/skin/places/tag.png");
-}
-
-.bookmark-item[query][dayContainer] {
-  list-style-image: url("chrome://browser/skin/places/history.png");
-}
-
-.bookmark-item[query][hostContainer] {
-  list-style-image: url("chrome://global/skin/tree/folder.png");
-}
-
-.bookmark-item[query][hostContainer][open] {
-  list-style-image: url("chrome://global/skin/tree/folder.png");
-}
-
-@media (min-resolution: 2dppx) {
-  .bookmark-item[container] {
-    list-style-image: url("chrome://global/skin/tree/folder@2x.png");
-  }
-
-  .bookmark-item[container][livemark] {
-    list-style-image: url("chrome://browser/skin/page-livemarks@2x.png");
-  }
-
-  .bookmark-item[container][livemark] .bookmark-item {
-    list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-  }
-
-  .bookmark-item[container][query] {
-    list-style-image: url("chrome://browser/skin/places/query@2x.png");
-  }
-
-  .bookmark-item[query][tagContainer] {
-    list-style-image: url("chrome://browser/skin/places/tag@2x.png");
-  }
-
-  .bookmark-item[query][dayContainer] {
-    list-style-image: url("chrome://browser/skin/places/history@2x.png");
-  }
-
-  .bookmark-item[query][hostContainer] {
-    list-style-image: url("chrome://global/skin/tree/folder@2x.png");
-  }
-
-  .bookmark-item[query][hostContainer][open] {
-    list-style-image: url("chrome://global/skin/tree/folder@2x.png");
-  }
-}
-
 /* Workaround for native menubar inheritance */
 .openintabs-menuitem,
 .openlivemarksite-menuitem,
 .livemarkstatus-menuitem {
   list-style-image: none;
 }
 
 .bookmark-item[cutting] > .toolbarbutton-icon,
@@ -824,20 +751,16 @@ html|span.ac-emphasize-text-url {
 
 .browserContainer > findbar {
   background: @scopeBarBackground@;
   border-top: @scopeBarSeparatorBorder@;
   color: -moz-DialogText;
   text-shadow: none;
 }
 
-.bookmark-item {
-  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
-}
-
 .openintabs-menuitem {
   list-style-image: none;
 }
 
 /* ::::: tabbrowser ::::: */
 
 .tabbrowser-tabbox {
   margin: 0;
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -18,17 +18,16 @@ browser.jar:
   skin/classic/browser/menuPanel-help.png
   skin/classic/browser/menuPanel-help@2x.png
   skin/classic/browser/panel-expander-closed.png
   skin/classic/browser/panel-expander-closed@2x.png
   skin/classic/browser/panel-expander-open.png
   skin/classic/browser/panel-expander-open@2x.png
   skin/classic/browser/panel-plus-sign.png
   skin/classic/browser/page-livemarks.png
-  skin/classic/browser/page-livemarks@2x.png
   skin/classic/browser/pageInfo.css
   skin/classic/browser/searchbar.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/toolbarbutton-dropmarker.png
   skin/classic/browser/toolbarbutton-dropmarker@2x.png
   skin/classic/browser/webRTC-indicator.css
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
 * skin/classic/browser/customizableui/panelUI.css    (customizableui/panelUI.css)
@@ -41,23 +40,19 @@ browser.jar:
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
   skin/classic/browser/notification-icons/geo-blocked.svg  (notification-icons/geo-blocked.svg)
   skin/classic/browser/notification-icons/geo.svg          (notification-icons/geo.svg)
   skin/classic/browser/places/allBookmarks.png              (places/allBookmarks.png)
 * skin/classic/browser/places/places.css                    (places/places.css)
   skin/classic/browser/places/organizer.css                 (places/organizer.css)
-  skin/classic/browser/places/query.png                     (places/query.png)
-  skin/classic/browser/places/query@2x.png                  (places/query@2x.png)
   skin/classic/browser/places/bookmarksMenu.png             (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png          (places/bookmarksToolbar.png)
   skin/classic/browser/places/bookmarksToolbar@2x.png       (places/bookmarksToolbar@2x.png)
-  skin/classic/browser/places/history.png                   (places/history.png)
-  skin/classic/browser/places/history@2x.png                (places/history@2x.png)
   skin/classic/browser/places/toolbar.png                   (places/toolbar.png)
   skin/classic/browser/places/toolbarDropMarker.png         (places/toolbarDropMarker.png)
   skin/classic/browser/places/editBookmarkOverlay.css       (places/editBookmarkOverlay.css)
   skin/classic/browser/places/starred48.png                 (places/starred48.png)
   skin/classic/browser/places/starred48@2x.png              (places/starred48@2x.png)
   skin/classic/browser/places/unfiledBookmarks.png          (places/unfiledBookmarks.png)
   skin/classic/browser/places/unfiledBookmarks@2x.png       (places/unfiledBookmarks@2x.png)
   skin/classic/browser/places/tag.png                       (places/tag.png)
deleted file mode 100644
index 7b170897cc04d914e4163b915d155b804a45625c..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index e5a00b56d38ca5af9b9d912e6ee2121ebfc90bbf..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 684b374ff5732cfa75ec8d81201874eaaef679a9..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 0ccb8470256d32b201cfb508c7276310b7d7e46b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 20b458aaca5ec3f06510c9d12ea1ffb70a48fe67..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
rename from browser/themes/shared/icons/toolbar.svg
rename to browser/themes/shared/icons/bookmarks-toolbar.svg
--- a/browser/themes/shared/icons/toolbar.svg
+++ b/browser/themes/shared/icons/bookmarks-toolbar.svg
@@ -1,6 +1,7 @@
 <!-- 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/. -->
 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
-  <path fill="context-fill" fill-opacity="context-fill-opacity" d="M13 1H3a3.007 3.007 0 0 0-3 3v8a3.009 3.009 0 0 0 3 3h10a3.005 3.005 0 0 0 3-3V4a3.012 3.012 0 0 0-3-3zM3 3h10a1 1 0 0 1 1 1v1H2V4a1 1 0 0 1 1-1zm11 3v1H2V6zm-1 7H3a1 1 0 0 1-1-1V8h12v4a1 1 0 0 1-1 1z"/>
+  <path d="M13 1H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h11a2 2 0 0 0 2-2V4a3 3 0 0 0-3-3zm1 11a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1z"/>
+  <path d="M9.466 6.269l-.964-1.934-.185-.305-.662.028-1.101 2.211-2.039.364-.329.084-.184.613L5.67 9.123 5.365 11.3l-.023.351.552.356 2.116-1.102 1.844.96.319.138.525-.395-.347-2.485 1.462-1.573.214-.268-.227-.596-2.334-.417z"/>
 </svg>
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -165,17 +165,17 @@
   skin/classic/browser/save.svg                       (../shared/icons/save.svg)
   skin/classic/browser/settings.svg                   (../shared/icons/settings.svg)
   skin/classic/browser/sidebars.svg                   (../shared/icons/sidebars.svg)
   skin/classic/browser/sidebars-right.svg             (../shared/icons/sidebars-right.svg)
   skin/classic/browser/stop.svg                       (../shared/icons/stop.svg)
   skin/classic/browser/stop-to-reload.svg             (../shared/icons/stop-to-reload.svg)
   skin/classic/browser/sync.svg                       (../shared/icons/sync.svg)
   skin/classic/browser/synced-tabs.svg                (../shared/icons/synced-tabs.svg)
-  skin/classic/browser/toolbar.svg                    (../shared/icons/toolbar.svg)
+  skin/classic/browser/bookmarks-toolbar.svg          (../shared/icons/bookmarks-toolbar.svg)
   skin/classic/browser/webIDE.svg                     (../shared/icons/webIDE.svg)
   skin/classic/browser/window.svg                     (../shared/icons/window.svg)
   skin/classic/browser/zoom-in.svg                    (../shared/icons/zoom-in.svg)
   skin/classic/browser/zoom-out.svg                   (../shared/icons/zoom-out.svg)
 
 
   skin/classic/browser/search-indicator.png                    (../shared/search/search-indicator.png)
   skin/classic/browser/search-indicator@2x.png                 (../shared/search/search-indicator@2x.png)
--- a/browser/themes/shared/menupanel.inc.css
+++ b/browser/themes/shared/menupanel.inc.css
@@ -92,17 +92,17 @@
   list-style-image: url("chrome://browser/skin/sidebars.svg");
 }
 
 #panelMenu_bookmarkingTools {
   list-style-image: url("chrome://browser/skin/developer.svg");
 }
 
 #panelMenu_viewBookmarksToolbar {
-  list-style-image: url("chrome://browser/skin/toolbar.svg");
+  list-style-image: url("chrome://browser/skin/bookmarks-toolbar.svg");
 }
 
 #appMenu-library-bookmarks-button,
 #panelMenuBookmarkThisPage {
   list-style-image: url("chrome://browser/skin/bookmark-hollow.svg");
 }
 
 #panelMenuBookmarkThisPage[starred] {
--- a/browser/themes/shared/toolbarbutton-icons.inc.css
+++ b/browser/themes/shared/toolbarbutton-icons.inc.css
@@ -181,17 +181,17 @@ toolbar[brighttext] {
 }
 
 #home-button {
   list-style-image: url("chrome://browser/skin/home.svg");
 }
 
 #bookmarks-toolbar-button,
 #bookmarks-toolbar-placeholder {
-  list-style-image: url("chrome://browser/skin/bookmark-hollow.svg");
+  list-style-image: url("chrome://browser/skin/bookmarks-toolbar.svg");
 }
 
 #bookmarks-menu-button {
   list-style-image: url("chrome://browser/skin/bookmark-star-on-tray.svg");
 }
 
 #history-panelmenu {
   list-style-image: url("chrome://browser/skin/history.svg");
@@ -516,8 +516,59 @@ toolbar[brighttext] {
   animation-name: library-bookmark-animation-rtl;
 }
 
 .toolbarbutton-animatable-box[animate="bookmark"][fade] > .toolbarbutton-animatable-image {
   animation-name: library-bookmark-fade;
   animation-duration: 2s;
   animation-timing-function: ease-out;
 }
+
+/* ----- BOOKMARK BUTTONS ----- */
+
+.bookmark-item {
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
+  -moz-context-properties: fill;
+  fill: currentColor;
+}
+
+.bookmark-item[container] {
+  list-style-image: url("chrome://browser/skin/places/folder.svg");
+}
+
+.bookmark-item[container][livemark] {
+  list-style-image: url("chrome://browser/skin/places/folder-live.svg");
+}
+
+.bookmark-item[container][livemark] .bookmark-item {
+  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
+  -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.bookmark-item[container][livemark] .bookmark-item[visited] {
+  -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+.bookmark-item[container][query] {
+  list-style-image: url("chrome://browser/skin/places/folder-smart.svg");
+}
+
+.bookmark-item[query][tagContainer] {
+  list-style-image: url("chrome://browser/skin/places/tag.png");
+  -moz-image-region: auto;
+}
+
+.bookmark-item[query][dayContainer] {
+  list-style-image: url("chrome://browser/skin/places/history.svg");
+  -moz-image-region: auto;
+}
+
+.bookmark-item[query][hostContainer] {
+  list-style-image: url("chrome://browser/skin/places/folder.svg");
+}
+
+%ifdef XP_MACOSX
+@media (min-resolution: 2dppx) {
+  .bookmark-item[query][tagContainer] {
+    list-style-image: url("chrome://browser/skin/places/tag@2x.png");
+  }
+}
+%endif
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -335,70 +335,16 @@ menuitem.bookmark-item {
   margin-top: 0;
   margin-bottom: 0;
 }
 
 .bookmark-item > .menu-iconic-left > .menu-iconic-icon {
   padding-inline-start: 0px;
 }
 
-/* ::::: bookmark items ::::: */
-
-.bookmark-item  {
-  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.svg");
-}
-
-.bookmark-item[container] {
-  list-style-image: url("chrome://global/skin/icons/folder-item.png");
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-
-.bookmark-item[container][open] {
-  -moz-image-region: rect(16px, 32px, 32px, 16px);
-}
-
-.bookmark-item[container][livemark] {
-  list-style-image: url("chrome://browser/skin/livemark-folder.png");
-  -moz-image-region: auto;
-}
-
-.bookmark-item[container][livemark] .bookmark-item {
-  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-
-.bookmark-item[container][livemark] .bookmark-item[visited] {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-
-.bookmark-item[container][query] {
-  list-style-image: url("chrome://browser/skin/places/query.png");
-  -moz-image-region: auto;
-}
-
-.bookmark-item[query][tagContainer] {
-  list-style-image: url("chrome://browser/skin/places/tag.png");
-  -moz-image-region: auto;
-}
-
-.bookmark-item[query][dayContainer] {
-  list-style-image: url("chrome://browser/skin/places/calendar.png");
-  -moz-image-region: auto;
-}
-
-.bookmark-item[query][hostContainer] {
-  list-style-image: url("chrome://global/skin/icons/folder-item.png");
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-
-.bookmark-item[query][hostContainer][open] {
-  list-style-image: url("chrome://global/skin/icons/folder-item.png");
-  -moz-image-region: rect(16px, 32px, 32px, 16px);
-}
-
 .bookmark-item[cutting] > .toolbarbutton-icon,
 .bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
   opacity: 0.5;
 }
 
 .bookmark-item[cutting] > .toolbarbutton-text,
 .bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
   opacity: 0.7;
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -5,17 +5,16 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 #include ../shared/jar.inc.mn
   skin/classic/browser/sanitizeDialog.css
   skin/classic/browser/aboutSessionRestore-window-icon.png
 * skin/classic/browser/syncedtabs/sidebar.css     (syncedtabs/sidebar.css)
 * skin/classic/browser/browser.css
 * skin/classic/browser/compacttheme.css
-  skin/classic/browser/livemark-folder.png
   skin/classic/browser/menuPanel-customize.png
   skin/classic/browser/menuPanel-customize@2x.png
   skin/classic/browser/menuPanel-exit.png
   skin/classic/browser/menuPanel-exit@2x.png
   skin/classic/browser/menuPanel-help.png
   skin/classic/browser/menuPanel-help@2x.png
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
@@ -37,20 +36,18 @@ browser.jar:
   skin/classic/browser/feeds/feedIcon16.png                    (feeds/feedIcon16.png)
   skin/classic/browser/feeds/subscribe.css                     (feeds/subscribe.css)
 * skin/classic/browser/newtab/newTab.css                       (newtab/newTab.css)
   skin/classic/browser/notification-icons/geo-blocked.svg      (notification-icons/geo-blocked.svg)
   skin/classic/browser/notification-icons/geo-detailed.svg     (notification-icons/geo-detailed.svg)
   skin/classic/browser/notification-icons/geo.svg              (notification-icons/geo.svg)
 * skin/classic/browser/places/places.css                       (places/places.css)
 * skin/classic/browser/places/organizer.css                    (places/organizer.css)
-  skin/classic/browser/places/query.png                        (places/query.png)
   skin/classic/browser/places/bookmarksMenu.png                (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png             (places/bookmarksToolbar.png)
-  skin/classic/browser/places/calendar.png                     (places/calendar.png)
   skin/classic/browser/places/toolbarDropMarker.png            (places/toolbarDropMarker.png)
   skin/classic/browser/places/editBookmarkOverlay.css          (places/editBookmarkOverlay.css)
   skin/classic/browser/places/libraryToolbar.png               (places/libraryToolbar.png)
   skin/classic/browser/places/starred48.png                    (places/starred48.png)
   skin/classic/browser/places/tag.png                          (places/tag.png)
   skin/classic/browser/places/allBookmarks.png                 (places/allBookmarks.png)
   skin/classic/browser/places/unsortedBookmarks.png            (places/unsortedBookmarks.png)
   skin/classic/browser/places/downloads.png                    (places/downloads.png)
deleted file mode 100644
index 0be774e636aa662e5ed9f542030d1abf1aac1d3b..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 4127e59880945cc3d5ff68fd278615e0c385125e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 18b65bd71a4c0ce3dca6482a241f8018c5fa9f97..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/devtools/server/actors/webconsole/utils.js
+++ b/devtools/server/actors/webconsole/utils.js
@@ -303,29 +303,40 @@ exports.WebConsoleCommands = WebConsoleC
  * Find a node by ID.
  *
  * @param string id
  *        The ID of the element you want.
  * @return nsIDOMNode or null
  *         The result of calling document.querySelector(selector).
  */
 WebConsoleCommands._registerOriginal("$", function (owner, selector) {
-  return owner.window.document.querySelector(selector);
+  try {
+    return owner.window.document.querySelector(selector);
+  } catch (err) {
+    // Throw an error like `err` but that belongs to `owner.window`.
+    throw new owner.window.DOMException(err.message, err.name);
+  }
 });
 
 /**
  * Find the nodes matching a CSS selector.
  *
  * @param string selector
  *        A string that is passed to window.document.querySelectorAll.
  * @return nsIDOMNodeList
  *         Returns the result of document.querySelectorAll(selector).
  */
 WebConsoleCommands._registerOriginal("$$", function (owner, selector) {
-  let nodes = owner.window.document.querySelectorAll(selector);
+  let nodes;
+  try {
+    nodes = owner.window.document.querySelectorAll(selector);
+  } catch (err) {
+    // Throw an error like `err` but that belongs to `owner.window`.
+    throw new owner.window.DOMException(err.message, err.name);
+  }
 
   // Calling owner.window.Array.from() doesn't work without accessing the
   // wrappedJSObject, so just loop through the results instead.
   let result = new owner.window.Array();
   for (let i = 0; i < nodes.length; i++) {
     result.push(nodes[i]);
   }
   return result;
--- a/devtools/shared/webconsole/test/test_jsterm_queryselector.html
+++ b/devtools/shared/webconsole/test/test_jsterm_queryselector.html
@@ -29,16 +29,17 @@ function startTest() {
 
   attachConsoleToTab([], state => {
     gState = state;
     let tests = [
       setupWindow,
       checkQuerySelector,
       checkQuerySelectorAll,
       checkQuerySelectorAllNotExist,
+      checkQuerySelectorException,
       checkQuerySelectorAllException
     ];
     runTests(tests, testEnd);
   });
 }
 
 let setupWindow = Task.async(function*() {
   info ("Shimming window functions for the content privileged tab");
@@ -82,23 +83,44 @@ let checkQuerySelectorAllNotExist = Task
     class: "Array",
     preview: {
       length: 0
     }
   });
   nextTest();
 });
 
+let checkQuerySelectorException = Task.async(function*() {
+  info ("$ returns an exception if an invalid selector was provided");
+  let response = yield evaluateJS("$(':foo')");
+  checkObject(response, {
+    input: "$(':foo')",
+    exceptionMessage: "SyntaxError: ':foo' is not a valid selector",
+    exception: {
+      type: "object",
+      class: "DOMException",
+      preview: {
+        kind: "DOMException",
+        name: "SyntaxError",
+        message: "':foo' is not a valid selector"
+      }
+    }
+  });
+  nextTest();
+});
+
 let checkQuerySelectorAllException = Task.async(function*() {
   info ("$$ returns an exception if an invalid selector was provided");
   let response = yield evaluateJS("$$(':foo')");
   checkObject(response, {
     input: "$$(':foo')",
     exceptionMessage: "SyntaxError: ':foo' is not a valid selector",
     exception: {
+      type: "object",
+      class: "DOMException",
       preview: {
         kind: "DOMException",
         name: "SyntaxError",
         message: "':foo' is not a valid selector"
       }
     }
   });
   nextTest();
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -31,18 +31,30 @@ CustomElementCallback::Call()
       // this now in order to enqueue the attached callback. This is a spec
       // bug (w3c bug 27437).
       mOwnerData->mCreatedCallbackInvoked = true;
 
       // If ELEMENT is in a document and this document has a browsing context,
       // enqueue attached callback for ELEMENT.
       nsIDocument* document = mThisObject->GetComposedDoc();
       if (document && document->GetDocShell()) {
+        NodeInfo* ni = mThisObject->NodeInfo();
+        nsDependentAtomString extType(mOwnerData->mType);
+
+        // We need to do this because at this point, CustomElementDefinition is
+        // not set to CustomElementData yet, so EnqueueLifecycleCallback will
+        // fail to find the CE definition for this custom element.
+        // This will go away eventually since there is no created callback in v1.
+        CustomElementDefinition* definition =
+          nsContentUtils::LookupCustomElementDefinition(document,
+            ni->LocalName(), ni->NamespaceID(),
+            extType.IsEmpty() ? nullptr : &extType);
+
         nsContentUtils::EnqueueLifecycleCallback(
-          document, nsIDocument::eAttached, mThisObject);
+          document, nsIDocument::eAttached, mThisObject, nullptr, definition);
       }
 
       static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
       mOwnerData->mElementIsBeingCreated = false;
       break;
     }
     case nsIDocument::eAttached:
       static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
@@ -153,56 +165,25 @@ private:
 };
 
 } // namespace anonymous
 
 // Only needed for refcounted objects.
 NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementRegistry)
-  tmp->mCustomDefinitions.Clear();
   tmp->mConstructors.clear();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomDefinitions)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWhenDefinedPromiseMap)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry)
-  for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
-    auto& callbacks = iter.UserData()->mCallbacks;
-
-    if (callbacks->mAttributeChangedCallback.WasPassed()) {
-      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
-        "mCustomDefinitions->mCallbacks->mAttributeChangedCallback");
-      cb.NoteXPCOMChild(callbacks->mAttributeChangedCallback.Value());
-    }
-
-    if (callbacks->mCreatedCallback.WasPassed()) {
-      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
-        "mCustomDefinitions->mCallbacks->mCreatedCallback");
-      cb.NoteXPCOMChild(callbacks->mCreatedCallback.Value());
-    }
-
-    if (callbacks->mAttachedCallback.WasPassed()) {
-      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
-        "mCustomDefinitions->mCallbacks->mAttachedCallback");
-      cb.NoteXPCOMChild(callbacks->mAttachedCallback.Value());
-    }
-
-    if (callbacks->mDetachedCallback.WasPassed()) {
-      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
-        "mCustomDefinitions->mCallbacks->mDetachedCallback");
-      cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
-    }
-
-    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
-      "mCustomDefinitions->mConstructor");
-    cb.NoteXPCOMChild(iter.UserData()->mConstructor);
-  }
-
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomDefinitions)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementRegistry)
   for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) {
     aCallbacks.Trace(&iter.UserData()->mPrototype,
                      "mCustomDefinitions prototype",
@@ -243,17 +224,17 @@ CustomElementRegistry::~CustomElementReg
 
 CustomElementDefinition*
 CustomElementRegistry::LookupCustomElementDefinition(const nsAString& aLocalName,
                                                      const nsAString* aIs) const
 {
   nsCOMPtr<nsIAtom> localNameAtom = NS_Atomize(aLocalName);
   nsCOMPtr<nsIAtom> typeAtom = aIs ? NS_Atomize(*aIs) : localNameAtom;
 
-  CustomElementDefinition* data = mCustomDefinitions.Get(typeAtom);
+  CustomElementDefinition* data = mCustomDefinitions.GetWeak(typeAtom);
   if (data && data->mLocalName == localNameAtom) {
     return data;
   }
 
   return nullptr;
 }
 
 CustomElementDefinition*
@@ -262,17 +243,17 @@ CustomElementRegistry::LookupCustomEleme
 {
   JS::Rooted<JSObject*> constructor(aCx, js::CheckedUnwrap(aConstructor));
 
   const auto& ptr = mConstructors.lookup(constructor);
   if (!ptr) {
     return nullptr;
   }
 
-  CustomElementDefinition* definition = mCustomDefinitions.Get(ptr->value());
+  CustomElementDefinition* definition = mCustomDefinitions.GetWeak(ptr->value());
   MOZ_ASSERT(definition, "Definition must be found in mCustomDefinitions");
 
   return definition;
 }
 
 void
 CustomElementRegistry::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
 {
@@ -281,17 +262,17 @@ CustomElementRegistry::RegisterUnresolve
   // Candidate may be a custom element through extension,
   // in which case the custom element type name will not
   // match the element tag name. e.g. <button is="x-button">.
   nsCOMPtr<nsIAtom> typeName = aTypeName;
   if (!typeName) {
     typeName = info->NameAtom();
   }
 
-  if (mCustomDefinitions.Get(typeName)) {
+  if (mCustomDefinitions.GetWeak(typeName)) {
     return;
   }
 
   nsTArray<nsWeakPtr>* unresolved = mCandidatesMap.LookupOrAdd(typeName);
   nsWeakPtr* elem = unresolved->AppendElement();
   *elem = do_GetWeakReference(aElement);
   aElement->AddStates(NS_EVENT_STATE_UNRESOLVED);
 }
@@ -340,61 +321,45 @@ CustomElementRegistry::SetupCustomElemen
   SyncInvokeReactions(nsIDocument::eCreated, aElement, definition);
 }
 
 UniquePtr<CustomElementCallback>
 CustomElementRegistry::CreateCustomElementCallback(
   nsIDocument::ElementCallbackType aType, Element* aCustomElement,
   LifecycleCallbackArgs* aArgs, CustomElementDefinition* aDefinition)
 {
+  MOZ_ASSERT(aDefinition, "CustomElementDefinition should not be null");
+
   RefPtr<CustomElementData> elementData = aCustomElement->GetCustomElementData();
   MOZ_ASSERT(elementData, "CustomElementData should exist");
 
-  // Let DEFINITION be ELEMENT's definition
-  CustomElementDefinition* definition = aDefinition;
-  if (!definition) {
-    mozilla::dom::NodeInfo* info = aCustomElement->NodeInfo();
-
-    // Make sure we get the correct definition in case the element
-    // is a extended custom element e.g. <button is="x-button">.
-    nsCOMPtr<nsIAtom> typeAtom = elementData ?
-      elementData->mType.get() : info->NameAtom();
-
-    definition = mCustomDefinitions.Get(typeAtom);
-    if (!definition || definition->mLocalName != info->NameAtom()) {
-      // Trying to enqueue a callback for an element that is not
-      // a custom element. We are done, nothing to do.
-      return nullptr;
-    }
-  }
-
   // Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
   CallbackFunction* func = nullptr;
   switch (aType) {
     case nsIDocument::eCreated:
-      if (definition->mCallbacks->mCreatedCallback.WasPassed()) {
-        func = definition->mCallbacks->mCreatedCallback.Value();
+      if (aDefinition->mCallbacks->mCreatedCallback.WasPassed()) {
+        func = aDefinition->mCallbacks->mCreatedCallback.Value();
       }
       break;
 
     case nsIDocument::eAttached:
-      if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
-        func = definition->mCallbacks->mAttachedCallback.Value();
+      if (aDefinition->mCallbacks->mAttachedCallback.WasPassed()) {
+        func = aDefinition->mCallbacks->mAttachedCallback.Value();
       }
       break;
 
     case nsIDocument::eDetached:
-      if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
-        func = definition->mCallbacks->mDetachedCallback.Value();
+      if (aDefinition->mCallbacks->mDetachedCallback.WasPassed()) {
+        func = aDefinition->mCallbacks->mDetachedCallback.Value();
       }
       break;
 
     case nsIDocument::eAttributeChanged:
-      if (definition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
-        func = definition->mCallbacks->mAttributeChangedCallback.Value();
+      if (aDefinition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
+        func = aDefinition->mCallbacks->mAttributeChangedCallback.Value();
       }
       break;
   }
 
   // If there is no such callback, stop.
   if (!func) {
     return nullptr;
   }
@@ -440,31 +405,21 @@ CustomElementRegistry::SyncInvokeReactio
 }
 
 void
 CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
                                                 Element* aCustomElement,
                                                 LifecycleCallbackArgs* aArgs,
                                                 CustomElementDefinition* aDefinition)
 {
-  RefPtr<CustomElementData> elementData = aCustomElement->GetCustomElementData();
-  MOZ_ASSERT(elementData, "CustomElementData should exist");
-
-  // Let DEFINITION be ELEMENT's definition
   CustomElementDefinition* definition = aDefinition;
   if (!definition) {
-    mozilla::dom::NodeInfo* info = aCustomElement->NodeInfo();
-
-    // Make sure we get the correct definition in case the element
-    // is a extended custom element e.g. <button is="x-button">.
-    nsCOMPtr<nsIAtom> typeAtom = elementData ?
-      elementData->mType.get() : info->NameAtom();
-
-    definition = mCustomDefinitions.Get(typeAtom);
-    if (!definition || definition->mLocalName != info->NameAtom()) {
+    definition = aCustomElement->GetCustomElementDefinition();
+    if (!definition ||
+        definition->mLocalName != aCustomElement->NodeInfo()->NameAtom()) {
       return;
     }
   }
 
   auto callback =
     CreateCustomElementCallback(aType, aCustomElement, aArgs, definition);
   if (!callback) {
     return;
@@ -488,17 +443,18 @@ CustomElementRegistry::EnqueueLifecycleC
   reactionsStack->EnqueueCallbackReaction(this, aCustomElement, definition,
                                           Move(callback));
 }
 
 void
 CustomElementRegistry::GetCustomPrototype(nsIAtom* aAtom,
                                           JS::MutableHandle<JSObject*> aPrototype)
 {
-  mozilla::dom::CustomElementDefinition* definition = mCustomDefinitions.Get(aAtom);
+  mozilla::dom::CustomElementDefinition* definition =
+    mCustomDefinitions.GetWeak(aAtom);
   if (definition) {
     aPrototype.set(definition->mPrototype);
   } else {
     aPrototype.set(nullptr);
   }
 }
 
 void
@@ -626,28 +582,28 @@ CustomElementRegistry::Define(const nsAS
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     return;
   }
 
   /**
    * 3. If this CustomElementRegistry contains an entry with name name, then
    *    throw a "NotSupportedError" DOMException and abort these steps.
    */
-  if (mCustomDefinitions.Get(nameAtom)) {
+  if (mCustomDefinitions.GetWeak(nameAtom)) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
   /**
    * 4. If this CustomElementRegistry contains an entry with constructor constructor,
    *    then throw a "NotSupportedError" DOMException and abort these steps.
    */
   const auto& ptr = mConstructors.lookup(constructorUnwrapped);
   if (ptr) {
-    MOZ_ASSERT(mCustomDefinitions.Get(ptr->value()),
+    MOZ_ASSERT(mCustomDefinitions.GetWeak(ptr->value()),
                "Definition must be found in mCustomDefinitions");
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
   }
 
   /**
    * 5. Let localName be name.
    * 6. Let extends be the value of the extends member of options, or null if
@@ -829,34 +785,35 @@ CustomElementRegistry::Define(const nsAS
   /**
    * 12. Add definition to this CustomElementRegistry.
    */
   if (!mConstructors.put(constructorUnwrapped, nameAtom)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  CustomElementDefinition* definition =
+  RefPtr<CustomElementDefinition> definition =
     new CustomElementDefinition(nameAtom,
                                 localNameAtom,
                                 &aFunctionConstructor,
                                 Move(observedAttributes),
                                 constructorPrototype,
                                 callbacks,
                                 0 /* TODO dependent on HTML imports. Bug 877072 */);
 
-  mCustomDefinitions.Put(nameAtom, definition);
+  CustomElementDefinition* def = definition.get();
+  mCustomDefinitions.Put(nameAtom, definition.forget());
 
   MOZ_ASSERT(mCustomDefinitions.Count() == mConstructors.count(),
              "Number of entries should be the same");
 
   /**
    * 13. 14. 15. Upgrade candidates
    */
-  UpgradeCandidates(nameAtom, definition, aRv);
+  UpgradeCandidates(nameAtom, def, aRv);
 
   /**
    * 16. If this CustomElementRegistry's when-defined promise map contains an
    *     entry with key name:
    *     1. Let promise be the value of that entry.
    *     2. Resolve promise with undefined.
    *     3. Delete the entry with key name from this CustomElementRegistry's
    *        when-defined promise map.
@@ -869,17 +826,17 @@ CustomElementRegistry::Define(const nsAS
 
 }
 
 void
 CustomElementRegistry::Get(JSContext* aCx, const nsAString& aName,
                            JS::MutableHandle<JS::Value> aRetVal)
 {
   nsCOMPtr<nsIAtom> nameAtom(NS_Atomize(aName));
-  CustomElementDefinition* data = mCustomDefinitions.Get(nameAtom);
+  CustomElementDefinition* data = mCustomDefinitions.GetWeak(nameAtom);
 
   if (!data) {
     aRetVal.setUndefined();
     return;
   }
 
   aRetVal.setObject(*data->mConstructor->Callback(aCx));
 }
@@ -895,17 +852,17 @@ CustomElementRegistry::WhenDefined(const
   }
 
   nsCOMPtr<nsIAtom> nameAtom(NS_Atomize(aName));
   if (!nsContentUtils::IsCustomElementName(nameAtom)) {
     promise->MaybeReject(NS_ERROR_DOM_SYNTAX_ERR);
     return promise.forget();
   }
 
-  if (mCustomDefinitions.Get(nameAtom)) {
+  if (mCustomDefinitions.GetWeak(nameAtom)) {
     promise->MaybeResolve(JS::UndefinedHandleValue);
     return promise.forget();
   }
 
   auto entry = mWhenDefinedPromiseMap.LookupForAdd(nameAtom);
   if (entry) {
     promise = entry.Data();
   } else {
@@ -999,16 +956,19 @@ CustomElementRegistry::Upgrade(Element* 
     // Empty element's custom element reaction queue.
     data->mReactionQueue.Clear();
     return;
   }
 
   // Step 8.
   data->mState = CustomElementData::State::eCustom;
 
+  // Step 9.
+  aElement->SetCustomElementDefinition(aDefinition);
+
   // This is for old spec.
   nsContentUtils::EnqueueLifecycleCallback(aElement->OwnerDoc(),
                                            nsIDocument::eCreated,
                                            aElement, nullptr, aDefinition);
 }
 
 //-----------------------------------------------------
 // CustomElementReactionsStack
@@ -1151,16 +1111,60 @@ CustomElementReactionsStack::InvokeReact
     reactions.Clear();
   }
   aElementQueue->Clear();
 }
 
 //-----------------------------------------------------
 // CustomElementDefinition
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementDefinition)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementDefinition)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConstructor)
+  tmp->mPrototype = nullptr;
+  tmp->mCallbacks = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementDefinition)
+  mozilla::dom::LifecycleCallbacks* callbacks = tmp->mCallbacks.get();
+
+  if (callbacks->mAttributeChangedCallback.WasPassed()) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
+      "mCallbacks->mAttributeChangedCallback");
+    cb.NoteXPCOMChild(callbacks->mAttributeChangedCallback.Value());
+  }
+
+  if (callbacks->mCreatedCallback.WasPassed()) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mCreatedCallback");
+    cb.NoteXPCOMChild(callbacks->mCreatedCallback.Value());
+  }
+
+  if (callbacks->mAttachedCallback.WasPassed()) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mAttachedCallback");
+    cb.NoteXPCOMChild(callbacks->mAttachedCallback.Value());
+  }
+
+  if (callbacks->mDetachedCallback.WasPassed()) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCallbacks->mDetachedCallback");
+    cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
+  }
+
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mConstructor");
+  cb.NoteXPCOMChild(tmp->mConstructor);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementDefinition)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPrototype)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CustomElementDefinition, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CustomElementDefinition, Release)
+
+
 CustomElementDefinition::CustomElementDefinition(nsIAtom* aType,
                                                  nsIAtom* aLocalName,
                                                  Function* aConstructor,
                                                  nsCOMArray<nsIAtom>&& aObservedAttributes,
                                                  JSObject* aPrototype,
                                                  LifecycleCallbacks* aCallbacks,
                                                  uint32_t aDocOrder)
   : mType(aType),
--- a/dom/base/CustomElementRegistry.h
+++ b/dom/base/CustomElementRegistry.h
@@ -9,28 +9,29 @@
 
 #include "js/GCHashTable.h"
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FunctionBinding.h"
+#include "mozilla/dom/WebComponentsBinding.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsGenericHTMLElement.h"
 #include "nsWrapperCache.h"
 #include "nsContentUtils.h"
 
 class nsDocument;
 
 namespace mozilla {
 namespace dom {
 
 struct CustomElementData;
 struct ElementDefinitionOptions;
-struct LifecycleCallbacks;
 class CallbackFunction;
 class CustomElementReaction;
 class Function;
 class Promise;
 
 struct LifecycleCallbackArgs
 {
   nsString name;
@@ -112,26 +113,45 @@ struct CustomElementData
   // custom element reaction queue as described in the custom element spec.
   // There is 1 reaction in reaction queue, when 1) it becomes disconnected,
   // 2) it’s adopted into a new document, 3) its attributes are changed,
   // appended, removed, or replaced.
   // There are 3 reactions in reaction queue when doing upgrade operation,
   // e.g., create an element, insert a node.
   AutoTArray<UniquePtr<CustomElementReaction>, 3> mReactionQueue;
 
+  RefPtr<CustomElementDefinition> mCustomElementDefinition;
+
+  void
+  SetCustomElementDefinition(CustomElementDefinition* aDefinition)
+  {
+    MOZ_ASSERT(!mCustomElementDefinition);
+
+    mCustomElementDefinition = aDefinition;
+  }
+
+  CustomElementDefinition*
+  GetCustomElementDefinition()
+  {
+    return mCustomElementDefinition;
+  }
+
 private:
   virtual ~CustomElementData() {}
 };
 
 #define ALEADY_CONSTRUCTED_MARKER nullptr
 
 // The required information for a custom element as defined in:
 // https://html.spec.whatwg.org/multipage/scripting.html#custom-element-definition
 struct CustomElementDefinition
 {
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CustomElementDefinition)
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CustomElementDefinition)
+
   CustomElementDefinition(nsIAtom* aType,
                           nsIAtom* aLocalName,
                           Function* aConstructor,
                           nsCOMArray<nsIAtom>&& aObservedAttributes,
                           JSObject* aPrototype,
                           mozilla::dom::LifecycleCallbacks* aCallbacks,
                           uint32_t aDocOrder);
 
@@ -167,16 +187,19 @@ struct CustomElementDefinition
   bool IsInObservedAttributeList(nsIAtom* aName)
   {
     if (mObservedAttributes.IsEmpty()) {
       return false;
     }
 
     return mObservedAttributes.Contains(aName);
   }
+
+private:
+  ~CustomElementDefinition() {}
 };
 
 class CustomElementReaction
 {
 public:
   explicit CustomElementReaction(CustomElementRegistry* aRegistry,
                                  CustomElementDefinition* aDefinition)
     : mRegistry(aRegistry)
@@ -389,17 +412,17 @@ private:
    */
   void RegisterUnresolvedElement(Element* aElement,
                                  nsIAtom* aTypeName = nullptr);
 
   void UpgradeCandidates(nsIAtom* aKey,
                          CustomElementDefinition* aDefinition,
                          ErrorResult& aRv);
 
-  typedef nsClassHashtable<nsISupportsHashKey, CustomElementDefinition>
+  typedef nsRefPtrHashtable<nsISupportsHashKey, CustomElementDefinition>
     DefinitionMap;
   typedef nsClassHashtable<nsISupportsHashKey, nsTArray<nsWeakPtr>>
     CandidateMap;
   typedef JS::GCHashMap<JS::Heap<JSObject*>,
                         nsCOMPtr<nsIAtom>,
                         js::MovableCellHasher<JS::Heap<JSObject*>>,
                         js::SystemAllocPolicy> ConstructorMap;
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3576,49 +3576,49 @@ Element::GetTransformToAncestor(Element&
   nsIFrame* ancestorFrame = aAncestor.GetPrimaryFrame();
 
   Matrix4x4 transform;
   if (primaryFrame) {
     // If aAncestor is not actually an ancestor of this (including nullptr),
     // then the call to GetTransformToAncestor will return the transform
     // all the way up through the parent chain.
     transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
-      ancestorFrame, true);
+      ancestorFrame, nsIFrame::IN_CSS_UNITS);
   }
 
   DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
   RefPtr<DOMMatrixReadOnly> result(matrix);
   return result.forget();
 }
 
 already_AddRefed<DOMMatrixReadOnly>
 Element::GetTransformToParent()
 {
   nsIFrame* primaryFrame = GetPrimaryFrame();
 
   Matrix4x4 transform;
   if (primaryFrame) {
     nsIFrame* parentFrame = primaryFrame->GetParent();
     transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
-      parentFrame, true);
+      parentFrame, nsIFrame::IN_CSS_UNITS);
   }
 
   DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
   RefPtr<DOMMatrixReadOnly> result(matrix);
   return result.forget();
 }
 
 already_AddRefed<DOMMatrixReadOnly>
 Element::GetTransformToViewport()
 {
   nsIFrame* primaryFrame = GetPrimaryFrame();
   Matrix4x4 transform;
   if (primaryFrame) {
     transform = nsLayoutUtils::GetTransformToAncestor(primaryFrame,
-      nsLayoutUtils::GetDisplayRootFrame(primaryFrame), true);
+      nsLayoutUtils::GetDisplayRootFrame(primaryFrame), nsIFrame::IN_CSS_UNITS);
   }
 
   DOMMatrixReadOnly* matrix = new DOMMatrix(this, transform);
   RefPtr<DOMMatrixReadOnly> result(matrix);
   return result.forget();
 }
 
 already_AddRefed<Animation>
@@ -4209,16 +4209,36 @@ Element::ClearServoData(nsIDocument* aDo
 void
 Element::SetCustomElementData(CustomElementData* aData)
 {
   nsExtendedDOMSlots *slots = ExtendedDOMSlots();
   MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
   slots->mCustomElementData = aData;
 }
 
+CustomElementDefinition*
+Element::GetCustomElementDefinition() const
+{
+  CustomElementData* data = GetCustomElementData();
+  if (!data) {
+    return nullptr;
+  }
+
+  return data->GetCustomElementDefinition();
+}
+
+void
+Element::SetCustomElementDefinition(CustomElementDefinition* aDefinition)
+{
+  CustomElementData* data = GetCustomElementData();
+  MOZ_ASSERT(data);
+
+  data->SetCustomElementDefinition(aDefinition);
+}
+
 MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf)
 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(ServoElementMallocEnclosingSizeOf)
 
 void
 Element::AddSizeOfExcludingThis(nsWindowSizes& aSizes, size_t* aNodeSize) const
 {
   FragmentOrElement::AddSizeOfExcludingThis(aSizes, aNodeSize);
 
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -551,16 +551,32 @@ public:
   /**
    * Sets the custom element data, ownership of the
    * callback data is taken by this element.
    *
    * @param aData The custom element data.
    */
   void SetCustomElementData(CustomElementData* aData);
 
+  /**
+   * Gets the custom element definition used by web components custom element.
+   *
+   * @return The custom element definition or null if element is not a custom
+   *         element or custom element is not defined yet.
+   */
+  CustomElementDefinition* GetCustomElementDefinition() const;
+
+  /**
+   * Sets the custom element definition, called when custom element is created
+   * or upgraded.
+   *
+   * @param aDefinition The custom element definition.
+   */
+  void SetCustomElementDefinition(CustomElementDefinition* aDefinition);
+
 protected:
   /**
    * Method to get the _intrinsic_ content state of this element.  This is the
    * state that is independent of the element's presentation.  To get the full
    * content state, use State().  See mozilla/EventStates.h for
    * the possible bits that could be set here.
    */
   virtual EventStates IntrinsicState() const;
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -807,16 +807,23 @@ FragmentOrElement::nsDOMSlots::Traverse(
 
   if (mExtendedSlots->mCustomElementData) {
     for (uint32_t i = 0;
          i < mExtendedSlots->mCustomElementData->mReactionQueue.Length(); i++) {
       if (mExtendedSlots->mCustomElementData->mReactionQueue[i]) {
         mExtendedSlots->mCustomElementData->mReactionQueue[i]->Traverse(cb);
       }
     }
+
+    if (mExtendedSlots->mCustomElementData->mCustomElementDefinition) {
+      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
+        "mExtendedSlots->mCustomElementData->mCustomElementDefinition");
+      cb.NoteNativeChild(mExtendedSlots->mCustomElementData->mCustomElementDefinition,
+        NS_CYCLE_COLLECTION_PARTICIPANT(CustomElementDefinition));
+    }
   }
 
   for (auto iter = mExtendedSlots->mRegisteredIntersectionObservers.Iter();
        !iter.Done(); iter.Next()) {
     DOMIntersectionObserver* observer = iter.Key();
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
                                        "mExtendedSlots->mRegisteredIntersectionObservers[i]");
     cb.NoteXPCOMChild(observer);
@@ -843,17 +850,22 @@ FragmentOrElement::nsDOMSlots::Unlink()
 
   mExtendedSlots->mSMILOverrideStyle = nullptr;
   mExtendedSlots->mControllers = nullptr;
   mExtendedSlots->mLabelsList = nullptr;
   mExtendedSlots->mShadowRoot = nullptr;
   mExtendedSlots->mContainingShadow = nullptr;
   MOZ_ASSERT(!(mExtendedSlots->mXBLBinding));
   mExtendedSlots->mXBLInsertionParent = nullptr;
-  mExtendedSlots->mCustomElementData = nullptr;
+  if (mExtendedSlots->mCustomElementData) {
+    if (mExtendedSlots->mCustomElementData->mCustomElementDefinition) {
+      mExtendedSlots->mCustomElementData->mCustomElementDefinition = nullptr;
+    }
+    mExtendedSlots->mCustomElementData = nullptr;
+  }
   mExtendedSlots->mRegisteredIntersectionObservers.Clear();
   nsCOMPtr<nsIFrameLoader> frameLoader =
     do_QueryInterface(mExtendedSlots->mFrameLoaderOrOpener);
   if (frameLoader) {
     static_cast<nsFrameLoader*>(frameLoader.get())->Destroy();
   }
   mExtendedSlots->mFrameLoaderOrOpener = nullptr;
 }
new file mode 100644
--- /dev/null
+++ b/dom/base/RangeBoundary.h
@@ -0,0 +1,256 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_RangeBoundary_h
+#define mozilla_RangeBoundary_h
+
+namespace mozilla {
+
+// This class will maintain a reference to the child immediately
+// before the boundary's offset. We try to avoid computing the
+// offset as much as possible and just ensure mRef points to the
+// correct child.
+//
+// mParent
+//    |
+// [child0] [child1] [child2]
+//            /      |
+//         mRef    mOffset=2
+//
+// If mOffset == 0, mRef is null.
+// For text nodes, mRef will always be null and the offset will
+// be kept up-to-date.
+
+template<typename ParentType, typename RefType>
+class RangeBoundaryBase;
+
+typedef RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>> RangeBoundary;
+typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary;
+
+// This class has two specializations, one using reference counting
+// pointers and one using raw pointers. This helps us avoid unnecessary
+// AddRef/Release calls.
+template<typename ParentType, typename RefType>
+class RangeBoundaryBase
+{
+  template<typename T, typename U>
+  friend class RangeBoundaryBase;
+
+  friend void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback&,
+                                          RangeBoundary&, const char*,
+                                          uint32_t);
+  friend void ImplCycleCollectionUnlink(RangeBoundary&);
+
+public:
+  RangeBoundaryBase(nsINode* aContainer, nsIContent* aRef)
+    : mParent(aContainer)
+    , mRef(aRef)
+  {
+    if (!mRef) {
+      mOffset = mozilla::Some(0);
+    } else {
+      mOffset.reset();
+    }
+  }
+
+  RangeBoundaryBase(nsINode* aContainer, int32_t aOffset)
+    : mParent(aContainer)
+    , mRef(nullptr)
+    , mOffset(mozilla::Some(aOffset))
+  {
+    if (mParent && mParent->IsContainerNode()) {
+      // Find a reference node
+      if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
+        mRef = aContainer->GetLastChild();
+      } else if (aOffset != 0) {
+        mRef = mParent->GetChildAt(aOffset - 1);
+        MOZ_ASSERT(mRef);
+      }
+
+      MOZ_ASSERT_IF(!mRef, aOffset == 0);
+    }
+
+    MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
+  }
+
+  RangeBoundaryBase()
+    : mParent(nullptr)
+    , mRef(nullptr)
+  {
+  }
+
+  // Needed for initializing RawRangeBoundary from an existing RangeBoundary.
+  template<typename PT, typename RT>
+  explicit RangeBoundaryBase(const RangeBoundaryBase<PT, RT>& aOther)
+    : mParent(aOther.mParent)
+    , mRef(aOther.mRef)
+    , mOffset(aOther.mOffset)
+  {
+  }
+
+  nsIContent*
+  Ref() const
+  {
+    return mRef;
+  }
+
+  nsINode*
+  Container() const
+  {
+    return mParent;
+  }
+
+  nsIContent*
+  GetChildAtOffset() const
+  {
+    if (!mParent || !mParent->IsContainerNode()) {
+      return nullptr;
+    }
+    if (!mRef) {
+      MOZ_ASSERT(Offset() == 0);
+      return mParent->GetFirstChild();
+    }
+    MOZ_ASSERT(mParent->GetChildAt(Offset()) == mRef->GetNextSibling());
+    return mRef->GetNextSibling();
+  }
+
+  uint32_t
+  Offset() const
+  {
+    if (mOffset.isSome()) {
+      return mOffset.value();
+    }
+
+    if (!mParent) {
+      return 0;
+    }
+
+    MOZ_ASSERT(mRef);
+    MOZ_ASSERT(mRef->GetParentNode() == mParent);
+    mOffset = mozilla::Some(mParent->IndexOf(mRef) + 1);
+
+    return mOffset.value();
+  }
+
+  void
+  InvalidateOffset()
+  {
+    MOZ_ASSERT(mParent);
+    MOZ_ASSERT(mParent->IsContainerNode(), "Range is positioned on a text node!");
+
+    if (!mRef) {
+      MOZ_ASSERT(mOffset.isSome() && mOffset.value() == 0);
+      return;
+    }
+    mOffset.reset();
+  }
+
+  void
+  Set(nsINode* aContainer, int32_t aOffset)
+  {
+    mParent = aContainer;
+    if (mParent && mParent->IsContainerNode()) {
+      // Find a reference node
+      if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
+        mRef = aContainer->GetLastChild();
+      } else if (aOffset == 0) {
+        mRef = nullptr;
+      } else {
+        mRef = mParent->GetChildAt(aOffset - 1);
+        MOZ_ASSERT(mRef);
+      }
+
+      MOZ_ASSERT_IF(!mRef, aOffset == 0);
+    } else {
+      mRef = nullptr;
+    }
+
+    mOffset = mozilla::Some(aOffset);
+    MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
+  }
+
+  void
+  SetAfterRef(nsINode* aParent, nsIContent* aRef)
+  {
+    mParent = aParent;
+    mRef = aRef;
+    if (!mRef) {
+      mOffset = mozilla::Some(0);
+    } else {
+      mOffset.reset();
+    }
+  }
+
+  bool
+  IsSet() const
+  {
+    return mParent && (mRef || mOffset.isSome());
+  }
+
+  bool
+  IsSetAndValid() const
+  {
+    if (!IsSet()) {
+      return false;
+    }
+
+    if (Ref()) {
+      return Ref()->GetParentNode() == Container();
+    }
+    return Offset() <= Container()->Length();
+  }
+
+  // Convenience methods for switching between the two types
+  // of RangeBoundary.
+  RangeBoundaryBase<nsINode*, nsIContent*>
+  AsRaw() const
+  {
+    return RangeBoundaryBase<nsINode*, nsIContent*>(*this);
+  }
+
+  template<typename A, typename B>
+  RangeBoundaryBase& operator=(const RangeBoundaryBase<A,B>& aOther)
+  {
+    mParent = aOther.mParent;
+    mRef = aOther.mRef;
+    mOffset = aOther.mOffset;
+    return *this;
+  }
+
+  template<typename A, typename B>
+  bool operator==(const RangeBoundaryBase<A, B>& aOther) const
+  {
+    return mParent == aOther.mParent &&
+      (mRef ? mRef == aOther.mRef : mOffset == aOther.mOffset);
+  }
+
+private:
+  ParentType mParent;
+  RefType mRef;
+
+  mutable mozilla::Maybe<uint32_t> mOffset;
+};
+
+inline void
+ImplCycleCollectionUnlink(RangeBoundary& aField)
+{
+  ImplCycleCollectionUnlink(aField.mParent);
+  ImplCycleCollectionUnlink(aField.mRef);
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            RangeBoundary& aField,
+                            const char* aName,
+                            uint32_t aFlags)
+{
+  ImplCycleCollectionTraverse(aCallback, aField.mParent, "mParent", 0);
+  ImplCycleCollectionTraverse(aCallback, aField.mRef, "mRef", 0);
+}
+
+} // namespace mozilla
+
+#endif // defined(mozilla_RangeBoundary_h)
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -135,16 +135,17 @@ if CONFIG['MOZ_WEBRTC']:
         'nsDOMDataChannel.h',
         'nsDOMDataChannelDeclarations.h',
     ]
 
 EXPORTS.mozilla += [
     'CORSMode.h',
     'FeedWriterEnabled.h',
     'FlushType.h',
+    'RangeBoundary.h',
     'TextInputProcessor.h',
     'UseCounter.h',
 ]
 
 EXPORTS.mozilla.dom += [
     '!UseCounterList.h',
     'AnonymousContent.h',
     'Attr.h',
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -10117,23 +10117,18 @@ nsContentUtils::SetupCustomElement(Eleme
   return registry->SetupCustomElement(aElement, aTypeExtension);
 }
 
 /* static */ CustomElementDefinition*
 nsContentUtils::GetElementDefinitionIfObservingAttr(Element* aCustomElement,
                                                     nsIAtom* aExtensionType,
                                                     nsIAtom* aAttrName)
 {
-  nsString extType = nsDependentAtomString(aExtensionType);
-  NodeInfo *ni = aCustomElement->NodeInfo();
-
   CustomElementDefinition* definition =
-    LookupCustomElementDefinition(aCustomElement->OwnerDoc(), ni->LocalName(),
-                                  ni->NamespaceID(),
-                                  extType.IsEmpty() ? nullptr : &extType);
+    aCustomElement->GetCustomElementDefinition();
 
   // Custom element not defined yet or attribute is not in the observed
   // attribute list.
   if (!definition || !definition->IsInObservedAttributeList(aAttrName)) {
     return nullptr;
   }
 
   return definition;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3035,47 +3035,16 @@ nsDOMWindowUtils::RenderDocument(const n
     nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
     NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
 
     // Render Document
     return presShell->RenderDocument(aRect, aFlags, aBackgroundColor, aThebesContext);
 }
 
 NS_IMETHODIMP
-nsDOMWindowUtils::GetCursorType(int16_t *aCursor)
-{
-  NS_ENSURE_ARG_POINTER(aCursor);
-
-  nsIDocument* doc = GetDocument();
-  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
-
-  bool isSameDoc = false;
-  do {
-    if (EventStateManager::sMouseOverDocument == doc) {
-      isSameDoc = true;
-      break;
-    }
-  } while ((doc = doc->GetParentDocument()));
-
-  if (!isSameDoc) {
-    *aCursor = eCursor_none;
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIWidget> widget = GetWidget();
-  if (!widget)
-    return NS_ERROR_FAILURE;
-
-  // fetch cursor value from window's widget
-  *aCursor = widget->GetCursor();
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDOMWindowUtils::GetDisplayDPI(float *aDPI)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return NS_ERROR_FAILURE;
 
   *aDPI = widget->GetDPI();
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -6308,17 +6308,18 @@ nsDocument::CustomElementConstructor(JSC
   }
 
   RefPtr<mozilla::dom::CustomElementRegistry> registry = window->CustomElements();
   if (!registry) {
     return true;
   }
 
   nsCOMPtr<nsIAtom> typeAtom(NS_Atomize(elemName));
-  CustomElementDefinition* definition = registry->mCustomDefinitions.Get(typeAtom);
+  CustomElementDefinition* definition =
+    registry->mCustomDefinitions.GetWeak(typeAtom);
   if (!definition) {
     return true;
   }
 
   RefPtr<Element> element;
 
   // We integrate with construction stack and do prototype swizzling here, so
   // that old upgrade behavior could also share the new upgrade steps.
@@ -6378,16 +6379,18 @@ nsDocument::CustomElementConstructor(JSC
     } else {
       element = ::CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
     }
 
     element->SetCustomElementData(
       new CustomElementData(definition->mType,
                             CustomElementData::State::eCustom));
 
+    element->SetCustomElementDefinition(definition);
+
     // It'll be removed when we deprecate custom elements v0.
     nsContentUtils::SyncInvokeReactions(nsIDocument::eCreated, element,
                                         definition);
     NS_ENSURE_TRUE(element, false);
   }
 
   // The prototype setup happens in Element::WrapObject().
   nsresult rv = nsContentUtils::WrapNative(aCx, element, element, args.rval());
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -352,20 +352,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   // the result of IsInSelection() which is used by tmp->Reset().
   MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(),
                         "Shouldn't be registered now that we're unlinking");
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart.mParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart.mRef)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd.mParent)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd.mRef)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsRange)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -18,16 +18,17 @@
 #include "nsIDOMNode.h"
 #include "nsLayoutUtils.h"
 #include "prmon.h"
 #include "nsStubMutationObserver.h"
 #include "nsWrapperCache.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
+#include "mozilla/RangeBoundary.h"
 
 namespace mozilla {
 class ErrorResult;
 namespace dom {
 struct ClientRectsAndTexts;
 class DocumentFragment;
 class DOMRect;
 class DOMRectList;
@@ -39,16 +40,18 @@ class nsRange final : public nsIDOMRange
                       public nsStubMutationObserver,
                       public nsWrapperCache,
                       // For linking together selection-associated ranges.
                       public mozilla::LinkedListElement<nsRange>
 {
   typedef mozilla::ErrorResult ErrorResult;
   typedef mozilla::dom::DOMRect DOMRect;
   typedef mozilla::dom::DOMRectList DOMRectList;
+  typedef mozilla::RangeBoundary RangeBoundary;
+  typedef mozilla::RawRangeBoundary RawRangeBoundary;
 
   virtual ~nsRange();
 
 public:
   explicit nsRange(nsINode* aNode);
 
   static nsresult CreateRange(nsIDOMNode* aStartContainer,
                               uint32_t aStartOffset,
@@ -77,21 +80,31 @@ public:
   // nsIDOMRange interface
   NS_DECL_NSIDOMRANGE
 
   nsINode* GetRoot() const
   {
     return mRoot;
   }
 
+  const RangeBoundary& StartRef() const
+  {
+    return mStart;
+  }
+
   nsINode* GetStartContainer() const
   {
     return mStart.Container();
   }
 
+  const RangeBoundary& EndRef() const
+  {
+    return mEnd;
+  }
+
   nsINode* GetEndContainer() const
   {
     return mEnd.Container();
   }
 
   uint32_t StartOffset() const
   {
     return static_cast<uint32_t>(mStart.Offset());
@@ -407,225 +420,16 @@ public:
    * will be empty.
    * @param aOutRanges the resulting set of ranges
    */
   void ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges);
 
   typedef nsTHashtable<nsPtrHashKey<nsRange> > RangeHashTable;
 protected:
 
-  // This class has two specializations, one using reference counting
-  // pointers and one using raw pointers. This helps us avoid unnecessary
-  // AddRef/Release calls.
-  template<typename ParentType, typename RefType>
-  class RangeBoundaryBase
-  {
-    // This class will maintain a reference to the child immediately
-    // before the boundary's offset. We try to avoid computing the
-    // offset as much as possible and just ensure mRef points to the
-    // correct child.
-    //
-    // mParent
-    //    |
-    // [child0] [child1] [child2]
-    //            /      |
-    //         mRef    mOffset=2
-    //
-    // If mOffset == 0, mRef is null.
-    // For text nodes, mRef will always be null and the offset will
-    // be kept up-to-date.
-
-    // for cycle collecting mParent and mRef;
-    friend class nsRange;
-
-  public:
-    RangeBoundaryBase(nsINode* aContainer, nsIContent* aRef)
-      : mParent(aContainer)
-      , mRef(aRef)
-    {
-      if (!mRef) {
-        mOffset = mozilla::Some(0);
-      } else {
-        mOffset.reset();
-      }
-    }
-
-    RangeBoundaryBase(nsINode* aContainer, int32_t aOffset)
-      : mParent(aContainer)
-      , mRef(nullptr)
-      , mOffset(mozilla::Some(aOffset))
-    {
-      if (mParent && mParent->IsContainerNode()) {
-        // Find a reference node
-        if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
-          mRef = aContainer->GetLastChild();
-        } else if (aOffset != 0) {
-          mRef = mParent->GetChildAt(aOffset - 1);
-          MOZ_ASSERT(mRef);
-        }
-
-        MOZ_ASSERT_IF(!mRef, aOffset == 0);
-      }
-
-      MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
-    }
-
-    RangeBoundaryBase()
-      : mParent(nullptr)
-      , mRef(nullptr)
-    {
-    }
-
-    // Needed for initializing RawRangeBoundary from an existing RangeBoundary.
-    explicit RangeBoundaryBase(const RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>>& aOther)
-      : mParent(aOther.mParent)
-      , mRef(aOther.mRef)
-      , mOffset(aOther.mOffset)
-    {
-    }
-
-    nsIContent*
-    Ref() const
-    {
-      return mRef;
-    }
-
-    nsINode*
-    Container() const
-    {
-      return mParent;
-    }
-
-    nsIContent*
-    GetChildAtOffset() const
-    {
-      if (!mParent || !mParent->IsContainerNode()) {
-        return nullptr;
-      }
-      if (!mRef) {
-        MOZ_ASSERT(Offset() == 0);
-        return mParent->GetFirstChild();
-      }
-      MOZ_ASSERT(mParent->GetChildAt(Offset()) == mRef->GetNextSibling());
-      return mRef->GetNextSibling();
-    }
-
-    uint32_t
-    Offset() const
-    {
-      if (mOffset.isSome()) {
-        return mOffset.value();
-      }
-
-      if (!mParent) {
-        return 0;
-      }
-
-      MOZ_ASSERT(mRef);
-      MOZ_ASSERT(mRef->GetParentNode() == mParent);
-      mOffset = mozilla::Some(mParent->IndexOf(mRef) + 1);
-
-      return mOffset.value();
-    }
-
-    void
-    InvalidateOffset()
-    {
-      MOZ_ASSERT(mParent);
-      MOZ_ASSERT(mParent->IsContainerNode(), "Range is positioned on a text node!");
-
-      if (!mRef) {
-        MOZ_ASSERT(mOffset.isSome() && mOffset.value() == 0);
-        return;
-      }
-      mOffset.reset();
-    }
-
-    void
-    AdjustOffset(int32_t aDelta)
-    {
-      MOZ_ASSERT(mRef);
-      mOffset = mozilla::Some(Offset() + aDelta);
-    }
-
-    void
-    Set(nsINode* aContainer, int32_t aOffset)
-    {
-      mParent = aContainer;
-      if (mParent && mParent->IsContainerNode()) {
-        // Find a reference node
-        if (aOffset == static_cast<int32_t>(aContainer->GetChildCount())) {
-          mRef = aContainer->GetLastChild();
-        } else if (aOffset == 0) {
-          mRef = nullptr;
-        } else {
-          mRef = mParent->GetChildAt(aOffset - 1);
-          MOZ_ASSERT(mRef);
-        }
-
-        MOZ_ASSERT_IF(!mRef, aOffset == 0);
-      } else {
-        mRef = nullptr;
-      }
-
-      mOffset = mozilla::Some(aOffset);
-      MOZ_ASSERT_IF(mRef, mRef->GetParentNode() == mParent);
-    }
-
-    void
-    SetAfterRef(nsINode* aParent, nsIContent* aRef)
-    {
-      mParent = aParent;
-      mRef = aRef;
-      if (!mRef) {
-        mOffset = mozilla::Some(0);
-      } else {
-        mOffset.reset();
-      }
-    }
-
-    bool
-    IsSet() const
-    {
-      return mParent && (mRef || mOffset.isSome());
-    }
-
-    // Convenience methods for switching between the two types
-    // of RangeBoundary.
-    RangeBoundaryBase<nsINode*, nsIContent*>
-    AsRaw() const
-    {
-      return RangeBoundaryBase<nsINode*, nsIContent*>(*this);
-    }
-
-    template<typename A, typename B>
-    RangeBoundaryBase& operator=(const RangeBoundaryBase<A,B>& aOther)
-    {
-      // Since the member variables may be nsCOMPtrs, better to try to avoid
-      // extra Release/AddRef calls.
-      if (mParent != aOther.mParent) {
-        mParent = aOther.mParent;
-      }
-      if (mRef != aOther.mRef) {
-        mRef = aOther.mRef;
-      }
-      mOffset = aOther.mOffset;
-      return *this;
-    }
-
-  private:
-    ParentType mParent;
-    RefType mRef;
-
-    mutable mozilla::Maybe<uint32_t> mOffset;
-  };
-
-  typedef RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>> RangeBoundary;
-  typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary;
-
   void RegisterCommonAncestor(nsINode* aNode);
   void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking);
   nsINode* IsValidBoundary(nsINode* aNode) const
   {
     return ComputeRootNode(aNode, mMaySpanAnonymousSubtrees);
   }
 
   /**
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -3677,16 +3677,18 @@ CreateHTMLElement(const GlobalObject& aG
     } else {
       // Customized built-in element.
       newElement = CreateHTMLElement(tag, nodeInfo.forget(), NOT_FROM_PARSER);
     }
 
     newElement->SetCustomElementData(
       new CustomElementData(definition->mType, CustomElementData::State::eCustom));
 
+    newElement->SetCustomElementDefinition(definition);
+
     return newElement.forget();
   }
 
   // Step 9.
   RefPtr<nsGenericHTMLElement>& element = constructionStack.LastElement();
 
   // Step 10.
   if (element == ALEADY_CONSTRUCTED_MARKER) {
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -42,166 +42,145 @@ using namespace widget;
 /******************************************************************/
 /* ContentEventHandler::RawRange                                  */
 /******************************************************************/
 
 void
 ContentEventHandler::RawRange::AssertStartIsBeforeOrEqualToEnd()
 {
   MOZ_ASSERT(
-    nsContentUtils::ComparePoints(mStartContainer,
-                                  static_cast<int32_t>(mStartOffset),
-                                  mEndContainer,
-                                  static_cast<int32_t>(mEndOffset)) <= 0);
-}
-
-bool
-ContentEventHandler::RawRange::IsValidOffset(nsINode* aContainer,
-                                             uint32_t aOffset) const
-{
-  return aContainer && aOffset <= aContainer->Length();
+    nsContentUtils::ComparePoints(mStart.Container(),
+                                  static_cast<int32_t>(mStart.Offset()),
+                                  mEnd.Container(),
+                                  static_cast<int32_t>(mEnd.Offset())) <= 0);
 }
 
 nsresult
-ContentEventHandler::RawRange::SetStart(nsINode* aStartContainer,
-                                        uint32_t aStartOffset)
+ContentEventHandler::RawRange::SetStart(const RawRangeBoundary& aStart)
 {
-  nsINode* newRoot = nsRange::ComputeRootNode(aStartContainer);
+  nsINode* newRoot = nsRange::ComputeRootNode(aStart.Container());
   if (!newRoot) {
     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
   }
 
-  if (!IsValidOffset(aStartContainer, aStartOffset)) {
+  if (!aStart.IsSetAndValid()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Collapse if not positioned yet, or if positioned in another document.
   if (!IsPositioned() || newRoot != mRoot) {
     mRoot = newRoot;
-    mStartContainer = mEndContainer = aStartContainer;
-    mStartOffset = mEndOffset = aStartOffset;
+    mStart = mEnd = aStart;
     return NS_OK;
   }
 
-  mStartContainer = aStartContainer;
-  mStartOffset = aStartOffset;
+  mStart = aStart;
   AssertStartIsBeforeOrEqualToEnd();
   return NS_OK;
 }
 
 nsresult
-ContentEventHandler::RawRange::SetEnd(nsINode* aEndContainer,
-                                      uint32_t aEndOffset)
+ContentEventHandler::RawRange::SetEnd(const RawRangeBoundary& aEnd)
 {
-  nsINode* newRoot = nsRange::ComputeRootNode(aEndContainer);
+  nsINode* newRoot = nsRange::ComputeRootNode(aEnd.Container());
   if (!newRoot) {
     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
   }
 
-  if (!IsValidOffset(aEndContainer, aEndOffset)) {
+  if (!aEnd.IsSetAndValid()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Collapse if not positioned yet, or if positioned in another document.
   if (!IsPositioned() || newRoot != mRoot) {
     mRoot = newRoot;
-    mStartContainer = mEndContainer = aEndContainer;
-    mStartOffset = mEndOffset = aEndOffset;
+    mStart = mEnd = aEnd;
     return NS_OK;
   }
 
-  mEndContainer = aEndContainer;
-  mEndOffset = aEndOffset;
+  mEnd = aEnd;
   AssertStartIsBeforeOrEqualToEnd();
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::RawRange::SetEndAfter(nsINode* aEndContainer)
 {
   uint32_t offset = 0;
   nsINode* container =
     nsRange::GetContainerAndOffsetAfter(aEndContainer, &offset);
   return SetEnd(container, offset);
 }
 
 void
 ContentEventHandler::RawRange::SetStartAndEnd(const nsRange* aRange)
 {
-  DebugOnly<nsresult> rv = SetStartAndEnd(aRange->GetStartContainer(),
-                                          aRange->StartOffset(),
-                                          aRange->GetEndContainer(),
-                                          aRange->EndOffset());
+  DebugOnly<nsresult> rv = SetStartAndEnd(aRange->StartRef().AsRaw(),
+                                          aRange->EndRef().AsRaw());
   MOZ_ASSERT(!aRange->IsPositioned() || NS_SUCCEEDED(rv));
 }
 
 nsresult
-ContentEventHandler::RawRange::SetStartAndEnd(nsINode* aStartContainer,
-                                              uint32_t aStartOffset,
-                                              nsINode* aEndContainer,
-                                              uint32_t aEndOffset)
+ContentEventHandler::RawRange::SetStartAndEnd(const RawRangeBoundary& aStart,
+                                              const RawRangeBoundary& aEnd)
 {
-  nsINode* newStartRoot = nsRange::ComputeRootNode(aStartContainer);
+  nsINode* newStartRoot = nsRange::ComputeRootNode(aStart.Container());
   if (!newStartRoot) {
     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
   }
-  if (!IsValidOffset(aStartContainer, aStartOffset)) {
+  if (!aStart.IsSetAndValid()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
-  if (aStartContainer == aEndContainer) {
-    if (!IsValidOffset(aEndContainer, aEndOffset)) {
+  if (aStart.Container() == aEnd.Container()) {
+    if (!aEnd.IsSetAndValid()) {
       return NS_ERROR_DOM_INDEX_SIZE_ERR;
     }
-    MOZ_ASSERT(aStartOffset <= aEndOffset);
+    MOZ_ASSERT(aStart.Offset() <= aEnd.Offset());
     mRoot = newStartRoot;
-    mStartContainer = mEndContainer = aStartContainer;
-    mStartOffset = aStartOffset;
-    mEndOffset = aEndOffset;
+    mStart = aStart;
+    mEnd = aEnd;
     return NS_OK;
   }
 
-  nsINode* newEndRoot = nsRange::ComputeRootNode(aEndContainer);
+  nsINode* newEndRoot = nsRange::ComputeRootNode(aEnd.Container());
   if (!newEndRoot) {
     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
   }
-  if (!IsValidOffset(aEndContainer, aEndOffset)) {
+  if (!aEnd.IsSetAndValid()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // If they have different root, this should be collapsed at the end point.
   if (newStartRoot != newEndRoot) {
     mRoot = newEndRoot;
-    mStartContainer = mEndContainer = aEndContainer;
-    mStartOffset = mEndOffset = aEndOffset;
+    mStart = mEnd = aEnd;
     return NS_OK;
   }
 
   // Otherwise, set the range as specified.
   mRoot = newStartRoot;
-  mStartContainer = aStartContainer;
-  mStartOffset = aStartOffset;
-  mEndContainer = aEndContainer;
-  mEndOffset = aEndOffset;
+  mStart = aStart;
+  mEnd = aEnd;
   AssertStartIsBeforeOrEqualToEnd();
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::RawRange::SelectNodeContents(
                                  nsINode* aNodeToSelectContents)
 {
   nsINode* newRoot = nsRange::ComputeRootNode(aNodeToSelectContents);
   if (!newRoot) {
     return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR;
   }
   mRoot = newRoot;
-  mStartContainer = mEndContainer = aNodeToSelectContents;
-  mStartOffset = 0;
-  mEndOffset = aNodeToSelectContents->Length();
+  mStart = RawRangeBoundary(aNodeToSelectContents, nullptr);
+  mEnd = RawRangeBoundary(aNodeToSelectContents,
+                          aNodeToSelectContents->GetLastChild());
   return NS_OK;
 }
 
 /******************************************************************/
 /* ContentEventHandler                                            */
 /******************************************************************/
 
 // NOTE
@@ -411,17 +390,17 @@ ContentEventHandler::InitCommon(Selectio
   // is a special selection.
   if (aSelectionType != SelectionType::eNormal) {
     MOZ_ASSERT(!mFirstSelectedRawRange.IsPositioned());
     return NS_OK;
   }
 
   // But otherwise, we need to assume that there is a selection range at the
   // beginning of the root content if aSelectionType is eNormal.
-  rv = mFirstSelectedRawRange.CollapseTo(mRootContent, 0);
+  rv = mFirstSelectedRawRange.CollapseTo(RawRangeBoundary(mRootContent, 0));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_UNEXPECTED;
   }
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::Init(WidgetQueryContentEvent* aEvent)
@@ -1155,17 +1134,17 @@ ContentEventHandler::SetRawRangeFromFlat
     *aNewOffset = aOffset;
   }
   if (aLastTextNode) {
     *aLastTextNode = nullptr;
   }
 
   // Special case like <br contenteditable>
   if (!mRootContent->HasChildren()) {
-    nsresult rv = aRawRange->CollapseTo(mRootContent, 0);
+    nsresult rv = aRawRange->CollapseTo(RawRangeBoundary(mRootContent, 0));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
   nsresult rv = iter->Init(mRootContent);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1610,18 +1589,18 @@ ContentEventHandler::EnsureNonEmptyRect(
   aRect.height = std::max(1, aRect.height);
   aRect.width = std::max(1, aRect.width);
 }
 
 ContentEventHandler::NodePosition
 ContentEventHandler::GetNodePositionHavingFlatText(
                        const NodePosition& aNodePosition)
 {
-  return GetNodePositionHavingFlatText(aNodePosition.mNode,
-                                       aNodePosition.mOffset);
+  return GetNodePositionHavingFlatText(aNodePosition.Container(),
+                                       aNodePosition.Offset());
 }
 
 ContentEventHandler::NodePosition
 ContentEventHandler::GetNodePositionHavingFlatText(nsINode* aNode,
                                                    int32_t aNodeOffset)
 {
   if (aNode->IsNodeOfType(nsINode::eTEXT)) {
     return NodePosition(aNode, aNodeOffset);
@@ -1639,20 +1618,21 @@ ContentEventHandler::GetNodePositionHavi
   if (aNodeOffset < childCount) {
     return NodePosition(aNode->GetChildAt(aNodeOffset), 0);
   }
 
   // If the offset represents "after" the node, we need to return the last
   // child of it.  For example, if a range is |<p>[<br>]</p>|, then, the
   // end point is {<p>, 1}.  In such case, callers need the <br> node.
   if (aNodeOffset == childCount) {
-    NodePosition result;
-    result.mNode = aNode->GetChildAt(childCount - 1);
-    result.mOffset = result.mNode->IsNodeOfType(nsINode::eTEXT) ?
-      static_cast<int32_t>(result.mNode->AsContent()->TextLength()) : 1;
+    nsINode* node = aNode->GetChildAt(childCount - 1);
+    return NodePosition(node,
+      node->IsNodeOfType(nsINode::eTEXT)
+        ? static_cast<int32_t>(node->AsContent()->TextLength())
+        : 1);
   }
 
   NS_WARNING("aNodeOffset is invalid value");
   return NodePosition();
 }
 
 ContentEventHandler::FrameAndNodeOffset
 ContentEventHandler::GetFirstFrameInRangeForTextRect(const RawRange& aRawRange)
@@ -1676,40 +1656,38 @@ ContentEventHandler::GetFirstFrameInRang
     }
 
     if (node->IsNodeOfType(nsINode::eTEXT)) {
       // If the range starts at the end of a text node, we need to find
       // next node which causes text.
       int32_t offsetInNode =
         node == aRawRange.GetStartContainer() ? aRawRange.StartOffset() : 0;
       if (static_cast<uint32_t>(offsetInNode) < node->Length()) {
-        nodePosition.mNode = node;
-        nodePosition.mOffset = offsetInNode;
+        nodePosition.Set(node, offsetInNode);
         break;
       }
       continue;
     }
 
     // If the element node causes a line break before it, it's the first
     // node causing text.
     if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
         IsMozBR(node->AsContent())) {
-      nodePosition.mNode = node;
-      nodePosition.mOffset = 0;
+      nodePosition.Set(node, 0);
     }
   }
 
-  if (!nodePosition.IsValid()) {
+  if (!nodePosition.IsSet()) {
     return FrameAndNodeOffset();
   }
 
   nsIFrame* firstFrame = nullptr;
-  GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
+  GetFrameForTextRect(nodePosition.Container(), nodePosition.Offset(),
                       true, &firstFrame);
-  return FrameAndNodeOffset(firstFrame, nodePosition.mOffset);
+  return FrameAndNodeOffset(firstFrame, nodePosition.Offset());
 }
 
 ContentEventHandler::FrameAndNodeOffset
 ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange)
 {
   NodePosition nodePosition;
   nsCOMPtr<nsIContentIterator> iter = NS_NewPreContentIterator();
   nsresult rv =
@@ -1756,76 +1734,79 @@ ContentEventHandler::GetLastFrameInRange
       break;
     }
 
     if (!node->IsContent() || node == nextNodeOfRangeEnd) {
       continue;
     }
 
     if (node->IsNodeOfType(nsINode::eTEXT)) {
-      nodePosition.mNode = node;
+      uint32_t offset;
       if (node == aRawRange.GetEndContainer()) {
-        nodePosition.mOffset = aRawRange.EndOffset();
+        offset = aRawRange.EndOffset();
       } else {
-        nodePosition.mOffset = node->Length();
+        offset = node->Length();
       }
+      nodePosition.Set(node, offset);
+
       // If the text node is empty or the last node of the range but the index
       // is 0, we should store current position but continue looking for
       // previous node (If there are no nodes before it, we should use current
       // node position for returning its frame).
-      if (!nodePosition.mOffset) {
+      if (!nodePosition.Offset()) {
         continue;
       }
       break;
     }
 
     if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
         IsMozBR(node->AsContent())) {
-      nodePosition.mNode = node;
-      nodePosition.mOffset = 0;
+      nodePosition.Set(node, 0);
       break;
     }
   }
 
-  if (!nodePosition.IsValid()) {
+  if (!nodePosition.IsSet()) {
     return FrameAndNodeOffset();
   }
 
   nsIFrame* lastFrame = nullptr;
-  GetFrameForTextRect(nodePosition.mNode, nodePosition.mOffset,
+  GetFrameForTextRect(nodePosition.Container(),
+                      nodePosition.Offset(),
                       true, &lastFrame);
   if (!lastFrame) {
     return FrameAndNodeOffset();
   }
 
   // If the last frame is a text frame, we need to check if the range actually
   // includes at least one character in the range.  Therefore, if it's not a
   // text frame, we need to do nothing anymore.
   if (!lastFrame->IsTextFrame()) {
-    return FrameAndNodeOffset(lastFrame, nodePosition.mOffset);
+    return FrameAndNodeOffset(lastFrame, nodePosition.Offset());
   }
 
   int32_t start, end;
   if (NS_WARN_IF(NS_FAILED(lastFrame->GetOffsets(start, end)))) {
     return FrameAndNodeOffset();
   }
 
   // If the start offset in the node is same as the computed offset in the
   // node and it's not 0, the frame shouldn't be added to the text rect.  So,
   // this should return previous text frame and its last offset if there is
   // at least one text frame.
-  if (nodePosition.mOffset && nodePosition.mOffset == start) {
-    GetFrameForTextRect(nodePosition.mNode, --nodePosition.mOffset,
-                        true, &lastFrame);
+  if (nodePosition.Offset() && nodePosition.Offset() == static_cast<uint32_t>(start)) {
+    nodePosition.Set(nodePosition.Container(), nodePosition.Offset() - 1);
+    GetFrameForTextRect(nodePosition.Container(), nodePosition.Offset(), true,
+                        &lastFrame);
     if (NS_WARN_IF(!lastFrame)) {
       return FrameAndNodeOffset();
     }
   }
 
-  return FrameAndNodeOffset(lastFrame, nodePosition.mOffset);
+  return FrameAndNodeOffset(lastFrame, nodePosition.Offset());
 }
 
 ContentEventHandler::FrameRelativeRect
 ContentEventHandler::GetLineBreakerRectBefore(nsIFrame* aFrame)
 {
   // Note that this method should be called only with an element's frame whose
   // open tag causes a line break or moz-<br> for computing empty last line's
   // rect.
@@ -2868,18 +2849,18 @@ ContentEventHandler::OnQueryDOMWidgetHit
 ContentEventHandler::GetFlatTextLengthInRange(
                        const NodePosition& aStartPosition,
                        const NodePosition& aEndPosition,
                        nsIContent* aRootContent,
                        uint32_t* aLength,
                        LineBreakType aLineBreakType,
                        bool aIsRemovingNode /* = false */)
 {
-  if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsValid()) ||
-      NS_WARN_IF(!aEndPosition.IsValid()) || NS_WARN_IF(!aLength)) {
+  if (NS_WARN_IF(!aRootContent) || NS_WARN_IF(!aStartPosition.IsSet()) ||
+      NS_WARN_IF(!aEndPosition.IsSet()) || NS_WARN_IF(!aLength)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   if (aStartPosition == aEndPosition) {
     *aLength = 0;
     return NS_OK;
   }
 
@@ -2891,82 +2872,82 @@ ContentEventHandler::GetFlatTextLengthIn
   // including it forcibly.
   NodePosition endPosition(aEndPosition);
 
   // This may be called for retrieving the text of removed nodes.  Even in this
   // case, the node thinks it's still in the tree because UnbindFromTree() will
   // be called after here.  However, the node was already removed from the
   // array of children of its parent.  So, be careful to handle this case.
   if (aIsRemovingNode) {
-    DebugOnly<nsIContent*> parent = aStartPosition.mNode->GetParent();
-    MOZ_ASSERT(parent && parent->IndexOf(aStartPosition.mNode) == -1,
+    DebugOnly<nsIContent*> parent = aStartPosition.Container()->GetParent();
+    MOZ_ASSERT(parent && parent->IndexOf(aStartPosition.Container()) == -1,
       "At removing the node, the node shouldn't be in the array of children "
       "of its parent");
-    MOZ_ASSERT(aStartPosition.mNode == endPosition.mNode,
+    MOZ_ASSERT(aStartPosition.Container() == endPosition.Container(),
       "At removing the node, start and end node should be same");
-    MOZ_ASSERT(aStartPosition.mOffset == 0,
+    MOZ_ASSERT(aStartPosition.Offset() == 0,
       "When the node is being removed, the start offset should be 0");
-    MOZ_ASSERT(static_cast<uint32_t>(endPosition.mOffset) ==
-                 endPosition.mNode->GetChildCount(),
+    MOZ_ASSERT(static_cast<uint32_t>(endPosition.Offset()) ==
+                 endPosition.Container()->GetChildCount(),
       "When the node is being removed, the end offset should be child count");
     iter = NS_NewPreContentIterator();
-    nsresult rv = iter->Init(aStartPosition.mNode);
+    nsresult rv = iter->Init(aStartPosition.Container());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   } else {
     RawRange prevRawRange;
     nsresult rv =
-      prevRawRange.SetStart(aStartPosition.mNode, aStartPosition.mOffset);
+      prevRawRange.SetStart(aStartPosition.AsRaw());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // When the end position is immediately after non-root element's open tag,
     // we need to include a line break caused by the open tag.
-    if (endPosition.mNode != aRootContent &&
+    if (endPosition.Container() != aRootContent &&
         endPosition.IsImmediatelyAfterOpenTag()) {
-      if (endPosition.mNode->HasChildren()) {
+      if (endPosition.Container()->HasChildren()) {
         // When the end node has some children, move the end position to before
         // the open tag of its first child.
-        nsINode* firstChild = endPosition.mNode->GetFirstChild();
+        nsINode* firstChild = endPosition.Container()->GetFirstChild();
         if (NS_WARN_IF(!firstChild)) {
           return NS_ERROR_FAILURE;
         }
         endPosition = NodePositionBefore(firstChild, 0);
       } else {
         // When the end node is empty, move the end position after the node.
-        nsIContent* parentContent = endPosition.mNode->GetParent();
+        nsIContent* parentContent = endPosition.Container()->GetParent();
         if (NS_WARN_IF(!parentContent)) {
           return NS_ERROR_FAILURE;
         }
-        int32_t indexInParent = parentContent->IndexOf(endPosition.mNode);
+        int32_t indexInParent = parentContent->IndexOf(endPosition.Container());
         if (NS_WARN_IF(indexInParent < 0)) {
           return NS_ERROR_FAILURE;
         }
         endPosition = NodePositionBefore(parentContent, indexInParent + 1);
       }
     }
 
-    if (endPosition.OffsetIsValid()) {
+    if (endPosition.IsSetAndValid()) {
       // Offset is within node's length; set end of range to that offset
-      rv = prevRawRange.SetEnd(endPosition.mNode, endPosition.mOffset);
+      rv = prevRawRange.SetEnd(endPosition.AsRaw());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       iter = NS_NewPreContentIterator();
       rv =
         iter->Init(prevRawRange.GetStartContainer(), prevRawRange.StartOffset(),
                    prevRawRange.GetEndContainer(), prevRawRange.EndOffset());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
-    } else if (endPosition.mNode != aRootContent) {
+    } else if (endPosition.Container() != aRootContent) {
       // Offset is past node's length; set end of range to end of node
-      rv = prevRawRange.SetEndAfter(endPosition.mNode);
+      rv = prevRawRange.SetEndAfter(endPosition.Container());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       iter = NS_NewPreContentIterator();
       rv =
         iter->Init(prevRawRange.GetStartContainer(), prevRawRange.StartOffset(),
                    prevRawRange.GetEndContainer(), prevRawRange.EndOffset());
       if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -2990,31 +2971,33 @@ ContentEventHandler::GetFlatTextLengthIn
     }
     if (!node->IsContent()) {
       continue;
     }
     nsIContent* content = node->AsContent();
 
     if (node->IsNodeOfType(nsINode::eTEXT)) {
       // Note: our range always starts from offset 0
-      if (node == endPosition.mNode) {
+      if (node == endPosition.Container()) {
+        // NOTE: We should have an offset here, as endPosition.Container() is a
+        // nsINode::eTEXT, which always has an offset.
         *aLength += GetTextLength(content, aLineBreakType,
-                                  endPosition.mOffset);
+                                  endPosition.Offset());
       } else {
         *aLength += GetTextLength(content, aLineBreakType);
       }
     } else if (ShouldBreakLineBefore(content, aRootContent)) {
       // If the start position is start of this node but doesn't include the
       // open tag, don't append the line break length.
-      if (node == aStartPosition.mNode && !aStartPosition.IsBeforeOpenTag()) {
+      if (node == aStartPosition.Container() && !aStartPosition.IsBeforeOpenTag()) {
         continue;
       }
       // If the end position is before the open tag, don't append the line
       // break length.
-      if (node == endPosition.mNode && endPosition.IsBeforeOpenTag()) {
+      if (node == endPosition.Container() && endPosition.IsBeforeOpenTag()) {
         continue;
       }
       *aLength += GetBRLength(aLineBreakType);
     }
   }
   return NS_OK;
 }
 
@@ -3091,17 +3074,18 @@ ContentEventHandler::AdjustCollapsedRang
   }
 
   // But if the found node isn't a text node, we cannot modify the range.
   if (!childNode || !childNode->IsNodeOfType(nsINode::eTEXT) ||
       NS_WARN_IF(offsetInChildNode < 0)) {
     return NS_OK;
   }
 
-  nsresult rv = aRawRange.CollapseTo(childNode, offsetInChildNode);
+  nsresult rv =
+    aRawRange.CollapseTo(RawRangeBoundary(childNode, offsetInChildNode));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult
 ContentEventHandler::GetStartFrameAndOffset(const RawRange& aRawRange,
--- a/dom/events/ContentEventHandler.h
+++ b/dom/events/ContentEventHandler.h
@@ -41,66 +41,72 @@ private:
   /**
    * RawRange is a helper class of ContentEventHandler class.  The caller is
    * responsible for making sure the start/end nodes are in document order.
    * This is enforced by assertions in DEBUG builds.
    */
   class MOZ_STACK_CLASS RawRange final
   {
   public:
-    RawRange()
-      : mStartOffset(0)
-      , mEndOffset(0)
-    {
-    }
+    RawRange() {}
 
     void Clear()
     {
-      mRoot = mStartContainer = mEndContainer = nullptr;
-      mStartOffset = mEndOffset = 0;
+      mRoot = nullptr;
+      mStart = RangeBoundary();
+      mEnd = RangeBoundary();
     }
 
     bool IsPositioned() const
     {
-      return mStartContainer && mEndContainer;
+      return mStart.IsSet() && mEnd.IsSet();
     }
     bool Collapsed() const
     {
-      return mStartContainer == mEndContainer &&
-             mStartOffset == mEndOffset &&
-             IsPositioned();
+      return mStart == mEnd && IsPositioned();
     }
-    nsINode* GetStartContainer() const { return mStartContainer; }
-    nsINode* GetEndContainer() const { return mEndContainer; }
-    uint32_t StartOffset() const { return mStartOffset; }
-    uint32_t EndOffset() const { return mEndOffset; }
+    nsINode* GetStartContainer() const { return mStart.Container(); }
+    nsINode* GetEndContainer() const { return mEnd.Container(); }
+    uint32_t StartOffset() const { return mStart.Offset(); }
+    uint32_t EndOffset() const { return mEnd.Offset(); }
+    nsIContent* StartRef() const { return mStart.Ref(); }
+    nsIContent* EndRef() const { return mEnd.Ref(); }
 
-    nsresult CollapseTo(nsINode* aContainer, uint32_t aOffset)
+    // XXX: Make these use RangeBoundaries...
+    nsresult CollapseTo(const RawRangeBoundary& aBoundary)
     {
-      return SetStartAndEnd(aContainer, aOffset, aContainer, aOffset);
+      return SetStartAndEnd(aBoundary, aBoundary);
     }
-    nsresult SetStart(nsINode* aStartContainer, uint32_t aStartOffset);
-    nsresult SetEnd(nsINode* aEndContainer, uint32_t aEndOffset);
+    nsresult SetStart(const RawRangeBoundary& aStart);
+    nsresult SetEnd(const RawRangeBoundary& aEnd);
+
+    // NOTE: These helpers can hide performance problems, as they perform a
+    // search to find aStartOffset in aStartContainer.
+    nsresult SetStart(nsINode* aStartContainer, uint32_t aStartOffset) {
+      return SetStart(RawRangeBoundary(aStartContainer, aStartOffset));
+    }
+    nsresult SetEnd(nsINode* aEndContainer, uint32_t aEndOffset) {
+      return SetEnd(RawRangeBoundary(aEndContainer, aEndOffset));
+    }
+
     nsresult SetEndAfter(nsINode* aEndContainer);
     void SetStartAndEnd(const nsRange* aRange);
-    nsresult SetStartAndEnd(nsINode* aStartContainer, uint32_t aStartOffset,
-                            nsINode* aEndContainer, uint32_t aEndOffset);
+    nsresult SetStartAndEnd(const RawRangeBoundary& aStart,
+                            const RawRangeBoundary& aEnd);
 
     nsresult SelectNodeContents(nsINode* aNodeToSelectContents);
 
   private:
-    bool IsValidOffset(nsINode* aContainer, uint32_t aOffset) const;
     nsINode* IsValidBoundary(nsINode* aNode) const;
     inline void AssertStartIsBeforeOrEqualToEnd();
 
     nsCOMPtr<nsINode> mRoot;
-    nsCOMPtr<nsINode> mStartContainer;
-    nsCOMPtr<nsINode> mEndContainer;
-    uint32_t mStartOffset;
-    uint32_t mEndOffset;
+
+    RangeBoundary mStart;
+    RangeBoundary mEnd;
   };
 
 public:
   typedef dom::Selection Selection;
 
   explicit ContentEventHandler(nsPresContext* aPresContext);
 
   // Handle aEvent in the current process.
@@ -158,86 +164,82 @@ protected:
 public:
   // FlatText means the text that is generated from DOM tree. The BR elements
   // are replaced to native linefeeds. Other elements are ignored.
 
   // NodePosition stores a pair of node and offset in the node.
   // When mNode is an element and mOffset is 0, the start position means after
   // the open tag of mNode.
   // This is useful to receive one or more sets of them instead of nsRange.
-  struct NodePosition
+  // This type is intended to be used for short-lived operations, and is thus
+  // marked MOZ_STACK_CLASS.
+  struct MOZ_STACK_CLASS NodePosition : public RangeBoundary
   {
-    nsCOMPtr<nsINode> mNode;
-    int32_t mOffset;
     // Only when mNode is an element node and mOffset is 0, mAfterOpenTag is
     // referred.
-    bool mAfterOpenTag;
+    bool mAfterOpenTag = true;
 
     NodePosition()
-      : mOffset(-1)
-      , mAfterOpenTag(true)
+      : RangeBoundary()
     {
     }
 
-    NodePosition(nsINode* aNode, int32_t aOffset)
-      : mNode(aNode)
-      , mOffset(aOffset)
-      , mAfterOpenTag(true)
+    NodePosition(nsINode* aContainer, int32_t aOffset)
+      : RangeBoundary(aContainer, aOffset)
+    {
+    }
+
+    NodePosition(nsINode* aContainer, nsIContent* aRef)
+      : RangeBoundary(aContainer, aRef)
     {
     }
 
     explicit NodePosition(const nsIFrame::ContentOffsets& aContentOffsets)
-      : mNode(aContentOffsets.content)
-      , mOffset(aContentOffsets.offset)
-      , mAfterOpenTag(true)
-    {
-    }
-
-  protected:
-    NodePosition(nsINode* aNode, int32_t aOffset, bool aAfterOpenTag)
-      : mNode(aNode)
-      , mOffset(aOffset)
-      , mAfterOpenTag(aAfterOpenTag)
+      : RangeBoundary(aContentOffsets.content, aContentOffsets.offset)
     {
     }
 
   public:
     bool operator==(const NodePosition& aOther) const
     {
-      return mNode == aOther.mNode &&
-             mOffset == aOther.mOffset &&
-             mAfterOpenTag == aOther.mAfterOpenTag;
+      return RangeBoundary::operator==(aOther) &&
+        mAfterOpenTag == aOther.mAfterOpenTag;
     }
 
-    bool IsValid() const
-    {
-      return mNode && mOffset >= 0;
-    }
-    bool OffsetIsValid() const
-    {
-      return IsValid() && static_cast<uint32_t>(mOffset) <= mNode->Length();
-    }
     bool IsBeforeOpenTag() const
     {
-      return IsValid() && mNode->IsElement() && !mOffset && !mAfterOpenTag;
+      return IsSet() &&
+        Container()->IsElement() &&
+        !Ref() &&
+        !mAfterOpenTag;
     }
     bool IsImmediatelyAfterOpenTag() const
     {
-      return IsValid() && mNode->IsElement() && !mOffset && mAfterOpenTag;
+      return IsSet() &&
+        Container()->IsElement() &&
+        !Ref() &&
+        mAfterOpenTag;
     }
   };
 
-  // NodePositionBefore isn't good name if mNode isn't an element node nor
-  // mOffset is not 0, though, when mNode is an element node and mOffset is 0,
-  // this is treated as before the open tag of mNode.
+  // NodePositionBefore isn't good name if Container() isn't an element node nor
+  // Offset() is not 0, though, when Container() is an element node and mOffset
+  // is 0, this is treated as before the open tag of Container().
   struct NodePositionBefore final : public NodePosition
   {
-    NodePositionBefore(nsINode* aNode, int32_t aOffset)
-      : NodePosition(aNode, aOffset, false)
+    NodePositionBefore(nsINode* aContainer, int32_t aOffset)
+      : NodePosition(aContainer, aOffset)
     {
+      mAfterOpenTag = false;
+    }
+
+    NodePositionBefore(nsINode* aContainer, nsIContent* aRef)
+      : NodePosition(aContainer, aRef)
+    {
+      mAfterOpenTag = false;
     }
   };
 
   // Get the flatten text length in the range.
   // @param aStartPosition      Start node and offset in the node of the range.
   // @param aEndPosition        End node and offset in the node of the range.
   // @param aRootContent        The root content of the editor or document.
   //                            aRootContent won't cause any text including
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -43,16 +43,39 @@ using namespace widget;
 LazyLogModule sIMECOLog("IMEContentObserver");
 
 static const char*
 ToChar(bool aBool)
 {
   return aBool ? "true" : "false";
 }
 
+// This method determines the node to use for the point before the current node.
+// If you have the following aContent and aContainer, and want to represent the
+// following point for `NodePosition` or `RangeBoundary`:
+//
+// <parent> {node} {node} | {node} </parent>
+//  ^                     ^     ^
+// aContainer           point  aContent
+//
+// This function will shift `aContent` to the left into the format which
+// `NodePosition` and `RangeBoundary` use:
+//
+// <parent> {node} {node} | {node} </parent>
+//  ^               ^     ^
+// aContainer    result  point
+static nsIContent*
+PointBefore(nsINode* aContainer, nsIContent* aContent)
+{
+  if (aContent) {
+    return aContent->GetPreviousSibling();
+  }
+  return aContainer->GetLastChild();
+}
+
 class WritingModeToString final : public nsAutoCString
 {
 public:
   explicit WritingModeToString(const WritingMode& aWritingMode)
   {
     if (!aWritingMode.IsVertical()) {
       AssignLiteral("Horizontal");
       return;
@@ -118,18 +141,18 @@ public:
 };
 
 /******************************************************************************
  * mozilla::IMEContentObserver
  ******************************************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
 
-// Note that we don't need to add mFirstAddedNodeContainer nor
-// mLastAddedNodeContainer to cycle collection because they are non-null only
+// Note that we don't need to add mFirstAddedContainer nor
+// mLastAddedContainer to cycle collection because they are non-null only
 // during short time and shouldn't be touched while they are non-null.
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
   nsAutoScriptBlocker scriptBlocker;
 
   tmp->NotifyIMEOfBlur();
   tmp->UnregisterObservers();
 
@@ -169,19 +192,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
  NS_INTERFACE_MAP_ENTRY(nsIEditorObserver)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelectionListener)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
 
 IMEContentObserver::IMEContentObserver()
-  : mFirstAddedNodeOffset(0)
-  , mLastAddedNodeOffset(0)
-  , mESM(nullptr)
+  : mESM(nullptr)
   , mIMENotificationRequests(nullptr)
   , mSuppressNotifications(0)
   , mPreCharacterDataChangeLength(-1)
   , mSendingNotification(NOTIFY_IME_OF_NOTHING)
   , mIsObserving(false)
   , mIMEHasFocus(false)
   , mNeedsToNotifyIMEOfFocusSet(false)
   , mNeedsToNotifyIMEOfTextChange(false)
@@ -996,154 +1017,163 @@ IMEContentObserver::CharacterDataChanged
   TextChangeData data(offset, oldEnd, newEnd,
                       IsEditorHandlingEventForComposition(),
                       IsEditorComposing());
   MaybeNotifyIMEOfTextChange(data);
 }
 
 void
 IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
-                                       int32_t aStartIndex,
-                                       int32_t aEndIndex)
+                                       nsIContent* aFirstContent,
+                                       nsIContent* aLastContent)
 {
   if (!NeedsTextChangeNotification()) {
     return;
   }
 
+  MOZ_ASSERT_IF(aFirstContent, aFirstContent->GetParentNode() == aContainer);
+  MOZ_ASSERT_IF(aLastContent, aLastContent->GetParentNode() == aContainer);
+
   mStartOfRemovingTextRangeCache.Clear();
 
   // If it's in a document change, nodes are added consecutively.  Therefore,
   // if we cache the first node and the last node, we need to compute the
   // range once.
   // FYI: This is not true if the change caused by an operation in the editor.
   if (IsInDocumentChange()) {
     // Now, mEndOfAddedTextCache may be invalid if node is added before
     // the last node in mEndOfAddedTextCache.  Clear it.
     mEndOfAddedTextCache.Clear();
     if (!HasAddedNodesDuringDocumentChange()) {
-      mFirstAddedNodeContainer = mLastAddedNodeContainer = aContainer;
-      mFirstAddedNodeOffset = aStartIndex;
-      mLastAddedNodeOffset = aEndIndex;
+      mFirstAddedContainer = mLastAddedContainer = aContainer;
+      mFirstAddedContent = aFirstContent;
+      mLastAddedContent = aLastContent;
       MOZ_LOG(sIMECOLog, LogLevel::Debug,
         ("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
          "consecutive added nodes", this));
       return;
     }
     // If first node being added is not next node of the last node,
     // notify IME of the previous range first, then, restart to cache the
     // range.
-    if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aStartIndex))) {
+    if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aFirstContent))) {
       // Flush the old range first.
       MaybeNotifyIMEOfAddedTextDuringDocumentChange();
-      mFirstAddedNodeContainer = aContainer;
-      mFirstAddedNodeOffset = aStartIndex;
+      mFirstAddedContainer = aContainer;
+      mFirstAddedContent = aFirstContent;
       MOZ_LOG(sIMECOLog, LogLevel::Debug,
         ("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
          "consecutive added nodes", this));
     }
-    mLastAddedNodeContainer = aContainer;
-    mLastAddedNodeOffset = aEndIndex;
+    mLastAddedContainer = aContainer;
+    mLastAddedContent = aLastContent;
     return;
   }
   MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
     "The cache should be cleared when document change finished");
 
   uint32_t offset = 0;
   nsresult rv = NS_OK;
-  if (!mEndOfAddedTextCache.Match(aContainer, aStartIndex)) {
+  if (!mEndOfAddedTextCache.Match(aContainer,
+                                  aFirstContent->GetPreviousSibling())) {
     mEndOfAddedTextCache.Clear();
     rv = ContentEventHandler::GetFlatTextLengthInRange(
                                 NodePosition(mRootContent, 0),
-                                NodePositionBefore(aContainer, aStartIndex),
+                                NodePositionBefore(aContainer,
+                                                   PointBefore(aContainer,
+                                                               aFirstContent)),
                                 mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
     if (NS_WARN_IF(NS_FAILED((rv)))) {
       return;
     }
   } else {
     offset = mEndOfAddedTextCache.mFlatTextLength;
   }
 
   // get offset at the end of the last added node
   uint32_t addingLength = 0;
   rv = ContentEventHandler::GetFlatTextLengthInRange(
-                              NodePositionBefore(aContainer, aStartIndex),
-                              NodePosition(aContainer, aEndIndex),
+                              NodePositionBefore(aContainer,
+                                                 PointBefore(aContainer,
+                                                             aFirstContent)),
+                              NodePosition(aContainer, aLastContent),
                               mRootContent, &addingLength,
                               LINE_BREAK_TYPE_NATIVE);
   if (NS_WARN_IF(NS_FAILED((rv)))) {
     mEndOfAddedTextCache.Clear();
     return;
   }
 
   // If multiple lines are being inserted in an HTML editor, next call of
   // NotifyContentAdded() is for adding next node.  Therefore, caching the text
   // length can skip to compute the text length before the adding node and
   // before of it.
-  mEndOfAddedTextCache.Cache(aContainer, aEndIndex, offset + addingLength);
+  mEndOfAddedTextCache.Cache(aContainer, aLastContent, offset + addingLength);
 
   if (!addingLength) {
     return;
   }
 
   TextChangeData data(offset, offset, offset + addingLength,
                       IsEditorHandlingEventForComposition(),
                       IsEditorComposing());
   MaybeNotifyIMEOfTextChange(data);
 }
 
 void
 IMEContentObserver::ContentAppended(nsIDocument* aDocument,
                                     nsIContent* aContainer,
                                     nsIContent* aFirstNewContent,
-                                    int32_t aNewIndexInContainer)
+                                    int32_t /* unused */)
 {
-  NotifyContentAdded(aContainer, aNewIndexInContainer,
-                     aContainer->GetChildCount());
+  NotifyContentAdded(NODE_FROM(aContainer, aDocument),
+                     aFirstNewContent, aContainer->GetLastChild());
 }
 
 void
 IMEContentObserver::ContentInserted(nsIDocument* aDocument,
                                     nsIContent* aContainer,
                                     nsIContent* aChild,
-                                    int32_t aIndexInContainer)
+                                    int32_t /* unused */)
 {
   NotifyContentAdded(NODE_FROM(aContainer, aDocument),
-                     aIndexInContainer, aIndexInContainer + 1);
+                     aChild, aChild);
 }
 
 void
 IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
                                    nsIContent* aContainer,
                                    nsIContent* aChild,
-                                   int32_t aIndexInContainer,
+                                   int32_t /* unused */,
                                    nsIContent* aPreviousSibling)
 {
   if (!NeedsTextChangeNotification()) {
     return;
   }
 
   mEndOfAddedTextCache.Clear();
   MaybeNotifyIMEOfAddedTextDuringDocumentChange();
 
   nsINode* containerNode = NODE_FROM(aContainer, aDocument);
 
   uint32_t offset = 0;
   nsresult rv = NS_OK;
-  if (!mStartOfRemovingTextRangeCache.Match(containerNode, aIndexInContainer)) {
+  if (!mStartOfRemovingTextRangeCache.Match(containerNode, aPreviousSibling)) {
     // At removing a child node of aContainer, we need the line break caused
-    // by open tag of aContainer.  Be careful when aIndexInContainer is 0.
+    // by open tag of aContainer.  Be careful when aPreviousSibling is nullptr.
+
     rv = ContentEventHandler::GetFlatTextLengthInRange(
                                 NodePosition(mRootContent, 0),
-                                NodePosition(containerNode, aIndexInContainer),
+                                NodePosition(containerNode, aPreviousSibling),
                                 mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mStartOfRemovingTextRangeCache.Clear();
       return;
     }
-    mStartOfRemovingTextRangeCache.Cache(containerNode, aIndexInContainer,
+    mStartOfRemovingTextRangeCache.Cache(containerNode, aPreviousSibling,
                                          offset);
   } else {
     offset = mStartOfRemovingTextRangeCache.mFlatTextLength;
   }
 
   // get offset at the end of the deleted node
   uint32_t textLength = 0;
   if (aChild->IsNodeOfType(nsINode::eTEXT)) {
@@ -1225,18 +1255,18 @@ IMEContentObserver::AttributeChanged(nsI
                       IsEditorHandlingEventForComposition(),
                       IsEditorComposing());
   MaybeNotifyIMEOfTextChange(data);
 }
 
 void
 IMEContentObserver::ClearAddedNodesDuringDocumentChange()
 {
-  mFirstAddedNodeContainer = mLastAddedNodeContainer = nullptr;
-  mFirstAddedNodeOffset = mLastAddedNodeOffset = 0;
+  mFirstAddedContainer = mLastAddedContainer = nullptr;
+  mFirstAddedContent = mLastAddedContent = nullptr;
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
     ("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()"
      ", finished storing consecutive nodes", this));
 }
 
 // static
 nsIContent*
 IMEContentObserver::GetChildNode(nsINode* aParent, int32_t aOffset)
@@ -1251,76 +1281,56 @@ IMEContentObserver::GetChildNode(nsINode
   if (aOffset == static_cast<int32_t>(aParent->Length() - 1)) {
     return aParent->GetLastChild();
   }
   return aParent->GetChildAt(aOffset);
 }
 
 bool
 IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent,
-                                              int32_t aOffset) const
+                                              nsIContent* aChild) const
 {
   MOZ_ASSERT(aParent);
-  MOZ_ASSERT(aOffset >= 0 &&
-             aOffset <= static_cast<int32_t>(aParent->Length()));
+  MOZ_ASSERT(aChild && aChild->GetParentNode() == aParent);
   MOZ_ASSERT(mRootContent);
   MOZ_ASSERT(HasAddedNodesDuringDocumentChange());
 
-  // If the parent node isn't changed, we can check it only with offset.
-  if (aParent == mLastAddedNodeContainer) {
-    if (NS_WARN_IF(mLastAddedNodeOffset != aOffset)) {
+  // If the parent node isn't changed, we can check that mLastAddedContent has
+  // aChild as its next sibling.
+  if (aParent == mLastAddedContainer) {
+    if (NS_WARN_IF(mLastAddedContent->GetNextSibling() != aChild)) {
       return false;
     }
     return true;
   }
 
-  // If the parent node is changed, that means that given offset should be the
-  // last added node not having next sibling.
-  if (NS_WARN_IF(mLastAddedNodeOffset !=
-                   static_cast<int32_t>(mLastAddedNodeContainer->Length()))) {
+  // If the parent node is changed, that means that the recorded last added node
+  // shouldn't have a sibling.
+  if (NS_WARN_IF(mLastAddedContent->GetNextSibling())) {
     return false;
   }
 
-  // If the node is aParent is a descendant of mLastAddedNodeContainer,
-  // aOffset should be 0.
-  if (mLastAddedNodeContainer == aParent->GetParent()) {
-    if (NS_WARN_IF(aOffset)) {
+  // If the node is aParent is a descendant of mLastAddedContainer,
+  // aChild should be the first child in the new container.
+  if (mLastAddedContainer == aParent->GetParent()) {
+    if (NS_WARN_IF(aChild->GetPreviousSibling())) {
       return false;
     }
     return true;
   }
 
   // Otherwise, we need to check it even with slow path.
-  nsIContent* lastAddedContent =
-    GetChildNode(mLastAddedNodeContainer, mLastAddedNodeOffset - 1);
-  if (NS_WARN_IF(!lastAddedContent)) {
-    return false;
-  }
-
   nsIContent* nextContentOfLastAddedContent =
-    lastAddedContent->GetNextNode(mRootContent->GetParentNode());
+    mLastAddedContent->GetNextNode(mRootContent->GetParentNode());
   if (NS_WARN_IF(!nextContentOfLastAddedContent)) {
     return false;
   }
-
-  nsIContent* startContent = GetChildNode(aParent, aOffset);
-  if (NS_WARN_IF(!startContent) ||
-      NS_WARN_IF(nextContentOfLastAddedContent != startContent)) {
+  if (NS_WARN_IF(nextContentOfLastAddedContent != aChild)) {
     return false;
   }
-#ifdef DEBUG
-  NS_WARNING_ASSERTION(
-    !aOffset || aOffset == static_cast<int32_t>(aParent->Length() - 1),
-    "Used slow path for aParent");
-  NS_WARNING_ASSERTION(
-    !(mLastAddedNodeOffset - 1) ||
-    mLastAddedNodeOffset ==
-      static_cast<int32_t>(mLastAddedNodeContainer->Length()),
-    "Used slow path for mLastAddedNodeContainer");
-#endif // #ifdef DEBUG
   return true;
 }
 
 void
 IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()
 {
   if (!HasAddedNodesDuringDocumentChange()) {
     return;
@@ -1333,32 +1343,33 @@ IMEContentObserver::MaybeNotifyIMEOfAdde
   // Notify IME of text change which is caused by added nodes now.
 
   // First, compute offset of start of first added node from start of the
   // editor.
   uint32_t offset;
   nsresult rv =
     ContentEventHandler::GetFlatTextLengthInRange(
                             NodePosition(mRootContent, 0),
-                            NodePosition(mFirstAddedNodeContainer,
-                                         mFirstAddedNodeOffset),
+                            NodePosition(mFirstAddedContainer,
+                                         PointBefore(mFirstAddedContainer,
+                                                     mFirstAddedContent)),
                             mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ClearAddedNodesDuringDocumentChange();
     return;
   }
 
   // Next, compute the text length of added nodes.
   uint32_t length;
   rv =
     ContentEventHandler::GetFlatTextLengthInRange(
-                           NodePosition(mFirstAddedNodeContainer,
-                                        mFirstAddedNodeOffset),
-                           NodePosition(mLastAddedNodeContainer,
-                                        mLastAddedNodeOffset),
+                           NodePosition(mFirstAddedContainer,
+                                        PointBefore(mFirstAddedContainer,
+                                                    mFirstAddedContent)),
+                           NodePosition(mLastAddedContainer, mLastAddedContent),
                            mRootContent, &length, LINE_BREAK_TYPE_NATIVE);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ClearAddedNodesDuringDocumentChange();
     return;
   }
 
   // Finally, try to notify IME of the range.
   TextChangeData data(offset, offset, offset + length,
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -197,19 +197,19 @@ private:
   // beginning to update the contents and ending updating the contents.
   void BeginDocumentUpdate();
   void EndDocumentUpdate();
 
   // Following methods manages added nodes during a document change.
 
   /**
    * MaybeNotifyIMEOfAddedTextDuringDocumentChange() may send text change
-   * notification caused by the nodes added between mFirstAddedNodeOffset in
-   * mFirstAddedNodeContainer and mLastAddedNodeOffset in
-   * mLastAddedNodeContainer and forgets the range.
+   * notification caused by the nodes added between mFirstAddedContent in
+   * mFirstAddedContainer and mLastAddedContent in
+   * mLastAddedContainer and forgets the range.
    */
   void MaybeNotifyIMEOfAddedTextDuringDocumentChange();
 
   /**
    * IsInDocumentChange() returns true while the DOM tree is being modified
    * with mozAutoDocUpdate.  E.g., it's being modified by setting innerHTML or
    * insertAdjacentHTML().  This returns false when user types something in
    * the focused editor editor.
@@ -227,41 +227,42 @@ private:
   /**
    * HasAddedNodesDuringDocumentChange() returns true when this stores range
    * of nodes which were added into the DOM tree during a document change but
    * have not been sent to IME.  Note that this should always return false when
    * IsInDocumentChange() returns false.
    */
   bool HasAddedNodesDuringDocumentChange() const
   {
-    return mFirstAddedNodeContainer && mLastAddedNodeContainer;
+    return mFirstAddedContainer && mLastAddedContainer;
   }
 
   /**
-   * Returns true if the node at aOffset in aParent is next node of the node at
-   * mLastAddedNodeOffset in mLastAddedNodeContainer in pre-order tree
-   * traversal of the DOM.
+   * Returns true if the passed-in node in aParent is the next node of
+   * mLastAddedContent in pre-order tree traversal of the DOM.
    */
-  bool IsNextNodeOfLastAddedNode(nsINode* aParent, int32_t aOffset) const;
+  bool IsNextNodeOfLastAddedNode(nsINode* aParent, nsIContent* aChild) const;
 
   void PostFocusSetNotification();
   void MaybeNotifyIMEOfFocusSet();
   void PostTextChangeNotification();
   void MaybeNotifyIMEOfTextChange(const TextChangeDataBase& aTextChangeData);
   void CancelNotifyingIMEOfTextChange();
   void PostSelectionChangeNotification();
   void MaybeNotifyIMEOfSelectionChange(bool aCausedByComposition,
                                        bool aCausedBySelectionEvent,
                                        bool aOccurredDuringComposition);
   void PostPositionChangeNotification();
   void MaybeNotifyIMEOfPositionChange();
   void CancelNotifyingIMEOfPositionChange();
   void PostCompositionEventHandledNotification();
 
-  void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
+  void NotifyContentAdded(nsINode* aContainer,
+                          nsIContent* aFirstContent,
+                          nsIContent* aLastContent);
   void ObserveEditableNode();
   /**
    *  NotifyIMEOfBlur() notifies IME of blur.
    */
   void NotifyIMEOfBlur();
   /**
    *  UnregisterObservers() unregisters all listeners and observers.
    */
@@ -426,84 +427,84 @@ private:
   RefPtr<DocumentObserver> mDocumentObserver;
 
   /**
    * FlatTextCache stores flat text length from start of the content to
    * mNodeOffset of mContainerNode.
    */
   struct FlatTextCache
   {
-    // mContainerNode and mNodeOffset represent a point in DOM tree.  E.g.,
-    // if mContainerNode is a div element, mNodeOffset is index of its child.
+    // mContainerNode and mNode represent a point in DOM tree.  E.g.,
+    // if mContainerNode is a div element, mNode is a child.
     nsCOMPtr<nsINode> mContainerNode;
-    int32_t mNodeOffset;
+    // mNode points to the last child which participates in the current
+    // mFlatTextLength. If mNode is null, then that means that the end point for
+    // mFlatTextLength is immediately before the first child of mContainerNode.
+    nsCOMPtr<nsINode> mNode;
     // Length of flat text generated from contents between the start of content
     // and a child node whose index is mNodeOffset of mContainerNode.
     uint32_t mFlatTextLength;
 
     FlatTextCache()
-      : mNodeOffset(0)
-      , mFlatTextLength(0)
+      : mFlatTextLength(0)
     {
     }
 
     void Clear()
     {
       mContainerNode = nullptr;
-      mNodeOffset = 0;
+      mNode = nullptr;
       mFlatTextLength = 0;
     }
 
-    void Cache(nsINode* aContainer, int32_t aNodeOffset,
+    void Cache(nsINode* aContainer, nsINode* aNode,
                uint32_t aFlatTextLength)
     {
       MOZ_ASSERT(aContainer, "aContainer must not be null");
-      MOZ_ASSERT(
-        aNodeOffset <= static_cast<int32_t>(aContainer->GetChildCount()),
-        "aNodeOffset must be same as or less than the count of children");
+      MOZ_ASSERT(!aNode || aNode->GetParentNode() == aContainer,
+                 "aNode must be either null or a child of aContainer");
       mContainerNode = aContainer;
-      mNodeOffset = aNodeOffset;
+      mNode = aNode;
       mFlatTextLength = aFlatTextLength;
     }
 
-    bool Match(nsINode* aContainer, int32_t aNodeOffset) const
+    bool Match(nsINode* aContainer, nsINode* aNode) const
     {
-      return aContainer == mContainerNode && aNodeOffset == mNodeOffset;
+      return aContainer == mContainerNode && aNode == mNode;
     }
   };
   // mEndOfAddedTextCache caches text length from the start of content to
   // the end of the last added content only while an edit action is being
   // handled by the editor and no other mutation (e.g., removing node)
   // occur.
   FlatTextCache mEndOfAddedTextCache;
   // mStartOfRemovingTextRangeCache caches text length from the start of content
   // to the start of the last removed content only while an edit action is being
   // handled by the editor and no other mutation (e.g., adding node) occur.
   FlatTextCache mStartOfRemovingTextRangeCache;
 
-  // mFirstAddedNodeContainer is parent node of first added node in current
+  // mFirstAddedContainer is parent node of first added node in current
   // document change.  So, this is not nullptr only when a node was added
   // during a document change and the change has not been included into
   // mTextChangeData yet.
   // Note that this shouldn't be in cycle collection since this is not nullptr
   // only during a document change.
-  nsCOMPtr<nsINode> mFirstAddedNodeContainer;
-  // mLastAddedNodeContainer is parent node of last added node in current
+  nsCOMPtr<nsINode> mFirstAddedContainer;
+  // mLastAddedContainer is parent node of last added node in current
   // document change.  So, this is not nullptr only when a node was added
   // during a document change and the change has not been included into
   // mTextChangeData yet.
   // Note that this shouldn't be in cycle collection since this is not nullptr
   // only during a document change.
-  nsCOMPtr<nsINode> mLastAddedNodeContainer;
-  // mFirstAddedNodeOffset is offset of first added node in
-  // mFirstAddedNodeContainer.
-  int32_t mFirstAddedNodeOffset;
-  // mLastAddedNodeOffset is offset of *after* last added node in
-  // mLastAddedNodeContainer.  I.e., the index of last added node + 1.
-  int32_t mLastAddedNodeOffset;
+  nsCOMPtr<nsINode> mLastAddedContainer;
+
+  // mFirstAddedContent is the first node added in mFirstAddedContainer.
+  nsCOMPtr<nsIContent> mFirstAddedContent;
+  // mLastAddedContent is the last node added in mLastAddedContainer;
+  nsCOMPtr<nsIContent> mLastAddedContent;
 
   TextChangeData mTextChangeData;
 
   // mSelectionData is the last selection data which was notified.  The
   // selection information is modified by UpdateSelectionCache().  The reason
   // of the selection change is modified by MaybeNotifyIMEOfSelectionChange().
   SelectionChangeData mSelectionData;
 
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -76,22 +76,16 @@ interface nsIDOMWindowUtils : nsISupport
   /**
    * Whether the charset of the window's current document has been forced by
    * the user.
    * Cannot be accessed from unprivileged context (not content-accessible)
    */
   readonly attribute boolean docCharsetIsForced;
 
   /**
-   * Get current cursor type from this window
-   * @return the current value of nsCursor
-   */
-  short getCursorType();
-
-  /**
    * Function to get metadata associated with the window's current document
    * @param aName the name of the metadata.  This should be all lowercase.
    * @return the value of the metadata, or the empty string if it's not set
    *
    * Will throw a DOM security error if called without chrome privileges.
    */
   AString getDocumentMetadata(in AString aName);
 
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -121,16 +121,25 @@ ImageContainerListener::NotifyComposite(
 
 void
 ImageContainerListener::ClearImageContainer()
 {
   MutexAutoLock lock(mLock);
   mImageContainer = nullptr;
 }
 
+void
+ImageContainerListener::DropImageClient()
+{
+  MutexAutoLock lock(mLock);
+  if (mImageContainer) {
+    mImageContainer->DropImageClient();
+  }
+}
+
 already_AddRefed<ImageClient>
 ImageContainer::GetImageClient()
 {
   RecursiveMutexAutoLock mon(mRecursiveMutex);
   EnsureImageClient();
   RefPtr<ImageClient> imageClient = mImageClient;
   return imageClient.forget();
 }
@@ -157,37 +166,36 @@ ImageContainer::EnsureImageClient()
     return;
   }
 
   RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
   if (imageBridge) {
     mImageClient = imageBridge->CreateImageClient(CompositableType::IMAGE, this);
     if (mImageClient) {
       mAsyncContainerHandle = mImageClient->GetAsyncHandle();
-      mNotifyCompositeListener = new ImageContainerListener(this);
     } else {
       // It's okay to drop the async container handle since the ImageBridgeChild
       // is going to die anyway.
       mAsyncContainerHandle = CompositableHandle();
-      mNotifyCompositeListener = nullptr;
     }
   }
 }
 
 ImageContainer::ImageContainer(Mode flag)
 : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
   mGenerationCounter(++sGenerationCounter),
   mPaintCount(0),
   mDroppedImageCount(0),
   mImageFactory(new ImageFactory()),
   mRecycleBin(new BufferRecycleBin()),
   mIsAsync(flag == ASYNCHRONOUS),
   mCurrentProducerID(-1)
 {
   if (flag == ASYNCHRONOUS) {
+    mNotifyCompositeListener = new ImageContainerListener(this);
     EnsureImageClient();
   }
 }
 
 ImageContainer::ImageContainer(const CompositableHandle& aHandle)
   : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
   mGenerationCounter(++sGenerationCounter),
   mPaintCount(0),
@@ -376,16 +384,17 @@ bool ImageContainer::IsAsync() const
 {
   return mIsAsync;
 }
 
 CompositableHandle ImageContainer::GetAsyncContainerHandle()
 {
   NS_ASSERTION(IsAsync(), "Shared image ID is only relevant to async ImageContainers");
   NS_ASSERTION(mAsyncContainerHandle, "Should have a shared image ID");
+  RecursiveMutexAutoLock mon(mRecursiveMutex);
   EnsureImageClient();
   return mAsyncContainerHandle;
 }
 
 bool
 ImageContainer::HasCurrentImage()
 {
   RecursiveMutexAutoLock lock(mRecursiveMutex);
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -339,16 +339,17 @@ protected:
 class ImageContainerListener final {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageContainerListener)
 
 public:
   explicit ImageContainerListener(ImageContainer* aImageContainer);
 
   void NotifyComposite(const ImageCompositeNotification& aNotification);
   void ClearImageContainer();
+  void DropImageClient();
 private:
   typedef mozilla::Mutex Mutex;
 
   ~ImageContainerListener();
 
   Mutex mLock;
   ImageContainer* mImageContainer;
 };
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -244,17 +244,17 @@ ImageBridgeChild::ShutdownStep2(Synchron
 }
 
 void
 ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   mCanSend = false;
   {
     MutexAutoLock lock(mContainerMapLock);
-    mImageContainers.Clear();
+    mImageContainerListeners.Clear();
   }
 }
 
 void
 ImageBridgeChild::DeallocPImageBridgeChild()
 {
   this->Release();
 }
@@ -317,30 +317,30 @@ ImageBridgeChild::Connect(CompositableCl
   // Note: this is static, rather than per-IBC, so IDs are not re-used across
   // ImageBridgeChild instances. This is relevant for the GPU process, where
   // we don't want old IDs to potentially leak into a recreated ImageBridge.
   static uint64_t sNextID = 1;
   uint64_t id = sNextID++;
 
   {
     MutexAutoLock lock(mContainerMapLock);
-    MOZ_ASSERT(!mImageContainers.Contains(id));
-    mImageContainers.Put(id, aImageContainer);
+    MOZ_ASSERT(!mImageContainerListeners.Contains(id));
+    mImageContainerListeners.Put(id, aImageContainer->GetImageContainerListener());
   }
 
   CompositableHandle handle(id);
   aCompositable->InitIPDL(handle);
   SendNewCompositable(handle, aCompositable->GetTextureInfo(), GetCompositorBackendType());
 }
 
 void
 ImageBridgeChild::ForgetImageContainer(const CompositableHandle& aHandle)
 {
   MutexAutoLock lock(mContainerMapLock);
-  mImageContainers.Remove(aHandle.Value());
+  mImageContainerListeners.Remove(aHandle.Value());
 }
 
 Thread* ImageBridgeChild::GetThread() const
 {
   return sImageBridgeChildThread;
 }
 
 /* static */ RefPtr<ImageBridgeChild>
@@ -734,19 +734,26 @@ void
 ImageBridgeChild::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aIdentifier)
 {
   bool disablingWebRender = GetCompositorBackendType() == LayersBackend::LAYERS_WR &&
                             aIdentifier.mParentBackend != LayersBackend::LAYERS_WR;
   IdentifyTextureHost(aIdentifier);
   if (disablingWebRender) {
     // ImageHost is incompatible between WebRender enabled and WebRender disabled.
     // Then drop all ImageContainers' ImageClients during disabling WebRender.
-    MutexAutoLock lock(mContainerMapLock);
-    for (auto iter = mImageContainers.Iter(); !iter.Done(); iter.Next()) {
-      ImageContainer* container = iter.Data();
+    nsTArray<RefPtr<ImageContainerListener> > listeners;
+    {
+      MutexAutoLock lock(mContainerMapLock);
+      for (auto iter = mImageContainerListeners.Iter(); !iter.Done(); iter.Next()) {
+        RefPtr<ImageContainerListener>& listener = iter.Data();
+        listeners.AppendElement(listener);
+      }
+    }
+    // Drop ImageContainer's ImageClient whithout holding mContainerMapLock to avoid deadlock.
+    for (auto container : listeners) {
       container->DropImageClient();
     }
   }
 }
 
 RefPtr<ImageClient>
 ImageBridgeChild::CreateImageClient(CompositableType aType,
                                     ImageContainer* aImageContainer)
@@ -1009,20 +1016,18 @@ ImageBridgeChild::RecvParentAsyncMessage
 
 mozilla::ipc::IPCResult
 ImageBridgeChild::RecvDidComposite(InfallibleTArray<ImageCompositeNotification>&& aNotifications)
 {
   for (auto& n : aNotifications) {
     RefPtr<ImageContainerListener> listener;
     {
       MutexAutoLock lock(mContainerMapLock);
-      ImageContainer* imageContainer;
-      imageContainer = mImageContainers.Get(n.compositable().Value());
-      if (imageContainer) {
-        listener = imageContainer->GetImageContainerListener();
+      if (auto entry = mImageContainerListeners.Lookup(n.compositable().Value())) {
+        listener = entry.Data();
       }
     }
     if (listener) {
       listener->NotifyComposite(n);
     }
   }
   return IPC_OK();
 }
@@ -1123,17 +1128,17 @@ ImageBridgeChild::ReleaseCompositable(co
   }
 
   if (!DestroyInTransaction(aHandle)) {
     SendReleaseCompositable(aHandle);
   }
 
   {
     MutexAutoLock lock(mContainerMapLock);
-    mImageContainers.Remove(aHandle.Value());
+    mImageContainerListeners.Remove(aHandle.Value());
   }
 }
 
 bool
 ImageBridgeChild::CanSend() const
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   return mCanSend;
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -36,16 +36,17 @@ namespace ipc {
 class Shmem;
 } // namespace ipc
 
 namespace layers {
 
 class AsyncCanvasRenderer;
 class ImageClient;
 class ImageContainer;
+class ImageContainerListener;
 class ImageBridgeParent;
 class CompositableClient;
 struct CompositableTransaction;
 class Image;
 class TextureClient;
 class SynchronousTask;
 struct AllocShmemParams;
 
@@ -393,15 +394,15 @@ private:
    * It defer calling of TextureClient recycle callback.
    */
   nsRefPtrHashtable<nsUint64HashKey, TextureClient> mTexturesWaitingRecycled;
 
   /**
    * Mapping from async compositable IDs to image containers.
    */
   Mutex mContainerMapLock;
-  nsDataHashtable<nsUint64HashKey, ImageContainer*> mImageContainers;
+  nsRefPtrHashtable<nsUint64HashKey, ImageContainerListener> mImageContainerListeners;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -389,16 +389,36 @@ public:
     {
       MOZ_ASSERT(!Done());
       mCurrent++;
     }
   };
 
   RectIterator RectIter() const { return RectIterator(*this); }
 
+  static inline pixman_box32_t RectToBox(const nsRect &aRect)
+  {
+    pixman_box32_t box = { aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost() };
+    return box;
+  }
+
+  static inline pixman_box32_t RectToBox(const mozilla::gfx::IntRect &aRect)
+  {
+    pixman_box32_t box = { aRect.X(), aRect.Y(), aRect.XMost(), aRect.YMost() };
+    return box;
+  }
+
+
+  static inline nsRect BoxToRect(const pixman_box32_t &aBox)
+  {
+    return nsRect(aBox.x1, aBox.y1,
+                  aBox.x2 - aBox.x1,
+                  aBox.y2 - aBox.y1);
+  }
+
 private:
   pixman_region32_t mImpl;
 
 #ifndef MOZ_TREE_PIXMAN
   // For compatibility with pixman versions older than 0.25.2.
   static inline void
   pixman_region32_clear(pixman_region32_t *region)
   {
@@ -426,36 +446,16 @@ private:
       pixman_region32_clear(&mImpl);
     } else {
       pixman_box32_t box = RectToBox(aRect);
       pixman_region32_reset(&mImpl, &box);
     }
     return *this;
   }
 
-  static inline pixman_box32_t RectToBox(const nsRect &aRect)
-  {
-    pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
-    return box;
-  }
-
-  static inline pixman_box32_t RectToBox(const mozilla::gfx::IntRect &aRect)
-  {
-    pixman_box32_t box = { aRect.x, aRect.y, aRect.XMost(), aRect.YMost() };
-    return box;
-  }
-
-
-  static inline nsRect BoxToRect(const pixman_box32_t &aBox)
-  {
-    return nsRect(aBox.x1, aBox.y1,
-                  aBox.x2 - aBox.x1,
-                  aBox.y2 - aBox.y1);
-  }
-
   pixman_region32_t* Impl() const
   {
     return const_cast<pixman_region32_t*>(&mImpl);
   }
 };
 
 namespace mozilla {
 namespace gfx {
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -668,23 +668,27 @@ MessageChannel::CanSend() const
     }
     MonitorAutoLock lock(*mMonitor);
     return Connected();
 }
 
 void
 MessageChannel::WillDestroyCurrentMessageLoop()
 {
-#if !defined(ANDROID)
+#if defined(DEBUG)
 #if defined(MOZ_CRASHREPORTER)
     CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProtocolName"),
                                        nsDependentCString(mName));
 #endif
     MOZ_CRASH("MessageLoop destroyed before MessageChannel that's bound to it");
 #endif
+
+    // Clear mWorkerThread to avoid posting to it in the future.
+    MonitorAutoLock lock(*mMonitor);
+    mWorkerLoop = nullptr;
 }
 
 void
 MessageChannel::Clear()
 {
     // Don't clear mWorkerThread; we use it in AssertLinkThread() and
     // AssertWorkerThread().
     //
@@ -1959,17 +1963,17 @@ MessageChannel::MessageTask::Post()
     mScheduled = true;
 
     RefPtr<MessageTask> self = this;
     nsCOMPtr<nsIEventTarget> eventTarget =
         mChannel->mListener->GetMessageEventTarget(mMessage);
 
     if (eventTarget) {
         eventTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
-    } else {
+    } else if (mChannel->mWorkerLoop) {
         mChannel->mWorkerLoop->PostTask(self.forget());
     }
 }
 
 void
 MessageChannel::MessageTask::Clear()
 {
     mChannel->AssertWorkerThread();
@@ -2401,17 +2405,19 @@ MessageChannel::SetReplyTimeoutMs(int32_
 
 void
 MessageChannel::OnChannelConnected(int32_t peer_id)
 {
     MOZ_RELEASE_ASSERT(!mPeerPidSet);
     mPeerPidSet = true;
     mPeerPid = peer_id;
     RefPtr<CancelableRunnable> task = mOnChannelConnectedTask;
-    mWorkerLoop->PostTask(task.forget());
+    if (mWorkerLoop) {
+        mWorkerLoop->PostTask(task.forget());
+    }
 }
 
 void
 MessageChannel::DispatchOnChannelConnected()
 {
     AssertWorkerThread();
     MOZ_RELEASE_ASSERT(mPeerPidSet);
     mListener->OnChannelConnected(mPeerPid);
@@ -2594,29 +2600,31 @@ MessageChannel::OnNotifyMaybeChannelErro
 
     if (IsOnCxxStack()) {
       mChannelErrorTask = NewNonOwningCancelableRunnableMethod(
         "ipc::MessageChannel::OnNotifyMaybeChannelError",
         this,
         &MessageChannel::OnNotifyMaybeChannelError);
       RefPtr<Runnable> task = mChannelErrorTask;
       // 10 ms delay is completely arbitrary
-      mWorkerLoop->PostDelayedTask(task.forget(), 10);
+      if (mWorkerLoop) {
+          mWorkerLoop->PostDelayedTask(task.forget(), 10);
+      }
       return;
     }
 
     NotifyMaybeChannelError();
 }
 
 void
 MessageChannel::PostErrorNotifyTask()
 {
     mMonitor->AssertCurrentThreadOwns();
 
-    if (mChannelErrorTask)
+    if (mChannelErrorTask || !mWorkerLoop)
         return;
 
     // This must be the last code that runs on this thread!
     mChannelErrorTask = NewNonOwningCancelableRunnableMethod(
       "ipc::MessageChannel::OnNotifyMaybeChannelError",
       this,
       &MessageChannel::OnNotifyMaybeChannelError);
     RefPtr<Runnable> task = mChannelErrorTask;
--- a/js/src/devtools/rootAnalysis/CFG.js
+++ b/js/src/devtools/rootAnalysis/CFG.js
@@ -35,47 +35,55 @@ function isMatchingDestructor(constructo
     var callee = edge.Exp[0];
     if (callee.Kind != "Var")
         return false;
     var variable = callee.Variable;
     assert(variable.Kind == "Func");
     if (variable.Name[1].charAt(0) != '~')
         return false;
 
+    // Note that in some situations, a regular function can begin with '~', so
+    // we don't necessarily have a destructor in hand. This is probably a
+    // sixgill artifact, but in js::wasm::ModuleGenerator::~ModuleGenerator, a
+    // templatized static inline EraseIf is invoked, and it gets named ~EraseIf
+    // for some reason.
+    if (!("PEdgeCallInstance" in edge))
+        return false;
+
     var constructExp = constructor.PEdgeCallInstance.Exp;
     assert(constructExp.Kind == "Var");
 
     var destructExp = edge.PEdgeCallInstance.Exp;
     if (destructExp.Kind != "Var")
         return false;
 
     return sameVariable(constructExp.Variable, destructExp.Variable);
 }
 
 // Return all calls within the RAII scope of any constructor matched by
 // isConstructor(). (Note that this would be insufficient if you needed to
 // treat each instance separately, such as when different regions of a function
 // body were guarded by these constructors and you needed to do something
 // different with each.)
-function allRAIIGuardedCallPoints(bodies, body, isConstructor)
+function allRAIIGuardedCallPoints(typeInfo, bodies, body, isConstructor)
 {
     if (!("PEdge" in body))
         return [];
 
     var points = [];
 
     for (var edge of body.PEdge) {
         if (edge.Kind != "Call")
             continue;
         var callee = edge.Exp[0];
         if (callee.Kind != "Var")
             continue;
         var variable = callee.Variable;
         assert(variable.Kind == "Func");
-        if (!isConstructor(edge.Type, variable.Name))
+        if (!isConstructor(typeInfo, edge.Type, variable.Name))
             continue;
         if (!("PEdgeCallInstance" in edge))
             continue;
         if (edge.PEdgeCallInstance.Exp.Kind != "Var")
             continue;
 
         Array.prototype.push.apply(points, pointsInRAIIScope(bodies, body, edge));
     }
--- a/js/src/devtools/rootAnalysis/analyzeRoots.js
+++ b/js/src/devtools/rootAnalysis/analyzeRoots.js
@@ -1,15 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 
 "use strict";
 
 loadRelativeToScript('utility.js');
 loadRelativeToScript('annotations.js');
 loadRelativeToScript('CFG.js');
+loadRelativeToScript('dumpCFG.js');
 
 var sourceRoot = (os.getenv('SOURCE') || '') + '/'
 
 var functionName;
 var functionBodies;
 
 if (typeof scriptArgs[0] != 'string' || typeof scriptArgs[1] != 'string')
     throw "Usage: analyzeRoots.js [-f function_name] <gcFunctions.lst> <gcEdges.txt> <suppressedFunctions.lst> <gcTypes.txt> <typeInfo.txt> [start end [tmpfile]]";
@@ -24,32 +25,32 @@ var gcFunctionsFile = scriptArgs[0] || "
 var gcEdgesFile = scriptArgs[1] || "gcEdges.txt";
 var suppressedFunctionsFile = scriptArgs[2] || "suppressedFunctions.lst";
 var gcTypesFile = scriptArgs[3] || "gcTypes.txt";
 var typeInfoFile = scriptArgs[4] || "typeInfo.txt";
 var batch = (scriptArgs[5]|0) || 1;
 var numBatches = (scriptArgs[6]|0) || 1;
 var tmpfile = scriptArgs[7] || "tmp.txt";
 
-GCSuppressionTypes = loadTypeInfo(typeInfoFile)["Suppress GC"] || [];
-
 var gcFunctions = {};
 var text = snarf("gcFunctions.lst").split("\n");
 assert(text.pop().length == 0);
 for (var line of text)
     gcFunctions[mangled(line)] = true;
 
 var suppressedFunctions = {};
 var text = snarf(suppressedFunctionsFile).split("\n");
 assert(text.pop().length == 0);
 for (var line of text) {
     suppressedFunctions[line] = true;
 }
 text = null;
 
+var typeInfo = loadTypeInfo(typeInfoFile);
+
 var gcEdges = {};
 text = snarf(gcEdgesFile).split('\n');
 assert(text.pop().length == 0);
 for (var line of text) {
     var [ block, edge, func ] = line.split(" || ");
     if (!(block in gcEdges))
         gcEdges[block] = {}
     gcEdges[block][edge] = func;
@@ -744,17 +745,18 @@ function printEntryTrace(functionName, e
 
         print("    " + lineText + (edgeText.length ? ": " + edgeText : ""));
         entry = entry.why;
     }
 }
 
 function isRootedType(type)
 {
-    return type.Kind == "CSU" && isRootedTypeName(type.Name);
+    return type.Kind == "CSU" && ((type.Name in typeInfo.RootedPointers) ||
+                                  (type.Name in typeInfo.RootedGCThings));
 }
 
 function typeDesc(type)
 {
     if (type.Kind == "CSU") {
         return type.Name;
     } else if ('Type' in type) {
         var inner = typeDesc(type.Type);
@@ -836,17 +838,17 @@ var end = start_next - 1;
 
 function process(name, json) {
     functionName = name;
     functionBodies = JSON.parse(json);
 
     for (var body of functionBodies)
         body.suppressed = [];
     for (var body of functionBodies) {
-        for (var [pbody, id] of allRAIIGuardedCallPoints(functionBodies, body, isSuppressConstructor))
+        for (var [pbody, id] of allRAIIGuardedCallPoints(typeInfo, functionBodies, body, isSuppressConstructor))
             pbody.suppressed[id] = true;
     }
     processBodies(functionName);
 }
 
 if (theFunctionNameToFind) {
     var data = xdb.read_entry(theFunctionNameToFind);
     var json = data.readString();
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -1,16 +1,12 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 
 "use strict";
 
-// RAII types within which we should assume GC is suppressed, eg
-// AutoSuppressGC.
-var GCSuppressionTypes = [];
-
 // Ignore calls made through these function pointers
 var ignoreIndirectCalls = {
     "mallocSizeOf" : true,
     "aMallocSizeOf" : true,
     "__conv" : true,
     "__convf" : true,
     "prerrortable.c:callback_newtable" : true,
     "mozalloc_oom.cpp:void (* gAbortHandler)(size_t)" : true,
@@ -38,21 +34,30 @@ function indirectCallCannotGC(fullCaller
     if (name == "op" && /GetWeakmapKeyDelegate/.test(caller))
         return true;
 
     // hook called during script finalization which cannot GC.
     if (/CallDestroyScriptHook/.test(caller))
         return true;
 
     // template method called during marking and hence cannot GC
-    if (name == "op" && caller.indexOf("bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark(JSObject*)") != -1)
+    if (name == "op" && caller.includes("bool js::WeakMap<Key, Value, HashPolicy>::keyNeedsMark(JSObject*)"))
     {
         return true;
     }
 
+    // Call through a 'callback' function pointer, in a place where we're going
+    // to be throwing a JS exception.
+    if (name == "callback" && caller.includes("js::ErrorToException"))
+        return true;
+
+    // The math cache only gets called with non-GC math functions.
+    if (name == "f" && caller.includes("js::MathCache::lookup"))
+        return true;
+
     return false;
 }
 
 // Ignore calls through functions pointers with these types
 var ignoreClasses = {
     "JSStringFinalizer" : true,
     "SprintfState" : true,
     "SprintfStateStr" : true,
@@ -213,16 +218,18 @@ var ignoreFunctions = {
     "void test::RingbufferDumper::OnTestPartResult(testing::TestPartResult*)" : true,
 
     "float64 JS_GetCurrentEmbedderTime()" : true,
 
     "uint64 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
     "uint32 js::TenuringTracer::moveObjectToTenured(JSObject*, JSObject*, int32)" : true,
     "void js::Nursery::freeMallocedBuffers()" : true,
 
+    "void js::AutoEnterOOMUnsafeRegion::crash(uint64, int8*)" : true,
+
     // It would be cool to somehow annotate that nsTHashtable<T> will use
     // nsTHashtable<T>::s_MatchEntry for its matchEntry function pointer, but
     // there is no mechanism for that. So we will just annotate a particularly
     // troublesome logging-related usage.
     "EntryType* nsTHashtable<EntryType>::PutEntry(nsTHashtable<EntryType>::KeyType, const fallible_t&) [with EntryType = nsBaseHashtableET<nsCharPtrHashKey, nsAutoPtr<mozilla::LogModule> >; nsTHashtable<EntryType>::KeyType = const char*; nsTHashtable<EntryType>::fallible_t = mozilla::fallible_t]" : true,
     "EntryType* nsTHashtable<EntryType>::GetEntry(nsTHashtable<EntryType>::KeyType) const [with EntryType = nsBaseHashtableET<nsCharPtrHashKey, nsAutoPtr<mozilla::LogModule> >; nsTHashtable<EntryType>::KeyType = const char*]" : true,
     "EntryType* nsTHashtable<EntryType>::PutEntry(nsTHashtable<EntryType>::KeyType) [with EntryType = nsBaseHashtableET<nsPtrHashKey<const mozilla::BlockingResourceBase>, nsAutoPtr<mozilla::DeadlockDetector<mozilla::BlockingResourceBase>::OrderingEntry> >; nsTHashtable<EntryType>::KeyType = const mozilla::BlockingResourceBase*]" : true,
     "EntryType* nsTHashtable<EntryType>::GetEntry(nsTHashtable<EntryType>::KeyType) const [with EntryType = nsBaseHashtableET<nsPtrHashKey<const mozilla::BlockingResourceBase>, nsAutoPtr<mozilla::DeadlockDetector<mozilla::BlockingResourceBase>::OrderingEntry> >; nsTHashtable<EntryType>::KeyType = const mozilla::BlockingResourceBase*]" : true,
@@ -282,87 +289,88 @@ function ignoreGCFunction(mangled)
     // exhibit that behavior currently. For non-test code, we have dynamic and
     // static checks that ensure we don't GC. However, for test code we opt out
     // of static checks here, because of the above stated GMock/GTest issues,
     // and rely on only the dynamic checks provided by AutoAssertCannotGC.
     if (isHeapSnapshotMockClass(fun) || isGTest(fun))
         return true;
 
     // Templatized function
-    if (fun.indexOf("void nsCOMPtr<T>::Assert_NoQueryNeeded()") >= 0)
+    if (fun.includes("void nsCOMPtr<T>::Assert_NoQueryNeeded()"))
         return true;
 
     // These call through an 'op' function pointer.
-    if (fun.indexOf("js::WeakMap<Key, Value, HashPolicy>::getDelegate(") >= 0)
+    if (fun.includes("js::WeakMap<Key, Value, HashPolicy>::getDelegate("))
         return true;
 
     // XXX modify refillFreeList<NoGC> to not need data flow analysis to understand it cannot GC.
     if (/refillFreeList/.test(fun) && /\(js::AllowGC\)0u/.test(fun))
         return true;
     return false;
 }
 
 function stripUCSAndNamespace(name)
 {
     name = name.replace(/(struct|class|union|const) /g, "");
     name = name.replace(/(js::ctypes::|js::|JS::|mozilla::dom::|mozilla::)/g, "");
     return name;
 }
 
-function isRootedGCTypeName(name)
+function extraRootedGCThings()
+{
+    return [ 'JSAddonId' ];
+}
+
+function extraRootedPointers()
 {
-    return (name == "JSAddonId");
+    return [
+        'ModuleValidator',
+        'JSErrorResult',
+        'WrappableJSErrorResult',
+
+        // These are not actually rooted, but are only used in the context of
+        // AutoKeepAtoms.
+        'js::frontend::TokenStream',
+        'js::frontend::TokenStream::Position',
+
+        'mozilla::ErrorResult',
+        'mozilla::IgnoredErrorResult',
+        'mozilla::dom::binding_detail::FastErrorResult',
+    ];
 }
 
 function isRootedGCPointerTypeName(name)
 {
     name = stripUCSAndNamespace(name);
 
     if (name.startsWith('MaybeRooted<'))
         return /\(js::AllowGC\)1u>::RootType/.test(name);
 
-    if (name == "ErrorResult" ||
-        name == "JSErrorResult" ||
-        name == "WrappableJSErrorResult" ||
-        name == "binding_detail::FastErrorResult" ||
-        name == "IgnoredErrorResult" ||
-        name == "frontend::TokenStream" ||
-        name == "frontend::TokenStream::Position" ||
-        name == "ModuleValidator")
-    {
-        return true;
-    }
-
     return name.startsWith('Rooted') || name.startsWith('PersistentRooted');
 }
 
-function isRootedTypeName(name)
-{
-    return isRootedGCTypeName(name) || isRootedGCPointerTypeName(name);
-}
-
 function isUnsafeStorage(typeName)
 {
     typeName = stripUCSAndNamespace(typeName);
     return typeName.startsWith('UniquePtr<');
 }
 
-function isSuppressConstructor(edgeType, varName)
+function isSuppressConstructor(typeInfo, edgeType, varName)
 {
     // Check whether this could be a constructor
     if (edgeType.Kind != 'Function')
         return false;
     if (!('TypeFunctionCSU' in edgeType))
         return false;
     if (edgeType.Type.Kind != 'Void')
         return false;
 
     // Check whether the type is a known suppression type.
     var type = edgeType.TypeFunctionCSU.Type.Name;
-    if (GCSuppressionTypes.indexOf(type) == -1)
+    if (!(type in typeInfo.GCSuppressors))
         return false;
 
     // And now make sure this is the constructor, not some other method on a
     // suppression type. varName[0] contains the qualified name.
     var [ mangled, unmangled ] = splitFunction(varName[0]);
     if (mangled.search(/C\dE/) == -1)
         return false; // Mangled names of constructors have C<num>E
     var m = unmangled.match(/([~\w]+)(?:<.*>)?\(/);
--- a/js/src/devtools/rootAnalysis/computeCallgraph.js
+++ b/js/src/devtools/rootAnalysis/computeCallgraph.js
@@ -123,17 +123,17 @@ function processBody(functionName, body)
             } else {
                 printErr("invalid " + callee.kind + " callee");
                 debugger;
             }
         }
     }
 }
 
-GCSuppressionTypes = loadTypeInfo(typeInfo_filename)["Suppress GC"] || [];
+var typeInfo = loadTypeInfo(typeInfo_filename);
 
 loadTypes("src_comp.xdb");
 
 var xdb = xdbLibrary();
 xdb.open("src_body.xdb");
 
 printErr("Finished loading data structures");
 
@@ -150,17 +150,17 @@ if (theFunctionNameToFind) {
 }
 
 function process(functionName, functionBodies)
 {
     for (var body of functionBodies)
         body.suppressed = [];
 
     for (var body of functionBodies) {
-        for (var [pbody, id] of allRAIIGuardedCallPoints(functionBodies, body, isSuppressConstructor))
+        for (var [pbody, id] of allRAIIGuardedCallPoints(typeInfo, functionBodies, body, isSuppressConstructor))
             pbody.suppressed[id] = true;
     }
 
     for (var body of functionBodies)
         processBody(functionName, body);
 
     // GCC generates multiple constructors and destructors ("in-charge" and
     // "not-in-charge") to handle virtual base classes. They are normally
--- a/js/src/devtools/rootAnalysis/computeGCTypes.js
+++ b/js/src/devtools/rootAnalysis/computeGCTypes.js
@@ -3,30 +3,35 @@
 "use strict";
 
 loadRelativeToScript('utility.js');
 loadRelativeToScript('annotations.js');
 
 var gcTypes_filename = scriptArgs[0] || "gcTypes.txt";
 var typeInfo_filename = scriptArgs[1] || "typeInfo.txt";
 
-var annotations = {
+var typeInfo = {
     'GCPointers': [],
     'GCThings': [],
     'NonGCTypes': {}, // unused
     'NonGCPointers': {},
+    'RootedGCThings': {},
     'RootedPointers': {},
+
+    // RAII types within which we should assume GC is suppressed, eg
+    // AutoSuppressGC.
     'GCSuppressors': {},
 };
 
 var gDescriptors = new Map; // Map from descriptor string => Set of typeName
 
 var structureParents = {}; // Map from field => list of <parent, fieldName>
 var pointerParents = {}; // Map from field => list of <parent, fieldName>
 var baseClasses = {}; // Map from struct name => list of base class name strings
+var subClasses = {}; // Map from struct name => list of subclass  name strings
 
 var gcTypes = {}; // map from parent struct => Set of GC typed children
 var gcPointers = {}; // map from parent struct => Set of GC typed children
 var gcFields = new Map;
 
 var rootedPointers = {};
 
 function processCSU(csu, body)
@@ -56,27 +61,27 @@ function processCSU(csu, body)
         }
     }
 
     for (let { 'Name': [ annType, tag ] } of (body.Annotation || [])) {
         if (annType != 'Tag')
             continue;
 
         if (tag == 'GC Pointer')
-            annotations.GCPointers.push(csu);
+            typeInfo.GCPointers.push(csu);
         else if (tag == 'Invalidated by GC')
-            annotations.GCPointers.push(csu);
+            typeInfo.GCPointers.push(csu);
         else if (tag == 'GC Thing')
-            annotations.GCThings.push(csu);
+            typeInfo.GCThings.push(csu);
         else if (tag == 'Suppressed GC Pointer')
-            annotations.NonGCPointers[csu] = true;
+            typeInfo.NonGCPointers[csu] = true;
         else if (tag == 'Rooted Pointer')
-            annotations.RootedPointers[csu] = true;
+            typeInfo.RootedPointers[csu] = true;
         else if (tag == 'Suppress GC')
-            annotations.GCSuppressors[csu] = true;
+            typeInfo.GCSuppressors[csu] = true;
     }
 }
 
 // csu.field is of type inner
 function addNestedStructure(csu, inner, field)
 {
     if (!(inner in structureParents))
         structureParents[inner] = [];
@@ -86,16 +91,19 @@ function addNestedStructure(csu, inner, 
 
     structureParents[inner].push([ csu, field ]);
 }
 
 function addBaseClass(csu, base) {
     if (!(csu in baseClasses))
         baseClasses[csu] = [];
     baseClasses[csu].push(base);
+    if (!(base in subClasses))
+        subClasses[base] = [];
+    subClasses[base].push(csu);
     var k = baseClasses[csu].length;
     addNestedStructure(csu, base, `<base-${k}>`);
 }
 
 function addNestedPointer(csu, inner, field)
 {
     if (!(inner in pointerParents))
         pointerParents[inner] = [];
@@ -114,32 +122,34 @@ for (var csuIndex = minStream; csuIndex 
     var json = JSON.parse(data.readString());
     assert(json.length == 1);
     processCSU(csu.readString(), json[0]);
 
     xdb.free_string(csu);
     xdb.free_string(data);
 }
 
+for (const typename of extraRootedGCThings())
+    typeInfo.RootedGCThings[typename] = true;
+
+for (const typename of extraRootedPointers())
+    typeInfo.RootedPointers[typename] = true;
+
 // Now that we have the whole hierarchy set up, add all the types and propagate
 // info.
-for (let csu of annotations.GCThings)
+for (const csu of typeInfo.GCThings)
     addGCType(csu);
-for (let csu of annotations.GCPointers)
+for (const csu of typeInfo.GCPointers)
     addGCPointer(csu);
 
-function stars(n) { return n ? '*' + stars(n-1) : '' };
-
 // "typeName is a (pointer to a)^'typePtrLevel' GC type because it contains a field
 // named 'child' of type 'why' (or pointer to 'why' if fieldPtrLevel == 1), which is
 // itself a GCThing or GCPointer."
 function markGCType(typeName, child, why, typePtrLevel, fieldPtrLevel, indent)
 {
-    //printErr(`${indent}${typeName}${stars(typePtrLevel)} may be a gctype/ptr because of its child '${child}' of type ${why}${stars(fieldPtrLevel)}`);
-
     // Some types, like UniquePtr, do not mark/trace/relocate their contained
     // pointers and so should not hold them live across a GC. UniquePtr in
     // particular should be the only thing pointing to a structure containing a
     // GCPointer, so nothing else can possibly trace it and it'll die when the
     // UniquePtr goes out of scope. So we say that memory pointed to by a
     // UniquePtr is just as unsafe as the stack for storing GC pointers.
     if (!fieldPtrLevel && isUnsafeStorage(typeName)) {
         // The UniquePtr itself is on the stack but when you dereference the
@@ -161,29 +171,32 @@ function markGCType(typeName, child, why
     var ptrLevel = typePtrLevel + fieldPtrLevel;
 
     // ...except when > 2 levels of pointers away from an actual GC thing, stop
     // searching the graph. (This would just be > 1, except that a UniquePtr
     // field might still have a GC pointer.)
     if (ptrLevel > 2)
         return;
 
-    if (ptrLevel == 0 && isRootedGCTypeName(typeName))
+    if (isRootedGCPointerTypeName(typeName) && !(typeName in typeInfo.RootedPointers))
+        printErr("FIXME: use in-source annotation for " + typeName);
+
+    if (ptrLevel == 0 && (typeName in typeInfo.RootedGCThings))
         return;
-    if (ptrLevel == 1 && isRootedGCPointerTypeName(typeName))
+    if (ptrLevel == 1 && (isRootedGCPointerTypeName(typeName) || (typeName in typeInfo.RootedPointers)))
         return;
 
     if (ptrLevel == 0) {
-        if (typeName in annotations.NonGCTypes)
+        if (typeName in typeInfo.NonGCTypes)
             return;
         if (!(typeName in gcTypes))
             gcTypes[typeName] = new Set();
         gcTypes[typeName].add(why);
     } else if (ptrLevel == 1) {
-        if (typeName in annotations.NonGCPointers)
+        if (typeName in typeInfo.NonGCPointers)
             return;
         if (!(typeName in gcPointers))
             gcPointers[typeName] = new Set();
         gcPointers[typeName].add(why);
     }
 
     if (ptrLevel < 2) {
         if (!gcFields.has(typeName))
@@ -210,38 +223,44 @@ function addGCType(typeName, child, why,
     markGCType(typeName, '<annotation>', '(annotation)', 0, 0, "");
 }
 
 function addGCPointer(typeName)
 {
     markGCType(typeName, '<pointer-annotation>', '(annotation)', 1, 0, "");
 }
 
-// Add an arbitrary descriptor to a type, and apply it recursively to all base
-// structs and structs that contain the given typeName as a field.
-function addDescriptor(typeName, descriptor)
+// Call a function for a type and every type that contains the type in a field
+// or as a base class (which internally is pretty much the same thing --
+// sublcasses are structs beginning with the base class and adding on their
+// local fields.)
+function foreachContainingStruct(typeName, func, seen = new Set())
 {
-    if (!gDescriptors.has(descriptor))
-        gDescriptors.set(descriptor, new Set);
-    let descriptorTypes = gDescriptors.get(descriptor);
-    if (!descriptorTypes.has(typeName)) {
-        descriptorTypes.add(typeName);
+    function recurse(container, typeName) {
+        if (seen.has(typeName))
+            return;
+        seen.add(typeName);
+
+        func(container, typeName);
+
+        if (typeName in subClasses) {
+            for (const sub of subClasses[typeName])
+                recurse("subclass of " + typeName, sub);
+        }
         if (typeName in structureParents) {
-            for (let [holder, field] of structureParents[typeName])
-                addDescriptor(holder, descriptor);
-        }
-        if (typeName in baseClasses) {
-            for (let base of baseClasses[typeName])
-                addDescriptor(base, descriptor);
+            for (const [holder, field] of structureParents[typeName])
+                recurse(field + " : " + typeName, holder);
         }
     }
+
+    recurse('<annotation>', typeName);
 }
 
 for (var type of listNonGCPointers())
-    annotations.NonGCPointers[type] = true;
+    typeInfo.NonGCPointers[type] = true;
 
 function explain(csu, indent, seen) {
     if (!seen)
         seen = new Set();
     seen.add(csu);
     if (!gcFields.has(csu))
         return;
     var fields = gcFields.get(csu);
@@ -283,17 +302,19 @@ for (var csu in gcTypes) {
 for (var csu in gcPointers) {
     print("GCPointer: " + csu);
     explain(csu, "  ");
 }
 
 // Redirect output to the typeInfo file and close the gcTypes file.
 os.file.close(os.file.redirect(typeInfo_filename));
 
-for (let csu in annotations.GCSuppressors)
-    addDescriptor(csu, 'Suppress GC');
+// Compute the set of types that suppress GC within their RAII scopes (eg
+// AutoSuppressGC, AutoSuppressGCForAnalysis).
+var seen = new Set();
+for (let csu in typeInfo.GCSuppressors)
+    foreachContainingStruct(csu,
+                            (holder, typeName) => { typeInfo.GCSuppressors[typeName] = holder },
+                            seen);
 
-for (let [descriptor, types] of gDescriptors) {
-    for (let csu of types)
-        print(descriptor + "$$" + csu);
-}
+print(JSON.stringify(typeInfo, null, 4));
 
 os.file.close(os.file.redirect(origOut));
--- a/js/src/devtools/rootAnalysis/dumpCFG.js
+++ b/js/src/devtools/rootAnalysis/dumpCFG.js
@@ -236,17 +236,20 @@ function str_edge(edge, env) {
   if (Kind == 'Loop')
     return str_loop(prefix, edge);
 
   print(JSON.stringify(edge, null, 4));
   throw "unhandled edge type";
 }
 
 function str(unknown) {
-  if ("Index" in unknown) {
+  if ("Name" in unknown) {
+    return str_Variable(unknown);
+  } else if ("Index" in unknown) {
+    // Note: Variable also has .Index, with a different meaning.
     return str_edge(unknown);
   } else if ("Kind" in unknown) {
     if ("BlockId" in unknown)
       return str_Variable(unknown);
     return str_value(unknown);
   } else if ("Type" in unknown) {
     return str_Type(unknown);
   }
--- a/js/src/devtools/rootAnalysis/utility.js
+++ b/js/src/devtools/rootAnalysis/utility.js
@@ -262,16 +262,10 @@ function addToKeyedList(collection, key,
 {
     if (!(key in collection))
         collection[key] = [];
     collection[key].push(entry);
 }
 
 function loadTypeInfo(filename)
 {
-    var info = {};
-    for (var line of readFileLines_gen(filename)) {
-        line = line.replace(/\n/, "");
-        let [property, name] = line.split("$$");
-        addToKeyedList(info, property, name);
-    }
-    return info;
+    return JSON.parse(os.file.readFile(filename));
 }
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -32,16 +32,17 @@ if CONFIG['MOZ_DEBUG']:
 
 XPIDL_MODULE = 'layout_base'
 
 EXPORTS += [
     'CaretAssociationHint.h',
     'FrameProperties.h',
     'LayoutLogging.h',
     'MobileViewportManager.h',
+    'nsAutoLayoutPhase.h',
     'nsBidi.h',
     'nsBidiPresUtils.h',
     'nsCaret.h',
     'nsChangeHint.h',
     'nsCompatibility.h',
     'nsCSSFrameConstructor.h',
     'nsFrameManager.h',
     'nsFrameManagerBase.h',
--- a/layout/base/nsAutoLayoutPhase.cpp
+++ b/layout/base/nsAutoLayoutPhase.cpp
@@ -28,32 +28,47 @@ nsAutoLayoutPhase::~nsAutoLayoutPhase()
 
 void
 nsAutoLayoutPhase::Enter()
 {
   switch (mPhase) {
     case eLayoutPhase_Paint:
       MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
                  "recurring into paint");
+      MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_DisplayListBuilding] == 0,
+                 "recurring into paint from display list building");
       MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
                  "painting in the middle of reflow");
       MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
                  "painting in the middle of frame construction");
       break;
+    case eLayoutPhase_DisplayListBuilding:
+      // It's fine and expected to be in a paint here.
+      MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_DisplayListBuilding] == 0,
+                 "recurring into display list building");
+      MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
+                 "display list building in the middle of reflow");
+      MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
+                 "display list building in the middle of frame construction");
+      break;
     case eLayoutPhase_Reflow:
       MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
                  "reflowing in the middle of a paint");
+      MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_DisplayListBuilding] == 0,
+                 "reflowing in the middle of a display list building");
       MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
                  "recurring into reflow");
       MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
                  "reflowing in the middle of frame construction");
       break;
     case eLayoutPhase_FrameC:
       MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Paint] == 0,
                  "constructing frames in the middle of a paint");
+      MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_DisplayListBuilding] == 0,
+                 "constructing frames in the middle of a display list building");
       MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_Reflow] == 0,
                  "constructing frames in the middle of reflow");
       MOZ_ASSERT(mPresContext->mLayoutPhaseCount[eLayoutPhase_FrameC] == 0,
                  "recurring into frame construction");
       MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
                  "constructing frames and scripts are not blocked");
       break;
     case eLayoutPhase_COUNT:
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -2704,32 +2704,56 @@ nsLayoutUtils::PostTranslate(Matrix4x4& 
             0.0f);
   if (aRounded) {
     gfxOrigin.x = NS_round(gfxOrigin.x);
     gfxOrigin.y = NS_round(gfxOrigin.y);
   }
   aTransform.PostTranslate(gfxOrigin);
 }
 
+// We want to this return true for the scroll frame, but not the
+// scrolled frame (which has the same content).
+bool
+nsLayoutUtils::FrameHasDisplayPort(nsIFrame* aFrame, nsIFrame* aScrolledFrame)
+{
+  if (!aFrame->GetContent() || !HasDisplayPort(aFrame->GetContent())) {
+    return false;
+  }
+  nsIScrollableFrame* sf = do_QueryFrame(aFrame);
+  if (sf) {
+    if (aScrolledFrame && aScrolledFrame != sf->GetScrolledFrame()) {
+      return false;
+    }
+    return true;
+  }
+  return false;
+}
+
 Matrix4x4
 nsLayoutUtils::GetTransformToAncestor(nsIFrame *aFrame,
                                       const nsIFrame *aAncestor,
-                                      bool aInCSSUnits)
+                                      uint32_t aFlags,
+                                      nsIFrame** aOutAncestor)
 {
   nsIFrame* parent;
   Matrix4x4 ctm;
   if (aFrame == aAncestor) {
     return ctm;
   }
-  ctm = aFrame->GetTransformMatrix(aAncestor, &parent, aInCSSUnits);
-  while (parent && parent != aAncestor) {
+  ctm = aFrame->GetTransformMatrix(aAncestor, &parent, aFlags);
+  while (parent && parent != aAncestor &&
+    (!(aFlags & nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) ||
+      (!parent->IsStackingContext() && !FrameHasDisplayPort(parent)))) {
     if (!parent->Extend3DContext()) {
       ctm.ProjectTo2D();
     }
-    ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent, aInCSSUnits);
+    ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent, aFlags);
+  }
+  if (aOutAncestor) {
+    *aOutAncestor = parent;
   }
   return ctm;
 }
 
 gfxSize
 nsLayoutUtils::GetTransformToAncestorScale(nsIFrame* aFrame)
 {
   Matrix4x4 transform = GetTransformToAncestor(aFrame,
@@ -3024,25 +3048,31 @@ TransformGfxPointFromAncestor(nsIFrame *
   return true;
 }
 
 static Rect
 TransformGfxRectToAncestor(nsIFrame *aFrame,
                            const Rect &aRect,
                            const nsIFrame *aAncestor,
                            bool* aPreservesAxisAlignedRectangles = nullptr,
-                           Maybe<Matrix4x4>* aMatrixCache = nullptr)
+                           Maybe<Matrix4x4>* aMatrixCache = nullptr,
+                           bool aStopAtStackingContextAndDisplayPort = false,
+                           nsIFrame** aOutAncestor = nullptr)
 {
   Matrix4x4 ctm;
   if (aMatrixCache && *aMatrixCache) {
     // We are given a matrix to use, so use it
     ctm = aMatrixCache->value();
   } else {
     // Else, compute it
-    ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
+    uint32_t flags = 0;
+    if (aStopAtStackingContextAndDisplayPort) {
+      flags |= nsIFrame::STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT;
+    }
+    ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor, flags, aOutAncestor);
     if (aMatrixCache) {
       // and put it in the cache, if provided
       *aMatrixCache = Some(ctm);
     }
   }
   // Fill out the axis-alignment flag
   if (aPreservesAxisAlignedRectangles) {
     Matrix matrix2d;
@@ -3093,36 +3123,42 @@ nsLayoutUtils::TransformAncestorPointToF
                    NSFloatPixelsToAppUnits(float(result.y), factor));
 }
 
 nsRect
 nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame,
                                             const nsRect& aRect,
                                             const nsIFrame* aAncestor,
                                             bool* aPreservesAxisAlignedRectangles /* = nullptr */,
-                                            Maybe<Matrix4x4>* aMatrixCache /* = nullptr */)
+                                            Maybe<Matrix4x4>* aMatrixCache /* = nullptr */,
+                                            bool aStopAtStackingContextAndDisplayPort /* = false */,
+                                            nsIFrame** aOutAncestor /* = nullptr */)
 {
   SVGTextFrame* text = GetContainingSVGTextFrame(aFrame);
 
   float srcAppUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
   Rect result;
 
   if (text) {
     result = ToRect(text->TransformFrameRectFromTextChild(aRect, aFrame));
-    result = TransformGfxRectToAncestor(text, result, aAncestor, nullptr, aMatrixCache);
+    result = TransformGfxRectToAncestor(text, result, aAncestor,
+                                        nullptr, aMatrixCache,
+                                        aStopAtStackingContextAndDisplayPort, aOutAncestor);
     // TransformFrameRectFromTextChild could involve any kind of transform, we
     // could drill down into it to get an answer out of it but we don't yet.
     if (aPreservesAxisAlignedRectangles)
       *aPreservesAxisAlignedRectangles = false;
   } else {
     result = Rect(NSAppUnitsToFloatPixels(aRect.x, srcAppUnitsPerDevPixel),
                   NSAppUnitsToFloatPixels(aRect.y, srcAppUnitsPerDevPixel),
                   NSAppUnitsToFloatPixels(aRect.width, srcAppUnitsPerDevPixel),
                   NSAppUnitsToFloatPixels(aRect.height, srcAppUnitsPerDevPixel));
-    result = TransformGfxRectToAncestor(aFrame, result, aAncestor, aPreservesAxisAlignedRectangles, aMatrixCache);
+    result = TransformGfxRectToAncestor(aFrame, result, aAncestor,
+                                        aPreservesAxisAlignedRectangles, aMatrixCache,
+                                        aStopAtStackingContextAndDisplayPort, aOutAncestor);
   }
 
   float destAppUnitsPerDevPixel = aAncestor->PresContext()->AppUnitsPerDevPixel();
   return nsRect(NSFloatPixelsToAppUnits(float(result.x), destAppUnitsPerDevPixel),
                 NSFloatPixelsToAppUnits(float(result.y), destAppUnitsPerDevPixel),
                 NSFloatPixelsToAppUnits(float(result.width), destAppUnitsPerDevPixel),
                 NSFloatPixelsToAppUnits(float(result.height), destAppUnitsPerDevPixel));
 }
@@ -3487,16 +3523,56 @@ nsLayoutUtils::ExpireDisplayPortOnAsyncS
       // If the trigger succeeded, we stop because when the trigger executes
       // it will call this function again to trigger the next ancestor up the
       // chain.
       break;
     }
   }
 }
 
+void
+nsLayoutUtils::AddExtraBackgroundItems(nsDisplayListBuilder& aBuilder,
+                                       nsDisplayList& aList,
+                                       nsIFrame* aFrame,
+                                       const nsRect& aCanvasArea,
+                                       const nsRegion& aVisibleRegion,
+                                       nscolor aBackstop)
+{
+  LayoutFrameType frameType = aFrame->Type();
+  nsPresContext* presContext = aFrame->PresContext();
+  nsIPresShell* presShell = presContext->PresShell();
+
+  // For the viewport frame in print preview/page layout we want to paint
+  // the grey background behind the page, not the canvas color.
+  if (frameType == LayoutFrameType::Viewport &&
+      nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
+    nsRect bounds = nsRect(aBuilder.ToReferenceFrame(aFrame),
+                           aFrame->GetSize());
+    nsDisplayListBuilder::AutoBuildingDisplayList
+      buildingDisplayList(&aBuilder, aFrame, bounds, false);
+    presShell->AddPrintPreviewBackgroundItem(aBuilder, aList, aFrame, bounds);
+  } else if (frameType != LayoutFrameType::Page) {
+    // For printing, this function is first called on an nsPageFrame, which
+    // creates a display list with a PageContent item. The PageContent item's
+    // paint function calls this function on the nsPageFrame's child which is
+    // an nsPageContentFrame. We only want to add the canvas background color
+    // item once, for the nsPageContentFrame.
+
+    // Add the canvas background color to the bottom of the list. This
+    // happens after we've built the list so that AddCanvasBackgroundColorItem
+    // can monkey with the contents if necessary.
+    nsRect canvasArea = aVisibleRegion.GetBounds();
+    canvasArea.IntersectRect(aCanvasArea, canvasArea);
+    nsDisplayListBuilder::AutoBuildingDisplayList
+      buildingDisplayList(&aBuilder, aFrame, canvasArea, false);
+    presShell->AddCanvasBackgroundColorItem(
+      aBuilder, aList, aFrame, canvasArea, aBackstop);
+  }
+}
+
 nsresult
 nsLayoutUtils::PaintFrame(gfxContext* aRenderingContext, nsIFrame* aFrame,
                           const nsRegion& aDirtyRegion, nscolor aBackstop,
                           nsDisplayListBuilderMode aBuilderMode,
                           PaintFrameFlags aFlags)
 {
   AUTO_PROFILER_LABEL("nsLayoutUtils::PaintFrame", GRAPHICS);
 
@@ -3645,43 +3721,17 @@ nsLayoutUtils::PaintFrame(gfxContext* aR
       }
 
       nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(&builder, id);
 
       builder.SetDirtyRect(dirtyRect);
       aFrame->BuildDisplayListForStackingContext(&builder, &list);
     }
 
-    LayoutFrameType frameType = aFrame->Type();
-
-    // For the viewport frame in print preview/page layout we want to paint
-    // the grey background behind the page, not the canvas color.
-    if (frameType == LayoutFrameType::Viewport &&
-        nsLayoutUtils::NeedsPrintPreviewBackground(presContext)) {
-      nsRect bounds = nsRect(builder.ToReferenceFrame(aFrame),
-                             aFrame->GetSize());
-      nsDisplayListBuilder::AutoBuildingDisplayList
-        buildingDisplayList(&builder, aFrame, bounds, false);
-      presShell->AddPrintPreviewBackgroundItem(builder, list, aFrame, bounds);
-    } else if (frameType != LayoutFrameType::Page) {
-      // For printing, this function is first called on an nsPageFrame, which
-      // creates a display list with a PageContent item. The PageContent item's
-      // paint function calls this function on the nsPageFrame's child which is
-      // an nsPageContentFrame. We only want to add the canvas background color
-      // item once, for the nsPageContentFrame.
-
-      // Add the canvas background color to the bottom of the list. This
-      // happens after we've built the list so that AddCanvasBackgroundColorItem
-      // can monkey with the contents if necessary.
-      canvasArea.IntersectRect(canvasArea, visibleRegion.GetBounds());
-      nsDisplayListBuilder::AutoBuildingDisplayList
-        buildingDisplayList(&builder, aFrame, canvasArea, false);
-      presShell->AddCanvasBackgroundColorItem(
-             builder, list, aFrame, canvasArea, aBackstop);
-    }
+    AddExtraBackgroundItems(builder, list, aFrame, canvasArea, visibleRegion, aBackstop);
 
     builder.LeavePresShell(aFrame, &list);
 
     if (!record.GetStart().IsNull() && gfxPrefs::LayersDrawFPS()) {
       if (RefPtr<LayerManager> lm = builder.GetWidgetLayerManager()) {
         if (PaintTiming* pt = ClientLayerManager::MaybeGetPaintTiming(lm)) {
           pt->dlMs() = (TimeStamp::Now() - record.GetStart()).ToMilliseconds();
         }
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -208,16 +208,24 @@ public:
     RelativeTo aRelativeTo = RelativeTo::ScrollPort);
 
   /**
    * Check whether the given element has a displayport.
    */
   static bool HasDisplayPort(nsIContent* aContent);
 
   /**
+   * Check whether the given frame has a displayport. It returns false
+   * for scrolled frames and true for the corresponding scroll frame.
+   * Optionally pass the child, and it only returns true if the child is the
+   * scrolled frame for the displayport.
+   */
+  static bool FrameHasDisplayPort(nsIFrame* aFrame, nsIFrame* aScrolledFrame = nullptr);
+
+  /**
    * Check if the given element has a margins based displayport but is missing a
    * displayport base rect that it needs to properly compute a displayport rect.
    */
   static bool IsMissingDisplayPortBaseRect(nsIContent* aContent);
 
   /**
    * Go through the IPC Channel and update displayport margins for content
    * elements based on UpdateFrame messages. The messages are left in the
@@ -860,27 +868,30 @@ public:
    *   recomputing the matrix.
    * non-null pointer to a non-empty Matrix4x4 - The provided matrix will be used
    *   as the transform matrix and applied to the rect.
    */
   static nsRect TransformFrameRectToAncestor(nsIFrame* aFrame,
                                              const nsRect& aRect,
                                              const nsIFrame* aAncestor,
                                              bool* aPreservesAxisAlignedRectangles = nullptr,
-                                             mozilla::Maybe<Matrix4x4>* aMatrixCache = nullptr);
+                                             mozilla::Maybe<Matrix4x4>* aMatrixCache = nullptr,
+                                             bool aStopAtStackingContextAndDisplayPort = false,
+                                             nsIFrame** aOutAncestor = nullptr);
 
 
   /**
    * Gets the transform for aFrame relative to aAncestor. Pass null for
    * aAncestor to go up to the root frame. aInCSSUnits set to true will
    * return CSS units, set to false (the default) will return App units.
    */
   static Matrix4x4 GetTransformToAncestor(nsIFrame *aFrame,
                                           const nsIFrame *aAncestor,
-                                          bool aInCSSUnits = false);
+                                          uint32_t aFlags = 0,
+                                          nsIFrame** aOutAncestor = nullptr);
 
   /**
    * Gets the scale factors of the transform for aFrame relative to the root
    * frame if this transform is 2D, or the identity scale factors otherwise.
    */
   static gfxSize GetTransformToAncestorScale(nsIFrame* aFrame);
 
   /**
@@ -2230,16 +2241,23 @@ public:
    *          created by script with XHTML.
    *
    *  <body><p contenteditable="true"></p></body>
    *    returns nullptr because <body> isn't editable.
    */
   static nsIContent*
     GetEditableRootContentByContentEditable(nsIDocument* aDocument);
 
+  static void AddExtraBackgroundItems(nsDisplayListBuilder& aBuilder,
+                                      nsDisplayList& aList,
+                                      nsIFrame* aFrame,
+                                      const nsRect& aCanvasArea,
+                                      const nsRegion& aVisibleRegion,
+                                      nscolor aBackstop);
+
   /**
    * Returns true if the passed in prescontext needs the dark grey background
    * that goes behind the page of a print preview presentation.
    */
   static bool NeedsPrintPreviewBackground(nsPresContext* aPresContext);
 
   /**
    * Adds all font faces used in the frame tree starting from aFrame
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -103,16 +103,17 @@ enum nsPresContext_CachedIntPrefType {
 const uint8_t kPresContext_DefaultVariableFont_ID = 0x00; // kGenericFont_moz_variable
 const uint8_t kPresContext_DefaultFixedFont_ID    = 0x01; // kGenericFont_moz_fixed
 
 #ifdef DEBUG
 struct nsAutoLayoutPhase;
 
 enum nsLayoutPhase {
   eLayoutPhase_Paint,
+  eLayoutPhase_DisplayListBuilding, // sometimes a subset of the paint phase
   eLayoutPhase_Reflow,
   eLayoutPhase_FrameC,
   eLayoutPhase_COUNT
 };
 #endif
 
 /* Used by nsPresContext::HasAuthorSpecifiedRules */
 #define NS_AUTHOR_SPECIFIED_BACKGROUND      (1 << 0)
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -361,18 +361,16 @@ TextOverflow::TextOverflow(nsDisplayList
       mAdjustForPixelSnapping = mCanHaveInlineAxisScrollbar;
     }
     // Use a null containerSize to convert a vector from logical to physical.
     const nsSize nullContainerSize;
     mContentArea.MoveBy(mBlockWM,
                         LogicalPoint(mBlockWM,
                                      mScrollableFrame->GetScrollPosition(),
                                      nullContainerSize));
-    nsIFrame* scrollFrame = do_QueryFrame(mScrollableFrame);
-    scrollFrame->AddStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
   }
   uint8_t direction = aBlockFrame->StyleVisibility()->mDirection;
   const nsStyleTextReset* style = aBlockFrame->StyleTextReset();
   if (mBlockWM.IsBidiLTR()) {
     mIStart.Init(style->mTextOverflow.GetLeft(direction));
     mIEnd.Init(style->mTextOverflow.GetRight(direction));
   } else {
     mIStart.Init(style->mTextOverflow.GetRight(direction));
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1322,20 +1322,27 @@ nsIFrame::GetMarginRectRelativeToSelf() 
   r.Inflate(m);
   return r;
 }
 
 bool
 nsIFrame::IsTransformed(const nsStyleDisplay* aStyleDisplay,
                         EffectSet* aEffectSet) const
 {
+  return IsCSSTransformed(aStyleDisplay, aEffectSet) ||
+         IsSVGTransformed();
+}
+
+bool
+nsIFrame::IsCSSTransformed(const nsStyleDisplay* aStyleDisplay,
+                           EffectSet* aEffectSet) const
+{
   MOZ_ASSERT(aStyleDisplay == StyleDisplay());
   return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
           (aStyleDisplay->HasTransform(this) ||
-           IsSVGTransformed() ||
            HasAnimationOfTransform(aEffectSet)));
 }
 
 bool
 nsIFrame::HasAnimationOfTransform(EffectSet* aEffectSet) const
 {
   EffectSet* effects =
     aEffectSet ? aEffectSet : EffectSet::GetEffectSet(this);
@@ -1397,17 +1404,17 @@ bool
 nsIFrame::Combines3DTransformWithAncestors(const nsStyleDisplay* aStyleDisplay,
                                            EffectSet* aEffectSet) const
 {
   MOZ_ASSERT(aStyleDisplay == StyleDisplay());
   nsIFrame* parent = GetFlattenedTreeParentPrimaryFrame();
   if (!parent || !parent->Extend3DContext()) {
     return false;
   }
-  return IsTransformed(aStyleDisplay,aEffectSet) ||
+  return IsCSSTransformed(aStyleDisplay, aEffectSet) ||
          BackfaceIsHidden(aStyleDisplay);
 }
 
 bool
 nsIFrame::In3DContextAndBackfaceIsHidden(EffectSet* aEffectSet) const
 {
   // While both tests fail most of the time, test BackfaceIsHidden()
   // first since it's likely to fail faster.
@@ -3103,31 +3110,20 @@ nsIFrame::BuildDisplayListForChild(nsDis
   }
 
   // Child is composited if it's transformed, partially transparent, or has
   // SVG effects or a blend mode..
   EffectSet* effectSet = EffectSet::GetEffectSet(child);
   const nsStyleDisplay* disp = child->StyleDisplay();
   const nsStyleEffects* effects = child->StyleEffects();
   const nsStylePosition* pos = child->StylePosition();
-  bool isVisuallyAtomic = child->HasOpacity(effectSet)
-    || child->IsTransformed(disp, effectSet)
-    // strictly speaking, 'perspective' doesn't require visual atomicity,
-    // but the spec says it acts like the rest of these
-    || disp->mChildPerspective.GetUnit() == eStyleUnit_Coord
-    || effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL
-    || nsSVGIntegrationUtils::UsingEffectsForFrame(child);
-
+  bool isVisuallyAtomic = child->IsVisuallyAtomic(effectSet, disp, effects);
   bool isPositioned = disp->IsAbsPosContainingBlock(child);
-  bool isStackingContext =
-    (isPositioned && (disp->IsPositionForcingStackingContext() ||
-                      pos->mZIndex.GetUnit() == eStyleUnit_Integer)) ||
-     (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
-     disp->mIsolation != NS_STYLE_ISOLATION_AUTO ||
-     isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
+  bool isStackingContext = child->IsStackingContext(disp, pos, isPositioned, isVisuallyAtomic) ||
+                           (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
 
   if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) ||
       ((effects->mClipFlags & NS_STYLE_CLIP_RECT) &&
        IsSVGContentWithCSSClip(child)) ||
        disp->mIsolation != NS_STYLE_ISOLATION_AUTO ||
        (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
       (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
     // If you change this, also change IsPseudoStackingContextFromStyle()
@@ -6293,32 +6289,32 @@ nsIFrame::GetFlattenedTreeParentPrimaryF
   }
   nsIContent* parent = GetContent()->GetFlattenedTreeParent();
   return parent ? parent->GetPrimaryFrame() : nullptr;
 }
 
 Matrix4x4
 nsIFrame::GetTransformMatrix(const nsIFrame* aStopAtAncestor,
                              nsIFrame** aOutAncestor,
-                             bool aInCSSUnits)
+                             uint32_t aFlags)
 {
   NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!");
 
   /* If we're transformed, we want to hand back the combination
    * transform/translate matrix that will apply our current transform, then
    * shift us to our parent.
    */
   if (IsTransformed()) {
     /* Compute the delta to the parent, which we need because we are converting
      * coordinates to our parent.
      */
     NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
                  "Cannot transform the viewport frame!");
-    int32_t scaleFactor = (aInCSSUnits ? PresContext()->AppUnitsPerCSSPixel()
-                                       : PresContext()->AppUnitsPerDevPixel());
+    int32_t scaleFactor = ((aFlags & IN_CSS_UNITS) ? PresContext()->AppUnitsPerCSSPixel()
+                                                   : PresContext()->AppUnitsPerDevPixel());
 
     Matrix4x4 result = nsDisplayTransform::GetResultingTransformMatrix(this,
                          nsPoint(0,0), scaleFactor,
                          nsDisplayTransform::INCLUDE_PERSPECTIVE|nsDisplayTransform::OFFSET_BY_ORIGIN,
                          nullptr);
     *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this);
     nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
     /* Combine the raw transform with a translation to our parent. */
@@ -6376,35 +6372,39 @@ nsIFrame::GetTransformMatrix(const nsIFr
    * we have to check to see if we have a parent.  If not, we'll set the
    * outparam to null (indicating that there's nothing left) and will hand back
    * the identity matrix.
    */
   if (!*aOutAncestor)
     return Matrix4x4();
 
   /* Keep iterating while the frame can't possibly be transformed. */
+  nsIFrame* current = this;
   while (!(*aOutAncestor)->IsTransformed() &&
          !nsLayoutUtils::IsPopup(*aOutAncestor) &&
-         *aOutAncestor != aStopAtAncestor) {
+         *aOutAncestor != aStopAtAncestor &&
+         (!(aFlags & STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) ||
+          (!(*aOutAncestor)->IsStackingContext() && !nsLayoutUtils::FrameHasDisplayPort(*aOutAncestor, current)))) {
     /* If no parent, stop iterating.  Otherwise, update the ancestor. */
     nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
     if (!parent)
       break;
 
+    current = *aOutAncestor;
     *aOutAncestor = parent;
   }
 
   NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?");
 
   /* Translate from this frame to our ancestor, if it exists.  That's the
    * entire transform, so we're done.
    */
   nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
-  int32_t scaleFactor = (aInCSSUnits ? PresContext()->AppUnitsPerCSSPixel()
-                                     : PresContext()->AppUnitsPerDevPixel());
+  int32_t scaleFactor = ((aFlags & IN_CSS_UNITS) ? PresContext()->AppUnitsPerCSSPixel()
+                                                 : PresContext()->AppUnitsPerDevPixel());
   return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
                                 NSAppUnitsToFloatPixels(delta.y, scaleFactor),
                                 0.0f);
 }
 
 static void InvalidateRenderingObservers(nsIFrame* aDisplayRoot, nsIFrame* aFrame)
 {
   MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
@@ -10501,16 +10501,51 @@ nsIFrame::IsPseudoStackingContextFromSty
     return true;
   }
   const nsStyleDisplay* disp = StyleDisplay();
   return disp->IsAbsPosContainingBlock(this) ||
          disp->IsFloating(this) ||
          (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT);
 }
 
+bool
+nsIFrame::IsVisuallyAtomic(EffectSet* aEffectSet,
+                           const nsStyleDisplay* aStyleDisplay,
+                           const nsStyleEffects* aStyleEffects) {
+  return HasOpacity(aEffectSet) ||
+         IsTransformed(aStyleDisplay) ||
+         // strictly speaking, 'perspective' doesn't require visual atomicity,
+         // but the spec says it acts like the rest of these
+         aStyleDisplay->mChildPerspective.GetUnit() == eStyleUnit_Coord ||
+         aStyleEffects->mMixBlendMode != NS_STYLE_BLEND_NORMAL ||
+         nsSVGIntegrationUtils::UsingEffectsForFrame(this);
+}
+
+bool
+nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
+                            const nsStylePosition* aStylePosition,
+                            bool aIsPositioned,
+                            bool aIsVisuallyAtomic) {
+  return (aIsPositioned && (aStyleDisplay->IsPositionForcingStackingContext() ||
+                           aStylePosition->mZIndex.GetUnit() == eStyleUnit_Integer)) ||
+         (aStyleDisplay->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
+         aStyleDisplay->mIsolation != NS_STYLE_ISOLATION_AUTO ||
+         aIsVisuallyAtomic;
+}
+
+bool
+nsIFrame::IsStackingContext()
+{
+  const nsStyleDisplay* disp = StyleDisplay();
+  bool isPositioned = disp->IsAbsPosContainingBlock(this);
+  bool isVisuallyAtomic = IsVisuallyAtomic(EffectSet::GetEffectSet(this),
+                                           disp, StyleEffects());
+  return IsStackingContext(disp, StylePosition(), isPositioned, isVisuallyAtomic);
+}
+
 Element*
 nsIFrame::GetPseudoElement(CSSPseudoElementType aType)
 {
   if (!mContent) {
     return nullptr;
   }
 
   if (aType == CSSPseudoElementType::before) {
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -551,27 +551,16 @@ FRAME_STATE_BIT(Block, 63, NS_BLOCK_HAS_
 // == Frame state bits that apply to bullet frames ============================
 
 FRAME_STATE_GROUP(Bullet, nsBulletFrame)
 
 FRAME_STATE_BIT(Block, 62, BULLET_FRAME_HAS_FONT_INFLATION)
 FRAME_STATE_BIT(Block, 63, BULLET_FRAME_IMAGE_LOADING)
 
 
-// == Frame state bits that apply to scroll frames ============================
-
-FRAME_STATE_GROUP(Scroll, nsIScrollableFrame)
-
-// When set, the next scroll operation on the scrollframe will invalidate its
-// entire contents. Useful for text-overflow.
-// This bit is cleared after each time the scrollframe is scrolled. Whoever
-// needs to set it should set it again on each paint.
-FRAME_STATE_BIT(Scroll, 20, NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL)
-
-
 // == Frame state bits that apply to image frames =============================
 
 FRAME_STATE_GROUP(Image, nsImageFrame)
 
 FRAME_STATE_BIT(Image, 20, IMAGE_SIZECONSTRAINED)
 FRAME_STATE_BIT(Image, 21, IMAGE_GOTINITIALREFLOW)
 
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2416,22 +2416,16 @@ static void AdjustViews(nsIFrame* aFrame
     }
     nsFrameList::Enumerator childFrames(lists.CurrentList());
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       AdjustViews(childFrames.get());
     }
   }
 }
 
-static bool
-NeedToInvalidateOnScroll(nsIFrame* aFrame)
-{
-  return (aFrame->GetStateBits() & NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL) != 0;
-}
-
 bool ScrollFrameHelper::IsIgnoringViewportClipping() const
 {
   if (!mIsRoot)
     return false;
   nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
     (nsLayoutUtils::GetCrossDocParentFrame(mOuter->PresContext()->PresShell()->GetRootFrame()));
   return subdocFrame && !subdocFrame->ShouldClipSubdocument();
 }
@@ -2598,24 +2592,17 @@ void ScrollFrameHelper::ScrollVisual()
   // scroll frame of a content document, then IsAlwaysActive()
   // will return true from now on and MarkNotRecentlyScrolled() won't
   // have any effect.
   mHasBeenScrolled = true;
 
   AdjustViews(mScrolledFrame);
   // We need to call this after fixing up the view positions
   // to be consistent with the frame hierarchy.
-  bool needToInvalidateOnScroll = NeedToInvalidateOnScroll(mOuter);
-  mOuter->RemoveStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL);
-  if (needToInvalidateOnScroll) {
-    MarkNotRecentlyScrolled();
-  } else {
-    MarkRecentlyScrolled();
-  }
-
+  MarkRecentlyScrolled();
 }
 
 /**
  * Clamp desired scroll position aDesired and range [aDestLower, aDestUpper]
  * to [aBoundLower, aBoundUpper] and then select the appunit value from among
  * aBoundLower, aBoundUpper and those such that (aDesired - aCurrent) *
  * aRes/aAppUnitsPerPixel is an integer (or as close as we can get
  * modulo rounding to appunits) that is in [aDestLower, aDestUpper] and
@@ -3274,19 +3261,16 @@ ScrollFrameHelper::BuildDisplayList(nsDi
   if (aBuilder->IsForFrameVisibility()) {
     NotifyApproximateFrameVisibilityUpdate(false);
   }
 
   mOuter->DisplayBorderBackgroundOutline(aBuilder, aLists);
 
   if (aBuilder->IsPaintingToWindow()) {
     mScrollPosAtLastPaint = GetScrollPosition();
-    if (IsMaybeScrollingActive() && NeedToInvalidateOnScroll(mOuter)) {
-      MarkNotRecentlyScrolled();
-    }
     if (IsMaybeScrollingActive()) {
       if (mScrollPosForLayerPixelAlignment == nsPoint(-1,-1)) {
         mScrollPosForLayerPixelAlignment = mScrollPosAtLastPaint;
       }
     } else {
       mScrollPosForLayerPixelAlignment = nsPoint(-1,-1);
     }
   }
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1720,16 +1720,22 @@ public:
    *   time; otherwise, just left it as nullptr.
    */
   bool IsTransformed(const nsStyleDisplay* aStyleDisplay, mozilla::EffectSet* aEffectSet = nullptr) const;
   bool IsTransformed(mozilla::EffectSet* aEffectSet = nullptr) const {
     return IsTransformed(StyleDisplay(), aEffectSet);
   }
 
   /**
+   * Same as IsTransformed, except that it doesn't take SVG transforms
+   * into account.
+   */
+  bool IsCSSTransformed(const nsStyleDisplay* aStyleDisplay, mozilla::EffectSet* aEffectSet = nullptr) const;
+
+  /**
    * True if this frame has any animation of transform in effect.
    *
    * @param aEffectSet: This function may need to look up EffectSet property.
    *   If a caller already have one, pass it in can save property look up
    *   time; otherwise, just left it as nullptr.
    */
   bool HasAnimationOfTransform(mozilla::EffectSet* aEffectSet = nullptr) const;
 
@@ -2784,19 +2790,23 @@ public:
    *   this frame has no ancestor, *aOutAncestor will be set to null. If
    * this frame is not a root frame, then *aOutAncestor will be in the same
    * document as this frame. If this frame IsTransformed(), then *aOutAncestor
    * will be the parent frame (if not preserve-3d) or the nearest non-transformed
    * ancestor (if preserve-3d).
    * @return A Matrix4x4 that converts points in this frame's coordinate space
    *   into points in aOutAncestor's coordinate space.
    */
+  enum {
+    IN_CSS_UNITS = 1 << 0,
+    STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT = 1 << 1
+  };
   Matrix4x4 GetTransformMatrix(const nsIFrame* aStopAtAncestor,
                                nsIFrame **aOutAncestor,
-                               bool aInCSSUnits = false);
+                               uint32_t aFlags = 0);
 
   /**
    * Bit-flags to pass to IsFrameOfType()
    */
   enum {
     eMathML =                           1 << 0,
     eSVG =                              1 << 1,
     eSVGForeignObject =                 1 << 2,
@@ -3445,16 +3455,37 @@ public:
   /**
    * Determines whether this frame is a pseudo stacking context, looking
    * only as style --- i.e., assuming that it's in-flow and not a replaced
    * element and not an SVG element.
    * XXX maybe check IsTransformed()?
    */
   bool IsPseudoStackingContextFromStyle();
 
+  /**
+   * Determines if this frame has a container effect that requires
+   * it to paint as a visually atomic unit.
+   */
+  bool IsVisuallyAtomic(mozilla::EffectSet* aEffectSet,
+                        const nsStyleDisplay* aStyleDisplay,
+                        const nsStyleEffects* aStyleEffects);
+
+  /**
+   * Determines if this frame is a stacking context.
+   *
+   * @param aIsPositioned The precomputed result of IsAbsPosContainingBlock
+   * on the StyleDisplay().
+   * @param aIsVisuallyAtomic The precomputed result of IsVisuallyAtomic.
+   */
+  bool IsStackingContext(const nsStyleDisplay* aStyleDisplay,
+                         const nsStylePosition* aStylePosition,
+                         bool aIsPositioned,
+                         bool aIsVisuallyAtomic);
+  bool IsStackingContext();
+
   virtual bool HonorPrintBackgroundSettings() { return true; }
 
   /**
    * Determine whether the frame is logically empty, which is roughly
    * whether the layout would be the same whether or not the frame is
    * present.  Placeholder frames should return true.  Block frames
    * should be considered empty whenever margins collapse through them,
    * even though those margins are relevant.  Text frames containing
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -1213,16 +1213,20 @@ void
 nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
                                      bool aPointerEventsNoneDoc)
 {
   PresShellState* state = mPresShellStates.AppendElement();
   state->mPresShell = aReferenceFrame->PresContext()->PresShell();
   state->mCaretFrame = nullptr;
   state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
 
+#ifdef DEBUG
+  state->mAutoLayoutPhase.emplace(aReferenceFrame->PresContext(), eLayoutPhase_DisplayListBuilding);
+#endif
+
   state->mPresShell->UpdateCanvasBackground();
 
   if (mIsPaintingToWindow) {
     mReferenceFrame->AddPaintedPresShell(state->mPresShell);
 
     state->mPresShell->IncrementPaintCount();
   }
 
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -32,16 +32,17 @@
 #include "FrameMetrics.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/gfx/UserData.h"
 #include "mozilla/layers/LayerAttributes.h"
 #include "nsCSSRenderingBorders.h"
+#include "nsAutoLayoutPhase.h"
 #include "nsDisplayItemTypes.h"
 
 #include <stdint.h>
 #include "nsTHashtable.h"
 
 #include <stdlib.h>
 #include <algorithm>
 
@@ -1469,16 +1470,19 @@ private:
    * Add the current frame to the AGR budget if possible and remember
    * the outcome. Subsequent calls will return the same value as
    * returned here.
    */
   bool AddToAGRBudget(nsIFrame* aFrame);
 
   struct PresShellState {
     nsIPresShell* mPresShell;
+#ifdef DEBUG
+    mozilla::Maybe<nsAutoLayoutPhase> mAutoLayoutPhase;
+#endif
     nsIFrame*     mCaretFrame;
     nsRect        mCaretRect;
     mozilla::Maybe<OutOfFlowDisplayData> mFixedBackgroundDisplayData;
     uint32_t      mFirstFrameMarkedForDisplay;
     bool          mIsBackgroundOnly;
     // This is a per-document flag turning off event handling for all content
     // in the document, and is set when we enter a subdocument for a pointer-
     // events:none frame.
new file mode 100644
--- /dev/null
+++ b/layout/reftests/transform-3d/preserve3d-8-ref.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+</head>
+<body style="width:100px">
+  <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="js" viewBox="0 0 200 200" style="transform: rotateX(180deg)">
+    <g>
+      <rect width="100" height="100" style="fill:rgb(0,0,255)"></rect>
+    </g>
+  </svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/transform-3d/preserve3d-8.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+</head>
+<body>
+  <div style="transform: rotatex(45deg); transform-style: preserve-3d; width: 100px;">
+    <div style="transform: rotatex(45deg); transform-style: preserve-3d; width: 100px;">
+      <div style="transform: rotatex(45deg); transform-style: preserve-3d; width: 100px;">
+        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="js" viewBox="0 0 200 200" style="transform: rotateX(45deg)">
+          <g>
+            <rect width="100" height="100" style="fill:rgb(0,0,255)"></rect>
+          </g>
+        </svg>
+      </div>
+    </div>
+  </div>
+</body>
+</html>
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -672,16 +672,17 @@ public class BrowserApp extends GeckoApp
 
             @Override
             public boolean onTouch(View v, MotionEvent event) {
                 return false;
             }
         });
 
         mProgressView = (AnimatedProgressBar) findViewById(R.id.page_progress);
+        mDynamicToolbar.setLayerView(mLayerView);
         mProgressView.setDynamicToolbar(mDynamicToolbar);
         mBrowserToolbar.setProgressBar(mProgressView);
 
         // Initialize Tab History Controller.
         tabHistoryController = new TabHistoryController(new OnShowTabHistory() {
             @Override
             public void onShowHistory(final List<TabHistoryPage> historyPageList, final int toIndex) {
                 runOnUiThread(new Runnable() {
--- a/parser/html/moz.build
+++ b/parser/html/moz.build
@@ -50,17 +50,16 @@ EXPORTS += [
     'nsHtml5UTF16Buffer.h',
     'nsHtml5UTF16BufferHSupplement.h',
     'nsHtml5ViewSourceUtils.h',
     'nsIContentHandle.h',
     'nsParserUtils.h',
 ]
 
 UNIFIED_SOURCES += [
-    'nsHtml5Atom.cpp',
     'nsHtml5AtomTable.cpp',
     'nsHtml5AttributeName.cpp',
     'nsHtml5DependentUTF16Buffer.cpp',
     'nsHtml5DocumentBuilder.cpp',
     'nsHtml5ElementName.cpp',
     'nsHtml5Highlighter.cpp',
     'nsHtml5HtmlAttributes.cpp',
     'nsHtml5MetaScanner.cpp',
deleted file mode 100644
--- a/parser/html/nsHtml5Atom.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsHtml5Atom.h"
-#include "nsAutoPtr.h"
-#include "mozilla/Unused.h"
-
-nsHtml5Atom::nsHtml5Atom(const nsAString& aString)
-{
-  mLength = aString.Length();
-  SetKind(AtomKind::HTML5Atom);
-  RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
-  if (buf) {
-    mString = static_cast<char16_t*>(buf->Data());
-  } else {
-    const size_t size = (mLength + 1) * sizeof(char16_t);
-    buf = nsStringBuffer::Alloc(size);
-    if (MOZ_UNLIKELY(!buf)) {
-      // We OOM because atom allocations should be small and it's hard to
-      // handle them more gracefully in a constructor.
-      NS_ABORT_OOM(size);
-    }
-    mString = static_cast<char16_t*>(buf->Data());
-    CopyUnicodeTo(aString, 0, mString, mLength);
-    mString[mLength] = char16_t(0);
-  }
-
-  NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
-  NS_ASSERTION(buf && buf->StorageSize() >= (mLength+1) * sizeof(char16_t),
-               "enough storage");
-  NS_ASSERTION(Equals(aString), "correct data");
-
-  // Take ownership of buffer
-  mozilla::Unused << buf.forget();
-}
-
-nsHtml5Atom::~nsHtml5Atom()
-{
-  nsStringBuffer::FromData(mString)->Release();
-}
-
-NS_IMETHODIMP
-nsHtml5Atom::QueryInterface(REFNSIID aIID, void** aInstancePtr)
-{
-  NS_NOTREACHED("Attempt to call QueryInterface an nsHtml5Atom.");
-  return NS_ERROR_UNEXPECTED;
-}
-
-NS_IMETHODIMP
-nsHtml5Atom::ToUTF8String(nsACString& aReturn)
-{
-  NS_NOTREACHED("Should not attempt to convert to an UTF-8 string.");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP_(size_t)
-nsHtml5Atom::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
-{
-  NS_NOTREACHED("Should not call SizeOfIncludingThis.");
-  return 0;
-}
-
deleted file mode 100644
--- a/parser/html/nsHtml5Atom.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsHtml5Atom_h
-#define nsHtml5Atom_h
-
-#include "nsIAtom.h"
-#include "mozilla/Attributes.h"
-
-/**
- * A dynamic atom implementation meant for use within the nsHtml5Tokenizer and 
- * nsHtml5TreeBuilder owned by one nsHtml5Parser or nsHtml5StreamParser 
- * instance.
- *
- * Usage is documented in nsHtml5AtomTable and nsIAtom.
- */
-class nsHtml5Atom final : public nsIAtom
-{
-  public:
-    NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) final;
-    NS_DECL_NSIATOM
-
-    explicit nsHtml5Atom(const nsAString& aString);
-    ~nsHtml5Atom();
-};
-
-#endif // nsHtml5Atom_h
--- a/parser/html/nsHtml5AtomTable.cpp
+++ b/parser/html/nsHtml5AtomTable.cpp
@@ -1,31 +1,31 @@
 /* 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 "nsHtml5AtomTable.h"
-#include "nsHtml5Atom.h"
 #include "nsThreadUtils.h"
 
 nsHtml5AtomEntry::nsHtml5AtomEntry(KeyTypePointer aStr)
   : nsStringHashKey(aStr)
-  , mAtom(new nsHtml5Atom(*aStr))
+  , mAtom(new nsAtom(nsAtom::AtomKind::HTML5Atom, *aStr, 0))
 {
 }
 
 nsHtml5AtomEntry::nsHtml5AtomEntry(const nsHtml5AtomEntry& aOther)
   : nsStringHashKey(aOther)
   , mAtom(nullptr)
 {
   NS_NOTREACHED("nsHtml5AtomTable is broken and tried to copy an entry");
 }
 
 nsHtml5AtomEntry::~nsHtml5AtomEntry()
 {
+  delete mAtom;
 }
 
 nsHtml5AtomTable::nsHtml5AtomTable()
   : mRecentlyUsedParserAtoms{}
 {
 #ifdef DEBUG
   mPermittedLookupEventTarget = mozilla::GetCurrentThreadSerialEventTarget();
 #endif
--- a/parser/html/nsHtml5AtomTable.h
+++ b/parser/html/nsHtml5AtomTable.h
@@ -2,36 +2,30 @@
  * 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 nsHtml5AtomTable_h
 #define nsHtml5AtomTable_h
 
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
-#include "nsAutoPtr.h"
 #include "nsIAtom.h"
 #include "nsISerialEventTarget.h"
 
 #define RECENTLY_USED_PARSER_ATOMS_SIZE 31
 
-class nsHtml5Atom;
-
 class nsHtml5AtomEntry : public nsStringHashKey
 {
   public:
     explicit nsHtml5AtomEntry(KeyTypePointer aStr);
     nsHtml5AtomEntry(const nsHtml5AtomEntry& aOther);
     ~nsHtml5AtomEntry();
-    inline nsHtml5Atom* GetAtom()
-    {
-      return mAtom;
-    }
+    inline nsAtom* GetAtom() { return mAtom; }
   private:
-    nsAutoPtr<nsHtml5Atom> mAtom;
+    nsAtom* mAtom;
 };
 
 /**
  * nsHtml5AtomTable provides non-locking lookup and creation of atoms for 
  * nsHtml5Parser or nsHtml5StreamParser.
  *
  * The hashtable holds dynamically allocated atoms that are private to an 
  * instance of nsHtml5Parser or nsHtml5StreamParser. (Static atoms are used on 
--- a/parser/html/nsHtml5SpeculativeLoad.cpp
+++ b/parser/html/nsHtml5SpeculativeLoad.cpp
@@ -1,14 +1,15 @@
 /* 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 "nsHtml5SpeculativeLoad.h"
 #include "nsHtml5TreeOpExecutor.h"
+#include "mozilla/Encoding.h"
 
 nsHtml5SpeculativeLoad::nsHtml5SpeculativeLoad()
   :
 #ifdef DEBUG
   mOpCode(eSpeculativeLoadUninitialized),
 #endif
   mIsAsync(false),
   mIsDefer(false)
--- a/parser/htmlparser/nsParserCIID.h
+++ b/parser/htmlparser/nsParserCIID.h
@@ -1,25 +1,13 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsParserCIID_h__
 #define nsParserCIID_h__
 
-#include "nsISupports.h"
-#include "nsIFactory.h"
-#include "nsIComponentManager.h"
-
 // {2ce606b0-bee6-11d1-aad9-00805f8a3e14}
 #define NS_PARSER_CID      \
 { 0x2ce606b0, 0xbee6, 0x11d1, { 0xaa, 0xd9, 0x0, 0x80, 0x5f, 0x8a, 0x3e, 0x14 } }
 
-// {a6cf9107-15b3-11d2-932e-00805f8add32}
-#define NS_CNAVDTD_CID \
-{ 0xa6cf9107, 0x15b3, 0x11d2, { 0x93, 0x2e, 0x0, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } }
-
-// {FFF4FBE9-528A-4b37-819D-FC18F3A401A7}
-#define NS_EXPAT_DRIVER_CID \
-{ 0xfff4fbe9, 0x528a, 0x4b37, { 0x81, 0x9d, 0xfc, 0x18, 0xf3, 0xa4, 0x1, 0xa7 } }
-
 #endif
--- a/parser/htmlparser/nsParserModule.cpp
+++ b/parser/htmlparser/nsParserModule.cpp
@@ -1,80 +1,46 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "nsIAtom.h"
-#include "nsString.h"
-#include "nspr.h"
-#include "nsCOMPtr.h"
 #include "mozilla/ModuleUtils.h"
-#include "nsParserCIID.h"
 #include "nsParser.h"
-#include "CNavDTD.h"
-#include "nsHTMLTokenizer.h"
-//#include "nsTextTokenizer.h"
-#include "nsElementTable.h"
-#include "nsSAXAttributes.h"
-#include "nsSAXLocator.h"
+#include "nsParserCIID.h"
+#include "nsHTMLTags.h"
 #include "nsSAXXMLReader.h"
 
-#if defined(DEBUG)
-#include "nsExpatDriver.h"
-#endif
-
 //----------------------------------------------------------------------
 
-#if defined(DEBUG)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsExpatDriver)
-#endif
-
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsParser)
-NS_GENERIC_FACTORY_CONSTRUCTOR(CNavDTD)
-
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsSAXAttributes)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSAXXMLReader)
 
-#if defined(DEBUG)
-NS_DEFINE_NAMED_CID(NS_EXPAT_DRIVER_CID);
-#endif
 NS_DEFINE_NAMED_CID(NS_PARSER_CID);
-NS_DEFINE_NAMED_CID(NS_CNAVDTD_CID);
-NS_DEFINE_NAMED_CID(NS_SAXATTRIBUTES_CID);
 NS_DEFINE_NAMED_CID(NS_SAXXMLREADER_CID);
 
 static const mozilla::Module::CIDEntry kParserCIDs[] = {
-#if defined(DEBUG)
-  { &kNS_EXPAT_DRIVER_CID, false, nullptr, nsExpatDriverConstructor },
-#endif
   { &kNS_PARSER_CID, false, nullptr, nsParserConstructor },
-  { &kNS_CNAVDTD_CID, false, nullptr, CNavDTDConstructor },
-  { &kNS_SAXATTRIBUTES_CID, false, nullptr, nsSAXAttributesConstructor },
   { &kNS_SAXXMLREADER_CID, false, nullptr, nsSAXXMLReaderConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kParserContracts[] = {
-  { NS_SAXATTRIBUTES_CONTRACTID, &kNS_SAXATTRIBUTES_CID },
   { NS_SAXXMLREADER_CONTRACTID, &kNS_SAXXMLREADER_CID },
   { nullptr }
 };
 
 static nsresult
 Initialize()
 {
   nsresult rv = nsHTMLTags::AddRefTable();
   NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef DEBUG
   CheckElementTable();
-#endif
-
-#ifdef DEBUG
   nsHTMLTags::TestTagTable();
 #endif
 
   return rv;
 }
 
 static void
 Shutdown()
--- a/parser/xml/nsSAXAttributes.h
+++ b/parser/xml/nsSAXAttributes.h
@@ -8,22 +8,16 @@
 
 #include "nsISupports.h"
 #include "nsISAXAttributes.h"
 #include "nsISAXMutableAttributes.h"
 #include "nsTArray.h"
 #include "nsString.h"
 #include "mozilla/Attributes.h"
 
-#define NS_SAXATTRIBUTES_CONTRACTID "@mozilla.org/saxparser/attributes;1"
-#define NS_SAXATTRIBUTES_CID  \
-{/* {7bb40992-77eb-43db-9a4e-39d3bcc483ae}*/ \
-0x7bb40992, 0x77eb, 0x43db, \
-{ 0x9a, 0x4e, 0x39, 0xd3, 0xbc, 0xc3, 0x83, 0xae} }
-
 struct SAXAttr
 {
   nsString uri;
   nsString localName;
   nsString qName;
   nsString type;
   nsString value;
 };
--- a/parser/xml/nsSAXLocator.h
+++ b/parser/xml/nsSAXLocator.h
@@ -5,22 +5,16 @@
 
 #ifndef nsSAXLocator_h__
 #define nsSAXLocator_h__
 
 #include "nsISAXLocator.h"
 #include "nsString.h"
 #include "mozilla/Attributes.h"
 
-#define NS_SAXLOCATOR_CONTRACTID "@mozilla.org/saxparser/locator;1"
-#define NS_SAXLOCATOR_CID  \
-{/* {c1cd4045-846b-43bb-a95e-745a3d7b40e0}*/ \
-0xc1cd4045, 0x846b, 0x43bb, \
-{ 0xa9, 0x5e, 0x74, 0x5a, 0x3d, 0x7b, 0x40, 0xe0} }
-
 class nsSAXLocator final : public nsISAXLocator
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISAXLOCATOR
 
   nsSAXLocator(nsString& aPublicId,
                nsString& aSystemId,
--- a/testing/mozharness/configs/releases/bouncer_firefox_beta.py
+++ b/testing/mozharness/configs/releases/bouncer_firefox_beta.py
@@ -101,17 +101,16 @@ config = {
                 },
                 "win64": {
                     "path": "/firefox/releases/%(version)s/update/win64/:lang/firefox-%(version)s.complete.mar",
                     "bouncer-platform": "win64",
                 },
             },
         },
     },
-    },
     "partials": {
         "releases-dir": {
             "product-name": "Firefox-%(version)s-Partial-%(prev_version)s",
             "check_uptake": True,
             "ssl-only": False,
             "add-locales": True,
             "paths": {
                 "linux": {
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -692,20 +692,16 @@ nsBaseWidget::SetSizeMode(nsSizeMode aMo
   mSizeMode = aMode;
 }
 
 //-------------------------------------------------------------------------
 //
 // Get this component cursor
 //
 //-------------------------------------------------------------------------
-nsCursor nsBaseWidget::GetCursor()
-{
-  return mCursor;
-}
 
 void
 nsBaseWidget::SetCursor(nsCursor aCursor)
 {
   mCursor = aCursor;
 }
 
 nsresult
--- a/widget/nsBaseWidget.h
+++ b/widget/nsBaseWidget.h
@@ -169,17 +169,16 @@ public:
     return mSizeMode;
   }
 
   virtual bool            IsFullyOccluded() const override
   {
     return mIsFullyOccluded;
   }
 
-  virtual nsCursor        GetCursor() override;
   virtual void            SetCursor(nsCursor aCursor) override;
   virtual nsresult        SetCursor(imgIContainer* aCursor,
                                     uint32_t aHotspotX, uint32_t aHotspotY) override;
   virtual void            ClearCachedCursor() override { mUpdateCursor = true; }
   virtual void            SetTransparencyMode(nsTransparencyMode aMode) override;
   virtual nsTransparencyMode GetTransparencyMode() override;
   virtual void            GetWindowClipRegion(nsTArray<LayoutDeviceIntRect>* aRects) override;
   virtual void            SetWindowShadowStyle(int32_t aStyle) override {}
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -956,24 +956,16 @@ class nsIWidget : public nsISupports
      *
      * @param aColor the new background color
      *
      */
 
     virtual void SetBackgroundColor(const nscolor &aColor) { }
 
     /**
-     * Get the cursor for this widget.
-     *
-     * @return this widget's cursor.
-     */
-
-    virtual nsCursor GetCursor(void) = 0;
-
-    /**
      * Set the cursor for this widget
      *
      * @param aCursor the new cursor for this widget
      */
     virtual void SetCursor(nsCursor aCursor) = 0;
 
     /**
      * If a cursor type is currently cached locally for this widget, clear the
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -23,26 +23,26 @@
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsAutoPtr.h"
 #include "nsUnicharUtils.h"
 #include "nsPrintfCString.h"
 
 // There are two kinds of atoms handled by this module.
 //
-// - Dynamic: the Atom itself is heap allocated, as is the nsStringBuffer it
-//   points to. |gAtomTable| holds weak references to dynamic Atoms. When the
-//   refcount of a dynamic Atom drops to zero, we increment a static counter.
-//   When that counter reaches a certain threshold, we iterate over the Atom
-//   table, removing and deleting dynamic Atoms with refcount zero. This allows
+// - Dynamic: the atom itself is heap allocated, as is the nsStringBuffer it
+//   points to. |gAtomTable| holds weak references to dynamic atoms. When the
+//   refcount of a dynamic atom drops to zero, we increment a static counter.
+//   When that counter reaches a certain threshold, we iterate over the atom
+//   table, removing and deleting dynamic atoms with refcount zero. This allows
 //   us to avoid acquiring the atom table lock during normal refcounting.
 //
-// - Static: the Atom itself is heap allocated, but it points to a static
-//   nsStringBuffer. |gAtomTable| effectively owns static Atoms, because such
-//   Atoms ignore all AddRef/Release calls, which ensures they stay alive until
+// - Static: the atom itself is heap allocated, but it points to a static
+//   nsStringBuffer. |gAtomTable| effectively owns static atoms, because such
+//   atoms ignore all AddRef/Release calls, which ensures they stay alive until
 //   |gAtomTable| itself is destroyed whereupon they are explicitly deleted.
 //
 // Note that gAtomTable is used on multiple threads, and callers must acquire
 // gAtomTableLock before touching it.
 
 using namespace mozilla;
 
 //----------------------------------------------------------------------
@@ -62,16 +62,42 @@ class CheckStaticAtomSizes
                   (offsetof(nsFakeStringBuffer<1>, mStringData) ==
                    sizeof(nsStringBuffer)),
                   "mocked-up strings' representations should be compatible");
   }
 };
 
 //----------------------------------------------------------------------
 
+enum class GCKind {
+  RegularOperation,
+  Shutdown,
+};
+
+// This class encapsulates the functions that need access to nsAtom's private
+// members.
+class nsAtomFriend
+{
+public:
+  static void RegisterStaticAtoms(const nsStaticAtom* aAtoms,
+                                  uint32_t aAtomCount);
+
+  static void AtomTableClearEntry(PLDHashTable* aTable,
+                                  PLDHashEntryHdr* aEntry);
+
+  static void GCAtomTableLocked(const MutexAutoLock& aProofOfLock,
+                                GCKind aKind);
+
+  static already_AddRefed<nsIAtom> Atomize(const nsACString& aUTF8String);
+  static already_AddRefed<nsIAtom> Atomize(const nsAString& aUTF16String);
+  static already_AddRefed<nsIAtom> AtomizeMainThread(const nsAString& aUTF16Str);
+};
+
+//----------------------------------------------------------------------
+
 // gUnusedAtomCount is incremented when an atom loses its last reference
 // (and thus turned into unused state), and decremented when an unused
 // atom gets a reference again. The atom table relies on this value to
 // schedule GC. This value can temporarily go below zero when multiple
 // threads are operating the same atom, so it has to be signed so that
 // we wouldn't use overflow value for comparison.
 // See Atom::DynamicAddRef and Atom::DynamicRelease.
 static Atomic<int32_t, ReleaseAcquire> gUnusedAtomCount(0);
@@ -104,141 +130,104 @@ public:
 
 private:
   nsStringBuffer* mBuffer;
 };
 
 UniquePtr<nsTArray<FakeBufferRefcountHelper>> gFakeBuffers;
 #endif
 
-class Atom final : public nsIAtom
+// This constructor is for dynamic atoms and HTML5 atoms.
+nsAtom::nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash)
+  : mRefCnt(1)
 {
-public:
-  static already_AddRefed<Atom> CreateDynamic(const nsAString& aString,
-                                              uint32_t aHash)
-  {
-    // The refcount is appropriately initialized in the constructor.
-    return dont_AddRef(new Atom(aString, aHash));
-  }
-
-  static Atom* CreateStatic(nsStringBuffer* aStringBuffer, uint32_t aLength,
-                            uint32_t aHash)
-  {
-    return new Atom(aStringBuffer, aLength, aHash);
+  mLength = aString.Length();
+  SetKind(aKind);
+  MOZ_ASSERT(IsDynamicAtom() || IsHTML5Atom());
+  RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
+  if (buf) {
+    mString = static_cast<char16_t*>(buf->Data());
+  } else {
+    const size_t size = (mLength + 1) * sizeof(char16_t);
+    buf = nsStringBuffer::Alloc(size);
+    if (MOZ_UNLIKELY(!buf)) {
+      // We OOM because atom allocations should be small and it's hard to
+      // handle them more gracefully in a constructor.
+      NS_ABORT_OOM(size);
+    }
+    mString = static_cast<char16_t*>(buf->Data());
+    CopyUnicodeTo(aString, 0, mString, mLength);
+    mString[mLength] = char16_t(0);
   }
 
-  static void GCAtomTable();
+  mHash = aHash;
+  MOZ_ASSERT_IF(IsDynamicAtom(), mHash == HashString(mString, mLength));
 
-  enum class GCKind {
-    RegularOperation,
-    Shutdown,
-  };
-
-  static void GCAtomTableLocked(const MutexAutoLock& aProofOfLock,
-                                GCKind aKind);
+  NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
+  NS_ASSERTION(buf && buf->StorageSize() >= (mLength + 1) * sizeof(char16_t),
+               "enough storage");
+  NS_ASSERTION(Equals(aString), "correct data");
 
-private:
-  // This constructor is for dynamic Atoms.
-  Atom(const nsAString& aString, uint32_t aHash)
-    : mRefCnt(1)
-  {
-    mLength = aString.Length();
-    SetKind(AtomKind::DynamicAtom);
-    RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
-    if (buf) {
-      mString = static_cast<char16_t*>(buf->Data());
-    } else {
-      const size_t size = (mLength + 1) * sizeof(char16_t);
-      buf = nsStringBuffer::Alloc(size);
-      if (MOZ_UNLIKELY(!buf)) {
-        // We OOM because atom allocations should be small and it's hard to
-        // handle them more gracefully in a constructor.
-        NS_ABORT_OOM(size);
-      }
-      mString = static_cast<char16_t*>(buf->Data());
-      CopyUnicodeTo(aString, 0, mString, mLength);
-      mString[mLength] = char16_t(0);
-    }
+  // Take ownership of buffer
+  mozilla::Unused << buf.forget();
+}
 
-    mHash = aHash;
-    MOZ_ASSERT(mHash == HashString(mString, mLength));
-
-    NS_ASSERTION(mString[mLength] == char16_t(0), "null terminated");
-    NS_ASSERTION(buf && buf->StorageSize() >= (mLength + 1) * sizeof(char16_t),
-                 "enough storage");
-    NS_ASSERTION(Equals(aString), "correct data");
-
-    // Take ownership of buffer
-    mozilla::Unused << buf.forget();
-  }
-
-  // This constructor is for static Atoms.
-  Atom(nsStringBuffer* aStringBuffer, uint32_t aLength, uint32_t aHash)
-  {
-    mLength = aLength;
-    SetKind(AtomKind::StaticAtom);
-    mString = static_cast<char16_t*>(aStringBuffer->Data());
+// This constructor is for static atoms.
+nsAtom::nsAtom(nsStringBuffer* aStringBuffer, uint32_t aLength, uint32_t aHash)
+{
+  mLength = aLength;
+  SetKind(AtomKind::StaticAtom);
+  mString = static_cast<char16_t*>(aStringBuffer->Data());
 
 #if defined(NS_BUILD_REFCNT_LOGGING)
-    MOZ_ASSERT(NS_IsMainThread());
-    if (!gFakeBuffers) {
-      gFakeBuffers = MakeUnique<nsTArray<FakeBufferRefcountHelper>>();
-    }
-    gFakeBuffers->AppendElement(aStringBuffer);
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!gFakeBuffers) {
+    gFakeBuffers = MakeUnique<nsTArray<FakeBufferRefcountHelper>>();
+  }
+  gFakeBuffers->AppendElement(aStringBuffer);
 #endif
 
-    // Technically we could currently avoid doing this addref by instead making
-    // the static atom buffers have an initial refcount of 2.
-    aStringBuffer->AddRef();
+  // Technically we could currently avoid doing this addref by instead making
+  // the static atom buffers have an initial refcount of 2.
+  aStringBuffer->AddRef();
 
-    mHash = aHash;
-    MOZ_ASSERT(mHash == HashString(mString, mLength));
+  mHash = aHash;
+  MOZ_ASSERT(mHash == HashString(mString, mLength));
 
-    MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
-    MOZ_ASSERT(aStringBuffer &&
-               aStringBuffer->StorageSize() == (mLength + 1) * sizeof(char16_t),
-               "correct storage");
-  }
+  MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
+  MOZ_ASSERT(aStringBuffer &&
+             aStringBuffer->StorageSize() == (mLength + 1) * sizeof(char16_t),
+             "correct storage");
+}
 
-public:
-  // We don't need a virtual destructor because we always delete via an Atom*
-  // pointer (in AtomTableClearEntry() for static Atoms, and in
-  // GCAtomTableLocked() for dynamic Atoms), not an nsIAtom* pointer.
-  ~Atom() {
-    if (IsDynamicAtom()) {
-      nsStringBuffer::FromData(mString)->Release();
-    } else {
-      MOZ_ASSERT(IsStaticAtom());
-    }
+// We don't need a virtual destructor because we always delete via an nsAtom*
+// pointer (in AtomTableClearEntry() for static atoms, and in
+// GCAtomTableLocked() for dynamic atoms), not an nsIAtom* pointer.
+nsAtom::~nsAtom()
+{
+  if (!IsStaticAtom()) {
+    MOZ_ASSERT(IsDynamicAtom() || IsHTML5Atom());
+    nsStringBuffer::FromData(mString)->Release();
   }
+}
 
-  NS_DECL_NSIATOM
-  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) final;
-  typedef mozilla::TrueType HasThreadSafeRefCnt;
-
-  MozExternalRefCountType DynamicAddRef();
-  MozExternalRefCountType DynamicRelease();
-
-protected:
-  ThreadSafeAutoRefCnt mRefCnt;
-  NS_DECL_OWNINGTHREAD
-};
-
-NS_IMPL_QUERY_INTERFACE(Atom, nsIAtom);
+NS_IMPL_QUERY_INTERFACE(nsAtom, nsIAtom);
 
 NS_IMETHODIMP
-Atom::ToUTF8String(nsACString& aBuf)
+nsAtom::ToUTF8String(nsACString& aBuf)
 {
+  MOZ_ASSERT(!IsHTML5Atom(), "Called ToUTF8String() on an HTML5 atom");
   CopyUTF16toUTF8(nsDependentString(mString, mLength), aBuf);
   return NS_OK;
 }
 
 NS_IMETHODIMP_(size_t)
-Atom::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+nsAtom::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
 {
+  MOZ_ASSERT(!IsHTML5Atom(), "Called SizeOfIncludingThis() on an HTML5 atom");
   size_t n = aMallocSizeOf(this);
   // String buffers pointed to by static atoms are in static memory, and so
   // are not measured here.
   if (IsDynamicAtom()) {
     n += nsStringBuffer::FromData(mString)->SizeOfIncludingThisIfUnshared(
            aMallocSizeOf);
   } else {
     MOZ_ASSERT(IsStaticAtom());
@@ -246,33 +235,33 @@ Atom::SizeOfIncludingThis(MallocSizeOf a
   return n;
 }
 
 //----------------------------------------------------------------------
 
 NS_IMETHODIMP_(MozExternalRefCountType)
 nsIAtom::AddRef()
 {
-  MOZ_ASSERT(!IsHTML5Atom(), "Attempt to AddRef an nsHtml5Atom");
+  MOZ_ASSERT(!IsHTML5Atom(), "Attempt to AddRef an HTML5 atom");
   if (!IsDynamicAtom()) {
     MOZ_ASSERT(IsStaticAtom());
     return 2;
   }
-  return static_cast<Atom*>(this)->DynamicAddRef();
+  return static_cast<nsAtom*>(this)->DynamicAddRef();
 }
 
 NS_IMETHODIMP_(MozExternalRefCountType)
 nsIAtom::Release()
 {
-  MOZ_ASSERT(!IsHTML5Atom(), "Attempt to Release an nsHtml5Atom");
+  MOZ_ASSERT(!IsHTML5Atom(), "Attempt to Release an HTML5 atom");
   if (!IsDynamicAtom()) {
     MOZ_ASSERT(IsStaticAtom());
     return 1;
   }
-  return static_cast<Atom*>(this)->DynamicRelease();
+  return static_cast<nsAtom*>(this)->DynamicRelease();
 }
 
 //----------------------------------------------------------------------
 
 /**
  * The shared hash table for atom lookups.
  *
  * Callers must hold gAtomTableLock before manipulating the table.
@@ -329,20 +318,20 @@ struct AtomTableKey
   const char16_t* mUTF16String;
   const char* mUTF8String;
   uint32_t mLength;
   uint32_t mHash;
 };
 
 struct AtomTableEntry : public PLDHashEntryHdr
 {
-  // These references are either to dynamic Atoms, in which case they are
-  // non-owning, or they are to static Atoms, which aren't really refcounted.
+  // These references are either to dynamic atoms, in which case they are
+  // non-owning, or they are to static atoms, which aren't really refcounted.
   // See the comment at the top of this file for more details.
-  Atom* MOZ_NON_OWNING_REF mAtom;
+  nsAtom* MOZ_NON_OWNING_REF mAtom;
 };
 
 static PLDHashNumber
 AtomTableGetHash(const void* aKey)
 {
   const AtomTableKey* k = static_cast<const AtomTableKey*>(aKey);
   return k->mHash;
 }
@@ -358,76 +347,67 @@ AtomTableMatchKey(const PLDHashEntryHdr*
       CompareUTF8toUTF16(nsDependentCSubstring(k->mUTF8String,
                                                k->mUTF8String + k->mLength),
                          nsDependentAtomString(he->mAtom)) == 0;
   }
 
   return he->mAtom->Equals(k->mUTF16String, k->mLength);
 }
 
-static void
-AtomTableClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
+void
+nsAtomFriend::AtomTableClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
 {
   auto entry = static_cast<AtomTableEntry*>(aEntry);
-  Atom* atom = entry->mAtom;
+  nsAtom* atom = entry->mAtom;
   if (atom->IsStaticAtom()) {
-    // This case -- when the entry being cleared holds a static Atom -- only
-    // occurs when gAtomTable is destroyed, whereupon all static Atoms within it
+    // This case -- when the entry being cleared holds a static atom -- only
+    // occurs when gAtomTable is destroyed, whereupon all static atoms within it
     // must be explicitly deleted.
     delete atom;
   }
 }
 
 static void
 AtomTableInitEntry(PLDHashEntryHdr* aEntry, const void* aKey)
 {
   static_cast<AtomTableEntry*>(aEntry)->mAtom = nullptr;
 }
 
 static const PLDHashTableOps AtomTableOps = {
   AtomTableGetHash,
   AtomTableMatchKey,
   PLDHashTable::MoveEntryStub,
-  AtomTableClearEntry,
+  nsAtomFriend::AtomTableClearEntry,
   AtomTableInitEntry
 };
 
 //----------------------------------------------------------------------
 
 #define RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE 31
-static Atom*
+static nsAtom*
   sRecentlyUsedMainThreadAtoms[RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE] = {};
 
 void
-Atom::GCAtomTable()
-{
-  if (NS_IsMainThread()) {
-    MutexAutoLock lock(*gAtomTableLock);
-    GCAtomTableLocked(lock, GCKind::RegularOperation);
-  }
-}
-
-void
-Atom::GCAtomTableLocked(const MutexAutoLock& aProofOfLock, GCKind aKind)
+nsAtomFriend::GCAtomTableLocked(const MutexAutoLock& aProofOfLock, GCKind aKind)
 {
   MOZ_ASSERT(NS_IsMainThread());
   for (uint32_t i = 0; i < RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE; ++i) {
     sRecentlyUsedMainThreadAtoms[i] = nullptr;
   }
 
   int32_t removedCount = 0; // Use a non-atomic temporary for cheaper increments.
   nsAutoCString nonZeroRefcountAtoms;
   uint32_t nonZeroRefcountAtomsCount = 0;
   for (auto i = gAtomTable->Iter(); !i.Done(); i.Next()) {
     auto entry = static_cast<AtomTableEntry*>(i.Get());
     if (entry->mAtom->IsStaticAtom()) {
       continue;
     }
 
-    Atom* atom = entry->mAtom;
+    nsAtom* atom = entry->mAtom;
     if (atom->mRefCnt == 0) {
       i.Remove();
       delete atom;
       ++removedCount;
     }
 #ifdef NS_FREE_PERMANENT_DATA
     else if (aKind == GCKind::Shutdown && PR_GetEnv("XPCOM_MEM_BLOAT_LOG")) {
       // Only report leaking atoms in leak-checking builds in a run
@@ -468,18 +448,27 @@ Atom::GCAtomTableLocked(const MutexAutoL
   // so we won't try to resurrect a zero refcount atom while trying to delete
   // it.
 
   MOZ_ASSERT_IF(aKind == GCKind::Shutdown, removedCount == gUnusedAtomCount);
 
   gUnusedAtomCount -= removedCount;
 }
 
+static void
+GCAtomTable()
+{
+  if (NS_IsMainThread()) {
+    MutexAutoLock lock(*gAtomTableLock);
+    nsAtomFriend::GCAtomTableLocked(lock, GCKind::RegularOperation);
+  }
+}
+
 MozExternalRefCountType
-Atom::DynamicAddRef()
+nsAtom::DynamicAddRef()
 {
   MOZ_ASSERT(IsDynamicAtom());
   nsrefcnt count = ++mRefCnt;
   if (count == 1) {
     gUnusedAtomCount--;
   }
   return count;
 }
@@ -488,17 +477,17 @@ Atom::DynamicAddRef()
 // We set a lower GC threshold for atoms in debug builds so that we exercise
 // the GC machinery more often.
 static const int32_t kAtomGCThreshold = 20;
 #else
 static const int32_t kAtomGCThreshold = 10000;
 #endif
 
 MozExternalRefCountType
-Atom::DynamicRelease()
+nsAtom::DynamicRelease()
 {
   MOZ_ASSERT(IsDynamicAtom());
   MOZ_ASSERT(mRefCnt > 0);
   nsrefcnt count = --mRefCnt;
   if (count == 0) {
     if (++gUnusedAtomCount >= kAtomGCThreshold) {
       GCAtomTable();
     }
@@ -531,19 +520,19 @@ public:
   static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
   static PLDHashNumber HashKey(KeyTypePointer aKey)
   {
     return HashString(*aKey);
   }
 
   enum { ALLOW_MEMMOVE = true };
 
-  // Static Atoms aren't really refcounted. Because these entries live in a
+  // Static atoms aren't really refcounted. Because these entries live in a
   // global hashtable, this reference is essentially owning.
-  Atom* MOZ_OWNING_REF mAtom;
+  nsAtom* MOZ_OWNING_REF mAtom;
 };
 
 /**
  * A hashtable of static atoms that existed at app startup. This hashtable
  * helps nsHtml5AtomTable.
  */
 typedef nsTHashtable<StaticAtomEntry> StaticAtomTable;
 static StaticAtomTable* gStaticAtomTable = nullptr;
@@ -598,17 +587,17 @@ NS_ShutdownAtomTable()
   delete gStaticAtomTable;
   gStaticAtomTable = nullptr;
 
 #ifdef NS_FREE_PERMANENT_DATA
   // Do a final GC to satisfy leak checking. We skip this step in release
   // builds.
   {
     MutexAutoLock lock(*gAtomTableLock);
-    Atom::GCAtomTableLocked(lock, Atom::GCKind::Shutdown);
+    nsAtomFriend::GCAtomTableLocked(lock, GCKind::Shutdown);
   }
 #endif
 
   delete gAtomTable;
   gAtomTable = nullptr;
   delete gAtomTableLock;
   gAtomTableLock = nullptr;
 }
@@ -645,17 +634,18 @@ GetAtomHashEntry(const char16_t* aString
 {
   gAtomTableLock->AssertCurrentThreadOwns();
   AtomTableKey key(aString, aLength, aHashOut);
   // This is an infallible add.
   return static_cast<AtomTableEntry*>(gAtomTable->Add(&key));
 }
 
 void
-RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
+nsAtomFriend::RegisterStaticAtoms(const nsStaticAtom* aAtoms,
+                                  uint32_t aAtomCount)
 {
   MutexAutoLock lock(*gAtomTableLock);
 
   MOZ_RELEASE_ASSERT(!gStaticAtomTableSealed,
                      "Atom table has already been sealed!");
 
   if (!gStaticAtomTable) {
     gStaticAtomTable = new StaticAtomTable();
@@ -669,51 +659,57 @@ RegisterStaticAtoms(const nsStaticAtom* 
 
     uint32_t stringLen = stringBuffer->StorageSize() / sizeof(char16_t) - 1;
 
     uint32_t hash;
     AtomTableEntry* he =
       GetAtomHashEntry(static_cast<char16_t*>(stringBuffer->Data()),
                        stringLen, &hash);
 
-    Atom* atom = he->mAtom;
+    nsAtom* atom = he->mAtom;
     if (atom) {
       // Disallow creating a dynamic atom, and then later, while the
       // dynamic atom is still alive, registering that same atom as a
       // static atom.  It causes subtle bugs, and we're programming in
       // C++ here, not Smalltalk.
       if (!atom->IsStaticAtom()) {
         nsAutoCString name;
         atom->ToUTF8String(name);
         MOZ_CRASH_UNSAFE_PRINTF(
           "Static atom registration for %s should be pushed back", name.get());
       }
     } else {
-      atom = Atom::CreateStatic(stringBuffer, stringLen, hash);
+      atom = new nsAtom(stringBuffer, stringLen, hash);
       he->mAtom = atom;
     }
     *atomp = atom;
 
     if (!gStaticAtomTableSealed) {
       StaticAtomEntry* entry =
         gStaticAtomTable->PutEntry(nsDependentAtomString(atom));
       MOZ_ASSERT(atom->IsStaticAtom());
       entry->mAtom = atom;
     }
   }
 }
 
+void
+RegisterStaticAtoms(const nsStaticAtom* aAtoms, uint32_t aAtomCount)
+{
+  nsAtomFriend::RegisterStaticAtoms(aAtoms, aAtomCount);
+}
+
 already_AddRefed<nsIAtom>
 NS_Atomize(const char* aUTF8String)
 {
-  return NS_Atomize(nsDependentCString(aUTF8String));
+  return nsAtomFriend::Atomize(nsDependentCString(aUTF8String));
 }
 
 already_AddRefed<nsIAtom>
-NS_Atomize(const nsACString& aUTF8String)
+nsAtomFriend::Atomize(const nsACString& aUTF8String)
 {
   MutexAutoLock lock(*gAtomTableLock);
   uint32_t hash;
   AtomTableEntry* he = GetAtomHashEntry(aUTF8String.Data(),
                                         aUTF8String.Length(),
                                         &hash);
 
   if (he->mAtom) {
@@ -722,88 +718,109 @@ NS_Atomize(const nsACString& aUTF8String
     return atom.forget();
   }
 
   // This results in an extra addref/release of the nsStringBuffer.
   // Unfortunately there doesn't seem to be any APIs to avoid that.
   // Actually, now there is, sort of: ForgetSharedBuffer.
   nsString str;
   CopyUTF8toUTF16(aUTF8String, str);
-  RefPtr<Atom> atom = Atom::CreateDynamic(str, hash);
+  RefPtr<nsAtom> atom =
+    dont_AddRef(new nsAtom(nsAtom::AtomKind::DynamicAtom, str, hash));
 
   he->mAtom = atom;
 
   return atom.forget();
 }
 
 already_AddRefed<nsIAtom>
-NS_Atomize(const char16_t* aUTF16String)
+NS_Atomize(const nsACString& aUTF8String)
 {
-  return NS_Atomize(nsDependentString(aUTF16String));
+  return nsAtomFriend::Atomize(aUTF8String);
 }
 
 already_AddRefed<nsIAtom>
-NS_Atomize(const nsAString& aUTF16String)
+NS_Atomize(const char16_t* aUTF16String)
+{
+  return nsAtomFriend::Atomize(nsDependentString(aUTF16String));
+}
+
+already_AddRefed<nsIAtom>
+nsAtomFriend::Atomize(const nsAString& aUTF16String)
 {
   MutexAutoLock lock(*gAtomTableLock);
   uint32_t hash;
   AtomTableEntry* he = GetAtomHashEntry(aUTF16String.Data(),
                                         aUTF16String.Length(),
                                         &hash);
 
   if (he->mAtom) {
     nsCOMPtr<nsIAtom> atom = he->mAtom;
 
     return atom.forget();
   }
 
-  RefPtr<Atom> atom = Atom::CreateDynamic(aUTF16String, hash);
+  RefPtr<nsAtom> atom =
+    dont_AddRef(new nsAtom(nsAtom::AtomKind::DynamicAtom, aUTF16String, hash));
   he->mAtom = atom;
 
   return atom.forget();
 }
 
 already_AddRefed<nsIAtom>
-NS_AtomizeMainThread(const nsAString& aUTF16String)
+NS_Atomize(const nsAString& aUTF16String)
+{
+  return nsAtomFriend::Atomize(aUTF16String);
+}
+
+already_AddRefed<nsIAtom>
+nsAtomFriend::AtomizeMainThread(const nsAString& aUTF16String)
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsCOMPtr<nsIAtom> retVal;
   uint32_t hash;
   AtomTableKey key(aUTF16String.Data(), aUTF16String.Length(), &hash);
   uint32_t index = hash % RECENTLY_USED_MAIN_THREAD_ATOM_CACHE_SIZE;
-  Atom* atom = sRecentlyUsedMainThreadAtoms[index];
+  nsAtom* atom = sRecentlyUsedMainThreadAtoms[index];
   if (atom) {
     uint32_t length = atom->GetLength();
     if (length == key.mLength &&
         (memcmp(atom->GetUTF16String(),
                 key.mUTF16String, length * sizeof(char16_t)) == 0)) {
       retVal = atom;
       return retVal.forget();
     }
   }
 
   MutexAutoLock lock(*gAtomTableLock);
   AtomTableEntry* he = static_cast<AtomTableEntry*>(gAtomTable->Add(&key));
 
   if (he->mAtom) {
     retVal = he->mAtom;
   } else {
-    RefPtr<Atom> newAtom = Atom::CreateDynamic(aUTF16String, hash);
+    RefPtr<nsAtom> newAtom = dont_AddRef(
+      new nsAtom(nsAtom::AtomKind::DynamicAtom, aUTF16String, hash));
     he->mAtom = newAtom;
     retVal = newAtom.forget();
   }
 
   sRecentlyUsedMainThreadAtoms[index] = he->mAtom;
   return retVal.forget();
 }
 
+already_AddRefed<nsIAtom>
+NS_AtomizeMainThread(const nsAString& aUTF16String)
+{
+  return nsAtomFriend::AtomizeMainThread(aUTF16String);
+}
+
 nsrefcnt
 NS_GetNumberOfAtoms(void)
 {
-  Atom::GCAtomTable(); // Trigger a GC so that we return a deterministic result.
+  GCAtomTable(); // Trigger a GC so we return a deterministic result.
   MutexAutoLock lock(*gAtomTableLock);
   return gAtomTable->EntryCount();
 }
 
 int32_t
 NS_GetUnusedAtomCount(void)
 {
   return gUnusedAtomCount;
--- a/xpcom/ds/nsIAtom.h
+++ b/xpcom/ds/nsIAtom.h
@@ -74,17 +74,21 @@ public:
     return nsStringBuffer::FromData(mString);
   }
 
   NS_IMETHOD_(MozExternalRefCountType) AddRef() final;
   NS_IMETHOD_(MozExternalRefCountType) Release() final;
 
   // A hashcode that is better distributed than the actual atom pointer, for
   // use in situations that need a well-distributed hashcode.
-  uint32_t hash() const { return mHash; }
+  uint32_t hash() const
+  {
+    MOZ_ASSERT(!IsHTML5Atom());
+    return mHash;
+  }
 
 protected:
   uint32_t mLength: 30;
   uint32_t mKind: 2; // nsIAtom::AtomKind
   uint32_t mHash;
   // WARNING! There is an invisible constraint on |mString|: the chars it
   // points to must belong to an nsStringBuffer. This is so that the
   // nsStringBuffer::FromData() calls above are valid.
@@ -92,16 +96,40 @@ protected:
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIAtom, NS_IATOM_IID)
 
 #define NS_DECL_NSIATOM \
   NS_IMETHOD ToUTF8String(nsACString& _retval) override; \
   NS_IMETHOD_(size_t) SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) override;
 
+class nsAtom final : public nsIAtom
+{
+public:
+  NS_DECL_NSIATOM
+  NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) final;
+  typedef mozilla::TrueType HasThreadSafeRefCnt;
+
+private:
+  friend class nsIAtom;
+  friend class nsAtomFriend;
+  friend class nsHtml5AtomEntry;
+
+  // Construction and destruction is done entirely by |friend|s.
+  nsAtom(AtomKind aKind, const nsAString& aString, uint32_t aHash);
+  nsAtom(nsStringBuffer* aStringBuffer, uint32_t aLength, uint32_t aHash);
+  ~nsAtom();
+
+  MozExternalRefCountType DynamicAddRef();
+  MozExternalRefCountType DynamicRelease();
+
+  mozilla::ThreadSafeAutoRefCnt mRefCnt;
+  NS_DECL_OWNINGTHREAD
+};
+
 // The four forms of NS_Atomize (for use with |nsCOMPtr<nsIAtom>|) return the
 // atom for the string given. At any given time there will always be one atom
 // representing a given string. Atoms are intended to make string comparison
 // cheaper by simplifying it to pointer equality. A pointer to the atom that
 // does not own a reference is not guaranteed to be valid.
 
 // Find an atom that matches the given UTF-8 string. The string is assumed to
 // be zero terminated. Never returns null.
--- a/xpcom/rust/nserror/src/lib.rs
+++ b/xpcom/rust/nserror/src/lib.rs
@@ -17,17 +17,17 @@ pub type nsresult = u32;
 /// types. These methods are meaningless on non-nsresult values.
 pub trait NsresultExt {
     fn failed(self) -> bool;
     fn succeeded(self) -> bool;
     fn to_result(self) -> Result<nsresult, nsresult>;
 
     /// Get a printable name for the nsresult error code. This function returns
     /// a nsCString<'static>, which implements `Display`.
-    fn error_name(self) -> nsCString<'static>;
+    fn error_name(self) -> nsCString;
 }
 
 impl NsresultExt for nsresult {
     fn failed(self) -> bool {
         (self >> 31) != 0
     }
 
     fn succeeded(self) -> bool {
@@ -37,17 +37,17 @@ impl NsresultExt for nsresult {
     fn to_result(self) -> Result<nsresult, nsresult> {
         if self.failed() {
             Err(self)
         } else {
             Ok(self)
         }
     }
 
-    fn error_name(self) -> nsCString<'static> {
+    fn error_name(self) -> nsCString {
         let mut cstr = nsCString::new();
         unsafe {
             Gecko_GetErrorName(self, &mut *cstr);
         }
         cstr
     }
 }
 
--- a/xpcom/rust/nsstring/src/lib.rs
+++ b/xpcom/rust/nsstring/src/lib.rs
@@ -1,21 +1,27 @@
 //! This module provides rust bindings for the XPCOM string types.
 //!
 //! # TL;DR (what types should I use)
 //!
 //! Use `&{mut,} nsA[C]String` for functions in rust which wish to take or
 //! mutate XPCOM strings. The other string types `Deref` to this type.
 //!
-//! Use `ns[C]String<'a>` (`ns[C]String` in C++) for string struct members, and
-//! as an intermediate between rust string data structures (such as `String`,
-//! `Vec<u16>`, `&str`, and `&[u16]`) and `&{mut,} nsA[C]String` (using
-//! `ns[C]String::from(value)`). These conversions, when possible, will not
-//! perform any allocations. When using this type in structs shared with C++,
-//! the correct lifetime argument is usually `'static`.
+//! Use `ns[C]String` (`ns[C]String` in C++) for string struct members, and as
+//! an intermediate between rust string data structures (such as `String` or
+//! `Vec<u16>`) and `&{mut,} nsA[C]String` (using `ns[C]String::from(value)`).
+//! These conversions will attempt to re-use the passed-in buffer, appending a
+//! null.
+//!
+//! Use `ns[C]Str` (`nsDependent[C]String` in C++) as an intermediate between
+//! borrowed rust data structures (such as `&str` and `&[u16]`) and `&{mut,}
+//! nsA[C]String` (using `ns[C]Str::from(value)`). These conversions should not
+//! perform any allocations. This type is not safe to share with `C++` as a
+//! struct field, but passing the borrowed `&{mut,} nsA[C]String` over FFI is
+//! safe.
 //!
 //! Use `nsFixed[C]String` or `ns_auto_[c]string!` for dynamic stack allocated
 //! strings which are expected to hold short string values.
 //!
 //! Use `*{const,mut} nsA[C]String` (`{const,} nsA[C]String*` in C++) for
 //! function arguments passed across the rust/C++ language boundary.
 //!
 //! # String Types
@@ -43,39 +49,55 @@
 //! buffers, as information about the backing storage is preserved.
 //!
 //! An `&mut nsA[C]String` acts like rust's `&mut Cow<str>`, in that it is a
 //! mutable reference to a potentially borrowed string, which when modified will
 //! ensure that it owns its own backing storage. This type can be appended to
 //! with the methods `.append`, `.append_utf{8,16}`, and with the `write!`
 //! macro, and can be assigned to with `.assign`.
 //!
-//! ## `ns[C]String<'a>`
+//! ## `ns[C]Str<'a>`
 //!
 //! This type is an maybe-owned string type. It acts similarially to a
 //! `Cow<[{u8,u16}]>`. This type provides `Deref` and `DerefMut` implementations
 //! to `nsA[C]String`, which provides the methods for manipulating this type.
 //! This type's lifetime parameter, `'a`, represents the lifetime of the backing
 //! storage. When modified this type may re-allocate in order to ensure that it
 //! does not mutate its backing storage.
 //!
-//! `ns[C]String`s can be constructed either with `ns[C]String::new()`, which
-//! creates an empty `ns[C]String<'static>`, or through one of the provided
-//! `From` implementations. Both string types may be constructed `From<&'a
-//! str>`, with `nsCString` having a `'a` lifetime, as the storage is shared
-//! with the `str`, while `nsString` has a `'static` lifetime, as its storage
-//! has to be transcoded.
+//! `ns[C]Str`s can be constructed either with `ns[C]Str::new()`, which creates
+//! an empty `ns[C]Str<'static>`, or through one of the provided `From`
+//! implementations. Only `nsCStr` can be constructed `From<'a str>`, as
+//! constructing a `nsStr` would require transcoding. Use `ns[C]String` instead.
 //!
 //! When passing this type by reference, prefer passing a `&nsA[C]String` or
 //! `&mut nsA[C]String`. to passing this type.
 //!
 //! When passing this type across the language boundary, pass it as `*const
 //! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
-//! mutable reference. This struct may also be included in `#[repr(C)]`
-//! structs shared with C++.
+//! mutable reference.
+//!
+//! ## `ns[C]String`
+//!
+//! This type is an owned, null-terminated string type. This type provides
+//! `Deref` and `DerefMut` implementations to `nsA[C]String`, which provides the
+//! methods for manipulating this type.
+//!
+//! `ns[C]String`s can be constructed either with `ns[C]String::new()`, which
+//! creates an empty `ns[C]String`, or through one of the provided `From`
+//! implementations, which will try to avoid reallocating when possible,
+//! although a terminating `null` will be added.
+//!
+//! When passing this type by reference, prefer passing a `&nsA[C]String` or
+//! `&mut nsA[C]String`. to passing this type.
+//!
+//! When passing this type across the language boundary, pass it as `*const
+//! nsA[C]String` for an immutable reference, or `*mut nsA[C]String` for a
+//! mutable reference. This struct may also be included in `#[repr(C)]` structs
+//! shared with C++.
 //!
 //! ## `nsFixed[C]String<'a>`
 //!
 //! This type is a string type with fixed backing storage. It is created with
 //! `nsFixed[C]String::new(buffer)`, passing a mutable reference to a buffer as
 //! the argument. This buffer will be used as backing storage whenever the
 //! resulting string will fit within it, falling back to heap allocations only
 //! when the string size exceeds that of the backing buffer.
@@ -100,34 +122,33 @@
 //!
 //! Usage of this macro is similar to the C++ type `nsAuto[C]String`, but could
 //! not be implemented as a basic type due to the differences between rust and
 //! C++'s move semantics.
 //!
 //! ## `ns[C]StringRepr`
 //!
 //! This crate also provides the type `ns[C]StringRepr` which acts conceptually
-//! similar to an `ns[C]String<'static>`, however, it does not have a `Drop`
+//! similar to an `ns[C]String`, however, it does not have a `Drop`
 //! implementation.
 //!
 //! If this type is dropped in rust, it will not free its backing storage. This
 //! can be useful when implementing FFI types which contain `ns[C]String` members
 //! which invoke their member's destructors through C++ code.
 
 #![allow(non_camel_case_types)]
 #![deny(warnings)]
 
 #[macro_use]
 extern crate bitflags;
 
 use std::ops::{Deref, DerefMut};
 use std::marker::PhantomData;
 use std::borrow;
 use std::slice;
-use std::ptr;
 use std::mem;
 use std::fmt;
 use std::cmp;
 use std::str;
 use std::u32;
 use std::os::raw::c_void;
 
 //////////////////////////////////
@@ -170,16 +191,17 @@ use class_flags::ClassFlags;
 ////////////////////////////////////
 
 macro_rules! define_string_types {
     {
         char_t = $char_t: ty;
 
         AString = $AString: ident;
         String = $String: ident;
+        Str = $Str: ident;
         FixedString = $FixedString: ident;
 
         StringLike = $StringLike: ident;
         StringAdapter = $StringAdapter: ident;
 
         StringRepr = $StringRepr: ident;
 
         drop = $drop: ident;
@@ -202,16 +224,28 @@ macro_rules! define_string_types {
         #[derive(Debug)]
         pub struct $StringRepr {
             data: *const $char_t,
             length: u32,
             dataflags: DataFlags,
             classflags: ClassFlags,
         }
 
+        impl $StringRepr {
+            fn new(classflags: ClassFlags) -> $StringRepr {
+                static NUL: $char_t = 0;
+                $StringRepr {
+                    data: &NUL,
+                    length: 0,
+                    dataflags: data_flags::TERMINATED | data_flags::LITERAL,
+                    classflags: classflags,
+                }
+            }
+        }
+
         impl Deref for $StringRepr {
             type Target = $AString;
             fn deref(&self) -> &$AString {
                 unsafe {
                     mem::transmute(self)
                 }
             }
         }
@@ -380,219 +414,318 @@ macro_rules! define_string_types {
         }
 
         impl cmp::PartialEq<[$char_t]> for $AString {
             fn eq(&self, other: &[$char_t]) -> bool {
                 &self[..] == other
             }
         }
 
-        impl<'a> cmp::PartialEq<$String<'a>> for $AString {
-            fn eq(&self, other: &$String<'a>) -> bool {
+        impl cmp::PartialEq<$String> for $AString {
+            fn eq(&self, other: &$String) -> bool {
+                self.eq(&**other)
+            }
+        }
+
+        impl<'a> cmp::PartialEq<$Str<'a>> for $AString {
+            fn eq(&self, other: &$Str<'a>) -> bool {
                 self.eq(&**other)
             }
         }
 
         impl<'a> cmp::PartialEq<$FixedString<'a>> for $AString {
             fn eq(&self, other: &$FixedString<'a>) -> bool {
                 self.eq(&**other)
             }
         }
 
         #[repr(C)]
-        pub struct $String<'a> {
+        pub struct $Str<'a> {
             hdr: $StringRepr,
             _marker: PhantomData<&'a [$char_t]>,
         }
 
-        impl $String<'static> {
-            pub fn new() -> $String<'static> {
-                $String {
-                    hdr: $StringRepr {
-                        data: ptr::null(),
-                        length: 0,
-                        dataflags: DataFlags::empty(),
-                        classflags: class_flags::NULL_TERMINATED,
-                    },
+        impl $Str<'static> {
+            pub fn new() -> $Str<'static> {
+                $Str {
+                    hdr: $StringRepr::new(ClassFlags::empty()),
                     _marker: PhantomData,
                 }
             }
         }
 
-        impl<'a> Drop for $String<'a> {
+        impl<'a> Drop for $Str<'a> {
             fn drop(&mut self) {
                 unsafe {
                     $drop(&mut **self);
                 }
             }
         }
 
-        impl<'a> Deref for $String<'a> {
+        impl<'a> Deref for $Str<'a> {
             type Target = $AString;
             fn deref(&self) -> &$AString {
                 &self.hdr
             }
         }
 
-        impl<'a> DerefMut for $String<'a> {
+        impl<'a> DerefMut for $Str<'a> {
             fn deref_mut(&mut self) -> &mut $AString {
                 &mut self.hdr
             }
         }
 
-        impl<'a> AsRef<[$char_t]> for $String<'a> {
+        impl<'a> AsRef<[$char_t]> for $Str<'a> {
             fn as_ref(&self) -> &[$char_t] {
                 &self
             }
         }
 
-        impl<'a> From<&'a String> for $String<'a> {
-            fn from(s: &'a String) -> $String<'a> {
-                $String::from(&s[..])
-            }
-        }
-
-        impl<'a> From<&'a Vec<$char_t>> for $String<'a> {
-            fn from(s: &'a Vec<$char_t>) -> $String<'a> {
-                $String::from(&s[..])
-            }
-        }
-
-        impl<'a> From<&'a [$char_t]> for $String<'a> {
-            fn from(s: &'a [$char_t]) -> $String<'a> {
+        impl<'a> From<&'a [$char_t]> for $Str<'a> {
+            fn from(s: &'a [$char_t]) -> $Str<'a> {
                 assert!(s.len() < (u32::MAX as usize));
-                $String {
+                if s.is_empty() {
+                    return $Str::new();
+                }
+                $Str {
                     hdr: $StringRepr {
-                        data: if s.is_empty() { ptr::null() } else { s.as_ptr() },
+                        data: s.as_ptr(),
                         length: s.len() as u32,
                         dataflags: DataFlags::empty(),
-                        classflags: class_flags::NULL_TERMINATED,
+                        classflags: ClassFlags::empty(),
                     },
                     _marker: PhantomData,
                 }
             }
         }
 
-        impl From<Box<[$char_t]>> for $String<'static> {
-            fn from(s: Box<[$char_t]>) -> $String<'static> {
+        impl<'a> From<&'a Vec<$char_t>> for $Str<'a> {
+            fn from(s: &'a Vec<$char_t>) -> $Str<'a> {
+                $Str::from(&s[..])
+            }
+        }
+
+        impl<'a> From<&'a $AString> for $Str<'a> {
+            fn from(s: &'a $AString) -> $Str<'a> {
+                $Str::from(&s[..])
+            }
+        }
+
+        impl<'a> fmt::Write for $Str<'a> {
+            fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
+                $AString::write_str(self, s)
+            }
+        }
+
+        impl<'a> fmt::Display for $Str<'a> {
+            fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+                <$AString as fmt::Display>::fmt(self, f)
+            }
+        }
+
+        impl<'a> fmt::Debug for $Str<'a> {
+            fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+                <$AString as fmt::Debug>::fmt(self, f)
+            }
+        }
+
+        impl<'a> cmp::PartialEq for $Str<'a> {
+            fn eq(&self, other: &$Str<'a>) -> bool {
+                $AString::eq(self, other)
+            }
+        }
+
+        impl<'a> cmp::PartialEq<[$char_t]> for $Str<'a> {
+            fn eq(&self, other: &[$char_t]) -> bool {
+                $AString::eq(self, other)
+            }
+        }
+
+        impl<'a, 'b> cmp::PartialEq<&'b [$char_t]> for $Str<'a> {
+            fn eq(&self, other: &&'b [$char_t]) -> bool {
+                $AString::eq(self, *other)
+            }
+        }
+
+        impl<'a> cmp::PartialEq<str> for $Str<'a> {
+            fn eq(&self, other: &str) -> bool {
+                $AString::eq(self, other)
+            }
+        }
+
+        impl<'a, 'b> cmp::PartialEq<&'b str> for $Str<'a> {
+            fn eq(&self, other: &&'b str) -> bool {
+                $AString::eq(self, *other)
+            }
+        }
+
+        #[repr(C)]
+        pub struct $String {
+            hdr: $StringRepr,
+        }
+
+        impl $String {
+            pub fn new() -> $String {
+                $String {
+                    hdr: $StringRepr::new(class_flags::NULL_TERMINATED),
+                }
+            }
+        }
+
+        impl Drop for $String {
+            fn drop(&mut self) {
+                unsafe {
+                    $drop(&mut **self);
+                }
+            }
+        }
+
+        impl Deref for $String {
+            type Target = $AString;
+            fn deref(&self) -> &$AString {
+                &self.hdr
+            }
+        }
+
+        impl DerefMut for $String {
+            fn deref_mut(&mut self) -> &mut $AString {
+                &mut self.hdr
+            }
+        }
+
+        impl AsRef<[$char_t]> for $String {
+            fn as_ref(&self) -> &[$char_t] {
+                &self
+            }
+        }
+
+        impl<'a> From<&'a [$char_t]> for $String {
+            fn from(s: &'a [$char_t]) -> $String {
+                let mut res = $String::new();
+                res.assign(&$Str::from(&s[..]));
+                res
+            }
+        }
+
+        impl<'a> From<&'a Vec<$char_t>> for $String {
+            fn from(s: &'a Vec<$char_t>) -> $String {
+                $String::from(&s[..])
+            }
+        }
+
+        impl<'a> From<&'a $AString> for $String {
+            fn from(s: &'a $AString) -> $String {
+                $String::from(&s[..])
+            }
+        }
+
+        impl From<Box<[$char_t]>> for $String {
+            fn from(s: Box<[$char_t]>) -> $String {
+                s.to_vec().into()
+            }
+        }
+
+        impl From<Vec<$char_t>> for $String {
+            fn from(mut s: Vec<$char_t>) -> $String {
                 assert!(s.len() < (u32::MAX as usize));
                 if s.is_empty() {
                     return $String::new();
                 }
 
+                let length = s.len() as u32;
+                s.push(0); // null terminator
+
                 // SAFETY NOTE: This method produces an data_flags::OWNED
                 // ns[C]String from a Box<[$char_t]>. this is only safe
                 // because in the Gecko tree, we use the same allocator for
                 // Rust code as for C++ code, meaning that our box can be
                 // legally freed with libc::free().
-                let length = s.len() as u32;
                 let ptr = s.as_ptr();
                 mem::forget(s);
                 unsafe {
                     Gecko_IncrementStringAdoptCount(ptr as *mut _);
                 }
                 $String {
                     hdr: $StringRepr {
                         data: ptr,
                         length: length,
-                        dataflags: data_flags::OWNED,
+                        dataflags: data_flags::OWNED | data_flags::TERMINATED,
                         classflags: class_flags::NULL_TERMINATED,
-                    },
-                    _marker: PhantomData,
+                    }
                 }
             }
         }
 
-        impl From<Vec<$char_t>> for $String<'static> {
-            fn from(s: Vec<$char_t>) -> $String<'static> {
-                s.into_boxed_slice().into()
-            }
-        }
-
-        impl<'a> From<&'a $AString> for $String<'static> {
-            fn from(s: &'a $AString) -> $String<'static> {
-                let mut string = $String::new();
-                string.assign(s);
-                string
-            }
-        }
-
-        impl<'a> fmt::Write for $String<'a> {
+        impl fmt::Write for $String {
             fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
                 $AString::write_str(self, s)
             }
         }
 
-        impl<'a> fmt::Display for $String<'a> {
+        impl fmt::Display for $String {
             fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
                 <$AString as fmt::Display>::fmt(self, f)
             }
         }
 
-        impl<'a> fmt::Debug for $String<'a> {
+        impl fmt::Debug for $String {
             fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
                 <$AString as fmt::Debug>::fmt(self, f)
             }
         }
 
-        impl<'a> cmp::PartialEq for $String<'a> {
-            fn eq(&self, other: &$String<'a>) -> bool {
+        impl cmp::PartialEq for $String {
+            fn eq(&self, other: &$String) -> bool {
                 $AString::eq(self, other)
             }
         }
 
-        impl<'a> cmp::PartialEq<[$char_t]> for $String<'a> {
+        impl cmp::PartialEq<[$char_t]> for $String {
             fn eq(&self, other: &[$char_t]) -> bool {
                 $AString::eq(self, other)
             }
         }
 
-        impl<'a, 'b> cmp::PartialEq<&'b [$char_t]> for $String<'a> {
-            fn eq(&self, other: &&'b [$char_t]) -> bool {
+        impl<'a> cmp::PartialEq<&'a [$char_t]> for $String {
+            fn eq(&self, other: &&'a [$char_t]) -> bool {
                 $AString::eq(self, *other)
             }
         }
 
-        impl<'a> cmp::PartialEq<str> for $String<'a> {
+        impl cmp::PartialEq<str> for $String {
             fn eq(&self, other: &str) -> bool {
                 $AString::eq(self, other)
             }
         }
 
-        impl<'a, 'b> cmp::PartialEq<&'b str> for $String<'a> {
-            fn eq(&self, other: &&'b str) -> bool {
+        impl<'a> cmp::PartialEq<&'a str> for $String {
+            fn eq(&self, other: &&'a str) -> bool {
                 $AString::eq(self, *other)
             }
         }
 
         /// A nsFixed[C]String is a string which uses a fixed size mutable
         /// backing buffer for storing strings which will fit within that
         /// buffer, rather than using heap allocations.
         #[repr(C)]
         pub struct $FixedString<'a> {
-            base: $String<'a>,
+            base: $String,
             capacity: u32,
             buffer: *mut $char_t,
             _marker: PhantomData<&'a mut [$char_t]>,
         }
 
         impl<'a> $FixedString<'a> {
             pub fn new(buf: &'a mut [$char_t]) -> $FixedString<'a> {
                 let len = buf.len();
                 assert!(len < (u32::MAX as usize));
                 let buf_ptr = buf.as_mut_ptr();
                 $FixedString {
                     base: $String {
-                        hdr: $StringRepr {
-                            data: ptr::null(),
-                            length: 0,
-                            dataflags: DataFlags::empty(),
-                            classflags: class_flags::FIXED | class_flags::NULL_TERMINATED,
-                        },
-                        _marker: PhantomData,
+                        hdr: $StringRepr::new(class_flags::FIXED | class_flags::NULL_TERMINATED),
                     },
                     capacity: len as u32,
                     buffer: buf_ptr,
                     _marker: PhantomData,
                 }
             }
         }
 
@@ -662,17 +795,17 @@ macro_rules! define_string_types {
                 $AString::eq(self, *other)
             }
         }
 
         /// An adapter type to allow for passing both types which coerce to
         /// &[$char_type], and &$AString to a function, while still performing
         /// optimized operations when passed the $AString.
         pub enum $StringAdapter<'a> {
-            Borrowed($String<'a>),
+            Borrowed($Str<'a>),
             Abstract(&'a $AString),
         }
 
         impl<'a> $StringAdapter<'a> {
             fn as_ptr(&self) -> *const $AString {
                 &**self
             }
         }
@@ -715,57 +848,64 @@ macro_rules! define_string_types {
         }
 
         impl $StringLike for $AString {
             fn adapt(&self) -> $StringAdapter {
                 $StringAdapter::Abstract(self)
             }
         }
 
-        impl<'a> $StringLike for $String<'a> {
+        impl<'a> $StringLike for $Str<'a> {
+            fn adapt(&self) -> $StringAdapter {
+                $StringAdapter::Abstract(self)
+            }
+        }
+
+        impl $StringLike for $String {
             fn adapt(&self) -> $StringAdapter {
                 $StringAdapter::Abstract(self)
             }
         }
 
         impl<'a> $StringLike for $FixedString<'a> {
             fn adapt(&self) -> $StringAdapter {
                 $StringAdapter::Abstract(self)
             }
         }
 
         impl $StringLike for [$char_t] {
             fn adapt(&self) -> $StringAdapter {
-                $StringAdapter::Borrowed($String::from(self))
+                $StringAdapter::Borrowed($Str::from(self))
             }
         }
 
         impl $StringLike for Vec<$char_t> {
             fn adapt(&self) -> $StringAdapter {
-                $StringAdapter::Borrowed($String::from(&self[..]))
+                $StringAdapter::Borrowed($Str::from(&self[..]))
             }
         }
 
         impl $StringLike for Box<[$char_t]> {
             fn adapt(&self) -> $StringAdapter {
-                $StringAdapter::Borrowed($String::from(&self[..]))
+                $StringAdapter::Borrowed($Str::from(&self[..]))
             }
         }
     }
 }
 
 ///////////////////////////////////////////
 // Bindings for nsCString (u8 char type) //
 ///////////////////////////////////////////
 
 define_string_types! {
     char_t = u8;
 
     AString = nsACString;
     String = nsCString;
+    Str = nsCStr;
     FixedString = nsFixedCString;
 
     StringLike = nsCStringLike;
     StringAdapter = nsCStringAdapter;
 
     StringRepr = nsCStringRepr;
 
     drop = Gecko_FinalizeCString;
@@ -800,30 +940,48 @@ impl nsACString {
         }
     }
 
     pub unsafe fn as_str_unchecked(&self) -> &str {
         str::from_utf8_unchecked(self)
     }
 }
 
-impl<'a> From<&'a str> for nsCString<'a> {
-    fn from(s: &'a str) -> nsCString<'a> {
+impl<'a> From<&'a str> for nsCStr<'a> {
+    fn from(s: &'a str) -> nsCStr<'a> {
         s.as_bytes().into()
     }
 }
 
-impl From<Box<str>> for nsCString<'static> {
-    fn from(s: Box<str>) -> nsCString<'static> {
+impl<'a> From<&'a String> for nsCStr<'a> {
+    fn from(s: &'a String) -> nsCStr<'a> {
+        nsCStr::from(&s[..])
+    }
+}
+
+impl<'a> From<&'a str> for nsCString {
+    fn from(s: &'a str) -> nsCString {
+        s.as_bytes().into()
+    }
+}
+
+impl<'a> From<&'a String> for nsCString {
+    fn from(s: &'a String) -> nsCString {
+        nsCString::from(&s[..])
+    }
+}
+
+impl From<Box<str>> for nsCString {
+    fn from(s: Box<str>) -> nsCString {
         s.into_string().into()
     }
 }
 
-impl From<String> for nsCString<'static> {
-    fn from(s: String) -> nsCString<'static> {
+impl From<String> for nsCString {
+    fn from(s: String) -> nsCString {
         s.into_bytes().into()
     }
 }
 
 // Support for the write!() macro for appending to nsACStrings
 impl fmt::Write for nsACString {
     fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
         self.append(&nsCString::from(s));
@@ -846,29 +1004,29 @@ impl fmt::Debug for nsACString {
 impl cmp::PartialEq<str> for nsACString {
     fn eq(&self, other: &str) -> bool {
         &self[..] == other.as_bytes()
     }
 }
 
 impl nsCStringLike for str {
     fn adapt(&self) -> nsCStringAdapter {
-        nsCStringAdapter::Borrowed(nsCString::from(self))
+        nsCStringAdapter::Borrowed(nsCStr::from(self))
     }
 }
 
 impl nsCStringLike for String {
     fn adapt(&self) -> nsCStringAdapter {
-        nsCStringAdapter::Borrowed(nsCString::from(&self[..]))
+        nsCStringAdapter::Borrowed(nsCStr::from(&self[..]))
     }
 }
 
 impl nsCStringLike for Box<str> {
     fn adapt(&self) -> nsCStringAdapter {
-        nsCStringAdapter::Borrowed(nsCString::from(&self[..]))
+        nsCStringAdapter::Borrowed(nsCStr::from(&self[..]))
     }
 }
 
 #[macro_export]
 macro_rules! ns_auto_cstring {
     ($name:ident) => {
         let mut buf: [u8; 64] = [0; 64];
         let mut $name = $crate::nsFixedCString::new(&mut buf);
@@ -879,16 +1037,17 @@ macro_rules! ns_auto_cstring {
 // Bindings for nsString (u16 char type) //
 ///////////////////////////////////////////
 
 define_string_types! {
     char_t = u16;
 
     AString = nsAString;
     String = nsString;
+    Str = nsStr;
     FixedString = nsFixedString;
 
     StringLike = nsStringLike;
     StringAdapter = nsStringAdapter;
 
     StringRepr = nsStringRepr;
 
     drop = Gecko_FinalizeString;
@@ -921,22 +1080,28 @@ impl nsAString {
         } else {
             Err(())
         }
     }
 }
 
 // NOTE: The From impl for a string slice for nsString produces a <'static>
 // lifetime, as it allocates.
-impl<'a> From<&'a str> for nsString<'static> {
-    fn from(s: &'a str) -> nsString<'static> {
+impl<'a> From<&'a str> for nsString {
+    fn from(s: &'a str) -> nsString {
         s.encode_utf16().collect::<Vec<u16>>().into()
     }
 }
 
+impl<'a> From<&'a String> for nsString {
+    fn from(s: &'a String) -> nsString {
+        nsString::from(&s[..])
+    }
+}
+
 // Support for the write!() macro for writing to nsStrings
 impl fmt::Write for nsAString {
     fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
         // Directly invoke gecko's routines for appending utf8 strings to
         // nsAString values, to avoid as much overhead as possible
         self.append_utf8(&nsCString::from(s));
         Ok(())
     }
@@ -1017,16 +1182,18 @@ pub mod test_helpers {
     //! It is public to ensure that these testing functions are avaliable to
     //! gtest code.
 
     use super::{
         nsFixedCString,
         nsFixedString,
         nsCString,
         nsString,
+        nsCStr,
+        nsStr,
         nsCStringRepr,
         nsStringRepr,
         data_flags,
         class_flags,
     };
     use std::mem;
 
     /// Generates an #[no_mangle] extern "C" function which returns the size and
@@ -1037,33 +1204,37 @@ pub mod test_helpers {
             #[allow(non_snake_case)]
             pub extern fn $fname(size: *mut usize, align: *mut usize) {
                 unsafe {
                     *size = mem::size_of::<$T>();
                     *align = mem::align_of::<$T>();
                 }
             }
         };
-        ($T:ty, $U:ty, $fname:ident) => {
+        ($T:ty, $U:ty, $V:ty, $fname:ident) => {
             #[no_mangle]
             #[allow(non_snake_case)]
             pub extern fn $fname(size: *mut usize, align: *mut usize) {
                 unsafe {
                     *size = mem::size_of::<$T>();
                     *align = mem::align_of::<$T>();
 
                     assert_eq!(*size, mem::size_of::<$U>());
                     assert_eq!(*align, mem::align_of::<$U>());
+                    assert_eq!(*size, mem::size_of::<$V>());
+                    assert_eq!(*align, mem::align_of::<$V>());
                 }
             }
         }
     }
 
-    size_align_check!(nsStringRepr, nsString<'static>, Rust_Test_ReprSizeAlign_nsString);
-    size_align_check!(nsCStringRepr, nsCString<'static>, Rust_Test_ReprSizeAlign_nsCString);
+    size_align_check!(nsStringRepr, nsString, nsStr<'static>,
+                      Rust_Test_ReprSizeAlign_nsString);
+    size_align_check!(nsCStringRepr, nsCString, nsCStr<'static>,
+                      Rust_Test_ReprSizeAlign_nsCString);
     size_align_check!(nsFixedString<'static>, Rust_Test_ReprSizeAlign_nsFixedString);
     size_align_check!(nsFixedCString<'static>, Rust_Test_ReprSizeAlign_nsFixedCString);
 
     /// Generates a $[no_mangle] extern "C" function which returns the size,
     /// alignment and offset in the parent struct of a given member, with the
     /// given name.
     ///
     /// This method can trigger Undefined Behavior if the accessing the member
@@ -1083,17 +1254,17 @@ pub mod test_helpers {
                     *align = mem::align_of_val(&tmp.$member);
                     *offset =
                         (&tmp.$member as *const _ as usize) -
                         (&tmp as *const _ as usize);
                     mem::forget(tmp);
                 }
             }
         };
-        ($T:ty, $U:ty, $member:ident, $method:ident) => {
+        ($T:ty, $U:ty, $V:ty, $member:ident, $method:ident) => {
             #[no_mangle]
             #[allow(non_snake_case)]
             pub extern fn $method(size: *mut usize,
                                   align: *mut usize,
                                   offset: *mut usize) {
                 unsafe {
                     // Create a temporary value of type T to get offsets, sizes
                     // and alignments from.
@@ -1107,29 +1278,45 @@ pub mod test_helpers {
 
                     let tmp: $U = mem::zeroed();
                     assert_eq!(*size, mem::size_of_val(&tmp.hdr.$member));
                     assert_eq!(*align, mem::align_of_val(&tmp.hdr.$member));
                     assert_eq!(*offset,
                                (&tmp.hdr.$member as *const _ as usize) -
                                (&tmp as *const _ as usize));
                     mem::forget(tmp);
+
+                    let tmp: $V = mem::zeroed();
+                    assert_eq!(*size, mem::size_of_val(&tmp.hdr.$member));
+                    assert_eq!(*align, mem::align_of_val(&tmp.hdr.$member));
+                    assert_eq!(*offset,
+                               (&tmp.hdr.$member as *const _ as usize) -
+                               (&tmp as *const _ as usize));
+                    mem::forget(tmp);
                 }
             }
         }
     }
 
-    member_check!(nsStringRepr, nsString<'static>, data, Rust_Test_Member_nsString_mData);
-    member_check!(nsStringRepr, nsString<'static>, length, Rust_Test_Member_nsString_mLength);
-    member_check!(nsStringRepr, nsString<'static>, dataflags, Rust_Test_Member_nsString_mDataFlags);
-    member_check!(nsStringRepr, nsString<'static>, classflags, Rust_Test_Member_nsString_mClassFlags);
-    member_check!(nsCStringRepr, nsCString<'static>, data, Rust_Test_Member_nsCString_mData);
-    member_check!(nsCStringRepr, nsCString<'static>, length, Rust_Test_Member_nsCString_mLength);
-    member_check!(nsCStringRepr, nsCString<'static>, dataflags, Rust_Test_Member_nsCString_mDataFlags);
-    member_check!(nsCStringRepr, nsCString<'static>, classflags, Rust_Test_Member_nsCString_mClassFlags);
+    member_check!(nsStringRepr, nsString, nsStr<'static>,
+                  data, Rust_Test_Member_nsString_mData);
+    member_check!(nsStringRepr, nsString, nsStr<'static>,
+                  length, Rust_Test_Member_nsString_mLength);
+    member_check!(nsStringRepr, nsString, nsStr<'static>,
+                  dataflags, Rust_Test_Member_nsString_mDataFlags);
+    member_check!(nsStringRepr, nsString, nsStr<'static>,
+                  classflags, Rust_Test_Member_nsString_mClassFlags);
+    member_check!(nsCStringRepr, nsCString, nsCStr<'static>,
+                  data, Rust_Test_Member_nsCString_mData);
+    member_check!(nsCStringRepr, nsCString, nsCStr<'static>,
+                  length, Rust_Test_Member_nsCString_mLength);
+    member_check!(nsCStringRepr, nsCString, nsCStr<'static>,
+                  dataflags, Rust_Test_Member_nsCString_mDataFlags);
+    member_check!(nsCStringRepr, nsCString, nsCStr<'static>,
+                  classflags, Rust_Test_Member_nsCString_mClassFlags);
     member_check!(nsFixedString<'static>, capacity, Rust_Test_Member_nsFixedString_mFixedCapacity);
     member_check!(nsFixedString<'static>, buffer, Rust_Test_Member_nsFixedString_mFixedBuf);
     member_check!(nsFixedCString<'static>, capacity, Rust_Test_Member_nsFixedCString_mFixedCapacity);
     member_check!(nsFixedCString<'static>, buffer, Rust_Test_Member_nsFixedCString_mFixedBuf);
 
     #[no_mangle]
     #[allow(non_snake_case)]
     pub extern fn Rust_Test_NsStringFlags(f_terminated: *mut u16,