Merge m-c to fx-team, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 21 Apr 2016 15:03:13 -0700
changeset 332185 467bd00c72db8f6f34c4d9740378abb90365269f
parent 332184 e52d359e267ec429e1c56642d117e383d8fae4e9 (current diff)
parent 332177 0891f0fa044cba28024849803e170ed7700e01e0 (diff)
child 332186 fe77c894cb9ab23dc3b232f831261be76e4d6aa9
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team, a=merge MozReview-Commit-ID: m0xI0OSGys
js/src/tests/ecma_6/ArrayBuffer/indivisible-byteLength.js
layout/style/test/test_webkit_box_orient.html
testing/web-platform/meta/XMLHttpRequest/open-url-multi-window-2.htm.ini
testing/web-platform/meta/XMLHttpRequest/open-url-multi-window-5.htm.ini
testing/web-platform/tests/web-animations/keyframe-effect/getComputedTiming-currentIteration.html
testing/web-platform/tests/web-animations/keyframe-effect/getComputedTiming-progress.html
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -429,19 +429,19 @@ Accessible::NativeState()
 
   nsIFrame *frame = GetFrame();
   if (frame) {
     if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
       state |= states::FLOATING;
 
     // XXX we should look at layout for non XUL box frames, but need to decide
     // how that interacts with ARIA.
-    if (HasOwnContent() && mContent->IsXULElement() && frame->IsBoxFrame()) {
+    if (HasOwnContent() && mContent->IsXULElement() && frame->IsXULBoxFrame()) {
       const nsStyleXUL* xulStyle = frame->StyleXUL();
-      if (xulStyle && frame->IsBoxFrame()) {
+      if (xulStyle && frame->IsXULBoxFrame()) {
         // In XUL all boxes are either vertical or horizontal
         if (xulStyle->mBoxOrient == NS_STYLE_BOX_ORIENT_VERTICAL)
           state |= states::VERTICAL;
         else
           state |= states::HORIZONTAL;
       }
     }
   }
--- a/accessible/html/HTMLTableAccessible.cpp
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -1051,17 +1051,17 @@ HTMLTableAccessible::IsProbablyLayoutTab
   if (!tableFrame)
     RETURN_LAYOUT_ANSWER(false, "table with no frame!");
 
   nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0);
   if (!cellFrame)
     RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!");
 
   nsMargin border;
-  cellFrame->GetBorder(border);
+  cellFrame->GetXULBorder(border);
   if (border.top && border.bottom && border.left && border.right) {
     RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
   }
 
   /**
    * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward
    */
 
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -114,16 +114,19 @@ var handleContentContextMenu = function 
   let baseURI = doc.baseURI;
   let referrer = doc.referrer;
   let referrerPolicy = doc.referrerPolicy;
   let frameOuterWindowID = doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
                                           .getInterface(Ci.nsIDOMWindowUtils)
                                           .outerWindowID;
   let loginFillInfo = LoginManagerContent.getFieldContext(event.target);
 
+  // The same-origin check will be done in nsContextMenu.openLinkInTab.
+  let parentAllowsMixedContent = !!docShell.mixedContentChannel;
+
   // get referrer attribute from clicked link and parse it
   // if per element referrer is enabled, the element referrer overrules
   // the document wide referrer
   if (Services.prefs.getBoolPref("network.http.enablePerElementReferrer")) {
     let referrerAttrValue = Services.netUtils.parseAttributePolicyString(event.target.
                             getAttribute("referrerpolicy"));
     if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT) {
       referrerPolicy = referrerAttrValue;
@@ -175,17 +178,17 @@ var handleContentContextMenu = function 
 
     let customMenuItems = PageMenuChild.build(event.target);
     let principal = doc.nodePrincipal;
     sendRpcMessage("contextmenu",
                    { editFlags, spellInfo, customMenuItems, addonInfo,
                      principal, docLocation, charSet, baseURI, referrer,
                      referrerPolicy, contentType, contentDisposition,
                      frameOuterWindowID, selectionInfo, disableSetDesktopBg,
-                     loginFillInfo, },
+                     loginFillInfo, parentAllowsMixedContent },
                    { event, popupNode: event.target });
   }
   else {
     // Break out to the parent window and pass the add-on info along
     let browser = docShell.chromeEventHandler;
     let mainWin = browser.ownerDocument.defaultView;
     mainWin.gContextMenuContentData = {
       isRemote: false,
@@ -198,16 +201,17 @@ var handleContentContextMenu = function 
       charSet: charSet,
       referrer: referrer,
       referrerPolicy: referrerPolicy,
       contentType: contentType,
       contentDisposition: contentDisposition,
       selectionInfo: selectionInfo,
       disableSetDesktopBackground: disableSetDesktopBg,
       loginFillInfo,
+      parentAllowsMixedContent,
     };
   }
 }
 
 Cc["@mozilla.org/eventlistenerservice;1"]
   .getService(Ci.nsIEventListenerService)
   .addSystemEventListener(global, "contextmenu", handleContentContextMenu, false);
 
@@ -397,16 +401,32 @@ var ClickEventHandler = {
           json.bookmark = node.getAttribute("rel") == "sidebar";
           if (json.bookmark) {
             event.preventDefault(); // Need to prevent the pageload.
           }
         }
       }
       json.noReferrer = BrowserUtils.linkHasNoReferrer(node)
 
+      // Check if the link needs to be opened with mixed content allowed.
+      // Only when the owner doc has |mixedContentChannel| and the same origin
+      // should we allow mixed content.
+      json.allowMixedContent = false;
+      let docshell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIWebNavigation)
+                             .QueryInterface(Ci.nsIDocShell);
+      if (docShell.mixedContentChannel) {
+        const sm = Services.scriptSecurityManager;
+        try {
+          let targetURI = BrowserUtils.makeURI(href);
+          sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
+          json.allowMixedContent = true;
+        } catch (e) {}
+      }
+
       sendAsyncMessage("Content:Click", json);
       return;
     }
 
     // This might be middle mouse navigation.
     if (event.button == 1) {
       sendAsyncMessage("Content:Click", json);
     }
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -975,22 +975,22 @@ nsContextMenu.prototype = {
                this._openLinkInParameters({ private: true }));
   },
 
   // Open linked-to URL in a new tab.
   openLinkInTab: function(event) {
     urlSecurityCheck(this.linkURL, this.principal);
     let referrerURI = gContextMenuContentData.documentURIObject;
 
-    // if the mixedContentChannel is present and the referring URI passes
+    // if its parent allows mixed content and the referring URI passes
     // a same origin check with the target URI, we can preserve the users
     // decision of disabling MCB on a page for it's child tabs.
     let persistAllowMixedContentInChildTab = false;
 
-    if (this.browser.docShell && this.browser.docShell.mixedContentChannel) {
+    if (gContextMenuContentData.parentAllowsMixedContent) {
       const sm = Services.scriptSecurityManager;
       try {
         let targetURI = this.linkURI;
         sm.checkSameOriginURI(referrerURI, targetURI, false);
         persistAllowMixedContentInChildTab = true;
       }
       catch (e) { }
     }
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4256,16 +4256,17 @@
                                           referrer: data.referrer,
                                           referrerPolicy: data.referrerPolicy,
                                           contentType: data.contentType,
                                           contentDisposition: data.contentDisposition,
                                           frameOuterWindowID: data.frameOuterWindowID,
                                           selectionInfo: data.selectionInfo,
                                           disableSetDesktopBackground: data.disableSetDesktopBg,
                                           loginFillInfo: data.loginFillInfo,
+                                          parentAllowsMixedContent: data.parentAllowsMixedContent,
                                         };
               let popup = browser.ownerDocument.getElementById("contentAreaContextMenu");
               let event = gContextMenuContentData.event;
               popup.openPopupAtScreen(event.screenX, event.screenY, true);
               break;
             }
             case "DOMServiceWorkerFocusClient":
             case "DOMWebNotificationClicked": {
--- a/browser/modules/ContentClick.jsm
+++ b/browser/modules/ContentClick.jsm
@@ -75,12 +75,14 @@ var ContentClick = {
     if (where == "current")
       return;
 
     // Todo(903022): code for where == save
 
     let params = { charset: browser.characterSet,
                    referrerURI: browser.documentURI,
                    referrerPolicy: json.referrerPolicy,
-                   noReferrer: json.noReferrer };
+                   noReferrer: json.noReferrer,
+                   allowMixedContent: json.allowMixedContent };
+
     window.openLinkIn(json.href, where, params);
   }
 };
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -20,18 +20,16 @@ MOZ_ARG_WITH_STRING(android-version,
     android_version=$withval)
 
 if test $android_version -lt MIN_ANDROID_VERSION ; then
     AC_MSG_ERROR([--with-android-version must be at least MIN_ANDROID_VERSION.])
 fi
 
 case "$target" in
 *-android*|*-linuxandroid*)
-    NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-android-version=$android_version"
-
     AC_MSG_CHECKING([for android platform directory])
 
     case "$target_cpu" in
     arm)
         target_name=arm
         ;;
     i?86)
         target_name=x86
--- a/build/autoconf/arch.m4
+++ b/build/autoconf/arch.m4
@@ -244,18 +244,9 @@ if test "$CPU_ARCH" = "arm"; then
 
 fi # CPU_ARCH = arm
 
 AC_SUBST(HAVE_ARM_SIMD)
 AC_SUBST(HAVE_ARM_NEON)
 AC_SUBST(BUILD_ARM_NEON)
 AC_SUBST(ARM_ARCH)
 
-if test -n "$MOZ_ARCH"; then
-  NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-arch=$MOZ_ARCH"
-  NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb=$MOZ_THUMB"
-  NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb-interwork=$MOZ_THUMB_INTERWORK"
-  NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-fpu=$MOZ_FPU"
-  NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-float-abi=$MOZ_FLOAT_ABI"
-  NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-soft-float=$MOZ_SOFT_FLOAT"
-fi
-
 ])
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -163,23 +163,25 @@ https://sectest1.example.org:443       p
 https://sub.sectest2.example.org:443   privileged
 https://sectest2.example.org:443
 https://sub.sectest1.example.org:443
 
 #
 # Used while testing the url-classifier
 #
 http://malware.example.com:80
+http://unwanted.example.com:80
 http://tracking.example.com:80
 http://not-tracking.example.com:80
 http://tracking.example.org:80
 http://itisatracker.org:80
 http://trackertest.org:80
 
 https://malware.example.com:443
+https://unwanted.example.com:443
 https://tracking.example.com:443
 https://not-tracking.example.com:443
 https://tracking.example.org:443
 
 # Bug 483437, 484111
 https://www.bank1.com:443           privileged,cert=escapeattack1
 
 #
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -235,19 +235,20 @@ endif
 COBJS = $(notdir $(CSRCS:.c=.$(OBJ_SUFFIX)))
 SOBJS = $(notdir $(SSRCS:.S=.$(OBJ_SUFFIX)))
 # CPPSRCS can have different extensions (eg: .cpp, .cc)
 CPPOBJS = $(notdir $(addsuffix .$(OBJ_SUFFIX),$(basename $(CPPSRCS))))
 CMOBJS = $(notdir $(CMSRCS:.m=.$(OBJ_SUFFIX)))
 CMMOBJS = $(notdir $(CMMSRCS:.mm=.$(OBJ_SUFFIX)))
 # ASFILES can have different extensions (.s, .asm)
 ASOBJS = $(notdir $(addsuffix .$(OBJ_SUFFIX),$(basename $(ASFILES))))
-RSOBJS = $(addprefix lib,$(notdir $(RSSRCS:.rs=.$(LIB_SUFFIX))))
+RSOBJS = $(addprefix lib,$(notdir $(RSSRCS:.rs=.rlib)))
+RS_STATICLIB_CRATE_OBJ = $(addprefix lib,$(notdir $(RS_STATICLIB_CRATE_SRC:.rs=.$(LIB_SUFFIX))))
 ifndef OBJS
-_OBJS = $(COBJS) $(SOBJS) $(CPPOBJS) $(CMOBJS) $(CMMOBJS) $(ASOBJS) $(RSOBJS)
+_OBJS = $(COBJS) $(SOBJS) $(CPPOBJS) $(CMOBJS) $(CMMOBJS) $(ASOBJS) $(RSOBJS) $(RS_STATICLIB_CRATE_OBJ)
 OBJS = $(strip $(_OBJS))
 endif
 
 HOST_COBJS = $(addprefix host_,$(notdir $(HOST_CSRCS:.c=.$(OBJ_SUFFIX))))
 # HOST_CPPOBJS can have different extensions (eg: .cpp, .cc)
 HOST_CPPOBJS = $(addprefix host_,$(notdir $(addsuffix .$(OBJ_SUFFIX),$(basename $(HOST_CPPSRCS)))))
 HOST_CMOBJS = $(addprefix host_,$(notdir $(HOST_CMSRCS:.m=.$(OBJ_SUFFIX))))
 HOST_CMMOBJS = $(addprefix host_,$(notdir $(HOST_CMMSRCS:.mm=.$(OBJ_SUFFIX))))
@@ -854,19 +855,22 @@ endif
 define src_objdep
 $(basename $2$(notdir $1)).$(OBJ_SUFFIX): $1 $$(call mkdir_deps,$$(MDDEPDIR))
 endef
 $(foreach f,$(CSRCS) $(SSRCS) $(CPPSRCS) $(CMSRCS) $(CMMSRCS) $(ASFILES),$(eval $(call src_objdep,$(f))))
 $(foreach f,$(HOST_CSRCS) $(HOST_CPPSRCS) $(HOST_CMSRCS) $(HOST_CMMSRCS),$(eval $(call src_objdep,$(f),host_)))
 
 # The Rust compiler only outputs library objects, and so we need different
 # mangling to generate dependency rules for it.
-mk_libname = $(basename lib$(notdir $1)).$(LIB_SUFFIX)
+mk_libname = $(basename lib$(notdir $1)).rlib
 src_libdep = $(call mk_libname,$1): $1 $$(call mkdir_deps,$$(MDDEPDIR))
+mk_global_crate_libname = $(basename lib$(notdir $1)).$(LIB_SUFFIX)
+crate_src_libdep = $(call mk_global_crate_libname,$1): $1 $$(call mkdir_deps,$$(MDDEPDIR))
 $(foreach f,$(RSSRCS),$(eval $(call src_libdep,$(f))))
+$(foreach f,$(RS_STATICLIB_CRATE_SRC),$(eval $(call crate_src_libdep,$(f))))
 
 $(OBJS) $(HOST_OBJS) $(PROGOBJS) $(HOST_PROGOBJS): $(GLOBAL_DEPS)
 
 # Rules for building native targets must come first because of the host_ prefix
 $(HOST_COBJS):
 	$(REPORT_BUILD_VERBOSE)
 	$(ELOG) $(HOST_CC) $(HOST_OUTOPTION)$@ -c $(HOST_CPPFLAGS) $(HOST_CFLAGS) $(INCLUDES) $(NSPR_CFLAGS) $(_VPATH_SRCS)
 
@@ -909,18 +913,22 @@ ifdef ASFILES
 	$(REPORT_BUILD_VERBOSE)
 	$(AS) $(ASOUTOPTION)$@ $(ASFLAGS) $($(notdir $<)_FLAGS) $(AS_DASH_C_FLAG) $(_VPATH_SRCS)
 endif
 
 ifdef MOZ_RUST
 # Assume any system libraries rustc links against are already
 # in the target's LIBS.
 $(RSOBJS):
-	$(REPORT_BUILD_VERBOSE)
-	$(RUSTC) $(RUSTFLAGS) --crate-type staticlib --emit dep-info=$(MDDEPDIR)/$(call mk_libname,$<).pp,link=$(call mk_libname,$<) $(_VPATH_SRCS)
+	$(REPORT_BUILD)
+	$(RUSTC) $(RUSTFLAGS) --crate-type rlib --emit dep-info=$(MDDEPDIR)/$(call mk_libname,$<).pp,link=$(call mk_libname,$<) $(_VPATH_SRCS)
+
+$(RS_STATICLIB_CRATE_OBJ):
+	$(REPORT_BUILD)
+	$(RUSTC) $(RUSTFLAGS) --crate-type staticlib $(RLIB_EXTERN_CRATE_OPTIONS) --emit dep-info=$(MDDEPDIR)/$(call mk_global_crate_libname,$(RS_STATICLIB_CRATE_SRC)).pp,link=$@ $(RS_STATICLIB_CRATE_SRC)
 endif
 
 $(SOBJS):
 	$(REPORT_BUILD)
 	$(AS) -o $@ $(ASFLAGS) $($(notdir $<)_FLAGS) $(LOCAL_INCLUDES) $(TARGET_LOCAL_INCLUDES) -c $<
 
 $(CPPOBJS):
 	$(REPORT_BUILD_VERBOSE)
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -213,46 +213,48 @@ KeyframeEffectReadOnly::GetLocalTime() c
   Nullable<TimeDuration> result;
   if (mAnimation) {
     result = mAnimation->GetCurrentTime();
   }
   return result;
 }
 
 void
-KeyframeEffectReadOnly::GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const
+KeyframeEffectReadOnly::GetComputedTimingAsDict(
+    ComputedTimingProperties& aRetVal) const
 {
   const Nullable<TimeDuration> currentTime = GetLocalTime();
   GetComputedTimingDictionary(GetComputedTimingAt(currentTime,
                                                   SpecifiedTiming()),
                               currentTime,
                               SpecifiedTiming(),
                               aRetVal);
 }
 
 ComputedTiming
 KeyframeEffectReadOnly::GetComputedTimingAt(
-                          const Nullable<TimeDuration>& aLocalTime,
-                          const TimingParams& aTiming)
+    const Nullable<TimeDuration>& aLocalTime,
+    const TimingParams& aTiming)
 {
   const StickyTimeDuration zeroDuration;
 
   // Always return the same object to benefit from return-value optimization.
   ComputedTiming result;
 
   if (aTiming.mDuration) {
     MOZ_ASSERT(aTiming.mDuration.ref() >= zeroDuration,
                "Iteration duration should be positive");
     result.mDuration = aTiming.mDuration.ref();
   }
 
   MOZ_ASSERT(aTiming.mIterations >= 0.0 && !IsNaN(aTiming.mIterations),
              "mIterations should be nonnegative & finite, as ensured by "
              "ValidateIterations or CSSParser");
   result.mIterations = aTiming.mIterations;
+
   MOZ_ASSERT(aTiming.mIterationStart >= 0.0,
              "mIterationStart should be nonnegative, as ensured by "
              "ValidateIterationStart");
   result.mIterationStart = aTiming.mIterationStart;
 
   result.mActiveDuration = aTiming.ActiveDuration();
   result.mEndTime = aTiming.EndTime();
   result.mFill = aTiming.mFill == dom::FillMode::Auto ?
@@ -261,118 +263,87 @@ KeyframeEffectReadOnly::GetComputedTimin
 
   // The default constructor for ComputedTiming sets all other members to
   // values consistent with an animation that has not been sampled.
   if (aLocalTime.IsNull()) {
     return result;
   }
   const TimeDuration& localTime = aLocalTime.Value();
 
-  // When we finish exactly at the end of an iteration we need to report
-  // the end of the final iteration and not the start of the next iteration
-  // so we set up a flag for that case.
-  bool isEndOfFinalIteration = false;
-
-  // Get the normalized time within the active interval.
+  // Calculate the time within the active interval.
+  // https://w3c.github.io/web-animations/#active-time
   StickyTimeDuration activeTime;
   if (localTime >=
         std::min(StickyTimeDuration(aTiming.mDelay + result.mActiveDuration),
                  result.mEndTime)) {
     result.mPhase = ComputedTiming::AnimationPhase::After;
     if (!result.FillsForwards()) {
       // The animation isn't active or filling at this time.
-      result.mProgress.SetNull();
       return result;
     }
     activeTime = result.mActiveDuration;
-    double finiteProgress =
-      (IsInfinite(result.mIterations) ? 0.0 : result.mIterations)
-      + result.mIterationStart;
-    isEndOfFinalIteration = result.mIterations != 0.0 &&
-                            fmod(finiteProgress, 1.0) == 0;
   } else if (localTime <
                std::min(StickyTimeDuration(aTiming.mDelay), result.mEndTime)) {
     result.mPhase = ComputedTiming::AnimationPhase::Before;
     if (!result.FillsBackwards()) {
       // The animation isn't active or filling at this time.
-      result.mProgress.SetNull();
       return result;
     }
     // activeTime is zero
   } else {
     MOZ_ASSERT(result.mActiveDuration != zeroDuration,
                "How can we be in the middle of a zero-duration interval?");
     result.mPhase = ComputedTiming::AnimationPhase::Active;
     activeTime = localTime - aTiming.mDelay;
   }
 
-  // Calculate the scaled active time
-  // (We handle the case where the iterationStart is zero separately in case
-  // the duration is infinity, since 0 * Infinity is undefined.)
-  StickyTimeDuration startOffset =
-    result.mIterationStart == 0.0
-    ? StickyTimeDuration(0)
-    : result.mDuration.MultDouble(result.mIterationStart);
-  StickyTimeDuration scaledActiveTime = activeTime + startOffset;
+  // Convert active time to a multiple of iterations.
+  // https://w3c.github.io/web-animations/#overall-progress
+  double overallProgress;
+  if (result.mDuration == zeroDuration) {
+    overallProgress = result.mPhase == ComputedTiming::AnimationPhase::Before
+                      ? 0.0
+                      : result.mIterations;
+  } else {
+    overallProgress = activeTime / result.mDuration;
+  }
 
-  // Get the position within the current iteration.
-  StickyTimeDuration iterationTime;
-  if (result.mDuration != zeroDuration &&
-      scaledActiveTime != StickyTimeDuration::Forever()) {
-    iterationTime = isEndOfFinalIteration
-                    ? result.mDuration
-      : scaledActiveTime % result.mDuration;
-  } /* else, either the duration is zero and iterationTime is zero,
-       or the scaledActiveTime is infinity in which case the iterationTime
-       should become infinity but we will not use the iterationTime in that
-       case so we just leave it as zero */
+  // Factor in iteration start offset.
+  if (IsFinite(overallProgress)) {
+    overallProgress += result.mIterationStart;
+  }
 
   // Determine the 0-based index of the current iteration.
-  if (result.mPhase == ComputedTiming::AnimationPhase::Before ||
-      result.mIterations == 0) {
-    result.mCurrentIteration = static_cast<uint64_t>(result.mIterationStart);
-  } else if (result.mPhase == ComputedTiming::AnimationPhase::After) {
-    result.mCurrentIteration =
-      IsInfinite(result.mIterations)
-      ? UINT64_MAX // In GetComputedTimingDictionary(), we will convert this
-                   // into Infinity.
-      : static_cast<uint64_t>(ceil(result.mIterations +
-                              result.mIterationStart)) - 1;
-  } else if (result.mDuration == StickyTimeDuration::Forever()) {
-    result.mCurrentIteration = static_cast<uint64_t>(result.mIterationStart);
-  } else {
-    result.mCurrentIteration =
-      static_cast<uint64_t>(scaledActiveTime / result.mDuration); // floor
+  // https://w3c.github.io/web-animations/#current-iteration
+  result.mCurrentIteration =
+    IsInfinite(result.mIterations) &&
+      result.mPhase == ComputedTiming::AnimationPhase::After
+    ? UINT64_MAX // In GetComputedTimingDictionary(),
+                 // we will convert this into Infinity
+    : static_cast<uint64_t>(overallProgress);
+
+  // Convert the overall progress to a fraction of a single iteration--the
+  // simply iteration progress.
+  // https://w3c.github.io/web-animations/#simple-iteration-progress
+  double progress = IsFinite(overallProgress)
+                    ? fmod(overallProgress, 1.0)
+                    : fmod(result.mIterationStart, 1.0);
+
+  // When we finish exactly at the end of an iteration we need to report
+  // the end of the final iteration and not the start of the next iteration.
+  if (result.mPhase == ComputedTiming::AnimationPhase::After &&
+      progress == 0.0 &&
+      result.mIterations != 0.0) {
+    progress = 1.0;
+    if (result.mCurrentIteration != UINT64_MAX) {
+      result.mCurrentIteration--;
+    }
   }
 
-  // Normalize the iteration time into a fraction of the iteration duration.
-  if (result.mPhase == ComputedTiming::AnimationPhase::Before ||
-      result.mIterations == 0) {
-    double progress = fmod(result.mIterationStart, 1.0);
-    result.mProgress.SetValue(progress);
-  } else if (result.mPhase == ComputedTiming::AnimationPhase::After) {
-    double progress;
-    if (isEndOfFinalIteration) {
-      progress = 1.0;
-    } else if (IsInfinite(result.mIterations)) {
-      progress = fmod(result.mIterationStart, 1.0);
-    } else {
-      progress = fmod(result.mIterations + result.mIterationStart, 1.0);
-    }
-    result.mProgress.SetValue(progress);
-  } else {
-    // We are in the active phase so the iteration duration can't be zero.
-    MOZ_ASSERT(result.mDuration != zeroDuration,
-               "In the active phase of a zero-duration animation?");
-    double progress = result.mDuration == StickyTimeDuration::Forever()
-                      ? fmod(result.mIterationStart, 1.0)
-                      : iterationTime / result.mDuration;
-    result.mProgress.SetValue(progress);
-  }
-
+  // Factor in the direction.
   bool thisIterationReverse = false;
   switch (aTiming.mDirection) {
     case PlaybackDirection::Normal:
       thisIterationReverse = false;
       break;
     case PlaybackDirection::Reverse:
       thisIterationReverse = true;
       break;
@@ -381,32 +352,35 @@ KeyframeEffectReadOnly::GetComputedTimin
       break;
     case PlaybackDirection::Alternate_reverse:
       thisIterationReverse = (result.mCurrentIteration & 1) == 0;
       break;
     default:
       MOZ_ASSERT(true, "Unknown PlaybackDirection type");
   }
   if (thisIterationReverse) {
-    result.mProgress.SetValue(1.0 - result.mProgress.Value());
+    progress = 1.0 - progress;
   }
 
+  // Calculate the 'before flag' which we use when applying step timing
+  // functions.
   if ((result.mPhase == ComputedTiming::AnimationPhase::After &&
        thisIterationReverse) ||
       (result.mPhase == ComputedTiming::AnimationPhase::Before &&
        !thisIterationReverse)) {
     result.mBeforeFlag = ComputedTimingFunction::BeforeFlag::Set;
   }
 
+  // Apply the easing.
   if (aTiming.mFunction) {
-    result.mProgress.SetValue(
-      aTiming.mFunction->GetValue(result.mProgress.Value(),
-                                  result.mBeforeFlag));
+    progress = aTiming.mFunction->GetValue(progress, result.mBeforeFlag);
   }
 
+  MOZ_ASSERT(IsFinite(progress), "Progress value should be finite");
+  result.mProgress.SetValue(progress);
   return result;
 }
 
 // https://w3c.github.io/web-animations/#in-play
 bool
 KeyframeEffectReadOnly::IsInPlay() const
 {
   if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) {
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -31,16 +31,17 @@ support-files =
   css-transitions/file_csstransition-transitionproperty.html
   css-transitions/file_document-get-animations.html
   css-transitions/file_effect-target.html
   css-transitions/file_element-get-animations.html
   css-transitions/file_keyframeeffect-getframes.html
   css-transitions/file_pseudoElement-get-animations.html
   document-timeline/file_document-timeline.html
   mozilla/file_deferred_start.html
+  mozilla/file_disabled_properties.html
   mozilla/file_hide_and_show.html
   mozilla/file_partial_keyframes.html
   testcommon.js
 
 [css-animations/test_animations-dynamic-changes.html]
 [css-animations/test_animation-cancel.html]
 [css-animations/test_animation-computed-timing.html]
 [css-animations/test_animation-currenttime.html]
@@ -73,10 +74,11 @@ skip-if = buildapp == 'mulet'
 skip-if = buildapp == 'mulet'
 [css-transitions/test_keyframeeffect-getframes.html]
 [css-transitions/test_pseudoElement-get-animations.html]
 [document-timeline/test_document-timeline.html]
 [document-timeline/test_request_animation_frame.html]
 skip-if = buildapp == 'mulet'
 [mozilla/test_deferred_start.html]
 skip-if = (toolkit == 'gonk' && debug)
+[mozilla/test_disabled_properties.html]
 [mozilla/test_hide_and_show.html]
 [mozilla/test_partial_keyframes.html]
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/mozilla/file_disabled_properties.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+function waitForSetPref(pref, value) {
+  return new Promise(function(resolve, reject) {
+    SpecialPowers.pushPrefEnv({ 'set': [[pref, value]] }, resolve);
+  });
+}
+
+/*
+ * These tests rely on the fact that the -webkit-text-fill-color property
+ * is disabled by the layout.css.prefixes.webkit pref. If we ever remove that
+ * pref we will need to substitute some other pref:property combination.
+ */
+
+promise_test(function(t) {
+  return waitForSetPref('layout.css.prefixes.webkit', true).then(() => {
+    var anim = addDiv(t).animate({ webkitTextFillColor: [ 'green', 'blue' ]});
+    assert_equals(anim.effect.getFrames().length, 2,
+                  'A property-indexed keyframe specifying only enabled'
+                  + ' properties produces keyframes');
+    return waitForSetPref('layout.css.prefixes.webkit', false);
+  }).then(() => {
+    var anim = addDiv(t).animate({ webkitTextFillColor: [ 'green', 'blue' ]});
+    assert_equals(anim.effect.getFrames().length, 0,
+                  'A property-indexed keyframe specifying only disabled'
+                  + ' properties produces no keyframes');
+  });
+}, 'Specifying a disabled property using a property-indexed keyframe');
+
+promise_test(function(t) {
+  var createAnim = () => {
+    var anim = addDiv(t).animate([ { webkitTextFillColor: 'green' },
+                                   { webkitTextFillColor: 'blue' } ]);
+    assert_equals(anim.effect.getFrames().length, 2,
+                  'Animation specified using a keyframe sequence should'
+                  + ' return the same number of keyframes regardless of'
+                  + ' whether or not the specified properties are disabled');
+    return anim;
+  };
+
+  var assert_has_property = (anim, index, descr, property) => {
+    assert_true(
+      anim.effect.getFrames()[index].hasOwnProperty(property),
+      `${descr} should have the '${property}' property`);
+  };
+  var assert_does_not_have_property = (anim, index, descr, property) => {
+    assert_false(
+      anim.effect.getFrames()[index].hasOwnProperty(property),
+      `${descr} should NOT have the '${property}' property`);
+  };
+
+  return waitForSetPref('layout.css.prefixes.webkit', true).then(() => {
+    var anim = createAnim();
+    assert_has_property(anim, 0, 'Initial keyframe', 'webkitTextFillColor');
+    assert_has_property(anim, 1, 'Final keyframe', 'webkitTextFillColor');
+    return waitForSetPref('layout.css.prefixes.webkit', false);
+  }).then(() => {
+    var anim = createAnim();
+    assert_does_not_have_property(anim, 0, 'Initial keyframe',
+                                  'webkitTextFillColor');
+    assert_does_not_have_property(anim, 1, 'Final keyframe',
+                                  'webkitTextFillColor');
+  });
+}, 'Specifying a disabled property using a keyframe sequence');
+
+done();
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/mozilla/test_disabled_properties.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+  { "set": [["dom.animations-api.core.enabled", true]]},
+  function() {
+    window.open("file_disabled_properties.html");
+  });
+</script>
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -186,16 +186,17 @@ EventSource::Init(nsISupports* aOwner,
                   bool aWithCredentials)
 {
   if (mReadyState != CONNECTING || !PrefEnabled()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
   NS_ENSURE_STATE(sgo);
+  // XXXbz why are we checking this?  This doesn't match anything in the spec.
   nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
   NS_ENSURE_STATE(scriptContext);
 
   nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
     do_QueryInterface(aOwner);
   NS_ENSURE_STATE(scriptPrincipal);
   nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
   NS_ENSURE_STATE(principal);
@@ -208,29 +209,23 @@ EventSource::Init(nsISupports* aOwner,
     nsJSUtils::GetCallingLocation(cx, mScriptFile, &mScriptLine,
                                   &mScriptColumn);
     mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx);
   }
 
   // Get the load group for the page. When requesting we'll add ourselves to it.
   // This way any pending requests will be automatically aborted if the user
   // leaves the page.
-  nsresult rv;
-  nsIScriptContext* sc = GetContextForEventHandlers(&rv);
-  if (sc) {
-    nsCOMPtr<nsIDocument> doc =
-      nsContentUtils::GetDocumentFromScriptContext(sc);
-    if (doc) {
-      mLoadGroup = doc->GetDocumentLoadGroup();
-    }
+  nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
+  if (doc) {
+    mLoadGroup = doc->GetDocumentLoadGroup();
   }
-
   // get the src
   nsCOMPtr<nsIURI> baseURI;
-  rv = GetBaseURI(getter_AddRefs(baseURI));
+  nsresult rv = GetBaseURI(getter_AddRefs(baseURI));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIURI> srcURI;
   rv = NS_NewURI(getter_AddRefs(srcURI), aURL, nullptr, baseURI);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
 
   // we observe when the window freezes and thaws
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
@@ -521,18 +516,19 @@ EventSource::AsyncOnChannelRedirect(nsIC
      return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   // update our channel
 
   mHttpChannel = do_QueryInterface(aNewChannel);
   NS_ENSURE_STATE(mHttpChannel);
 
-  rv = SetupHttpChannel();
-  NS_ENSURE_SUCCESS(rv, rv);
+  SetupHttpChannel();
+  // The HTTP impl already copies over the referrer and referrer policy on
+  // redirects, so we don't need to SetupReferrerPolicy().
 
   if ((aFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) {
     rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   aCallback->OnRedirectVerifyCallback(NS_OK);
 
@@ -588,68 +584,58 @@ EventSource::GetBaseURI(nsIURI **aBaseUR
 {
   NS_ENSURE_ARG_POINTER(aBaseURI);
 
   *aBaseURI = nullptr;
 
   nsCOMPtr<nsIURI> baseURI;
 
   // first we try from document->GetBaseURI()
-  nsresult rv;
-  nsIScriptContext* sc = GetContextForEventHandlers(&rv);
-  nsCOMPtr<nsIDocument> doc =
-    nsContentUtils::GetDocumentFromScriptContext(sc);
+  nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
   if (doc) {
     baseURI = doc->GetBaseURI();
   }
 
   // otherwise we get from the doc's principal
   if (!baseURI) {
-    rv = mPrincipal->GetURI(getter_AddRefs(baseURI));
+    nsresult rv = mPrincipal->GetURI(getter_AddRefs(baseURI));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   NS_ENSURE_STATE(baseURI);
 
   baseURI.forget(aBaseURI);
   return NS_OK;
 }
 
-net::ReferrerPolicy
-EventSource::GetReferrerPolicy()
-{
-  nsresult rv;
-  nsIScriptContext* sc = GetContextForEventHandlers(&rv);
-  NS_ENSURE_SUCCESS(rv, mozilla::net::RP_Default);
-
-  nsCOMPtr<nsIDocument> doc = nsContentUtils::GetDocumentFromScriptContext(sc);
-  return doc ? doc->GetReferrerPolicy() : mozilla::net::RP_Default;
-}
-
-nsresult
+void
 EventSource::SetupHttpChannel()
 {
   mHttpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET"));
 
   /* set the http request headers */
 
   mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
     NS_LITERAL_CSTRING(TEXT_EVENT_STREAM), false);
 
   // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header
 
   if (!mLastEventID.IsEmpty()) {
     mHttpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Last-Event-ID"),
       NS_ConvertUTF16toUTF8(mLastEventID), false);
   }
+}
 
-  nsCOMPtr<nsIURI> codebase;
-  nsresult rv = GetBaseURI(getter_AddRefs(codebase));
-  if (NS_SUCCEEDED(rv)) {
-    rv = mHttpChannel->SetReferrerWithPolicy(codebase, this->GetReferrerPolicy());
+nsresult
+EventSource::SetupReferrerPolicy()
+{
+  nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
+  if (doc) {
+    nsresult rv = mHttpChannel->SetReferrerWithPolicy(doc->GetDocumentURI(),
+                                                      doc->GetReferrerPolicy());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 nsresult
 EventSource::InitChannelAndRequestEventSource()
@@ -666,19 +652,17 @@ EventSource::InitChannelAndRequestEventS
   if (NS_FAILED(rv) || !isValidScheme) {
     DispatchFailConnection();
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsLoadFlags loadFlags;
   loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE;
 
-  nsIScriptContext* sc = GetContextForEventHandlers(&rv);
-  nsCOMPtr<nsIDocument> doc =
-    nsContentUtils::GetDocumentFromScriptContext(sc);
+  nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
 
   nsSecurityFlags securityFlags =
     nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
 
   if (mWithCredentials) {
     securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
   }
 
@@ -705,17 +689,18 @@ EventSource::InitChannelAndRequestEventS
                        loadFlags);       // aLoadFlags
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   mHttpChannel = do_QueryInterface(channel);
   NS_ENSURE_TRUE(mHttpChannel, NS_ERROR_NO_INTERFACE);
 
-  rv = SetupHttpChannel();
+  SetupHttpChannel();
+  rv = SetupReferrerPolicy();
   NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef DEBUG
   {
     nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks;
     mHttpChannel->GetNotificationCallbacks(getter_AddRefs(notificationCallbacks));
     MOZ_ASSERT(!notificationCallbacks);
   }
--- a/dom/base/EventSource.h
+++ b/dom/base/EventSource.h
@@ -102,19 +102,18 @@ protected:
   virtual ~EventSource();
 
   nsresult Init(nsISupports* aOwner,
                 const nsAString& aURL,
                 bool aWithCredentials);
 
   nsresult GetBaseURI(nsIURI **aBaseURI);
 
-  net::ReferrerPolicy GetReferrerPolicy();
-
-  nsresult SetupHttpChannel();
+  void SetupHttpChannel();
+  nsresult SetupReferrerPolicy();
   nsresult InitChannelAndRequestEventSource();
   nsresult ResetConnection();
   nsresult DispatchFailConnection();
   nsresult SetReconnectionTimeout();
 
   void AnnounceConnection();
   void DispatchAllMessageEvents();
   void ReestablishConnection();
--- a/dom/base/ResponsiveImageSelector.cpp
+++ b/dom/base/ResponsiveImageSelector.cpp
@@ -272,17 +272,19 @@ ResponsiveImageSelector::AppendCandidate
   }
 
   mCandidates.AppendElement(aCandidate);
 }
 
 void
 ResponsiveImageSelector::MaybeAppendDefaultCandidate()
 {
-  NS_ENSURE_TRUE(!mDefaultSourceURL.IsEmpty(), /* void */);
+  if (mDefaultSourceURL.IsEmpty()) {
+    return;
+  }
 
   int numCandidates = mCandidates.Length();
 
   // https://html.spec.whatwg.org/multipage/embedded-content.html#update-the-source-set
   // step 4.1.3:
   // If child has a src attribute whose value is not the empty string and source
   // set does not contain an image source with a density descriptor value of 1,
   // and no image source with a width descriptor, append child's src attribute
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -888,29 +888,27 @@ WebSocketImpl::GetInterface(const nsIID&
   AssertIsOnMainThread();
 
   if (!mWebSocket || mWebSocket->ReadyState() == WebSocket::CLOSED) {
     return NS_ERROR_FAILURE;
   }
 
   if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
       aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
-    nsresult rv;
-    nsIScriptContext* sc = mWebSocket->GetContextForEventHandlers(&rv);
-    nsCOMPtr<nsIDocument> doc =
-      nsContentUtils::GetDocumentFromScriptContext(sc);
-    if (!doc) {
+    nsCOMPtr<nsPIDOMWindowInner> win = mWebSocket->GetWindowIfCurrent();
+    if (!win) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
+    nsresult rv;
     nsCOMPtr<nsIPromptFactory> wwatch =
       do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsPIDOMWindowOuter> outerWindow = doc->GetWindow();
+    nsCOMPtr<nsPIDOMWindowOuter> outerWindow = win->GetOuterWindow();
     return wwatch->GetPrompt(outerWindow, aIID, aResult);
   }
 
   return QueryInterface(aIID, aResult);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // WebSocket
@@ -1489,40 +1487,38 @@ WebSocketImpl::Init(JSContext* aCx,
   }
 
   // parses the url
   aRv = ParseURL(PromiseFlatString(aURL));
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  nsIScriptContext* sc = nullptr;
-  {
-    nsresult rv;
-    sc = mWebSocket->GetContextForEventHandlers(&rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      aRv.Throw(rv);
-      return;
-    }
-  }
-
   nsCOMPtr<nsIURI> uri;
   {
     nsresult rv = NS_NewURI(getter_AddRefs(uri), mURI);
 
     // We crash here because we are sure that mURI is a valid URI, so either we
     // are OOM'ing or something else bad is happening.
     if (NS_WARN_IF(NS_FAILED(rv))) {
       MOZ_CRASH();
     }
   }
 
   // Check content policy.
   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-  nsCOMPtr<nsIDocument> originDoc = nsContentUtils::GetDocumentFromScriptContext(sc);
+  nsCOMPtr<nsIDocument> originDoc = mWebSocket->GetDocumentIfCurrent();
+  if (!originDoc) {
+    nsresult rv = mWebSocket->CheckInnerWindowCorrectness();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRv.Throw(rv);
+      return;
+    }
+  }
+
   mOriginDocument = do_GetWeakReference(originDoc);
   aRv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,
                                   uri,
                                   aPrincipal,
                                   originDoc,
                                   EmptyCString(),
                                   nullptr,
                                   &shouldLoad,
@@ -2651,21 +2647,17 @@ WebSocketImpl::Resume()
 NS_IMETHODIMP
 WebSocketImpl::GetLoadGroup(nsILoadGroup** aLoadGroup)
 {
   AssertIsOnMainThread();
 
   *aLoadGroup = nullptr;
 
   if (mIsMainThread) {
-    nsresult rv;
-    nsIScriptContext* sc = mWebSocket->GetContextForEventHandlers(&rv);
-    nsCOMPtr<nsIDocument> doc =
-      nsContentUtils::GetDocumentFromScriptContext(sc);
-
+    nsCOMPtr<nsIDocument> doc = mWebSocket->GetDocumentIfCurrent();
     if (doc) {
       *aLoadGroup = doc->GetDocumentLoadGroup().take();
     }
 
     return NS_OK;
   }
 
   MOZ_ASSERT(mWorkerPrivate);
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5838,33 +5838,16 @@ nsContentUtils::GetUTFOrigin(nsIURI* aUR
   else {
     aOrigin.AssignLiteral("null");
   }
   
   return NS_OK;
 }
 
 /* static */
-nsIDocument*
-nsContentUtils::GetDocumentFromScriptContext(nsIScriptContext *aScriptContext)
-{
-  if (!aScriptContext) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsPIDOMWindowOuter> window =
-    do_QueryInterface(aScriptContext->GetGlobalObject());
-  if (!window) {
-    return nullptr;
-  }
-
-  return window->GetDoc();
-}
-
-/* static */
 bool
 nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal, nsIChannel* aChannel, bool aAllowIfInheritsPrincipal)
 {
   nsCOMPtr<nsIURI> channelURI;
   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
   NS_ENSURE_SUCCESS(rv, false);
 
   return NS_SUCCEEDED(aPrincipal->CheckMayLoad(channelURI, false, aAllowIfInheritsPrincipal));
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1750,26 +1750,16 @@ public:
                                      bool aTrusted,
                                      nsIDOMEvent* aSourceEvent = nullptr,
                                      nsIPresShell* aShell = nullptr,
                                      bool aCtrl = false,
                                      bool aAlt = false,
                                      bool aShift = false,
                                      bool aMeta = false);
 
-  /**
-   * Gets the nsIDocument given the script context. Will return nullptr on failure.
-   *
-   * @param aScriptContext the script context to get the document for; can be null
-   *
-   * @return the document associated with the script context
-   */
-  static nsIDocument*
-  GetDocumentFromScriptContext(nsIScriptContext* aScriptContext);
-
   static bool CheckMayLoad(nsIPrincipal* aPrincipal, nsIChannel* aChannel, bool aAllowIfInheritsPrincipal);
 
   /**
    * The method checks whether the caller can access native anonymous content.
    * If there is no JS in the stack or privileged JS is running, this
    * method returns true, otherwise false.
    */
   static bool CanAccessNativeAnon();
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -272,32 +272,31 @@ nsImageLoadingContent::OnUnlockedDraw()
     return;
   }
 
   nsIFrame* frame = GetOurPrimaryFrame();
   if (!frame) {
     return;
   }
 
-  if (frame->GetVisibility() == Visibility::APPROXIMATELY_VISIBLE) {
-    // This frame is already marked visible; there's nothing to do.
-    return;
+  if (frame->IsVisibleOrMayBecomeVisibleSoon()) {
+    return;  // Nothing to do.
   }
 
   nsPresContext* presContext = frame->PresContext();
   if (!presContext) {
     return;
   }
 
   nsIPresShell* presShell = presContext->PresShell();
   if (!presShell) {
     return;
   }
 
-  presShell->EnsureFrameInApproximatelyVisibleList(frame);
+  presShell->MarkFrameVisibleInDisplayPort(frame);
 }
 
 nsresult
 nsImageLoadingContent::OnImageIsAnimated(imgIRequest *aRequest)
 {
   bool* requestFlag = GetRegisteredFlagForRequest(aRequest);
   if (requestFlag) {
     nsLayoutUtils::RegisterImageRequest(GetFramePresContext(),
@@ -514,17 +513,17 @@ nsImageLoadingContent::FrameDestroyed(ns
                                           &mPendingRequestRegistered);
   }
 
   UntrackImage(mCurrentRequest);
   UntrackImage(mPendingRequest);
 
   nsIPresShell* presShell = presContext ? presContext->GetPresShell() : nullptr;
   if (presShell) {
-    presShell->RemoveFrameFromApproximatelyVisibleList(aFrame);
+    presShell->MarkFrameNonvisible(aFrame);
   }
 }
 
 /* static */
 nsContentPolicyType
 nsImageLoadingContent::PolicyTypeForLoad(ImageLoadType aImageLoadType)
 {
   if (aImageLoadType == eImageLoadType_Imageset) {
@@ -1425,22 +1424,23 @@ nsImageLoadingContent::UnbindFromTree(bo
     doc->UnblockOnload(false);
 }
 
 void
 nsImageLoadingContent::OnVisibilityChange(Visibility aNewVisibility,
                                           const Maybe<OnNonvisible>& aNonvisibleAction)
 {
   switch (aNewVisibility) {
-    case Visibility::APPROXIMATELY_VISIBLE:
+    case Visibility::MAY_BECOME_VISIBLE:
+    case Visibility::IN_DISPLAYPORT:
       TrackImage(mCurrentRequest);
       TrackImage(mPendingRequest);
       break;
 
-    case Visibility::APPROXIMATELY_NONVISIBLE:
+    case Visibility::NONVISIBLE:
       UntrackImage(mCurrentRequest, aNonvisibleAction);
       UntrackImage(mPendingRequest, aNonvisibleAction);
       break;
 
     case Visibility::UNTRACKED:
       MOZ_ASSERT_UNREACHABLE("Shouldn't notify for untracked visibility");
       break;
   }
@@ -1456,21 +1456,21 @@ nsImageLoadingContent::TrackImage(imgIRe
              "Why haven't we heard of this request?");
 
   nsIDocument* doc = GetOurCurrentDoc();
   if (!doc) {
     return;
   }
 
   // We only want to track this request if we're visible. Ordinarily we check
-  // the visible count, but that requires a frame; in cases where
+  // whether our frame considers itself visible, but in cases where
   // GetOurPrimaryFrame() cannot obtain a frame (e.g. <feImage>), we assume
   // we're visible if FrameCreated() was called.
   nsIFrame* frame = GetOurPrimaryFrame();
-  if ((frame && frame->GetVisibility() == Visibility::APPROXIMATELY_NONVISIBLE) ||
+  if ((frame && !frame->IsVisibleOrMayBecomeVisibleSoon()) ||
       (!frame && !mFrameCreateCalled)) {
     return;
   }
 
   if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
     mCurrentRequestFlags |= REQUEST_IS_TRACKED;
     doc->AddImage(mCurrentRequest);
   }
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -1374,21 +1374,17 @@ nsXMLHttpRequest::GetLoadGroup() const
     return nullptr;
   }
 
   if (mLoadGroup) {
     nsCOMPtr<nsILoadGroup> ref = mLoadGroup;
     return ref.forget();
   }
 
-  nsresult rv = NS_ERROR_FAILURE;
-  nsIScriptContext* sc =
-    const_cast<nsXMLHttpRequest*>(this)->GetContextForEventHandlers(&rv);
-  nsCOMPtr<nsIDocument> doc =
-    nsContentUtils::GetDocumentFromScriptContext(sc);
+  nsIDocument* doc = GetDocumentIfCurrent();
   if (doc) {
     return doc->GetDocumentLoadGroup();
   }
 
   return nullptr;
 }
 
 nsresult
@@ -1547,20 +1543,25 @@ nsXMLHttpRequest::Open(const nsACString&
   mState &= ~XML_HTTP_REQUEST_ABORTED & ~XML_HTTP_REQUEST_TIMED_OUT;
 
   if (async) {
     mState |= XML_HTTP_REQUEST_ASYNC;
   } else {
     mState &= ~XML_HTTP_REQUEST_ASYNC;
   }
 
-  nsIScriptContext* sc = GetContextForEventHandlers(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIDocument> doc =
-    nsContentUtils::GetDocumentFromScriptContext(sc);
+  nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
+  if (!doc) {
+    // This could be because we're no longer current or because we're in some
+    // non-window context...
+    nsresult rv = CheckInnerWindowCorrectness();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return NS_ERROR_DOM_INVALID_STATE_ERR;
+    }
+  }
 
   nsCOMPtr<nsIURI> baseURI;
   if (mBaseURI) {
     baseURI = mBaseURI;
   }
   else if (doc) {
     baseURI = doc->GetBaseURI();
   }
@@ -2009,24 +2010,26 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
   }
 
   if (mState & XML_HTTP_REQUEST_PARSEBODY) {
     nsCOMPtr<nsIURI> baseURI, docURI;
     rv = mChannel->GetURI(getter_AddRefs(docURI));
     NS_ENSURE_SUCCESS(rv, rv);
     baseURI = docURI;
 
-    nsIScriptContext* sc = GetContextForEventHandlers(&rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    nsCOMPtr<nsIDocument> doc =
-      nsContentUtils::GetDocumentFromScriptContext(sc);
+    nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
     nsCOMPtr<nsIURI> chromeXHRDocURI, chromeXHRDocBaseURI;
     if (doc) {
       chromeXHRDocURI = doc->GetDocumentURI();
       chromeXHRDocBaseURI = doc->GetBaseURI();
+    } else {
+      // If we're no longer current, just kill the load, though it really should
+      // have been killed already.
+      nsresult rv = CheckInnerWindowCorrectness();
+      NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // Create an empty document from it.
     const nsAString& emptyStr = EmptyString();
     nsCOMPtr<nsIDOMDocument> responseDoc;
     nsIGlobalObject* global = DOMEventTargetHelper::GetParentObject();
 
     nsCOMPtr<nsIPrincipal> requestingPrincipal;
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -877,16 +877,19 @@ CreateInterfaceObjects(JSContext* cx, JS
              "Must have name precisely when we have an interface object");
   MOZ_ASSERT(!constructorClass || !constructor);
   MOZ_ASSERT(!protoClass == !protoCache,
              "If, and only if, there is an interface prototype object we need "
              "to cache it");
   MOZ_ASSERT(!(constructorClass || constructor) == !constructorCache,
              "If, and only if, there is an interface object we need to cache "
              "it");
+  MOZ_ASSERT(constructorProto || (!constructorClass && !constructor),
+             "Must have a constructor proto if we plan to create a constructor "
+             "object");
 
   JS::Rooted<JSObject*> proto(cx);
   if (protoClass) {
     proto =
       CreateInterfacePrototypeObject(cx, global, protoProto, protoClass,
                                      properties, chromeOnlyProperties,
                                      unscopableNames);
     if (!proto) {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -563,17 +563,19 @@ struct NamedConstructor
 
 /*
  * Create a DOM interface object (if constructorClass is non-null) and/or a
  * DOM interface prototype object (if protoClass is non-null).
  *
  * global is used as the parent of the interface object and the interface
  *        prototype object
  * protoProto is the prototype to use for the interface prototype object.
- * interfaceProto is the prototype to use for the interface object.
+ * interfaceProto is the prototype to use for the interface object.  This can be
+ *                null if both constructorClass and constructor are null (as in,
+ *                if we're not creating an interface object at all).
  * protoClass is the JSClass to use for the interface prototype object.
  *            This is null if we should not create an interface prototype
  *            object.
  * protoCache a pointer to a JSObject pointer where we should cache the
  *            interface prototype object. This must be null if protoClass is and
  *            vice versa.
  * constructorClass is the JSClass to use for the interface object.
  *                  This is null if we should not create an interface object or
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2855,21 +2855,25 @@ class CGCreateInterfaceObjectsMethod(CGA
             protoClass = "nullptr"
             protoCache = "nullptr"
             parentProto = "nullptr"
             getParentProto = None
 
         if needInterfaceObject:
             interfaceClass = "&sInterfaceObjectClass.mBase"
             interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name
+            getConstructorProto = CGGeneric(getConstructorProto)
+            constructorProto = "constructorProto"
         else:
             # We don't have slots to store the named constructors.
             assert len(self.descriptor.interface.namedConstructors) == 0
             interfaceClass = "nullptr"
             interfaceCache = "nullptr"
+            getConstructorProto = None
+            constructorProto = "nullptr"
 
         isGlobal = self.descriptor.isGlobal() is not None
         if not isGlobal and self.properties.hasNonChromeOnly():
             properties = "sNativeProperties.Upcast()"
         else:
             properties = "nullptr"
         if not isGlobal and self.properties.hasChromeOnly():
             chromeProperties = "nsContentUtils::ThreadsafeIsCallerChrome() ? sChromeOnlyNativeProperties.Upcast() : nullptr"
@@ -2877,26 +2881,27 @@ class CGCreateInterfaceObjectsMethod(CGA
             chromeProperties = "nullptr"
 
         call = fill(
             """
             JS::Heap<JSObject*>* protoCache = ${protoCache};
             JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
             dom::CreateInterfaceObjects(aCx, aGlobal, ${parentProto},
                                         ${protoClass}, protoCache,
-                                        constructorProto, ${interfaceClass}, ${constructHookHolder}, ${constructArgs}, ${namedConstructors},
+                                        ${constructorProto}, ${interfaceClass}, ${constructHookHolder}, ${constructArgs}, ${namedConstructors},
                                         interfaceCache,
                                         ${properties},
                                         ${chromeProperties},
                                         ${name}, aDefineOnGlobal,
                                         ${unscopableNames});
             """,
             protoClass=protoClass,
             parentProto=parentProto,
             protoCache=protoCache,
+            constructorProto=constructorProto,
             interfaceClass=interfaceClass,
             constructHookHolder=constructHookHolder,
             constructArgs=constructArgs,
             namedConstructors=namedConstructors,
             interfaceCache=interfaceCache,
             properties=properties,
             chromeProperties=chromeProperties,
             name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr",
@@ -3063,17 +3068,17 @@ class CGCreateInterfaceObjectsMethod(CGA
                 }
                 """,
                 protoCache=protoCache,
                 failureCode=failureCode))
         else:
             makeProtoPrototypeImmutable = None
 
         return CGList(
-            [getParentProto, CGGeneric(getConstructorProto), initIds,
+            [getParentProto, getConstructorProto, initIds,
              prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup,
              speciesSetup, makeProtoPrototypeImmutable],
             "\n").define()
 
 
 class CGGetPerInterfaceObject(CGAbstractMethod):
     """
     A method for getting a per-interface object (a prototype object or interface
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -188,17 +188,17 @@ static_assert(int(HeadersGuardEnum::None
               int(HeadersGuardEnum::Request_no_cors) == 2 &&
               int(HeadersGuardEnum::Response) == 3 &&
               int(HeadersGuardEnum::Immutable) == 4 &&
               int(HeadersGuardEnum::EndGuard_) == 5,
               "HeadersGuardEnum values are as expected");
 static_assert(int(ReferrerPolicy::_empty) == 0 &&
               int(ReferrerPolicy::No_referrer) == 1 &&
               int(ReferrerPolicy::No_referrer_when_downgrade) == 2 &&
-              int(ReferrerPolicy::Origin_only) == 3 &&
+              int(ReferrerPolicy::Origin) == 3 &&
               int(ReferrerPolicy::Origin_when_cross_origin) == 4 &&
               int(ReferrerPolicy::Unsafe_url) == 5 &&
               int(ReferrerPolicy::EndGuard_) == 6,
               "ReferrerPolicy values are as expected");
 static_assert(int(RequestMode::Same_origin) == 0 &&
               int(RequestMode::No_cors) == 1 &&
               int(RequestMode::Cors) == 2 &&
               int(RequestMode::Navigate) == 3 &&
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -428,49 +428,57 @@ WebGLContext::GetHeight() const
  * caps. Finally, resize the backbuffer to an acceptable size given the
  * requested size.
  */
 
 static bool
 IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature)
 {
     int32_t status;
-    if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature, &status)))
+    nsCString discardFailureId;
+    if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature,
+                                                           discardFailureId, &status)))
         return false;
 
     return status != nsIGfxInfo::FEATURE_STATUS_OK;
 }
 
 static bool
 HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
 {
     int32_t status;
 
+    nsCString discardFailureId;
     gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
                                          nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
+                                         discardFailureId,
                                          &status);
     if (status)
         return true;
     gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
                                          nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
+                                         discardFailureId,
                                          &status);
     if (status)
         return true;
     gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
                                          nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
+                                         discardFailureId,
                                          &status);
     if (status)
         return true;
     gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
                                          nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
+                                         discardFailureId,
                                          &status);
     if (status)
         return true;
     gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
                                          nsIGfxInfo::FEATURE_OPENGL_LAYERS,
+                                         discardFailureId,
                                          &status);
     if (status)
         return true;
 
     return false;
 }
 
 static void
--- a/dom/canvas/WebGLContextDraw.cpp
+++ b/dom/canvas/WebGLContextDraw.cpp
@@ -355,18 +355,20 @@ WebGLContext::DrawElements_check(GLsizei
     const GLsizei first = byteOffset / bytesPerElem;
     const CheckedUint32 checked_byteCount = bytesPerElem * CheckedUint32(count);
 
     if (!checked_byteCount.isValid()) {
         ErrorInvalidValue("%s: overflow in byteCount", info);
         return false;
     }
 
-    // Any checks below this depend on a program being available.
-    if (!mCurrentProgram) {
+    // Any checks below this depend on mActiveProgramLinkInfo being available.
+    if (!mActiveProgramLinkInfo) {
+        // Technically, this will only be null iff CURRENT_PROGRAM is null.
+        // But it's better to branch on what we actually care about.
         ErrorInvalidOperation("%s: null CURRENT_PROGRAM", info);
         return false;
     }
 
     if (!mBoundVertexArray->mElementArrayBuffer) {
         ErrorInvalidOperation("%s: must have element array buffer binding", info);
         return false;
     }
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1083,24 +1083,29 @@ WebGLContext::LinkProgram(WebGLProgram* 
     if (IsContextLost())
         return;
 
     if (!ValidateObject("linkProgram", prog))
         return;
 
     prog->LinkProgram();
 
-    if (prog->IsLinked()) {
+    if (!prog->IsLinked()) {
+        // If we failed to link, but `prog == mCurrentProgram`, we are *not* supposed to
+        // null out mActiveProgramLinkInfo.
+        return;
+    }
+
+    if (prog == mCurrentProgram) {
         mActiveProgramLinkInfo = prog->LinkInfo();
 
         if (gl->WorkAroundDriverBugs() &&
             gl->Vendor() == gl::GLVendor::NVIDIA)
         {
-            if (mCurrentProgram == prog)
-                gl->fUseProgram(prog->mGLName);
+            gl->fUseProgram(prog->mGLName);
         }
     }
 }
 
 void
 WebGLContext::PixelStorei(GLenum pname, GLint param)
 {
     if (IsContextLost())
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -880,64 +880,64 @@ WebGLProgram::UniformBlockBinding(GLuint
         return;
     }
 
     gl::GLContext* gl = mContext->GL();
     gl->MakeCurrent();
     gl->fUniformBlockBinding(mGLName, uniformBlockIndex, uniformBlockBinding);
 }
 
-bool
+void
 WebGLProgram::LinkProgram()
 {
     mContext->InvalidateBufferFetching(); // we do it early in this function
     // as some of the validation below changes program state
 
     mLinkLog.Truncate();
     mMostRecentLinkInfo = nullptr;
 
     if (!mVertShader || !mVertShader->IsCompiled()) {
         mLinkLog.AssignLiteral("Must have a compiled vertex shader attached.");
         mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
-        return false;
+        return;
     }
 
     if (!mFragShader || !mFragShader->IsCompiled()) {
         mLinkLog.AssignLiteral("Must have an compiled fragment shader attached.");
         mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
-        return false;
+        return;
     }
 
     if (!mFragShader->CanLinkTo(mVertShader, &mLinkLog)) {
         mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
-        return false;
+        return;
     }
 
     gl::GLContext* gl = mContext->gl;
     gl->MakeCurrent();
 
     if (gl->WorkAroundDriverBugs() &&
         mContext->mIsMesa)
     {
         // Bug 777028: Mesa can't handle more than 16 samplers per program,
         // counting each array entry.
         size_t numSamplerUniforms_upperBound = mVertShader->CalcNumSamplerUniforms() +
                                                mFragShader->CalcNumSamplerUniforms();
         if (numSamplerUniforms_upperBound > 16) {
             mLinkLog.AssignLiteral("Programs with more than 16 samplers are disallowed on"
                                    " Mesa drivers to avoid crashing.");
             mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
-            return false;
+            return;
         }
 
         // Bug 1203135: Mesa crashes internally if we exceed the reported maximum attribute count.
         if (mVertShader->NumAttributes() > mContext->MaxVertexAttribs()) {
             mLinkLog.AssignLiteral("Number of attributes exceeds Mesa's reported max attribute count.");
             mContext->GenerateWarning("linkProgram: %s", mLinkLog.BeginReading());
-            return false;
+            return;
         }
     }
 
     // Bind the attrib locations.
     // This can't be done trivially, because we have to deal with mapped attrib names.
     for (auto itr = mBoundAttribLocs.begin(); itr != mBoundAttribLocs.end(); ++itr) {
         const nsCString& name = itr->first;
         GLuint index = itr->second;
@@ -949,34 +949,33 @@ WebGLProgram::LinkProgram()
         // Bind the transform feedback varyings.
         // This can't be done trivially, because we have to deal with mapped names too.
         mVertShader->ApplyTransformFeedbackVaryings(mGLName,
                                                     mTransformFeedbackVaryings,
                                                     mTransformFeedbackBufferMode,
                                                     &mTempMappedVaryings);
     }
 
-    if (LinkAndUpdate())
-        return true;
+    LinkAndUpdate();
+    if (IsLinked())
+        return;
 
     // Failed link.
     if (mContext->ShouldGenerateWarnings()) {
         // report shader/program infoLogs as warnings.
         // note that shader compilation errors can be deferred to linkProgram,
         // which is why we can't do anything in compileShader. In practice we could
         // report in compileShader the translation errors generated by ANGLE,
         // but it seems saner to keep a single way of obtaining shader infologs.
         if (!mLinkLog.IsEmpty()) {
             mContext->GenerateWarning("linkProgram: Failed to link, leaving the following"
                                       " log:\n%s\n",
                                       mLinkLog.BeginReading());
         }
     }
-
-    return false;
 }
 
 bool
 WebGLProgram::UseProgram() const
 {
     if (!mMostRecentLinkInfo) {
         mContext->ErrorInvalidOperation("useProgram: Program has not been successfully"
                                         " linked.");
@@ -1008,17 +1007,17 @@ WebGLProgram::ValidateProgram() const
 #endif
 
     gl->fValidateProgram(mGLName);
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 
-bool
+void
 WebGLProgram::LinkAndUpdate()
 {
     mMostRecentLinkInfo = nullptr;
 
     gl::GLContext* gl = mContext->gl;
     gl->fLinkProgram(mGLName);
 
     // Grab the program log.
@@ -1034,25 +1033,20 @@ WebGLProgram::LinkAndUpdate()
     // Post link, temporary mapped varying names for transform feedback can be discarded.
     // The memory can only be deleted after log is queried or the link status will fail.
     std::vector<std::string> empty;
     empty.swap(mTempMappedVaryings);
 
     GLint ok = 0;
     gl->fGetProgramiv(mGLName, LOCAL_GL_LINK_STATUS, &ok);
     if (!ok)
-        return false;
+        return;
 
     mMostRecentLinkInfo = QueryProgramInfo(this, gl);
-
-    MOZ_ASSERT(mMostRecentLinkInfo);
-    if (!mMostRecentLinkInfo)
-        mLinkLog.AssignLiteral("Failed to gather program info.");
-
-    return mMostRecentLinkInfo;
+    MOZ_RELEASE_ASSERT(mMostRecentLinkInfo);
 }
 
 bool
 WebGLProgram::FindActiveOutputMappedNameByUserName(const nsACString& userName,
                                                    nsCString* const out_mappedName) const
 {
     if (mFragShader->FindActiveOutputMappedNameByUserName(userName, out_mappedName)) {
         return true;
--- a/dom/canvas/WebGLProgram.h
+++ b/dom/canvas/WebGLProgram.h
@@ -151,17 +151,17 @@ public:
     void GetActiveUniformBlockActiveUniforms(JSContext* cx, GLuint uniformBlockIndex,
                                              dom::Nullable<dom::OwningUnsignedLongOrUint32ArrayOrBoolean>& retval,
                                              ErrorResult& rv) const;
     already_AddRefed<WebGLUniformLocation> GetUniformLocation(const nsAString& name) const;
     void GetUniformIndices(const dom::Sequence<nsString>& uniformNames,
                            dom::Nullable< nsTArray<GLuint> >& retval) const;
     void UniformBlockBinding(GLuint uniformBlockIndex, GLuint uniformBlockBinding) const;
 
-    bool LinkProgram();
+    void LinkProgram();
     bool UseProgram() const;
     void ValidateProgram() const;
 
     ////////////////
 
     bool FindActiveOutputMappedNameByUserName(const nsACString& userName,
                                               nsCString* const out_mappedName) const;
     bool FindAttribUserNameByMappedName(const nsACString& mappedName,
@@ -190,17 +190,17 @@ public:
         return mContext;
     }
 
     virtual JSObject* WrapObject(JSContext* js, JS::Handle<JSObject*> givenProto) override;
 
 private:
     ~WebGLProgram();
 
-    bool LinkAndUpdate();
+    void LinkAndUpdate();
 
 public:
     const GLuint mGLName;
 
 private:
     WebGLRefPtr<WebGLShader> mVertShader;
     WebGLRefPtr<WebGLShader> mFragShader;
     std::map<nsCString, GLuint> mBoundAttribLocs;
--- a/dom/events/DOMEventTargetHelper.cpp
+++ b/dom/events/DOMEventTargetHelper.cpp
@@ -155,16 +155,37 @@ DOMEventTargetHelper::DisconnectFromOwne
   mParentObject = nullptr;
   // Event listeners can't be handled anymore, so we can release them here.
   if (mListenerManager) {
     mListenerManager->Disconnect();
     mListenerManager = nullptr;
   }
 }
 
+nsPIDOMWindowInner*
+DOMEventTargetHelper::GetWindowIfCurrent() const
+{
+  if (NS_FAILED(CheckInnerWindowCorrectness())) {
+    return nullptr;
+  }
+
+  return GetOwner();
+}
+
+nsIDocument*
+DOMEventTargetHelper::GetDocumentIfCurrent() const
+{
+  nsPIDOMWindowInner* win = GetWindowIfCurrent();
+  if (!win) {
+    return nullptr;
+  }
+
+  return win->GetDoc();
+}
+
 NS_IMETHODIMP
 DOMEventTargetHelper::RemoveEventListener(const nsAString& aType,
                                           nsIDOMEventListener* aListener,
                                           bool aUseCapture)
 {
   EventListenerManager* elm = GetExistingListenerManager();
   if (elm) {
     elm->RemoveEventListener(aType, aListener, aUseCapture);
@@ -351,21 +372,20 @@ DOMEventTargetHelper::GetContextForEvent
   nsPIDOMWindowInner* owner = GetOwner();
   return owner ? nsGlobalWindow::Cast(owner)->GetContextInternal()
                : nullptr;
 }
 
 nsresult
 DOMEventTargetHelper::WantsUntrusted(bool* aRetVal)
 {
-  nsresult rv;
-  nsIScriptContext* context = GetContextForEventHandlers(&rv);
+  nsresult rv = CheckInnerWindowCorrectness();
   NS_ENSURE_SUCCESS(rv, rv);
-  nsCOMPtr<nsIDocument> doc =
-    nsContentUtils::GetDocumentFromScriptContext(context);
+  
+  nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
   // We can let listeners on workers to always handle all the events.
   *aRetVal = (doc && !nsContentUtils::IsChromeDoc(doc)) || !NS_IsMainThread();
   return rv;
 }
 
 void
 DOMEventTargetHelper::EventListenerAdded(nsIAtom* aType)
 {
--- a/dom/events/DOMEventTargetHelper.h
+++ b/dom/events/DOMEventTargetHelper.h
@@ -15,16 +15,17 @@
 #include "nsIScriptContext.h"
 #include "nsIWeakReferenceUtils.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/EventTarget.h"
 
 struct JSCompartment;
+class nsIDocument;
 
 namespace mozilla {
 
 class ErrorResult;
 
 #define NS_DOMEVENTTARGETHELPER_IID \
 { 0xa28385c6, 0x9451, 0x4d7e, \
   { 0xa3, 0xdd, 0xf4, 0xb6, 0x87, 0x2f, 0xa4, 0x76 } }
@@ -121,26 +122,32 @@ public:
                        JSContext* aCx,
                        JS::Value* aValue);
   using dom::EventTarget::GetEventHandler;
   virtual nsPIDOMWindowOuter* GetOwnerGlobalForBindings() override
   {
     return nsPIDOMWindowOuter::GetFromCurrentInner(GetOwner());
   }
 
-  nsresult CheckInnerWindowCorrectness()
+  nsresult CheckInnerWindowCorrectness() const
   {
     NS_ENSURE_STATE(!mHasOrHasHadOwnerWindow || mOwnerWindow);
     if (mOwnerWindow && !mOwnerWindow->IsCurrentInnerWindow()) {
       return NS_ERROR_FAILURE;
     }
     return NS_OK;
   }
 
   nsPIDOMWindowInner* GetOwner() const { return mOwnerWindow; }
+  // Like GetOwner, but only returns non-null if the window being returned is
+  // current (in the "current document" sense of the HTML spec).
+  nsPIDOMWindowInner* GetWindowIfCurrent() const;
+  // Returns the document associated with this event target, if that document is
+  // the current document of its browsing context.  Will return null otherwise.
+  nsIDocument* GetDocumentIfCurrent() const;
   void BindToOwner(nsIGlobalObject* aOwner);
   void BindToOwner(nsPIDOMWindowInner* aOwner);
   void BindToOwner(DOMEventTargetHelper* aOther);
   virtual void DisconnectFromOwner();                   
   nsIGlobalObject* GetParentObject() const
   {
     return GetOwnerGlobal();
   }
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -270,17 +270,17 @@ FetchDriver::HttpFetch()
       net_referrerPolicy = net::RP_Default;
       break;
     case ReferrerPolicy::No_referrer:
       net_referrerPolicy = net::RP_No_Referrer;
       break;
     case ReferrerPolicy::No_referrer_when_downgrade:
       net_referrerPolicy = net::RP_No_Referrer_When_Downgrade;
       break;
-    case ReferrerPolicy::Origin_only:
+    case ReferrerPolicy::Origin:
       net_referrerPolicy = net::RP_Origin;
       break;
     case ReferrerPolicy::Origin_when_cross_origin:
       net_referrerPolicy = net::RP_Origin_When_Crossorigin;
       break;
     case ReferrerPolicy::Unsafe_url:
       net_referrerPolicy = net::RP_Unsafe_URL;
       break;
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -588,17 +588,18 @@ IDBCursor::Update(JSContext* aCx, JS::Ha
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_READ_ONLY_ERR);
     return nullptr;
   }
 
   if (mTransaction->GetMode() == IDBTransaction::CLEANUP ||
       IsSourceDeleted() ||
       !mHaveValue ||
       mType == Type_ObjectStoreKey ||
-      mType == Type_IndexKey) {
+      mType == Type_IndexKey ||
+      mContinueCalled) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
     return nullptr;
   }
 
   MOZ_ASSERT(mType == Type_ObjectStore || mType == Type_Index);
   MOZ_ASSERT(!mKey.IsUnset());
   MOZ_ASSERT_IF(mType == Type_Index, !mPrimaryKey.IsUnset());
 
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -170,16 +170,17 @@ IDBDatabase::IDBDatabase(IDBOpenDBReques
                          DatabaseSpec* aSpec)
   : IDBWrapperCache(aRequest)
   , mFactory(aFactory)
   , mSpec(aSpec)
   , mBackgroundActor(aActor)
   , mFileHandleDisabled(aRequest->IsFileHandleDisabled())
   , mClosed(false)
   , mInvalidated(false)
+  , mQuotaExceeded(false)
 {
   MOZ_ASSERT(aRequest);
   MOZ_ASSERT(aFactory);
   aFactory->AssertIsOnOwningThread();
   MOZ_ASSERT(aActor);
   MOZ_ASSERT(aSpec);
 }
 
@@ -660,23 +661,29 @@ IDBDatabase::Transaction(JSContext* aCx,
   }
 
   IDBTransaction::Mode mode;
   switch (aMode) {
     case IDBTransactionMode::Readonly:
       mode = IDBTransaction::READ_ONLY;
       break;
     case IDBTransactionMode::Readwrite:
-      mode = IDBTransaction::READ_WRITE;
+      if (mQuotaExceeded) {
+        mode = IDBTransaction::CLEANUP;
+        mQuotaExceeded = false;
+      } else {
+        mode = IDBTransaction::READ_WRITE;
+      }
       break;
     case IDBTransactionMode::Readwriteflush:
       mode = IDBTransaction::READ_WRITE_FLUSH;
       break;
     case IDBTransactionMode::Cleanup:
       mode = IDBTransaction::CLEANUP;
+      mQuotaExceeded = false;
       break;
     case IDBTransactionMode::Versionchange:
       return NS_ERROR_DOM_INVALID_ACCESS_ERR;
 
     default:
       MOZ_CRASH("Unknown mode!");
   }
 
@@ -700,17 +707,17 @@ IDBDatabase::Transaction(JSContext* aCx,
 
   MOZ_ALWAYS_TRUE(
     mBackgroundActor->SendPBackgroundIDBTransactionConstructor(actor,
                                                                sortedStoreNames,
                                                                mode));
 
   transaction->SetBackgroundActor(actor);
 
-  if (aMode == IDBTransactionMode::Cleanup) {
+  if (mode == IDBTransaction::CLEANUP) {
     ExpireFileActors(/* aExpireAll */ true);
   }
 
   transaction.forget(aTransaction);
   return NS_OK;
 }
 
 StorageType
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -78,16 +78,17 @@ class IDBDatabase final
   RefPtr<Observer> mObserver;
 
   // Weak refs, IDBMutableFile strongly owns this IDBDatabase object.
   nsTArray<IDBMutableFile*> mLiveMutableFiles;
 
   const bool mFileHandleDisabled;
   bool mClosed;
   bool mInvalidated;
+  bool mQuotaExceeded;
 
 public:
   static already_AddRefed<IDBDatabase>
   Create(IDBOpenDBRequest* aRequest,
          IDBFactory* aFactory,
          indexedDB::BackgroundDatabaseChild* aActor,
          DatabaseSpec* aSpec);
 
@@ -145,16 +146,22 @@ public:
   IsInvalidated() const
   {
     AssertIsOnOwningThread();
 
     return mInvalidated;
   }
 
   void
+  SetQuotaExceeded()
+  {
+    mQuotaExceeded = true;
+  }
+
+  void
   EnterSetVersionTransaction(uint64_t aNewVersion);
 
   void
   ExitSetVersionTransaction();
 
   // Called when a versionchange transaction is aborted to reset the
   // DatabaseInfo.
   void
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -759,16 +759,20 @@ IDBTransaction::FireCompleteOrAbortEvent
   nsCOMPtr<nsIDOMEvent> event;
   if (NS_SUCCEEDED(aResult)) {
     event = CreateGenericEvent(this,
                                nsDependentString(kCompleteEventType),
                                eDoesNotBubble,
                                eNotCancelable);
     MOZ_ASSERT(event);
   } else {
+    if (aResult == NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR) {
+      mDatabase->SetQuotaExceeded();
+    }
+
     if (!mError && !mAbortedByScript) {
       mError = new DOMError(GetOwner(), aResult);
     }
 
     event = CreateGenericEvent(this,
                                nsDependentString(kAbortEventType),
                                eDoesBubble,
                                eNotCancelable);
copy from dom/indexedDB/test/unit/test_quotaExceeded_recovery.js
copy to dom/indexedDB/test/unit/test_cleanup_transaction.js
--- a/dom/indexedDB/test/unit/test_quotaExceeded_recovery.js
+++ b/dom/indexedDB/test/unit/test_cleanup_transaction.js
@@ -9,17 +9,17 @@ var testGenerator = testSteps();
 
 function testSteps()
 {
   const spec = "http://foo.com";
   const name =
     this.window ? window.location.pathname : "test_quotaExceeded_recovery";
   const objectStoreName = "foo";
 
-  // We want 32 MB database file, but there's the group limit so we need to
+  // We want 32 MB database, but there's the group limit so we need to
   // multiply by 5.
   const tempStorageLimitKB = 32 * 1024 * 5;
 
   // Store in 1 MB chunks.
   const dataSize = 1024 * 1024;
 
   for (let blobs of [false, true]) {
     setTemporaryStorageLimit(tempStorageLimitKB);
@@ -79,33 +79,35 @@ function testSteps()
         j++;
         testGenerator.send(true);
       }
       trans.onabort = function(event) {
         is(trans.error.name, "QuotaExceededError", "Reached quota limit");
         testGenerator.send(false);
       }
 
-      let shouldContinue = yield undefined;
-      if (shouldContinue) {
+      let completeFired = yield undefined;
+      if (completeFired) {
         ok(true, "Got complete event");
       } else {
         ok(true, "Got abort event");
 
-        if (j==1) {
+        if (j == 1) {
+          // Plain cleanup transaction (just vacuuming and checkpointing)
+          // couldn't shrink database any further.
           break;
-        } else {
-          j = 1;
+        }
 
-          trans = db.transaction(objectStoreName, "cleanup");
-          trans.onabort = unexpectedSuccessHandler;;
-          trans.oncomplete = grabEventAndContinueHandler;
+        j = 1;
 
-          yield undefined;
-        }
+        trans = db.transaction(objectStoreName, "cleanup");
+        trans.onabort = unexpectedSuccessHandler;;
+        trans.oncomplete = grabEventAndContinueHandler;
+
+        yield undefined;
       }
     }
 
     info("Reopening database");
 
     db.close();
 
     request = indexedDB.openForPrincipal(getPrincipal(spec), name);
--- a/dom/indexedDB/test/unit/test_quotaExceeded_recovery.js
+++ b/dom/indexedDB/test/unit/test_quotaExceeded_recovery.js
@@ -9,23 +9,27 @@ var testGenerator = testSteps();
 
 function testSteps()
 {
   const spec = "http://foo.com";
   const name =
     this.window ? window.location.pathname : "test_quotaExceeded_recovery";
   const objectStoreName = "foo";
 
-  // We want 32 MB database file, but there's the group limit so we need to
-  // multiply by 5.
-  const tempStorageLimitKB = 32 * 1024 * 5;
+  // We want 8 MB database on Android and 32 MB database on other platforms.
+  const groupLimitMB = mozinfo.os == "android" ? 8 : 32;
+
+  // The group limit is calculated as 20% of the global temporary storage limit.
+  const tempStorageLimitKB = groupLimitMB * 5 * 1024;
 
   // Store in 1 MB chunks.
   const dataSize = 1024 * 1024;
 
+  const maxIter = 10;
+
   for (let blobs of [false, true]) {
     setTemporaryStorageLimit(tempStorageLimitKB);
 
     clearAllDatabases(continueToNextStepSync);
     yield undefined;
 
     info("Opening database");
 
@@ -37,117 +41,98 @@ function testSteps()
     yield undefined;
 
     // upgradeneeded
     request.onupgradeneeded = unexpectedSuccessHandler;
     request.onsuccess = grabEventAndContinueHandler;
 
     info("Creating objectStore");
 
-    request.result.createObjectStore(objectStoreName);
+    request.result.createObjectStore(objectStoreName, { autoIncrement: true });
 
     yield undefined;
 
     // success
     let db = request.result;
     db.onerror = errorHandler;
 
-    ok(true, "Adding data until quota is reached");
+    ok(true, "Filling database");
 
     let obj = {
       name: "foo"
     }
 
     if (!blobs) {
       obj.data = getRandomView(dataSize);
     }
 
+    let iter = 1;
     let i = 1;
     let j = 1;
     while (true) {
       if (blobs) {
         obj.data = getBlob(getView(dataSize));
       }
 
       let trans = db.transaction(objectStoreName, "readwrite");
-      request = trans.objectStore(objectStoreName).add(obj, i);
+      request = trans.objectStore(objectStoreName).add(obj);
       request.onerror = function(event)
       {
         event.stopPropagation();
       }
 
       trans.oncomplete = function(event) {
-        i++;
+        if (iter == 1) {
+          i++;
+        }
         j++;
         testGenerator.send(true);
       }
       trans.onabort = function(event) {
         is(trans.error.name, "QuotaExceededError", "Reached quota limit");
         testGenerator.send(false);
       }
 
-      let shouldContinue = yield undefined;
-      if (shouldContinue) {
+      let completeFired = yield undefined;
+      if (completeFired) {
         ok(true, "Got complete event");
-      } else {
-        ok(true, "Got abort event");
+        continue;
+      }
+
+      ok(true, "Got abort event");
+
+      if (iter++ == maxIter) {
+        break;
+      }
 
-        if (j==1) {
-          break;
-        } else {
-          j = 1;
+      if (iter > 1) {
+        ok(i == j, "Recycled entire database");
+        j = 1;
+      }
+
+      trans = db.transaction(objectStoreName, "readwrite");
 
-          trans = db.transaction(objectStoreName, "cleanup");
-          trans.onabort = unexpectedSuccessHandler;;
-          trans.oncomplete = grabEventAndContinueHandler;
-
-          yield undefined;
+      // Don't use a cursor for deleting stored blobs (Cursors prolong live
+      // of stored files since each record must be fetched from the database
+      // first which creates a memory reference to the stored blob.)
+      if (blobs) {
+        request = trans.objectStore(objectStoreName).clear();
+      } else {
+        request = trans.objectStore(objectStoreName).openCursor();
+        request.onsuccess = function(event) {
+          let cursor = event.target.result;
+          if (cursor) {
+            cursor.delete();
+            cursor.continue();
+          }
         }
       }
-    }
-
-    info("Reopening database");
-
-    db.close();
-
-    request = indexedDB.openForPrincipal(getPrincipal(spec), name);
-    request.onerror = errorHandler;
-    request.onsuccess = grabEventAndContinueHandler;
-
-    yield undefined;
-
-    db = request.result;
-    db.onerror = errorHandler;
-
-    info("Deleting some data")
-
-    let trans = db.transaction(objectStoreName, "cleanup");
-    trans.objectStore(objectStoreName).delete(1);
 
-    trans.onabort = unexpectedSuccessHandler;;
-    trans.oncomplete = grabEventAndContinueHandler;
-
-    yield undefined;
-
-    info("Adding data again")
-
-    trans = db.transaction(objectStoreName, "readwrite");
-    trans.objectStore(objectStoreName).add(obj, 1);
+      trans.onabort = unexpectedSuccessHandler;;
+      trans.oncomplete = grabEventAndContinueHandler;
 
-    trans.onabort = unexpectedSuccessHandler;
-    trans.oncomplete = grabEventAndContinueHandler;
-
-    yield undefined;
-
-    info("Deleting database");
-
-    db.close();
-
-    request = indexedDB.deleteForPrincipal(getPrincipal(spec), name);
-    request.onerror = errorHandler;
-    request.onsuccess = grabEventAndContinueHandler;
-
-    yield undefined;
+      yield undefined;
+    }
   }
 
   finishTest();
   yield undefined;
 }
--- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini
+++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini
@@ -20,16 +20,17 @@ support-files =
   schema18upgrade_profile.zip
   schema21upgrade_profile.zip
   xpcshell-shared.ini
 
 [include:xpcshell-shared.ini]
 
 [test_blob_file_backed.js]
 [test_bug1056939.js]
+[test_cleanup_transaction.js]
 [test_defaultStorageUpgrade.js]
 [test_globalObjects_ipc.js]
 skip-if = toolkit == 'android'
 [test_idle_maintenance.js]
 [test_invalidate.js]
 # disabled for the moment.
 skip-if = true
 [test_lowDiskSpace.js]
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4966,25 +4966,26 @@ ContentParent::RecvRecordingDeviceEvents
     NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents.");
   }
   return true;
 }
 
 bool
 ContentParent::RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
                                             int32_t* aStatus,
+                                            nsCString* aFailureId,
                                             bool* aSuccess)
 {
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   if (!gfxInfo) {
     *aSuccess = false;
     return true;
   }
 
-  *aSuccess = NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, aStatus));
+  *aSuccess = NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, *aFailureId, aStatus));
   return true;
 }
 
 bool
 ContentParent::RecvAddIdleObserver(const uint64_t& aObserver,
                                    const uint32_t& aIdleTimeInS)
 {
   nsresult rv;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1038,16 +1038,17 @@ private:
   virtual bool RecvAllocateLayerTreeId(const ContentParentId& aCpId,
                                        const TabId& aTabId,
                                        uint64_t* aId) override;
 
   virtual bool RecvDeallocateLayerTreeId(const uint64_t& aId) override;
 
   virtual bool RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
                                             int32_t* aStatus,
+                                            nsCString* aFailureId,
                                             bool* aSuccess) override;
 
   virtual bool RecvGraphicsError(const nsCString& aError) override;
 
   virtual bool
   RecvBeginDriverCrashGuard(const uint32_t& aGuardType,
                             bool* aOutCrashed) override;
 
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -993,17 +993,18 @@ parent:
      * @param isAudio recording start with microphone
      * @param isVideo recording start with camera
      */
     async RecordingDeviceEvents(nsString recordingStatus,
                                 nsString pageURL,
                                 bool isAudio,
                                 bool isVideo);
 
-    sync GetGraphicsFeatureStatus(int32_t aFeature) returns (int32_t aStatus, bool aSuccess);
+    sync GetGraphicsFeatureStatus(int32_t aFeature) returns (int32_t aStatus, nsCString aFailureCode,
+                                                             bool aSuccess);
 
     // Graphics errors
     async GraphicsError(nsCString aError);
 
     // Driver crash guards. aGuardType must be a member of CrashGuardType.
     sync BeginDriverCrashGuard(uint32_t aGuardType) returns (bool crashDetected);
     sync EndDriverCrashGuard(uint32_t aGuardType);
 
--- a/dom/media/AudioCompactor.h
+++ b/dom/media/AudioCompactor.h
@@ -44,17 +44,17 @@ public:
             uint32_t aFrames, uint32_t aChannels, CopyFunc aCopyFunc)
   {
     // If we are losing more than a reasonable amount to padding, try to chunk
     // the data.
     size_t maxSlop = AudioDataSize(aFrames, aChannels) / MAX_SLOP_DIVISOR;
 
     while (aFrames > 0) {
       uint32_t samples = GetChunkSamples(aFrames, aChannels, maxSlop);
-      if (aFrames * aChannels > mSamplesPadding) {
+      if (samples / aChannels > mSamplesPadding / aChannels + 1) {
         samples -= mSamplesPadding;
       }
       AlignedAudioBuffer buffer(samples);
       if (!buffer) {
         return false;
       }
 
       // Copy audio data to buffer using caller-provided functor.
--- a/dom/media/MediaQueue.h
+++ b/dom/media/MediaQueue.h
@@ -49,17 +49,16 @@ public:
     mPushEvent.Notify(RefPtr<T>(aItem));
   }
 
   inline void PushFront(T* aItem) {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     MOZ_ASSERT(aItem);
     NS_ADDREF(aItem);
     nsDeque::PushFront(aItem);
-    mPushEvent.Notify(RefPtr<T>(aItem));
   }
 
   inline already_AddRefed<T> PopFront() {
     ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     RefPtr<T> rv = dont_AddRef(static_cast<T*>(nsDeque::PopFront()));
     if (rv) {
       mPopEvent.Notify(rv);
     }
--- a/dom/media/android/AndroidMediaPluginHost.cpp
+++ b/dom/media/android/AndroidMediaPluginHost.cpp
@@ -109,17 +109,18 @@ static bool IsOmxSupported()
   }
 
   ScopedGfxFeatureReporter reporter("Stagefright", forceEnabled);
 
   if (!forceEnabled) {
     nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
     if (gfxInfo) {
       int32_t status;
-      if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, &status))) {
+      nsCString discardFailure;
+      if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, discardFailure, &status))) {
         if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
           NS_WARNING("XXX stagefright blacklisted\n");
           return false;
         }
       }
     }
   }
 
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -35,18 +35,18 @@ support-files =
   bipbop/bipbop12.m4s^headers^ bipbop/bipbop_video12.m4s^headers^
   bipbop/bipbop13.m4s^headers^ bipbop/bipbop_video13.m4s^headers^
 
 [test_BufferedSeek.html]
 [test_BufferedSeek_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_BufferingWait.html]
 skip-if = toolkit == 'android' #timeout android bug 1199531
-[test_BufferingWait_mp4.html]
-skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
+#[test_BufferingWait_mp4.html] #intermittent timeout bug 1258922
+#skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_DrainOnMissingData_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3
 [test_EndOfStream.html]
 skip-if = (true || toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187 and bug 1182946
 [test_EndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android' || buildapp == 'mulet')) # Not supported on xp and android 2.3
 [test_DurationUpdated.html]
 [test_DurationUpdated_mp4.html]
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -68,17 +68,18 @@ CreateDecoder(const nsACString& aMimeTyp
   return codec;
 }
 
 static bool
 GetFeatureStatus(int32_t aFeature)
 {
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
-  if (!gfxInfo || NS_FAILED(gfxInfo->GetFeatureStatus(aFeature, &status))) {
+  nsCString discardFailureId;
+  if (!gfxInfo || NS_FAILED(gfxInfo->GetFeatureStatus(aFeature, discardFailureId, &status))) {
     return false;
   }
   return status == nsIGfxInfo::FEATURE_STATUS_OK;
 };
 
 class VideoDataDecoder : public MediaCodecDataDecoder
 {
 public:
--- a/dom/media/webaudio/AudioNodeEngine.cpp
+++ b/dom/media/webaudio/AudioNodeEngine.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioNodeEngine.h"
 #ifdef BUILD_ARM_NEON
 #include "mozilla/arm.h"
 #include "AudioNodeEngineNEON.h"
 #endif
 #ifdef USE_SSE2
+#include "AlignmentUtils.h"
 #include "AudioNodeEngineSSE2.h"
 #endif
 
 namespace mozilla {
 
 already_AddRefed<ThreadSharedFloatArrayBufferList>
 ThreadSharedFloatArrayBufferList::Create(uint32_t aChannelCount,
                                          size_t aLength,
@@ -71,17 +72,20 @@ void AudioBufferAddWithScale(const float
 #ifdef BUILD_ARM_NEON
   if (mozilla::supports_neon()) {
     AudioBufferAddWithScale_NEON(aInput, aScale, aOutput, aSize);
     return;
   }
 #endif
 
 #ifdef USE_SSE2
-  if (mozilla::supports_sse2()) {
+  // TODO: See Bug 1266112, we should either fix the source of the unaligned
+  //       buffers or do as much as possible with vector instructions and
+  //       fallback to scalar instructions where necessary.
+  if (mozilla::supports_sse2() && IS_ALIGNED16(aInput) && IS_ALIGNED16(aOutput)) {
     AudioBufferAddWithScale_SSE(aInput, aScale, aOutput, aSize);
     return;
   }
 #endif
 
   if (aScale == 1.0f) {
     for (uint32_t i = 0; i < aSize; ++i) {
       aOutput[i] += aInput[i];
@@ -112,20 +116,23 @@ AudioBlockCopyChannelWithScale(const flo
 #ifdef BUILD_ARM_NEON
     if (mozilla::supports_neon()) {
       AudioBlockCopyChannelWithScale_NEON(aInput, aScale, aOutput);
       return;
     }
 #endif
 
 #ifdef USE_SSE2
-  if (mozilla::supports_sse2()) {
-    AudioBlockCopyChannelWithScale_SSE(aInput, aScale, aOutput);
-    return;
-  }
+    // TODO: See Bug 1266112, we should either fix the source of the unaligned
+    //       buffers or do as much as possible with vector instructions and
+    //       fallback to scalar instructions where necessary.
+    if (mozilla::supports_sse2() && IS_ALIGNED16(aInput) && IS_ALIGNED16(aOutput)) {
+      AudioBlockCopyChannelWithScale_SSE(aInput, aScale, aOutput);
+      return;
+    }
 #endif
 
     for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
       aOutput[i] = aInput[i]*aScale;
     }
   }
 }
 
--- a/dom/media/webaudio/AudioNodeEngineSSE2.cpp
+++ b/dom/media/webaudio/AudioNodeEngineSSE2.cpp
@@ -1,15 +1,15 @@
 /* -*- 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 "AudioNodeEngineSSE2.h"
 #include "AlignmentUtils.h"
-#include "AudioNodeEngineSSE2.h"
 #include <emmintrin.h>
 
 
 namespace mozilla {
 void
 AudioBufferAddWithScale_SSE(const float* aInput,
                             float aScale,
                             float* aOutput,
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -41,16 +41,17 @@ GetUserMediaLog()
 
 #undef LOG
 #define LOG(args) MOZ_LOG(GetUserMediaLog(), mozilla::LogLevel::Debug, args)
 
 namespace mozilla {
 
 // statics from AudioInputCubeb
 nsTArray<int>* AudioInputCubeb::mDeviceIndexes;
+int AudioInputCubeb::mDefaultDevice = -1;
 nsTArray<nsCString>* AudioInputCubeb::mDeviceNames;
 cubeb_device_collection* AudioInputCubeb::mDevices = nullptr;
 bool AudioInputCubeb::mAnyInUse = false;
 StaticMutex AudioInputCubeb::sMutex;
 
 // AudioDeviceID is an annoying opaque value that's really a string
 // pointer, and is freed when the cubeb_device_collection is destroyed
 
@@ -69,33 +70,38 @@ void AudioInputCubeb::UpdateDeviceList()
   }
   // We keep all the device names, but wipe the mappings and rebuild them
 
   // Calculate translation from existing mDevices to new devices. Note we
   // never end up with less devices than before, since people have
   // stashed indexes.
   // For some reason the "fake" device for automation is marked as DISABLED,
   // so white-list it.
+  mDefaultDevice = -1;
   for (uint32_t i = 0; i < devices->count; i++) {
     if (devices->device[i]->type == CUBEB_DEVICE_TYPE_INPUT && // paranoia
         (devices->device[i]->state == CUBEB_DEVICE_STATE_ENABLED ||
-         devices->device[i]->state == CUBEB_DEVICE_STATE_UNPLUGGED ||
          (devices->device[i]->state == CUBEB_DEVICE_STATE_DISABLED &&
           devices->device[i]->friendly_name &&
           strcmp(devices->device[i]->friendly_name, "Sine source at 440 Hz") == 0)))
     {
       auto j = mDeviceNames->IndexOf(devices->device[i]->device_id);
       if (j != nsTArray<nsCString>::NoIndex) {
         // match! update the mapping
         (*mDeviceIndexes)[j] = i;
       } else {
         // new device, add to the array
         mDeviceIndexes->AppendElement(i);
         mDeviceNames->AppendElement(devices->device[i]->device_id);
       }
+      if (devices->device[i]->preferred & CUBEB_DEVICE_PREF_VOICE) {
+        // There can be only one... we hope
+        NS_ASSERTION(mDefaultDevice == -1, "multiple default cubeb input devices!");
+        mDefaultDevice = i;
+      }
     }
   }
   StaticMutexAutoLock lock(sMutex);
   // swap state
   if (mDevices) {
     cubeb_device_collection_destroy(mDevices);
   }
   mDevices = devices;
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -162,16 +162,17 @@ class AudioInputCubeb final : public Aud
 {
 public:
   explicit AudioInputCubeb(webrtc::VoiceEngine* aVoiceEngine, int aIndex = 0) :
     AudioInput(aVoiceEngine), mSelectedDevice(aIndex), mInUseCount(0)
   {
     if (!mDeviceIndexes) {
       mDeviceIndexes = new nsTArray<int>;
       mDeviceNames = new nsTArray<nsCString>;
+      mDefaultDevice = -1;
     }
   }
 
   static void CleanupGlobalData()
   {
     if (mDevices) {
       // This doesn't require anything more than support for free()
       cubeb_device_collection_destroy(mDevices);
@@ -187,20 +188,25 @@ public:
   {
     UpdateDeviceList();
     aDevices = mDeviceIndexes->Length();
     return 0;
   }
 
   static int32_t DeviceIndex(int aIndex)
   {
+    // -1 = system default if any
     if (aIndex == -1) {
-      aIndex = 0; // -1 = system default
+      if (mDefaultDevice == -1) {
+        aIndex = 0;
+      } else {
+        aIndex = mDefaultDevice;
+      }
     }
-    if (aIndex >= (int) mDeviceIndexes->Length()) {
+    if (aIndex < 0 || aIndex >= (int) mDeviceIndexes->Length()) {
       return -1;
     }
     // Note: if the device is gone, this will be -1
     return (*mDeviceIndexes)[aIndex]; // translate to mDevices index
   }
 
   static StaticMutex& Mutex()
   {
@@ -286,16 +292,17 @@ private:
   // removed it will map to -1 (and opens of this device will need to check
   // for this - and be careful of threading access.  The mappings need to
   // updated on each re-enumeration.
   int mSelectedDevice;
   uint32_t mInUseCount;
 
   // pointers to avoid static constructors
   static nsTArray<int>* mDeviceIndexes;
+  static int mDefaultDevice; // -1 == not set
   static nsTArray<nsCString>* mDeviceNames;
   static cubeb_device_collection *mDevices;
   static bool mAnyInUse;
   static StaticMutex sMutex;
 };
 
 class AudioInputWebRTC final : public AudioInput
 {
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -2675,18 +2675,22 @@ PluginModuleParent::NPP_NewInternal(NPMI
 
     nsDependentCString strPluginType(pluginType);
     PluginInstanceParent* parentInstance =
         new PluginInstanceParent(this, instance, strPluginType, mNPNIface);
 
     if (mIsFlashPlugin) {
         parentInstance->InitMetadata(strPluginType, srcAttribute);
 #ifdef XP_WIN
-        // Force windowless mode (bug 1201904) when sandbox level >= 2
+        // Force windowless mode (bug 1201904) when sandbox level >= 2 or Win64
+#ifdef _WIN64
+        {
+#else
         if (mSandboxLevel >= 2) {
+#endif
            NS_NAMED_LITERAL_CSTRING(wmodeAttributeName, "wmode");
            NS_NAMED_LITERAL_CSTRING(opaqueAttributeValue, "opaque");
            auto wmodeAttributeIndex =
                names.IndexOf(wmodeAttributeName, 0, comparator);
            if (wmodeAttributeIndex != names.NoIndex) {
                if (!values[wmodeAttributeIndex].EqualsLiteral("transparent")) {
                    values[wmodeAttributeIndex].Assign(opaqueAttributeValue);
                }
--- a/dom/plugins/test/mochitest/mochitest.ini
+++ b/dom/plugins/test/mochitest/mochitest.ini
@@ -131,12 +131,12 @@ skip-if = toolkit != "cocoa"
 [test_streamNotify.html]
 [test_stringHandling.html]
 [test_twostreams.html]
 [test_visibility.html]
 skip-if = toolkit == "cocoa"
 [test_windowed_invalidate.html]
 skip-if = os != "win"
 [test_windowless_flash.html]
-skip-if = !(os == "win" && processor == "x86_64" && !e10s) # Bug 1253957
+skip-if = !(os == "win" && processor == "x86_64")
 [test_windowless_ime.html]
 skip-if = os != "win"
 [test_zero_opacity.html]
--- a/dom/push/Push.manifest
+++ b/dom/push/Push.manifest
@@ -1,8 +1,11 @@
 # DOM API
 component {cde1d019-fad8-4044-b141-65fb4fb7a245} Push.js
 contract @mozilla.org/push/PushManager;1 {cde1d019-fad8-4044-b141-65fb4fb7a245}
 
 # XPCOM components.
 component {daaa8d73-677e-4233-8acd-2c404bd01658} PushComponents.js
 contract @mozilla.org/push/Service;1 {daaa8d73-677e-4233-8acd-2c404bd01658}
 category app-startup PushServiceParent @mozilla.org/push/Service;1
+
+# For immediate loading of PushService instead of delayed loading.
+category android-push-service PushServiceParent @mozilla.org/push/Service;1
--- a/dom/push/PushComponents.js
+++ b/dom/push/PushComponents.js
@@ -78,16 +78,21 @@ PushServiceBase.prototype = {
       Services.obs.addObserver(this, "sessionstore-windows-restored", true);
       return;
     }
     if (topic === "sessionstore-windows-restored") {
       Services.obs.removeObserver(this, "sessionstore-windows-restored");
       this._handleReady();
       return;
     }
+    if (topic === "android-push-service") {
+      // Load PushService immediately.
+      this._handleReady();
+      return;
+    }
   },
 
   _deliverSubscription(request, props) {
     if (!props) {
       request.onPushSubscription(Cr.NS_OK, null);
       return;
     }
     request.onPushSubscription(Cr.NS_OK, new PushSubscription(props));
--- a/dom/tests/browser/browser_ConsoleAPI_originAttributes.js
+++ b/dom/tests/browser/browser_ConsoleAPI_originAttributes.js
@@ -24,23 +24,23 @@ const ConsoleObserver = {
 
       is(consoleAPIMessage.arguments[0], EXPECTED_CONSOLE_MESSAGE_CONTENT,
          "the consoleAPIMessage contains the expected message");
 
       ok(consoleAPIMessage.originAttributes, "the consoleAPImessage contains originattributes");
       is(consoleAPIMessage.originAttributes.addonId, FAKE_ADDON_ID,
          "the consoleAPImessage's originAttributes contains the expected addonId");
 
-      let cachedMessages = ConsoleAPIStorage.getEvents();
-
-      is(cachedMessages.length, 1, "found one console api messsage as expected");
+      let cachedMessages = ConsoleAPIStorage.getEvents().filter((msg) => {
+        return msg.originAttributes && msg.originAttributes.addonId == FAKE_ADDON_ID;
+      });
 
-      ok(cachedMessages[0].originAttributes, "the consoleAPImessage contains originattributes");
-      is(cachedMessages[0].originAttributes.addonId, FAKE_ADDON_ID,
-         "the consoleAPImessage's originAttributes contains the expected addonId");
+      is(cachedMessages.length, 1, "found the expected cached console messages from the addon");
+      is(cachedMessages[0] && cachedMessages[0].originAttributes.addonId, FAKE_ADDON_ID,
+         "the cached message's originAttributes contains the expected addonId");
 
       finish();
     }
   }
 };
 
 function test()
 {
--- a/dom/webidl/AnimationEffectReadOnly.webidl
+++ b/dom/webidl/AnimationEffectReadOnly.webidl
@@ -35,17 +35,17 @@ dictionary AnimationEffectTimingProperti
   PlaybackDirection                   direction = "normal";
   DOMString                           easing = "linear";
 };
 
 dictionary ComputedTimingProperties : AnimationEffectTimingProperties {
   unrestricted double   endTime = 0.0;
   unrestricted double   activeDuration = 0.0;
   double?               localTime = null;
-  unrestricted double?  progress = null;
+  double?               progress = null;
   unrestricted double?  currentIteration = null;
 };
 
 [Func="nsDocument::IsWebAnimationsEnabled"]
 interface AnimationEffectReadOnly {
   [Cached, Constant]
   readonly attribute AnimationEffectTimingReadOnly timing;
   [BinaryName="getComputedTimingAsDict"]
--- a/dom/webidl/Request.webidl
+++ b/dom/webidl/Request.webidl
@@ -56,9 +56,9 @@ enum RequestContext {
   "sharedworker", "subresource", "style", "track", "video", "worker", "xmlhttprequest",
   "xslt"
 };
 
 enum RequestMode { "same-origin", "no-cors", "cors", "navigate" };
 enum RequestCredentials { "omit", "same-origin", "include" };
 enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache" };
 enum RequestRedirect { "follow", "error", "manual" };
-enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin-only", "origin-when-cross-origin", "unsafe-url" };
+enum ReferrerPolicy { "", "no-referrer", "no-referrer-when-downgrade", "origin", "origin-when-cross-origin", "unsafe-url" };
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1194,17 +1194,17 @@ public:
     uint32_t referrerPolicy = 0;
     rv = httpChannel->GetReferrerPolicy(&referrerPolicy);
     NS_ENSURE_SUCCESS(rv, rv);
     switch (referrerPolicy) {
     case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER:
       mReferrerPolicy = ReferrerPolicy::No_referrer;
       break;
     case nsIHttpChannel::REFERRER_POLICY_ORIGIN:
-      mReferrerPolicy = ReferrerPolicy::Origin_only;
+      mReferrerPolicy = ReferrerPolicy::Origin;
       break;
     case nsIHttpChannel::REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE:
       mReferrerPolicy = ReferrerPolicy::No_referrer_when_downgrade;
       break;
     case nsIHttpChannel::REFERRER_POLICY_ORIGIN_WHEN_XORIGIN:
       mReferrerPolicy = ReferrerPolicy::Origin_when_cross_origin;
       break;
     case nsIHttpChannel::REFERRER_POLICY_UNSAFE_URL:
--- a/editor/libeditor/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/nsHTMLDataTransfer.cpp
@@ -339,17 +339,18 @@ nsHTMLEditor::DoInsertHTMLWithContext(co
 
   if (!cellSelectionMode)
   {
     rv = DeleteSelectionAndPrepareToCreateNode();
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (aClearStyle) {
       // pasting does not inherit local inline styles
-      nsCOMPtr<nsINode> tmpNode = selection->GetAnchorNode();
+      nsCOMPtr<nsIDOMNode> tmpNode =
+        do_QueryInterface(selection->GetAnchorNode());
       int32_t tmpOffset = static_cast<int32_t>(selection->AnchorOffset());
       rv = ClearStyle(address_of(tmpNode), &tmpOffset, nullptr, nullptr);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
   else
   {
     // delete whole cells: we will replace with new table content
--- a/editor/libeditor/nsHTMLEditRules.cpp
+++ b/editor/libeditor/nsHTMLEditRules.cpp
@@ -130,17 +130,17 @@ class nsBRNodeFunctor : public nsBoolDom
 class nsEmptyEditableFunctor : public nsBoolDomIterFunctor
 {
   public:
     explicit nsEmptyEditableFunctor(nsHTMLEditor* editor) : mHTMLEditor(editor) {}
     virtual bool operator()(nsINode* aNode) const
     {
       if (mHTMLEditor->IsEditable(aNode) &&
           (nsHTMLEditUtils::IsListItem(aNode) ||
-           nsHTMLEditUtils::IsTableCellOrCaption(*aNode))) {
+           nsHTMLEditUtils::IsTableCellOrCaption(GetAsDOMNode(aNode)))) {
         bool bIsEmptyNode;
         nsresult res = mHTMLEditor->IsEmptyNode(aNode, &bIsEmptyNode, false, false);
         NS_ENSURE_SUCCESS(res, false);
         if (bIsEmptyNode)
           return true;
       }
       return false;
     }
@@ -307,133 +307,128 @@ nsHTMLEditRules::DetachEditor()
   mHTMLEditor = nullptr;
   return nsTextEditRules::DetachEditor();
 }
 
 NS_IMETHODIMP
 nsHTMLEditRules::BeforeEdit(EditAction action,
                             nsIEditor::EDirection aDirection)
 {
-  if (mLockRulesSniffing) {
-    return NS_OK;
-  }
-
-  NS_ENSURE_STATE(mHTMLEditor);
-  nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
-
-  nsAutoLockRulesSniffing lockIt(this);
+  if (mLockRulesSniffing) return NS_OK;
+
+  nsAutoLockRulesSniffing lockIt((nsTextEditRules*)this);
   mDidExplicitlySetInterline = false;
 
-  if (!mActionNesting) {
-    mActionNesting++;
-
-    // Clear our flag about if just deleted a range
+  if (!mActionNesting++)
+  {
+    // clear our flag about if just deleted a range
     mDidRangedDelete = false;
 
-    // Remember where our selection was before edit action took place:
-
-    // Get selection
+    // remember where our selection was before edit action took place:
+
+    // get selection
+    NS_ENSURE_STATE(mHTMLEditor);
     RefPtr<Selection> selection = mHTMLEditor->GetSelection();
 
-    // Get the selection location
+    // get the selection location
     if (!selection->RangeCount()) {
       return NS_ERROR_UNEXPECTED;
     }
     mRangeItem->startNode = selection->GetRangeAt(0)->GetStartParent();
     mRangeItem->startOffset = selection->GetRangeAt(0)->StartOffset();
     mRangeItem->endNode = selection->GetRangeAt(0)->GetEndParent();
     mRangeItem->endOffset = selection->GetRangeAt(0)->EndOffset();
-    nsCOMPtr<nsINode> selStartNode = mRangeItem->startNode;
-    nsCOMPtr<nsINode> selEndNode = mRangeItem->endNode;
-
-    // Register with range updater to track this as we perturb the doc
+    nsCOMPtr<nsIDOMNode> selStartNode = GetAsDOMNode(mRangeItem->startNode);
+    nsCOMPtr<nsIDOMNode> selEndNode = GetAsDOMNode(mRangeItem->endNode);
+
+    // register this range with range updater to track this as we perturb the doc
+    NS_ENSURE_STATE(mHTMLEditor);
     (mHTMLEditor->mRangeUpdater).RegisterRangeItem(mRangeItem);
 
-    // Clear deletion state bool
+    // clear deletion state bool
     mDidDeleteSelection = false;
 
-    // Clear out mDocChangeRange and mUtilRange
-    if (mDocChangeRange) {
-      // Clear out our accounting of what changed
+    // clear out mDocChangeRange and mUtilRange
+    if(mDocChangeRange)
+    {
+      // clear out our accounting of what changed
       mDocChangeRange->Reset();
     }
-    if (mUtilRange) {
-      // Ditto for mUtilRange.
+    if(mUtilRange)
+    {
+      // ditto for mUtilRange.
       mUtilRange->Reset();
     }
 
-    // Remember current inline styles for deletion and normal insertion ops
+    // remember current inline styles for deletion and normal insertion operations
     if (action == EditAction::insertText ||
         action == EditAction::insertIMEText ||
         action == EditAction::deleteSelection ||
         IsStyleCachePreservingAction(action)) {
-      nsCOMPtr<nsINode> selNode =
-        aDirection == nsIEditor::eNext ? selEndNode : selStartNode;
-      nsresult rv = CacheInlineStyles(GetAsDOMNode(selNode));
-      NS_ENSURE_SUCCESS(rv, rv);
+      nsCOMPtr<nsIDOMNode> selNode = selStartNode;
+      if (aDirection == nsIEditor::eNext)
+        selNode = selEndNode;
+      nsresult res = CacheInlineStyles(selNode);
+      NS_ENSURE_SUCCESS(res, res);
     }
 
     // Stabilize the document against contenteditable count changes
+    NS_ENSURE_STATE(mHTMLEditor);
     nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument();
     NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
     nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
     NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
     if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
       htmlDoc->ChangeContentEditableCount(nullptr, +1);
       mRestoreContentEditableCount = true;
     }
 
-    // Check that selection is in subtree defined by body node
+    // check that selection is in subtree defined by body node
     ConfirmSelectionInBody();
-    // Let rules remember the top level action
+    // let rules remember the top level action
     mTheAction = action;
   }
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsHTMLEditRules::AfterEdit(EditAction action,
                            nsIEditor::EDirection aDirection)
 {
-  if (mLockRulesSniffing) {
-    return NS_OK;
-  }
-
-  NS_ENSURE_STATE(mHTMLEditor);
-  nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
+  if (mLockRulesSniffing) return NS_OK;
 
   nsAutoLockRulesSniffing lockIt(this);
 
-  MOZ_ASSERT(mActionNesting > 0);
-  nsresult rv = NS_OK;
-  mActionNesting--;
-  if (!mActionNesting) {
-    // Do all the tricky stuff
-    rv = AfterEditInner(action, aDirection);
-
-    // Free up selectionState range item
+  NS_PRECONDITION(mActionNesting>0, "bad action nesting!");
+  nsresult res = NS_OK;
+  if (!--mActionNesting)
+  {
+    // do all the tricky stuff
+    res = AfterEditInner(action, aDirection);
+
+    // free up selectionState range item
+    NS_ENSURE_STATE(mHTMLEditor);
     (mHTMLEditor->mRangeUpdater).DropRangeItem(mRangeItem);
 
     // Reset the contenteditable count to its previous value
     if (mRestoreContentEditableCount) {
+      NS_ENSURE_STATE(mHTMLEditor);
       nsCOMPtr<nsIDOMDocument> doc = mHTMLEditor->GetDOMDocument();
       NS_ENSURE_TRUE(doc, NS_ERROR_NOT_INITIALIZED);
       nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
       NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
       if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
         htmlDoc->ChangeContentEditableCount(nullptr, -1);
       }
       mRestoreContentEditableCount = false;
     }
   }
 
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
+  return res;
 }
 
 
 nsresult
 nsHTMLEditRules::AfterEditInner(EditAction action,
                                 nsIEditor::EDirection aDirection)
 {
   ConfirmSelectionInBody();
@@ -635,43 +630,42 @@ nsHTMLEditRules::WillDoAction(Selection*
     case EditAction::insertIMEText:
       UndefineCaretBidiLevel(aSelection);
       return WillInsertText(info->action, aSelection, aCancel, aHandled,
                             info->inString, info->outString, info->maxLength);
     case EditAction::loadHTML:
       return WillLoadHTML(aSelection, aCancel);
     case EditAction::insertBreak:
       UndefineCaretBidiLevel(aSelection);
-      return WillInsertBreak(*aSelection, aCancel, aHandled);
+      return WillInsertBreak(aSelection, aCancel, aHandled);
     case EditAction::deleteSelection:
       return WillDeleteSelection(aSelection, info->collapsedAction,
                                  info->stripWrappers, aCancel, aHandled);
     case EditAction::makeList:
       return WillMakeList(aSelection, info->blockType, info->entireList,
                           info->bulletType, aCancel, aHandled);
     case EditAction::indent:
       return WillIndent(aSelection, aCancel, aHandled);
     case EditAction::outdent:
       return WillOutdent(aSelection, aCancel, aHandled);
     case EditAction::setAbsolutePosition:
       return WillAbsolutePosition(aSelection, aCancel, aHandled);
     case EditAction::removeAbsolutePosition:
       return WillRemoveAbsolutePosition(aSelection, aCancel, aHandled);
     case EditAction::align:
-      return WillAlign(*aSelection, *info->alignType, aCancel, aHandled);
+      return WillAlign(aSelection, info->alignType, aCancel, aHandled);
     case EditAction::makeBasicBlock:
       return WillMakeBasicBlock(aSelection, info->blockType, aCancel, aHandled);
     case EditAction::removeList:
       return WillRemoveList(aSelection, info->bOrdered, aCancel, aHandled);
     case EditAction::makeDefListItem:
       return WillMakeDefListItem(aSelection, info->blockType, info->entireList,
                                  aCancel, aHandled);
     case EditAction::insertElement:
-      WillInsert(*aSelection, aCancel);
-      return NS_OK;
+      return WillInsert(aSelection, aCancel);
     case EditAction::decreaseZIndex:
       return WillRelativeChangeZIndex(aSelection, -1, aCancel, aHandled);
     case EditAction::increaseZIndex:
       return WillRelativeChangeZIndex(aSelection, 1, aCancel, aHandled);
     default:
       return nsTextEditRules::WillDoAction(aSelection, aInfo,
                                            aCancel, aHandled);
   }
@@ -793,149 +787,175 @@ nsHTMLEditRules::GetListItemState(bool *
 
   // hokey arithmetic with booleans
   if ( (*aDT + *aDD + bNonList) > 1) *aMixed = true;
 
   return NS_OK;
 }
 
 nsresult
-nsHTMLEditRules::GetAlignment(bool* aMixed, nsIHTMLEditor::EAlignment* aAlign)
-{
-  MOZ_ASSERT(aMixed && aAlign);
-
-  NS_ENSURE_STATE(mHTMLEditor);
-  nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
-
-  // For now, just return first alignment.  We'll lie about if it's mixed.
-  // This is for efficiency given that our current ui doesn't care if it's
-  // mixed.
-  // cmanske: NOT TRUE! We would like to pay attention to mixed state in Format
-  // | Align submenu!
-
-  // This routine assumes that alignment is done ONLY via divs
-
-  // Default alignment is left
+nsHTMLEditRules::GetAlignment(bool *aMixed, nsIHTMLEditor::EAlignment *aAlign)
+{
+  // for now, just return first alignment.  we'll lie about
+  // if it's mixed.  This is for efficiency
+  // given that our current ui doesn't care if it's mixed.
+  // cmanske: NOT TRUE! We would like to pay attention to mixed state
+  //  in Format | Align submenu!
+
+  // this routine assumes that alignment is done ONLY via divs
+
+  // default alignment is left
+  NS_ENSURE_TRUE(aMixed && aAlign, NS_ERROR_NULL_POINTER);
   *aMixed = false;
   *aAlign = nsIHTMLEditor::eLeft;
 
-  // Get selection
-  NS_ENSURE_STATE(mHTMLEditor->GetSelection());
-  OwningNonNull<Selection> selection = *mHTMLEditor->GetSelection();
-
-  // Get selection location
-  NS_ENSURE_TRUE(mHTMLEditor->GetRoot(), NS_ERROR_FAILURE);
-  OwningNonNull<Element> root = *mHTMLEditor->GetRoot();
-
-  int32_t rootOffset = root->GetParentNode() ?
-                       root->GetParentNode()->IndexOf(root) : -1;
-
-  NS_ENSURE_STATE(selection->GetRangeAt(0) &&
-                  selection->GetRangeAt(0)->GetStartParent());
-  OwningNonNull<nsINode> parent = *selection->GetRangeAt(0)->GetStartParent();
-  int32_t offset = selection->GetRangeAt(0)->StartOffset();
-
-  // Is the selection collapsed?
+  // get selection
+  NS_ENSURE_STATE(mHTMLEditor);
+  RefPtr<Selection> selection = mHTMLEditor->GetSelection();
+  NS_ENSURE_STATE(selection);
+
+  // get selection location
+  NS_ENSURE_STATE(mHTMLEditor);
+  nsCOMPtr<Element> rootElem = mHTMLEditor->GetRoot();
+  NS_ENSURE_TRUE(rootElem, NS_ERROR_FAILURE);
+
+  int32_t offset, rootOffset;
+  nsCOMPtr<nsINode> parent = nsEditor::GetNodeLocation(rootElem, &rootOffset);
+  NS_ENSURE_STATE(mHTMLEditor);
+  nsresult res = mHTMLEditor->GetStartNodeAndOffset(selection,
+                                                    getter_AddRefs(parent),
+                                                    &offset);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // is the selection collapsed?
   nsCOMPtr<nsINode> nodeToExamine;
-  if (selection->Collapsed() || parent->GetAsText()) {
-    // If selection is collapsed, we want to look at 'parent' and its ancestors
-    // for divs with alignment on them.  If we are in a text node, then that is
-    // the node of interest.
+  if (selection->Collapsed()) {
+    // if it is, we want to look at 'parent' and its ancestors
+    // for divs with alignment on them
+    nodeToExamine = parent;
+  }
+  else if (!mHTMLEditor) {
+    return NS_ERROR_UNEXPECTED;
+  }
+  else if (mHTMLEditor->IsTextNode(parent))
+  {
+    // if we are in a text node, then that is the node of interest
     nodeToExamine = parent;
   } else if (parent->IsHTMLElement(nsGkAtoms::html) && offset == rootOffset) {
-    // If we have selected the body, let's look at the first editable node
+    // if we have selected the body, let's look at the first editable node
+    NS_ENSURE_STATE(mHTMLEditor);
     nodeToExamine = mHTMLEditor->GetNextNode(parent, offset, true);
-  } else {
+  }
+  else
+  {
     nsTArray<RefPtr<nsRange>> arrayOfRanges;
-    GetPromotedRanges(selection, arrayOfRanges, EditAction::align);
-
-    // Use these ranges to construct a list of nodes to act on.
+    GetPromotedRanges(*selection, arrayOfRanges, EditAction::align);
+
+    // use these ranges to construct a list of nodes to act on.
     nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
-    nsresult rv = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
-                                       EditAction::align, TouchContent::no);
-    NS_ENSURE_SUCCESS(rv, rv);
+    res = GetNodesForOperation(arrayOfRanges, arrayOfNodes,
+                               EditAction::align, TouchContent::no);
+    NS_ENSURE_SUCCESS(res, res);
     nodeToExamine = arrayOfNodes.SafeElementAt(0);
   }
 
   NS_ENSURE_TRUE(nodeToExamine, NS_ERROR_NULL_POINTER);
 
   NS_NAMED_LITERAL_STRING(typeAttrName, "align");
+  nsIAtom  *dummyProperty = nullptr;
+  NS_ENSURE_STATE(mHTMLEditor);
   nsCOMPtr<Element> blockParent = mHTMLEditor->GetBlock(*nodeToExamine);
 
   NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
 
-  if (mHTMLEditor->IsCSSEnabled() &&
-      mHTMLEditor->mHTMLCSSUtils->IsCSSEditableProperty(blockParent, nullptr,
-                                                        &typeAttrName)) {
-    // We are in CSS mode and we know how to align this element with CSS
-    nsAutoString value;
-    // Let's get the value(s) of text-align or margin-left/margin-right
-    mHTMLEditor->mHTMLCSSUtils->GetCSSEquivalentToHTMLInlineStyleSet(
-        blockParent, nullptr, &typeAttrName, value, nsHTMLCSSUtils::eComputed);
-    if (value.EqualsLiteral("center") ||
-        value.EqualsLiteral("-moz-center") ||
-        value.EqualsLiteral("auto auto")) {
-      *aAlign = nsIHTMLEditor::eCenter;
+  NS_ENSURE_STATE(mHTMLEditor);
+  if (mHTMLEditor->IsCSSEnabled())
+  {
+    NS_ENSURE_STATE(mHTMLEditor);
+    if (mHTMLEditor->mHTMLCSSUtils->IsCSSEditableProperty(blockParent,
+                                                          dummyProperty,
+                                                          &typeAttrName)) {
+      // we are in CSS mode and we know how to align this element with CSS
+      nsAutoString value;
+      // let's get the value(s) of text-align or margin-left/margin-right
+      NS_ENSURE_STATE(mHTMLEditor);
+      mHTMLEditor->mHTMLCSSUtils->GetCSSEquivalentToHTMLInlineStyleSet(
+        blockParent, dummyProperty, &typeAttrName, value,
+        nsHTMLCSSUtils::eComputed);
+      if (value.EqualsLiteral("center") ||
+          value.EqualsLiteral("-moz-center") ||
+          value.EqualsLiteral("auto auto"))
+      {
+        *aAlign = nsIHTMLEditor::eCenter;
+        return NS_OK;
+      }
+      if (value.EqualsLiteral("right") ||
+          value.EqualsLiteral("-moz-right") ||
+          value.EqualsLiteral("auto 0px"))
+      {
+        *aAlign = nsIHTMLEditor::eRight;
+        return NS_OK;
+      }
+      if (value.EqualsLiteral("justify"))
+      {
+        *aAlign = nsIHTMLEditor::eJustify;
+        return NS_OK;
+      }
+      *aAlign = nsIHTMLEditor::eLeft;
       return NS_OK;
     }
-    if (value.EqualsLiteral("right") ||
-        value.EqualsLiteral("-moz-right") ||
-        value.EqualsLiteral("auto 0px")) {
-      *aAlign = nsIHTMLEditor::eRight;
-      return NS_OK;
-    }
-    if (value.EqualsLiteral("justify")) {
-      *aAlign = nsIHTMLEditor::eJustify;
-      return NS_OK;
-    }
-    *aAlign = nsIHTMLEditor::eLeft;
-    return NS_OK;
-  }
-
-  // Check up the ladder for divs with alignment
+  }
+
+  // check up the ladder for divs with alignment
   bool isFirstNodeToExamine = true;
-  for (; nodeToExamine; nodeToExamine = nodeToExamine->GetParentNode()) {
-    if (!isFirstNodeToExamine &&
-        nodeToExamine->IsHTMLElement(nsGkAtoms::table)) {
-      // The node to examine is a table and this is not the first node we
-      // examine; let's break here to materialize the 'inline-block' behaviour
-      // of html tables regarding to text alignment
+  while (nodeToExamine)
+  {
+    if (!isFirstNodeToExamine && nsHTMLEditUtils::IsTable(nodeToExamine))
+    {
+      // the node to examine is a table and this is not the first node
+      // we examine; let's break here to materialize the 'inline-block'
+      // behaviour of html tables regarding to text alignment
       return NS_OK;
     }
     if (nsHTMLEditUtils::SupportsAlignAttr(GetAsDOMNode(nodeToExamine))) {
-      // Check for alignment
-      nsAutoString typeAttrVal;
-      nodeToExamine->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::align,
-                                          typeAttrVal);
-      ToLowerCase(typeAttrVal);
-      if (!typeAttrVal.IsEmpty()) {
-        if (typeAttrVal.EqualsLiteral("center")) {
-          *aAlign = nsIHTMLEditor::eCenter;
-        } else if (typeAttrVal.EqualsLiteral("right")) {
-          *aAlign = nsIHTMLEditor::eRight;
-        } else if (typeAttrVal.EqualsLiteral("justify")) {
-          *aAlign = nsIHTMLEditor::eJustify;
-        } else {
-          *aAlign = nsIHTMLEditor::eLeft;
+      // check for alignment
+      nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(nodeToExamine);
+      if (elem)
+      {
+        nsAutoString typeAttrVal;
+        res = elem->GetAttribute(NS_LITERAL_STRING("align"), typeAttrVal);
+        ToLowerCase(typeAttrVal);
+        if (NS_SUCCEEDED(res) && typeAttrVal.Length())
+        {
+          if (typeAttrVal.EqualsLiteral("center"))
+            *aAlign = nsIHTMLEditor::eCenter;
+          else if (typeAttrVal.EqualsLiteral("right"))
+            *aAlign = nsIHTMLEditor::eRight;
+          else if (typeAttrVal.EqualsLiteral("justify"))
+            *aAlign = nsIHTMLEditor::eJustify;
+          else
+            *aAlign = nsIHTMLEditor::eLeft;
+          return res;
         }
-        return NS_OK;
       }
     }
     isFirstNodeToExamine = false;
+    nodeToExamine = nodeToExamine->GetParentNode();
   }
   return NS_OK;
 }
 
-static nsIAtom& MarginPropertyAtomForIndent(nsHTMLCSSUtils& aHTMLCSSUtils,
-                                            nsINode& aNode)
-{
+static nsIAtom* MarginPropertyAtomForIndent(nsHTMLCSSUtils* aHTMLCSSUtils,
+                                            nsIDOMNode* aNode) {
+  nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
+  NS_ENSURE_TRUE(node || !aNode, nsGkAtoms::marginLeft);
   nsAutoString direction;
-  aHTMLCSSUtils.GetComputedProperty(aNode, *nsGkAtoms::direction, direction);
+  aHTMLCSSUtils->GetComputedProperty(*node, *nsGkAtoms::direction, direction);
   return direction.EqualsLiteral("rtl") ?
-    *nsGkAtoms::marginRight : *nsGkAtoms::marginLeft;
+    nsGkAtoms::marginRight : nsGkAtoms::marginLeft;
 }
 
 nsresult
 nsHTMLEditRules::GetIndentState(bool *aCanIndent, bool *aCanOutdent)
 {
   NS_ENSURE_TRUE(aCanIndent && aCanOutdent, NS_ERROR_FAILURE);
   *aCanIndent = true;
   *aCanOutdent = false;
@@ -958,23 +978,24 @@ nsHTMLEditRules::GetIndentState(bool *aC
   for (auto& curNode : Reversed(arrayOfNodes)) {
     if (nsHTMLEditUtils::IsNodeThatCanOutdent(GetAsDOMNode(curNode))) {
       *aCanOutdent = true;
       break;
     }
     else if (useCSS) {
       // we are in CSS mode, indentation is done using the margin-left (or margin-right) property
       NS_ENSURE_STATE(mHTMLEditor);
-      nsIAtom& marginProperty =
-        MarginPropertyAtomForIndent(*mHTMLEditor->mHTMLCSSUtils, curNode);
+      nsIAtom* marginProperty =
+        MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils,
+                                    GetAsDOMNode(curNode));
       nsAutoString value;
       // retrieve its specified value
       NS_ENSURE_STATE(mHTMLEditor);
       mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(*curNode,
-                                                       marginProperty, value);
+                                                       *marginProperty, value);
       float f;
       nsCOMPtr<nsIAtom> unit;
       // get its number part and its unit
       NS_ENSURE_STATE(mHTMLEditor);
       mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
       // if the number part is strictly positive, outdent is possible
       if (0 < f) {
         *aCanOutdent = true;
@@ -1180,71 +1201,81 @@ nsHTMLEditRules::GetFormatString(nsIDOMN
 
   return NS_OK;
 }
 
 /********************************************************
  *  Protected rules methods
  ********************************************************/
 
-void
-nsHTMLEditRules::WillInsert(Selection& aSelection, bool* aCancel)
-{
-  MOZ_ASSERT(aCancel);
-
-  nsTextEditRules::WillInsert(aSelection, aCancel);
-
-  NS_ENSURE_TRUE_VOID(mHTMLEditor);
-  nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
-
-  // Adjust selection to prevent insertion after a moz-BR.  This next only
-  // works for collapsed selections right now, because selection is a pain to
-  // work with when not collapsed.  (no good way to extend start or end of
-  // selection), so we ignore those types of selections.
-  if (!aSelection.Collapsed()) {
-    return;
-  }
-
-  // If we are after a mozBR in the same block, then move selection to be
-  // before it
-  NS_ENSURE_TRUE_VOID(aSelection.GetRangeAt(0) &&
-                      aSelection.GetRangeAt(0)->GetStartParent());
-  OwningNonNull<nsINode> selNode = *aSelection.GetRangeAt(0)->GetStartParent();
-  int32_t selOffset = aSelection.GetRangeAt(0)->StartOffset();
-
-  // Get prior node
-  nsCOMPtr<nsIContent> priorNode = mHTMLEditor->GetPriorHTMLNode(selNode,
-                                                                 selOffset);
-  if (priorNode && nsTextEditUtils::IsMozBR(priorNode)) {
-    nsCOMPtr<Element> block1 = mHTMLEditor->GetBlock(selNode);
-    nsCOMPtr<Element> block2 = mHTMLEditor->GetBlockNodeParent(priorNode);
+nsresult
+nsHTMLEditRules::WillInsert(Selection* aSelection, bool* aCancel)
+{
+  nsresult res = nsTextEditRules::WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // Adjust selection to prevent insertion after a moz-BR.
+  // this next only works for collapsed selections right now,
+  // because selection is a pain to work with when not collapsed.
+  // (no good way to extend start or end of selection), so we ignore
+  // those types of selections.
+  if (!aSelection->Collapsed()) {
+    return NS_OK;
+  }
+
+  // if we are after a mozBR in the same block, then move selection
+  // to be before it
+  nsCOMPtr<nsIDOMNode> selNode, priorNode;
+  int32_t selOffset;
+  // get the (collapsed) selection location
+  NS_ENSURE_STATE(mHTMLEditor);
+  res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode),
+                                           &selOffset);
+  NS_ENSURE_SUCCESS(res, res);
+  // get prior node
+  NS_ENSURE_STATE(mHTMLEditor);
+  res = mHTMLEditor->GetPriorHTMLNode(selNode, selOffset,
+                                      address_of(priorNode));
+  if (NS_SUCCEEDED(res) && priorNode && nsTextEditUtils::IsMozBR(priorNode))
+  {
+    nsCOMPtr<nsIDOMNode> block1, block2;
+    if (IsBlockNode(selNode)) {
+      block1 = selNode;
+    }
+    else {
+      NS_ENSURE_STATE(mHTMLEditor);
+      block1 = mHTMLEditor->GetBlockNodeParent(selNode);
+    }
+    NS_ENSURE_STATE(mHTMLEditor);
+    block2 = mHTMLEditor->GetBlockNodeParent(priorNode);
 
     if (block1 && block1 == block2) {
-      // If we are here then the selection is right after a mozBR that is in
-      // the same block as the selection.  We need to move the selection start
-      // to be before the mozBR.
-      selNode = priorNode->GetParentNode();
-      selOffset = selNode->IndexOf(priorNode);
-      nsresult res = aSelection.Collapse(selNode, selOffset);
-      NS_ENSURE_SUCCESS_VOID(res);
+      // if we are here then the selection is right after a mozBR
+      // that is in the same block as the selection.  We need to move
+      // the selection start to be before the mozBR.
+      selNode = nsEditor::GetNodeLocation(priorNode, &selOffset);
+      res = aSelection->Collapse(selNode,selOffset);
+      NS_ENSURE_SUCCESS(res, res);
     }
   }
 
   if (mDidDeleteSelection &&
       (mTheAction == EditAction::insertText ||
        mTheAction == EditAction::insertIMEText ||
        mTheAction == EditAction::deleteSelection)) {
-    nsresult res = ReapplyCachedStyles();
-    NS_ENSURE_SUCCESS_VOID(res);
+    res = ReapplyCachedStyles();
+    NS_ENSURE_SUCCESS(res, res);
   }
   // For most actions we want to clear the cached styles, but there are
   // exceptions
   if (!IsStyleCachePreservingAction(mTheAction)) {
     ClearCachedStyles();
   }
+
+  return NS_OK;
 }
 
 nsresult
 nsHTMLEditRules::WillInsertText(EditAction aAction,
                                 Selection*       aSelection,
                                 bool            *aCancel,
                                 bool            *aHandled,
                                 const nsAString *inString,
@@ -1271,28 +1302,30 @@ nsHTMLEditRules::WillInsertText(EditActi
   // If the selection isn't collapsed, delete it.  Don't delete existing inline
   // tags, because we're hopefully going to insert text (bug 787432).
   if (!aSelection->Collapsed()) {
     NS_ENSURE_STATE(mHTMLEditor);
     res = mHTMLEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eNoStrip);
     NS_ENSURE_SUCCESS(res, res);
   }
 
-  WillInsert(*aSelection, aCancel);
+  res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = false;
 
   // we need to get the doc
   NS_ENSURE_STATE(mHTMLEditor);
   nsCOMPtr<nsIDocument> doc = mHTMLEditor->GetDocument();
-  NS_ENSURE_STATE(doc);
+  nsCOMPtr<nsIDOMDocument> domDoc = mHTMLEditor->GetDOMDocument();
+  NS_ENSURE_TRUE(doc && domDoc, NS_ERROR_NOT_INITIALIZED);
 
   // for every property that is set, insert a new inline style node
-  res = CreateStyleForInsertText(*aSelection, *doc);
+  res = CreateStyleForInsertText(aSelection, domDoc);
   NS_ENSURE_SUCCESS(res, res);
 
   // get the (collapsed) selection location
   NS_ENSURE_STATE(mHTMLEditor);
   NS_ENSURE_STATE(aSelection->GetRangeAt(0));
   nsCOMPtr<nsINode> selNode = aSelection->GetRangeAt(0)->GetStartParent();
   int32_t selOffset = aSelection->GetRangeAt(0)->StartOffset();
   NS_ENSURE_STATE(selNode);
@@ -1487,207 +1520,234 @@ nsHTMLEditRules::WillLoadHTML(Selection*
     mEditor->DeleteNode(mBogusNode);
     mBogusNode = nullptr;
   }
 
   return NS_OK;
 }
 
 nsresult
-nsHTMLEditRules::WillInsertBreak(Selection& aSelection, bool* aCancel,
-                                 bool* aHandled)
-{
-  MOZ_ASSERT(aCancel && aHandled);
+nsHTMLEditRules::WillInsertBreak(Selection* aSelection,
+                                 bool* aCancel, bool* aHandled)
+{
+  if (!aSelection || !aCancel || !aHandled) {
+    return NS_ERROR_NULL_POINTER;
+  }
+  // initialize out params
   *aCancel = false;
   *aHandled = false;
 
-  NS_ENSURE_STATE(mHTMLEditor);
-  nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
-
-  // If the selection isn't collapsed, delete it.
-  nsresult res;
-  if (!aSelection.Collapsed()) {
+  // if the selection isn't collapsed, delete it.
+  nsresult res = NS_OK;
+  if (!aSelection->Collapsed()) {
+    NS_ENSURE_STATE(mHTMLEditor);
     res = mHTMLEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
     NS_ENSURE_SUCCESS(res, res);
   }
 
-  WillInsert(aSelection, aCancel);
-
-  // Initialize out param.  We want to ignore result of WillInsert().
+  res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // initialize out param
+  // we want to ignore result of WillInsert()
   *aCancel = false;
 
-  // Split any mailcites in the way.  Should we abort this if we encounter
-  // table cell boundaries?
+  // split any mailcites in the way.
+  // should we abort this if we encounter table cell boundaries?
   if (IsMailEditor()) {
-    res = SplitMailCites(&aSelection, aHandled);
+    res = SplitMailCites(aSelection, aHandled);
     NS_ENSURE_SUCCESS(res, res);
     if (*aHandled) {
       return NS_OK;
     }
   }
 
-  // Smart splitting rules
-  NS_ENSURE_TRUE(aSelection.GetRangeAt(0) &&
-                 aSelection.GetRangeAt(0)->GetStartParent(),
-                 NS_ERROR_FAILURE);
-  OwningNonNull<nsINode> node = *aSelection.GetRangeAt(0)->GetStartParent();
-  int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
-
-  // Do nothing if the node is read-only
+  // smart splitting rules
+  nsCOMPtr<nsIDOMNode> node;
+  int32_t offset;
+
+  NS_ENSURE_STATE(mHTMLEditor);
+  res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(node),
+                                           &offset);
+  NS_ENSURE_SUCCESS(res, res);
+  NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
+
+  // do nothing if the node is read-only
+  NS_ENSURE_STATE(mHTMLEditor);
   if (!mHTMLEditor->IsModifiableNode(node)) {
     *aCancel = true;
     return NS_OK;
   }
 
-  // Identify the block
-  nsCOMPtr<Element> blockParent = mHTMLEditor->GetBlock(node);
+  // identify the block
+  nsCOMPtr<nsIDOMNode> blockParent;
+  if (IsBlockNode(node)) {
+    blockParent = node;
+  } else {
+    NS_ENSURE_STATE(mHTMLEditor);
+    blockParent = mHTMLEditor->GetBlockNodeParent(node);
+  }
   NS_ENSURE_TRUE(blockParent, NS_ERROR_FAILURE);
 
-  // If the active editing host is an inline element, or if the active editing
+  // if the active editing host is an inline element, or if the active editing
   // host is the block parent itself, just append a br.
-  nsCOMPtr<Element> host = mHTMLEditor->GetActiveEditingHost();
-  if (!nsEditorUtils::IsDescendantOf(blockParent, host)) {
+  NS_ENSURE_STATE(mHTMLEditor);
+  nsCOMPtr<nsIContent> hostContent = mHTMLEditor->GetActiveEditingHost();
+  nsCOMPtr<nsIDOMNode> hostNode = do_QueryInterface(hostContent);
+  if (!nsEditorUtils::IsDescendantOf(blockParent, hostNode)) {
     res = StandardBreakImpl(node, offset, aSelection);
     NS_ENSURE_SUCCESS(res, res);
     *aHandled = true;
     return NS_OK;
   }
 
-  // If block is empty, populate with br.  (For example, imagine a div that
-  // contains the word "text".  The user selects "text" and types return.
-  // "Text" is deleted leaving an empty block.  We want to put in one br to
-  // make block have a line.  Then code further below will put in a second br.)
+  // if block is empty, populate with br.  (for example, imagine a div that
+  // contains the word "text".  the user selects "text" and types return.
+  // "text" is deleted leaving an empty block.  we want to put in one br to
+  // make block have a line.  then code further below will put in a second br.)
   bool isEmpty;
-  IsEmptyBlock(*blockParent, &isEmpty);
+  IsEmptyBlock(blockParent, &isEmpty);
   if (isEmpty) {
-    nsCOMPtr<Element> br = mHTMLEditor->CreateBR(blockParent,
-                                                 blockParent->Length());
-    NS_ENSURE_STATE(br);
-  }
-
-  nsCOMPtr<Element> listItem = IsInListItem(blockParent);
-  if (listItem && listItem != host) {
-    ReturnInListItem(aSelection, *listItem, node, offset);
+    uint32_t blockLen;
+    NS_ENSURE_STATE(mHTMLEditor);
+    res = mHTMLEditor->GetLengthOfDOMNode(blockParent, blockLen);
+    NS_ENSURE_SUCCESS(res, res);
+    nsCOMPtr<nsIDOMNode> brNode;
+    NS_ENSURE_STATE(mHTMLEditor);
+    res = mHTMLEditor->CreateBR(blockParent, blockLen, address_of(brNode));
+    NS_ENSURE_SUCCESS(res, res);
+  }
+
+  nsCOMPtr<nsIDOMNode> listItem = IsInListItem(blockParent);
+  if (listItem && listItem != hostNode) {
+    ReturnInListItem(aSelection, listItem, node, offset);
     *aHandled = true;
     return NS_OK;
-  } else if (nsHTMLEditUtils::IsHeader(*blockParent)) {
-    // Headers: close (or split) header
-    ReturnInHeader(aSelection, *blockParent, node, offset);
+  } else if (nsHTMLEditUtils::IsHeader(blockParent)) {
+    // headers: close (or split) header
+    ReturnInHeader(aSelection, blockParent, node, offset);
     *aHandled = true;
     return NS_OK;
-  } else if (blockParent->IsHTMLElement(nsGkAtoms::p)) {
-    // Paragraphs: special rules to look for <br>s
-    res = ReturnInParagraph(&aSelection, GetAsDOMNode(blockParent),
-                            GetAsDOMNode(node), offset, aCancel, aHandled);
+  } else if (nsHTMLEditUtils::IsParagraph(blockParent)) {
+    // paragraphs: special rules to look for <br>s
+    res = ReturnInParagraph(aSelection, blockParent, node, offset,
+                            aCancel, aHandled);
     NS_ENSURE_SUCCESS(res, res);
-    // Fall through, we may not have handled it in ReturnInParagraph()
-  }
-
-  // If not already handled then do the standard thing
+    // fall through, we may not have handled it in ReturnInParagraph()
+  }
+
+  // if not already handled then do the standard thing
   if (!(*aHandled)) {
     *aHandled = true;
     return StandardBreakImpl(node, offset, aSelection);
   }
   return NS_OK;
 }
 
 nsresult
-nsHTMLEditRules::StandardBreakImpl(nsINode& aNode, int32_t aOffset,
-                                   Selection& aSelection)
-{
-  NS_ENSURE_STATE(mHTMLEditor);
-  nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
-
-  nsCOMPtr<Element> brNode;
+nsHTMLEditRules::StandardBreakImpl(nsIDOMNode* aNode, int32_t aOffset,
+                                   Selection* aSelection)
+{
+  nsCOMPtr<nsIDOMNode> brNode;
   bool bAfterBlock = false;
   bool bBeforeBlock = false;
-  nsCOMPtr<nsINode> node = &aNode;
-  nsresult res;
+  nsresult res = NS_OK;
+  nsCOMPtr<nsIDOMNode> node(aNode);
 
   if (IsPlaintextEditor()) {
-    brNode = mHTMLEditor->CreateBR(node, aOffset);
-    NS_ENSURE_STATE(brNode);
+    NS_ENSURE_STATE(mHTMLEditor);
+    res = mHTMLEditor->CreateBR(node, aOffset, address_of(brNode));
   } else {
+    NS_ENSURE_STATE(mHTMLEditor);
     nsWSRunObject wsObj(mHTMLEditor, node, aOffset);
     int32_t visOffset = 0;
     WSType wsType;
-    nsCOMPtr<nsINode> visNode;
-    wsObj.PriorVisibleNode(node, aOffset, address_of(visNode),
+    nsCOMPtr<nsINode> node_(do_QueryInterface(node)), visNode;
+    wsObj.PriorVisibleNode(node_, aOffset, address_of(visNode),
                            &visOffset, &wsType);
     if (wsType & WSType::block) {
       bAfterBlock = true;
     }
-    wsObj.NextVisibleNode(node, aOffset, address_of(visNode),
+    wsObj.NextVisibleNode(node_, aOffset, address_of(visNode),
                           &visOffset, &wsType);
     if (wsType & WSType::block) {
       bBeforeBlock = true;
     }
-    nsCOMPtr<nsIDOMNode> linkDOMNode;
-    if (mHTMLEditor->IsInLink(GetAsDOMNode(node), address_of(linkDOMNode))) {
-      // Split the link
-      nsCOMPtr<Element> linkNode = do_QueryInterface(linkDOMNode);
-      NS_ENSURE_STATE(linkNode || !linkDOMNode);
-      nsCOMPtr<nsINode> linkParent = linkNode->GetParentNode();
-      aOffset = mHTMLEditor->SplitNodeDeep(*linkNode, *node->AsContent(),
-                                           aOffset,
-                                           nsHTMLEditor::EmptyContainers::no);
+    NS_ENSURE_STATE(mHTMLEditor);
+    nsCOMPtr<nsIDOMNode> linkNode;
+    if (mHTMLEditor->IsInLink(node, address_of(linkNode))) {
+      // split the link
+      nsCOMPtr<nsIDOMNode> linkParent;
+      res = linkNode->GetParentNode(getter_AddRefs(linkParent));
+      NS_ENSURE_SUCCESS(res, res);
+      NS_ENSURE_STATE(mHTMLEditor);
+      nsCOMPtr<nsIContent> linkNodeContent = do_QueryInterface(linkNode);
+      NS_ENSURE_STATE(linkNodeContent || !linkNode);
+      aOffset = mHTMLEditor->SplitNodeDeep(*linkNodeContent,
+          *node_->AsContent(), aOffset, nsHTMLEditor::EmptyContainers::no);
       NS_ENSURE_STATE(aOffset != -1);
       node = linkParent;
     }
-    brNode = wsObj.InsertBreak(address_of(node), &aOffset, nsIEditor::eNone);
+    node_ = do_QueryInterface(node);
+    nsCOMPtr<Element> br =
+      wsObj.InsertBreak(address_of(node_), &aOffset, nsIEditor::eNone);
+    node = GetAsDOMNode(node_);
+    brNode = GetAsDOMNode(br);
     NS_ENSURE_TRUE(brNode, NS_ERROR_FAILURE);
   }
-  node = brNode->GetParentNode();
+  NS_ENSURE_SUCCESS(res, res);
+  node = nsEditor::GetNodeLocation(brNode, &aOffset);
   NS_ENSURE_TRUE(node, NS_ERROR_NULL_POINTER);
-  int32_t offset = node->IndexOf(brNode);
   if (bAfterBlock && bBeforeBlock) {
-    // We just placed a br between block boundaries.  This is the one case
+    // we just placed a br between block boundaries.  This is the one case
     // where we want the selection to be before the br we just placed, as the
     // br will be on a new line, rather than at end of prior line.
-    aSelection.SetInterlinePosition(true);
-    res = aSelection.Collapse(node, offset);
-    NS_ENSURE_SUCCESS(res, res);
+    aSelection->SetInterlinePosition(true);
+    res = aSelection->Collapse(node, aOffset);
   } else {
-    nsWSRunObject wsObj(mHTMLEditor, node, offset + 1);
+    NS_ENSURE_STATE(mHTMLEditor);
+    nsWSRunObject wsObj(mHTMLEditor, node, aOffset+1);
     nsCOMPtr<nsINode> secondBR;
     int32_t visOffset = 0;
     WSType wsType;
-    wsObj.NextVisibleNode(node, offset + 1, address_of(secondBR),
+    nsCOMPtr<nsINode> node_(do_QueryInterface(node));
+    wsObj.NextVisibleNode(node_, aOffset+1, address_of(secondBR),
                           &visOffset, &wsType);
     if (wsType == WSType::br) {
-      // The next thing after the break we inserted is another break.  Move the
-      // second break to be the first break's sibling.  This will prevent them
+      // the next thing after the break we inserted is another break.  Move
+      // the 2nd break to be the first breaks sibling.  This will prevent them
       // from being in different inline nodes, which would break
       // SetInterlinePosition().  It will also assure that if the user clicks
-      // away and then clicks back on their new blank line, they will still get
-      // the style from the line above.
-      nsCOMPtr<nsINode> brParent = secondBR->GetParentNode();
-      int32_t brOffset = brParent ? brParent->IndexOf(secondBR) : -1;
-      if (brParent != node || brOffset != offset + 1) {
-        res = mHTMLEditor->MoveNode(secondBR->AsContent(), node, offset + 1);
+      // away and then clicks back on their new blank line, they will still
+      // get the style from the line above.
+      int32_t brOffset;
+      nsCOMPtr<nsIDOMNode> brParent = nsEditor::GetNodeLocation(GetAsDOMNode(secondBR), &brOffset);
+      if (brParent != node || brOffset != aOffset + 1) {
+        NS_ENSURE_STATE(mHTMLEditor);
+        res = mHTMLEditor->MoveNode(secondBR->AsContent(), node_, aOffset + 1);
         NS_ENSURE_SUCCESS(res, res);
       }
     }
     // SetInterlinePosition(true) means we want the caret to stick to the
     // content on the "right".  We want the caret to stick to whatever is past
     // the break.  This is because the break is on the same line we were on,
     // but the next content will be on the following line.
 
     // An exception to this is if the break has a next sibling that is a block
     // node.  Then we stick to the left to avoid an uber caret.
-    nsCOMPtr<nsIContent> siblingNode = brNode->GetNextSibling();
-    if (siblingNode && IsBlockNode(GetAsDOMNode(siblingNode))) {
-      aSelection.SetInterlinePosition(false);
+    nsCOMPtr<nsIDOMNode> siblingNode;
+    brNode->GetNextSibling(getter_AddRefs(siblingNode));
+    if (siblingNode && IsBlockNode(siblingNode)) {
+      aSelection->SetInterlinePosition(false);
     } else {
-      aSelection.SetInterlinePosition(true);
-    }
-    res = aSelection.Collapse(node, offset + 1);
-    NS_ENSURE_SUCCESS(res, res);
-  }
-  return NS_OK;
+      aSelection->SetInterlinePosition(true);
+    }
+    res = aSelection->Collapse(node, aOffset+1);
+  }
+  return res;
 }
 
 nsresult
 nsHTMLEditRules::DidInsertBreak(Selection* aSelection, nsresult aResult)
 {
   return NS_OK;
 }
 
@@ -1965,16 +2025,19 @@ nsHTMLEditRules::WillDeleteSelection(Sel
       res = nsWSRunObject::PrepareToDeleteRange(mHTMLEditor,
           address_of(visNode), &so, address_of(visNode), &eo);
       NS_ENSURE_SUCCESS(res, res);
       NS_ENSURE_STATE(mHTMLEditor);
       res = mHTMLEditor->DeleteText(nodeAsText, std::min(so, eo),
                                     DeprecatedAbs(eo - so));
       *aHandled = true;
       NS_ENSURE_SUCCESS(res, res);
+
+      DeleteNodeIfCollapsedText(nodeAsText);
+
       res = InsertBRIfNeeded(aSelection);
       NS_ENSURE_SUCCESS(res, res);
 
       // Remember that we did a ranged delete for the benefit of
       // AfterEditInner().
       mDidRangedDelete = true;
 
       return NS_OK;
@@ -2163,19 +2226,17 @@ nsHTMLEditRules::WillDeleteSelection(Sel
       // Else we are joining content to block
 
       nsCOMPtr<nsINode> selPointNode = startNode;
       int32_t selPointOffset = startOffset;
       {
         NS_ENSURE_STATE(mHTMLEditor);
         nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
                                     address_of(selPointNode), &selPointOffset);
-        NS_ENSURE_STATE(leftNode && leftNode->IsContent() &&
-                        rightNode && rightNode->IsContent());
-        res = JoinBlocks(*leftNode->AsContent(), *rightNode->AsContent(),
+        res = JoinBlocks(GetAsDOMNode(leftNode), GetAsDOMNode(rightNode),
                          aCancel);
         *aHandled = true;
         NS_ENSURE_SUCCESS(res, res);
       }
       aSelection->Collapse(selPointNode, selPointOffset);
       return NS_OK;
     }
 
@@ -2215,30 +2276,29 @@ nsHTMLEditRules::WillDeleteSelection(Sel
       }
 
       nsCOMPtr<nsINode> selPointNode = startNode;
       int32_t selPointOffset = startOffset;
       {
         NS_ENSURE_STATE(mHTMLEditor);
         nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
                                     address_of(selPointNode), &selPointOffset);
-        NS_ENSURE_STATE(leftNode->IsContent() && rightNode->IsContent());
-        res = JoinBlocks(*leftNode->AsContent(), *rightNode->AsContent(),
+        res = JoinBlocks(GetAsDOMNode(leftNode), GetAsDOMNode(rightNode),
                          aCancel);
         *aHandled = true;
         NS_ENSURE_SUCCESS(res, res);
       }
       aSelection->Collapse(selPointNode, selPointOffset);
       return NS_OK;
     }
   }
 
 
   // Else we have a non-collapsed selection.  First adjust the selection.
-  res = ExpandSelectionForDeletion(*aSelection);
+  res = ExpandSelectionForDeletion(aSelection);
   NS_ENSURE_SUCCESS(res, res);
 
   // Remember that we did a ranged delete for the benefit of AfterEditInner().
   mDidRangedDelete = true;
 
   // Refresh start and end points
   NS_ENSURE_STATE(aSelection->GetRangeAt(0));
   startNode = aSelection->GetRangeAt(0)->GetStartParent();
@@ -2402,22 +2462,35 @@ nsHTMLEditRules::WillDeleteSelection(Sel
           NS_ENSURE_STATE(mHTMLEditor);
           OwningNonNull<nsGenericDOMDataNode> dataNode =
             *static_cast<nsGenericDOMDataNode*>(endNode.get());
           res = mHTMLEditor->DeleteText(dataNode, 0, endOffset);
           NS_ENSURE_SUCCESS(res, res);
         }
 
         if (join) {
-          res = JoinBlocks(*leftParent, *rightParent, aCancel);
+          res = JoinBlocks(GetAsDOMNode(leftParent), GetAsDOMNode(rightParent),
+                           aCancel);
           NS_ENSURE_SUCCESS(res, res);
         }
       }
     }
   }
+
+  // We might have left only collapsed whitespace in the start/end nodes
+  {
+    nsAutoTrackDOMPoint startTracker(mHTMLEditor->mRangeUpdater,
+                                     address_of(startNode), &startOffset);
+    nsAutoTrackDOMPoint endTracker(mHTMLEditor->mRangeUpdater,
+                                   address_of(endNode), &endOffset);
+
+    DeleteNodeIfCollapsedText(*startNode);
+    DeleteNodeIfCollapsedText(*endNode);
+  }
+
   // If we're joining blocks: if deleting forward the selection should be
   // collapsed to the end of the selection, if deleting backward the selection
   // should be collapsed to the beginning of the selection. But if we're not
   // joining then the selection should collapse to the beginning of the
   // selection if we'redeleting forward, because the end of the selection will
   // still be in the next block. And same thing for deleting backwards
   // (selection should collapse to the end, because the beginning will still be
   // in the first block). See Bug 507936
@@ -2425,16 +2498,38 @@ nsHTMLEditRules::WillDeleteSelection(Sel
     res = aSelection->Collapse(endNode, endOffset);
   } else {
     res = aSelection->Collapse(startNode, startOffset);
   }
   NS_ENSURE_SUCCESS(res, res);
   return NS_OK;
 }
 
+/**
+ * If aNode is a text node that contains only collapsed whitespace, delete it.
+ * It doesn't serve any useful purpose, and we don't want it to confuse code
+ * that doesn't correctly skip over it.
+ *
+ * If deleting the node fails (like if it's not editable), the caller should
+ * proceed as usual, so don't return any errors.
+ */
+void
+nsHTMLEditRules::DeleteNodeIfCollapsedText(nsINode& aNode)
+{
+  if (!aNode.GetAsText()) {
+    return;
+  }
+  bool empty;
+  nsresult res = mHTMLEditor->IsVisTextNode(aNode.AsContent(), &empty, false);
+  NS_ENSURE_SUCCESS_VOID(res);
+  if (empty) {
+    mHTMLEditor->DeleteNode(&aNode);
+  }
+}
+
 
 /*****************************************************************************************************
 *    InsertBRIfNeeded: determines if a br is needed for current selection to not be spastic.
 *    If so, it inserts one.  Callers responsibility to only call with collapsed selection.
 *         Selection* aSelection      the collapsed selection
 */
 nsresult
 nsHTMLEditRules::InsertBRIfNeeded(Selection* aSelection)
@@ -2497,278 +2592,304 @@ nsHTMLEditRules::GetGoodSelPointForNode(
        mHTMLEditor->IsVisBreak(&aNode)) &&
       aAction == nsIEditor::ePrevious) {
     ret.offset++;
   }
   return ret;
 }
 
 
-/**
- * This method is used to join two block elements.  The right element is always
- * joined to the left element.  If the elements are the same type and not
- * nested within each other, JoinNodesSmart is called (example, joining two
- * list items together into one).  If the elements are not the same type, or
- * one is a descendant of the other, we instead destroy the right block placing
- * its children into leftblock.  DTD containment rules are followed throughout.
- */
+/*****************************************************************************************************
+*    JoinBlocks: this method is used to join two block elements.  The right element is always joined
+*    to the left element.  If the elements are the same type and not nested within each other,
+*    JoinNodesSmart is called (example, joining two list items together into one).  If the elements
+*    are not the same type, or one is a descendant of the other, we instead destroy the right block
+*    placing its children into leftblock.  DTD containment rules are followed throughout.
+*         nsCOMPtr<nsIDOMNode> *aLeftBlock         pointer to the left block
+*         nsCOMPtr<nsIDOMNode> *aRightBlock        pointer to the right block; will have contents moved to left block
+*         bool *aCanceled                        return TRUE if we had to cancel operation
+*/
 nsresult
-nsHTMLEditRules::JoinBlocks(nsIContent& aLeftNode, nsIContent& aRightNode,
-                            bool* aCanceled)
-{
-  MOZ_ASSERT(aCanceled);
-
-  NS_ENSURE_STATE(mHTMLEditor);
-  nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
-
-  nsCOMPtr<Element> leftBlock = mHTMLEditor->GetBlock(aLeftNode);
-  nsCOMPtr<Element> rightBlock = mHTMLEditor->GetBlock(aRightNode);
-
-  // Sanity checks
-  NS_ENSURE_TRUE(leftBlock && rightBlock, NS_ERROR_NULL_POINTER);
-  NS_ENSURE_STATE(leftBlock != rightBlock);
-
-  if (nsHTMLEditUtils::IsTableElement(leftBlock) ||
-      nsHTMLEditUtils::IsTableElement(rightBlock)) {
-    // Do not try to merge table elements
+nsHTMLEditRules::JoinBlocks(nsIDOMNode *aLeftNode,
+                            nsIDOMNode *aRightNode,
+                            bool *aCanceled)
+{
+  NS_ENSURE_ARG_POINTER(aLeftNode && aRightNode);
+
+  nsCOMPtr<nsIDOMNode> aLeftBlock, aRightBlock;
+
+  if (IsBlockNode(aLeftNode)) {
+    aLeftBlock = aLeftNode;
+  } else if (aLeftNode) {
+    NS_ENSURE_STATE(mHTMLEditor);
+    aLeftBlock = mHTMLEditor->GetBlockNodeParent(aLeftNode);
+  }
+
+  if (IsBlockNode(aRightNode)) {
+    aRightBlock = aRightNode;
+  } else if (aRightNode) {
+    NS_ENSURE_STATE(mHTMLEditor);
+    aRightBlock = mHTMLEditor->GetBlockNodeParent(aRightNode);
+  }
+
+  // sanity checks
+  NS_ENSURE_TRUE(aLeftBlock && aRightBlock, NS_ERROR_NULL_POINTER);
+  NS_ENSURE_STATE(aLeftBlock != aRightBlock);
+
+  if (nsHTMLEditUtils::IsTableElement(aLeftBlock) ||
+      nsHTMLEditUtils::IsTableElement(aRightBlock)) {
+    // do not try to merge table elements
     *aCanceled = true;
     return NS_OK;
   }
 
-  // Make sure we don't try to move things into HR's, which look like blocks
-  // but aren't containers
-  if (leftBlock->IsHTMLElement(nsGkAtoms::hr)) {
-    leftBlock = mHTMLEditor->GetBlockNodeParent(leftBlock);
-  }
-  if (rightBlock->IsHTMLElement(nsGkAtoms::hr)) {
-    rightBlock = mHTMLEditor->GetBlockNodeParent(rightBlock);
-  }
-  NS_ENSURE_STATE(leftBlock && rightBlock);
-
-  // Bail if both blocks the same
-  if (leftBlock == rightBlock) {
+  // make sure we don't try to move thing's into HR's, which look like blocks but aren't containers
+  if (nsHTMLEditUtils::IsHR(aLeftBlock)) {
+    NS_ENSURE_STATE(mHTMLEditor);
+    nsCOMPtr<nsIDOMNode> realLeft = mHTMLEditor->GetBlockNodeParent(aLeftBlock);
+    aLeftBlock = realLeft;
+  }
+  if (nsHTMLEditUtils::IsHR(aRightBlock)) {
+    NS_ENSURE_STATE(mHTMLEditor);
+    nsCOMPtr<nsIDOMNode> realRight = mHTMLEditor->GetBlockNodeParent(aRightBlock);
+    aRightBlock = realRight;
+  }
+  NS_ENSURE_STATE(aLeftBlock && aRightBlock);
+
+  // bail if both blocks the same
+  if (aLeftBlock == aRightBlock) {
     *aCanceled = true;
     return NS_OK;
   }
 
   // Joining a list item to its parent is a NOP.
-  if (nsHTMLEditUtils::IsList(leftBlock) &&
-      nsHTMLEditUtils::IsListItem(rightBlock) &&
-      rightBlock->GetParentNode() == leftBlock) {
-    return NS_OK;
-  }
-
-  // Special rule here: if we are trying to join list items, and they are in
-  // different lists, join the lists instead.
-  bool mergeLists = false;
+  if (nsHTMLEditUtils::IsList(aLeftBlock) &&
+      nsHTMLEditUtils::IsListItem(aRightBlock)) {
+    nsCOMPtr<nsIDOMNode> rightParent;
+    aRightBlock->GetParentNode(getter_AddRefs(rightParent));
+    if (rightParent == aLeftBlock) {
+      return NS_OK;
+    }
+  }
+
+  // special rule here: if we are trying to join list items, and they are in different lists,
+  // join the lists instead.
+  bool bMergeLists = false;
   nsIAtom* existingList = nsGkAtoms::_empty;
-  int32_t offset;
-  nsCOMPtr<Element> leftList, rightList;
-  if (nsHTMLEditUtils::IsListItem(leftBlock) &&
-      nsHTMLEditUtils::IsListItem(rightBlock)) {
-    leftList = leftBlock->GetParentElement();
-    rightList = rightBlock->GetParentElement();
-    if (leftList && rightList && leftList != rightList &&
-        !nsEditorUtils::IsDescendantOf(leftList, rightBlock, &offset) &&
-        !nsEditorUtils::IsDescendantOf(rightList, leftBlock, &offset)) {
-      // There are some special complications if the lists are descendants of
-      // the other lists' items.  Note that it is okay for them to be
-      // descendants of the other lists themselves, which is the usual case for
-      // sublists in our implementation.
-      leftBlock = leftList;
-      rightBlock = rightList;
-      mergeLists = true;
-      existingList = leftList->NodeInfo()->NameAtom();
-    }
-  }
-
+  int32_t theOffset;
+  nsCOMPtr<nsIDOMNode> leftList, rightList;
+  if (nsHTMLEditUtils::IsListItem(aLeftBlock) &&
+      nsHTMLEditUtils::IsListItem(aRightBlock)) {
+    aLeftBlock->GetParentNode(getter_AddRefs(leftList));
+    aRightBlock->GetParentNode(getter_AddRefs(rightList));
+    if (leftList && rightList && (leftList!=rightList))
+    {
+      // there are some special complications if the lists are descendants of
+      // the other lists' items.  Note that it is ok for them to be descendants
+      // of the other lists themselves, which is the usual case for sublists
+      // in our impllementation.
+      if (!nsEditorUtils::IsDescendantOf(leftList, aRightBlock, &theOffset) &&
+          !nsEditorUtils::IsDescendantOf(rightList, aLeftBlock, &theOffset))
+      {
+        aLeftBlock = leftList;
+        aRightBlock = rightList;
+        bMergeLists = true;
+        NS_ENSURE_STATE(mHTMLEditor);
+        existingList = mHTMLEditor->GetTag(leftList);
+      }
+    }
+  }
+
+  NS_ENSURE_STATE(mHTMLEditor);
   nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
 
   nsresult res = NS_OK;
-  int32_t rightOffset = 0;
-  int32_t leftOffset = -1;
-
-  // offset below is where you find yourself in rightBlock when you traverse
-  // upwards from leftBlock
-  if (nsEditorUtils::IsDescendantOf(leftBlock, rightBlock, &rightOffset)) {
-    // Tricky case.  Left block is inside right block.  Do ws adjustment.  This
-    // just destroys non-visible ws at boundaries we will be joining.
+  int32_t  rightOffset = 0;
+  int32_t  leftOffset  = -1;
+
+  // theOffset below is where you find yourself in aRightBlock when you traverse upwards
+  // from aLeftBlock
+  if (nsEditorUtils::IsDescendantOf(aLeftBlock, aRightBlock, &rightOffset)) {
+    // tricky case.  left block is inside right block.
+    // Do ws adjustment.  This just destroys non-visible ws at boundaries we will be joining.
     rightOffset++;
+    NS_ENSURE_STATE(mHTMLEditor);
+    nsCOMPtr<nsINode> leftBlock(do_QueryInterface(aLeftBlock));
     res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
                                             nsWSRunObject::kBlockEnd,
                                             leftBlock);
     NS_ENSURE_SUCCESS(res, res);
 
     {
-      // We can't just track rightBlock because it's an Element.
-      nsCOMPtr<nsINode> trackingRightBlock(rightBlock);
       nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
-                                  address_of(trackingRightBlock),
-                                  &rightOffset);
+                                  address_of(aRightBlock), &rightOffset);
+      nsCOMPtr<nsINode> rightBlock(do_QueryInterface(aRightBlock));
       res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
                                               nsWSRunObject::kAfterBlock,
                                               rightBlock, rightOffset);
       NS_ENSURE_SUCCESS(res, res);
-      if (trackingRightBlock->IsElement()) {
-        rightBlock = trackingRightBlock->AsElement();
-      } else {
-        NS_ENSURE_STATE(trackingRightBlock->GetParentElement());
-        rightBlock = trackingRightBlock->GetParentElement();
-      }
     }
     // Do br adjustment.
     nsCOMPtr<nsIDOMNode> brNode;
-    res = CheckForInvisibleBR(GetAsDOMNode(leftBlock), kBlockEnd,
-                              address_of(brNode));
+    res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode));
     NS_ENSURE_SUCCESS(res, res);
-    if (mergeLists) {
-      // The idea here is to take all children in rightList that are past
-      // offset, and pull them into leftlist.
-      for (nsCOMPtr<nsIContent> child = rightList->GetChildAt(offset);
-           child; child = rightList->GetChildAt(rightOffset)) {
-        res = mHTMLEditor->MoveNode(child, leftList, -1);
+    if (bMergeLists)
+    {
+      // idea here is to take all children in  rightList that are past
+      // theOffset, and pull them into leftlist.
+      nsCOMPtr<nsIContent> parent(do_QueryInterface(rightList));
+      NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
+
+      nsIContent *child = parent->GetChildAt(theOffset);
+      nsCOMPtr<nsINode> leftList_ = do_QueryInterface(leftList);
+      NS_ENSURE_STATE(leftList_);
+      while (child)
+      {
+        NS_ENSURE_STATE(mHTMLEditor);
+        res = mHTMLEditor->MoveNode(child, leftList_, -1);
         NS_ENSURE_SUCCESS(res, res);
-      }
-    } else {
-      res = MoveBlock(GetAsDOMNode(leftBlock), GetAsDOMNode(rightBlock),
-                      leftOffset, rightOffset);
-    }
-    if (brNode) {
-      mHTMLEditor->DeleteNode(brNode);
-    }
-  // Offset below is where you find yourself in leftBlock when you traverse
-  // upwards from rightBlock
-  } else if (nsEditorUtils::IsDescendantOf(rightBlock, leftBlock,
-                                           &leftOffset)) {
-    // Tricky case.  Right block is inside left block.  Do ws adjustment.  This
-    // just destroys non-visible ws at boundaries we will be joining.
+
+        child = parent->GetChildAt(rightOffset);
+      }
+    }
+    else
+    {
+      res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset);
+    }
+    NS_ENSURE_STATE(mHTMLEditor);
+    if (brNode) mHTMLEditor->DeleteNode(brNode);
+  // theOffset below is where you find yourself in aLeftBlock when you traverse upwards
+  // from aRightBlock
+  } else if (nsEditorUtils::IsDescendantOf(aRightBlock, aLeftBlock, &leftOffset)) {
+    // tricky case.  right block is inside left block.
+    // Do ws adjustment.  This just destroys non-visible ws at boundaries we will be joining.
+    NS_ENSURE_STATE(mHTMLEditor);
+    nsCOMPtr<nsINode> rightBlock(do_QueryInterface(aRightBlock));
     res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
                                             nsWSRunObject::kBlockStart,
                                             rightBlock);
     NS_ENSURE_SUCCESS(res, res);
+    NS_ENSURE_STATE(mHTMLEditor);
     {
-      // We can't just track leftBlock because it's an Element, so track
-      // something else.
-      nsCOMPtr<nsINode> trackingLeftBlock(leftBlock);
       nsAutoTrackDOMPoint tracker(mHTMLEditor->mRangeUpdater,
-                                  address_of(trackingLeftBlock), &leftOffset);
+                                  address_of(aLeftBlock), &leftOffset);
+      nsCOMPtr<nsINode> leftBlock(do_QueryInterface(aLeftBlock));
       res = nsWSRunObject::ScrubBlockBoundary(mHTMLEditor,
                                               nsWSRunObject::kBeforeBlock,
                                               leftBlock, leftOffset);
       NS_ENSURE_SUCCESS(res, res);
-      if (trackingLeftBlock->IsElement()) {
-        leftBlock = trackingLeftBlock->AsElement();
-      } else {
-        NS_ENSURE_STATE(trackingLeftBlock->GetParentElement());
-        leftBlock = trackingLeftBlock->GetParentElement();
-      }
     }
     // Do br adjustment.
     nsCOMPtr<nsIDOMNode> brNode;
-    res = CheckForInvisibleBR(GetAsDOMNode(leftBlock), kBeforeBlock,
-                              address_of(brNode), leftOffset);
+    res = CheckForInvisibleBR(aLeftBlock, kBeforeBlock, address_of(brNode),
+                              leftOffset);
     NS_ENSURE_SUCCESS(res, res);
-    if (mergeLists) {
-      res = MoveContents(GetAsDOMNode(rightList), GetAsDOMNode(leftList),
-                         &leftOffset);
-    } else {
+    if (bMergeLists)
+    {
+      res = MoveContents(rightList, leftList, &leftOffset);
+    }
+    else
+    {
       // Left block is a parent of right block, and the parent of the previous
       // visible content.  Right block is a child and contains the contents we
       // want to move.
 
       int32_t previousContentOffset;
-      nsCOMPtr<nsINode> previousContentParent;
-
-      if (&aLeftNode == leftBlock) {
+      nsCOMPtr<nsIDOMNode> previousContentParent;
+
+      if (aLeftNode == aLeftBlock) {
         // We are working with valid HTML, aLeftNode is a block node, and is
-        // therefore allowed to contain rightBlock.  This is the simple case,
-        // we will simply move the content in rightBlock out of its block.
-        previousContentParent = leftBlock;
+        // therefore allowed to contain aRightBlock.  This is the simple case,
+        // we will simply move the content in aRightBlock out of its block.
+        previousContentParent = aLeftBlock;
         previousContentOffset = leftOffset;
       } else {
         // We try to work as well as possible with HTML that's already invalid.
         // Although "right block" is a block, and a block must not be contained
         // in inline elements, reality is that broken documents do exist.  The
         // DIRECT parent of "left NODE" might be an inline element.  Previous
         // versions of this code skipped inline parents until the first block
         // parent was found (and used "left block" as the destination).
         // However, in some situations this strategy moves the content to an
         // unexpected position.  (see bug 200416) The new idea is to make the
         // moving content a sibling, next to the previous visible content.
 
-        previousContentParent = aLeftNode.GetParentNode();
-        previousContentOffset = previousContentParent ?
-          previousContentParent->IndexOf(&aLeftNode) : -1;
+        previousContentParent =
+          nsEditor::GetNodeLocation(aLeftNode, &previousContentOffset);
 
         // We want to move our content just after the previous visible node.
         previousContentOffset++;
       }
 
       // Because we don't want the moving content to receive the style of the
       // previous content, we split the previous content's style.
 
-      nsCOMPtr<Element> editorRoot = mHTMLEditor->GetEditorRoot();
-      if (!editorRoot || &aLeftNode != editorRoot) {
-        nsCOMPtr<nsIContent> splittedPreviousContent;
+      NS_ENSURE_STATE(mHTMLEditor);
+      nsCOMPtr<nsINode> editorRoot = mHTMLEditor->GetEditorRoot();
+      if (!editorRoot || aLeftNode != editorRoot->AsDOMNode()) {
+        nsCOMPtr<nsIDOMNode> splittedPreviousContent;
+        NS_ENSURE_STATE(mHTMLEditor);
         res = mHTMLEditor->SplitStyleAbovePoint(address_of(previousContentParent),
                                                 &previousContentOffset,
                                                 nullptr, nullptr, nullptr,
-                                                getter_AddRefs(splittedPreviousContent));
+                                                address_of(splittedPreviousContent));
         NS_ENSURE_SUCCESS(res, res);
 
         if (splittedPreviousContent) {
-          previousContentParent = splittedPreviousContent->GetParentNode();
-          previousContentOffset = previousContentParent ?
-            previousContentParent->IndexOf(splittedPreviousContent) : -1;
+          previousContentParent =
+            nsEditor::GetNodeLocation(splittedPreviousContent,
+                                      &previousContentOffset);
         }
       }
 
-      res = MoveBlock(GetAsDOMNode(previousContentParent),
-                      GetAsDOMNode(rightBlock),
+      res = MoveBlock(previousContentParent, aRightBlock,
                       previousContentOffset, rightOffset);
-      NS_ENSURE_SUCCESS(res, res);
-    }
-    if (brNode) {
-      mHTMLEditor->DeleteNode(brNode);
-    }
-  } else {
-    // Normal case.  Blocks are siblings, or at least close enough.  An example
-    // of the latter is <p>paragraph</p><ul><li>one<li>two<li>three</ul>.  The
-    // first li and the p are not true siblings, but we still want to join them
-    // if you backspace from li into p.
-
-    // Adjust whitespace at block boundaries
+    }
+    NS_ENSURE_STATE(mHTMLEditor);
+    if (brNode) mHTMLEditor->DeleteNode(brNode);
+  }
+  else
+  {
+    // normal case.  blocks are siblings, or at least close enough to siblings.  An example
+    // of the latter is a <p>paragraph</p><ul><li>one<li>two<li>three</ul>.  The first
+    // li and the p are not true siblings, but we still want to join them if you backspace
+    // from li into p.
+
+    // adjust whitespace at block boundaries
+    NS_ENSURE_STATE(mHTMLEditor);
+    nsCOMPtr<Element> leftBlock(do_QueryInterface(aLeftBlock));
+    nsCOMPtr<Element> rightBlock(do_QueryInterface(aRightBlock));
     res = nsWSRunObject::PrepareToJoinBlocks(mHTMLEditor, leftBlock, rightBlock);
     NS_ENSURE_SUCCESS(res, res);
     // Do br adjustment.
     nsCOMPtr<nsIDOMNode> brNode;
-    res = CheckForInvisibleBR(GetAsDOMNode(leftBlock), kBlockEnd,
-                              address_of(brNode));
+    res = CheckForInvisibleBR(aLeftBlock, kBlockEnd, address_of(brNode));
     NS_ENSURE_SUCCESS(res, res);
-    if (mergeLists || leftBlock->NodeInfo()->NameAtom() ==
-                      rightBlock->NodeInfo()->NameAtom()) {
-      // Nodes are same type.  Merge them.
+    NS_ENSURE_STATE(mHTMLEditor);
+    if (bMergeLists || mHTMLEditor->NodesSameType(aLeftBlock, aRightBlock)) {
+      // nodes are same type.  merge them.
       ::DOMPoint pt = JoinNodesSmart(*leftBlock, *rightBlock);
-      if (pt.node && mergeLists) {
-        nsCOMPtr<Element> newBlock;
-        res = ConvertListType(rightBlock, getter_AddRefs(newBlock),
+      if (pt.node && bMergeLists) {
+        nsCOMPtr<nsIDOMNode> newBlock;
+        res = ConvertListType(aRightBlock, address_of(newBlock),
                               existingList, nsGkAtoms::li);
       }
-    } else {
-      // Nodes are dissimilar types.
-      res = MoveBlock(GetAsDOMNode(leftBlock), GetAsDOMNode(rightBlock),
-                      leftOffset, rightOffset);
-      NS_ENSURE_SUCCESS(res, res);
-    }
-    if (brNode) {
+    }
+    else
+    {
+      // nodes are disimilar types.
+      res = MoveBlock(aLeftBlock, aRightBlock, leftOffset, rightOffset);
+    }
+    if (NS_SUCCEEDED(res) && brNode)
+    {
+      NS_ENSURE_STATE(mHTMLEditor);
       res = mHTMLEditor->DeleteNode(brNode);
-      NS_ENSURE_SUCCESS(res, res);
-    }
-  }
-  return NS_OK;
+    }
+  }
+  return res;
 }
 
 
 /*****************************************************************************************************
 *    MoveBlock: this method is used to move the content from rightBlock into leftBlock
 *    Note that the "block" might merely be inline nodes between <br>s, or between blocks, etc.
 *    DTD containment rules are followed throughout.
 *         nsIDOMNode *aLeftBlock         parent to receive moved content
@@ -2937,17 +3058,18 @@ nsHTMLEditRules::WillMakeList(Selection*
                               bool* aHandled,
                               const nsAString* aItemType)
 {
   if (!aSelection || !aListType || !aCancel || !aHandled) {
     return NS_ERROR_NULL_POINTER;
   }
   OwningNonNull<nsIAtom> listType = NS_Atomize(*aListType);
 
-  WillInsert(*aSelection, aCancel);
+  nsresult res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
 
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = false;
   *aHandled = false;
 
   // deduce what tag to use for list items
   nsCOMPtr<nsIAtom> itemType;
@@ -2962,17 +3084,17 @@ nsHTMLEditRules::WillMakeList(Selection*
 
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
   // block parent, and then further expands to include any ancestors
   // whose children are all in the range
 
   *aHandled = true;
 
-  nsresult res = NormalizeSelection(aSelection);
+  res = NormalizeSelection(aSelection);
   NS_ENSURE_SUCCESS(res, res);
   NS_ENSURE_STATE(mHTMLEditor);
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
 
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
   res = GetListActionNodes(arrayOfNodes,
                            aEntireList ? EntireList::yes : EntireList::no);
   NS_ENSURE_SUCCESS(res, res);
@@ -3316,21 +3438,22 @@ nsHTMLEditRules::WillMakeBasicBlock(Sele
                                     bool *aHandled)
 {
   OwningNonNull<nsIAtom> blockType = NS_Atomize(*aBlockType);
   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
   // initialize out param
   *aCancel = false;
   *aHandled = false;
 
-  WillInsert(*aSelection, aCancel);
+  nsresult res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = false;
-  nsresult res = NormalizeSelection(aSelection);
+  res = NormalizeSelection(aSelection);
   NS_ENSURE_SUCCESS(res, res);
   NS_ENSURE_STATE(mHTMLEditor);
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   NS_ENSURE_STATE(mHTMLEditor);
   nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor);
   *aHandled = true;
   nsString tString(*aBlockType);
 
@@ -3494,24 +3617,25 @@ nsHTMLEditRules::WillIndent(Selection* a
 }
 
 nsresult
 nsHTMLEditRules::WillCSSIndent(Selection* aSelection,
                                bool* aCancel, bool* aHandled)
 {
   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
 
-  WillInsert(*aSelection, aCancel);
+  nsresult res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
 
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = false;
   *aHandled = true;
 
-  nsresult res = NormalizeSelection(aSelection);
+  res = NormalizeSelection(aSelection);
   NS_ENSURE_SUCCESS(res, res);
   NS_ENSURE_STATE(mHTMLEditor);
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
   nsTArray<OwningNonNull<nsRange>> arrayOfRanges;
   nsTArray<OwningNonNull<nsINode>> arrayOfNodes;
 
   // short circuit: detect case of collapsed selection inside an <li>.
   // just sublist that <li>.  This prevents bug 97797.
@@ -3697,24 +3821,25 @@ nsHTMLEditRules::WillCSSIndent(Selection
   return res;
 }
 
 nsresult
 nsHTMLEditRules::WillHTMLIndent(Selection* aSelection,
                                 bool* aCancel, bool* aHandled)
 {
   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
-  WillInsert(*aSelection, aCancel);
+  nsresult res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
 
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = false;
   *aHandled = true;
 
-  nsresult res = NormalizeSelection(aSelection);
+  res = NormalizeSelection(aSelection);
   NS_ENSURE_SUCCESS(res, res);
   NS_ENSURE_STATE(mHTMLEditor);
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
 
   // convert the selection ranges into "promoted" selection ranges:
   // this basically just expands the range to include the immediate
   // block parent, and then further expands to include any ancestors
   // whose children are all in the range
@@ -3984,22 +4109,23 @@ nsHTMLEditRules::WillOutdent(Selection* 
         NS_ENSURE_STATE(mHTMLEditor);
         res = mHTMLEditor->RemoveBlockContainer(GetAsDOMNode(curNode));
         NS_ENSURE_SUCCESS(res, res);
         continue;
       }
       // is it a block with a 'margin' property?
       if (useCSS && IsBlockNode(GetAsDOMNode(curNode))) {
         NS_ENSURE_STATE(mHTMLEditor);
-        nsIAtom& marginProperty =
-          MarginPropertyAtomForIndent(*mHTMLEditor->mHTMLCSSUtils, curNode);
+        nsIAtom* marginProperty =
+          MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils,
+                                      GetAsDOMNode(curNode));
         nsAutoString value;
         NS_ENSURE_STATE(mHTMLEditor);
-        mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(curNode,
-                                                         marginProperty,
+        mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(*curNode,
+                                                         *marginProperty,
                                                          value);
         float f;
         nsCOMPtr<nsIAtom> unit;
         NS_ENSURE_STATE(mHTMLEditor);
         mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
         if (f > 0)
         {
           RelativeChangeIndentationOfElementNode(GetAsDOMNode(curNode), -1);
@@ -4070,21 +4196,22 @@ nsHTMLEditRules::WillOutdent(Selection* 
           curBlockQuote = GetAsDOMNode(n);
           firstBQChild  = GetAsDOMNode(curNode);
           lastBQChild   = GetAsDOMNode(curNode);
           break;
         }
         else if (useCSS)
         {
           NS_ENSURE_STATE(mHTMLEditor);
-          nsIAtom& marginProperty =
-            MarginPropertyAtomForIndent(*mHTMLEditor->mHTMLCSSUtils, curNode);
+          nsIAtom* marginProperty =
+            MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils,
+                                        GetAsDOMNode(curNode));
           nsAutoString value;
           NS_ENSURE_STATE(mHTMLEditor);
-          mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(*n, marginProperty,
+          mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(*n, *marginProperty,
                                                            value);
           float f;
           nsCOMPtr<nsIAtom> unit;
           NS_ENSURE_STATE(mHTMLEditor);
           mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
           if (f > 0 && !(nsHTMLEditUtils::IsList(curParent) && nsHTMLEditUtils::IsList(curNode)))
           {
             curBlockQuote = GetAsDOMNode(n);
@@ -4375,26 +4502,29 @@ nsHTMLEditRules::ConvertListType(Element
 
 
 ///////////////////////////////////////////////////////////////////////////
 // CreateStyleForInsertText:  take care of clearing and setting appropriate
 //                            style nodes for text insertion.
 //
 //
 nsresult
-nsHTMLEditRules::CreateStyleForInsertText(Selection& aSelection,
-                                          nsIDocument& aDoc)
-{
-  MOZ_ASSERT(mHTMLEditor->mTypeInState);
-
-  nsresult res;
+nsHTMLEditRules::CreateStyleForInsertText(Selection* aSelection,
+                                          nsIDOMDocument *aDoc)
+{
+  MOZ_ASSERT(aSelection && aDoc && mHTMLEditor->mTypeInState);
+
   bool weDidSomething = false;
-  NS_ENSURE_STATE(aSelection.GetRangeAt(0));
-  nsCOMPtr<nsINode> node = aSelection.GetRangeAt(0)->GetStartParent();
-  int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
+  nsCOMPtr<nsIDOMNode> node, tmp;
+  int32_t offset;
+  NS_ENSURE_STATE(mHTMLEditor);
+  nsresult res = mHTMLEditor->GetStartNodeAndOffset(aSelection,
+                                                    getter_AddRefs(node),
+                                                    &offset);
+  NS_ENSURE_SUCCESS(res, res);
 
   // next examine our present style and make sure default styles are either
   // present or explicitly overridden.  If neither, add the default style to
   // the TypeInState
   int32_t length = mHTMLEditor->mDefaultStyles.Length();
   for (int32_t j = 0; j < length; j++) {
     PropItem* propItem = mHTMLEditor->mDefaultStyles[j];
     MOZ_ASSERT(propItem);
@@ -4417,18 +4547,19 @@ nsHTMLEditRules::CreateStyleForInsertTex
 
     if (!bAny) {
       // no style set for this prop/attr
       mHTMLEditor->mTypeInState->SetProp(propItem->tag, propItem->attr,
                                          propItem->value);
     }
   }
 
-  nsCOMPtr<Element> rootElement = aDoc.GetRootElement();
-  NS_ENSURE_STATE(rootElement);
+  nsCOMPtr<nsIDOMElement> rootElement;
+  res = aDoc->GetDocumentElement(getter_AddRefs(rootElement));
+  NS_ENSURE_SUCCESS(res, res);
 
   // process clearing any styles first
   nsAutoPtr<PropItem> item(mHTMLEditor->mTypeInState->TakeClearProperty());
   while (item && node != rootElement) {
     NS_ENSURE_STATE(mHTMLEditor);
     res = mHTMLEditor->ClearStyle(address_of(node), &offset,
                                   item->tag, &item->attr);
     NS_ENSURE_SUCCESS(res, res);
@@ -4438,295 +4569,332 @@ nsHTMLEditRules::CreateStyleForInsertTex
 
   // then process setting any styles
   int32_t relFontSize = mHTMLEditor->mTypeInState->TakeRelativeFontSize();
   item = mHTMLEditor->mTypeInState->TakeSetProperty();
 
   if (item || relFontSize) {
     // we have at least one style to add; make a new text node to insert style
     // nodes above.
-    if (RefPtr<Text> text = node->GetAsText()) {
+    if (mHTMLEditor->IsTextNode(node)) {
       // if we are in a text node, split it
       NS_ENSURE_STATE(mHTMLEditor);
-      offset = mHTMLEditor->SplitNodeDeep(*text, *text, offset);
+      nsCOMPtr<nsIContent> content = do_QueryInterface(node);
+      NS_ENSURE_STATE(content || !node);
+      offset = mHTMLEditor->SplitNodeDeep(*content, *content, offset);
       NS_ENSURE_STATE(offset != -1);
-      node = node->GetParentNode();
+      node->GetParentNode(getter_AddRefs(tmp));
+      node = tmp;
     }
     if (!mHTMLEditor->IsContainer(node)) {
       return NS_OK;
     }
-    OwningNonNull<Text> newNode = aDoc.CreateTextNode(EmptyString());
+    nsCOMPtr<nsIDOMNode> newNode;
+    nsCOMPtr<nsIDOMText> nodeAsText;
+    res = aDoc->CreateTextNode(EmptyString(), getter_AddRefs(nodeAsText));
+    NS_ENSURE_SUCCESS(res, res);
+    NS_ENSURE_TRUE(nodeAsText, NS_ERROR_NULL_POINTER);
+    newNode = do_QueryInterface(nodeAsText);
     NS_ENSURE_STATE(mHTMLEditor);
-    res = mHTMLEditor->InsertNode(newNode, *node, offset);
+    res = mHTMLEditor->InsertNode(newNode, node, offset);
     NS_ENSURE_SUCCESS(res, res);
     node = newNode;
     offset = 0;
     weDidSomething = true;
 
     if (relFontSize) {
       // dir indicated bigger versus smaller.  1 = bigger, -1 = smaller
-      nsHTMLEditor::FontSize dir = relFontSize > 0 ?
-        nsHTMLEditor::FontSize::incr : nsHTMLEditor::FontSize::decr;
+      int32_t dir = relFontSize > 0 ? 1 : -1;
       for (int32_t j = 0; j < DeprecatedAbs(relFontSize); j++) {
         NS_ENSURE_STATE(mHTMLEditor);
-        res = mHTMLEditor->RelativeFontChangeOnTextNode(dir, newNode, 0, -1);
+        res = mHTMLEditor->RelativeFontChangeOnTextNode(dir, nodeAsText,
+                                                        0, -1);
         NS_ENSURE_SUCCESS(res, res);
       }
     }
 
     while (item) {
       NS_ENSURE_STATE(mHTMLEditor);
-      res = mHTMLEditor->SetInlinePropertyOnNode(*node->AsContent(),
-                                                 *item->tag, &item->attr,
-                                                 item->value);
+      nsCOMPtr<nsIContent> content = do_QueryInterface(node);
+      NS_ENSURE_STATE(content || !node);
+      res = mHTMLEditor->SetInlinePropertyOnNode(*content, *item->tag,
+                                                 &item->attr, item->value);
       NS_ENSURE_SUCCESS(res, res);
       item = mHTMLEditor->mTypeInState->TakeSetProperty();
     }
   }
   if (weDidSomething) {
-    return aSelection.Collapse(node, offset);
+    return aSelection->Collapse(node, offset);
   }
 
   return NS_OK;
 }
 
 
-/**
- * Figure out if aNode is (or is inside) an empty block.  A block can have
- * children and still be considered empty, if the children are empty or
- * non-editable.
- */
+///////////////////////////////////////////////////////////////////////////
+// IsEmptyBlock: figure out if aNode is (or is inside) an empty block.
+//               A block can have children and still be considered empty,
+//               if the children are empty or non-editable.
+//
 nsresult
-nsHTMLEditRules::IsEmptyBlock(Element& aNode,
-                              bool* aOutIsEmptyBlock,
-                              MozBRCounts aMozBRCounts)
-{
-  MOZ_ASSERT(aOutIsEmptyBlock);
-  *aOutIsEmptyBlock = true;
-
-  NS_ENSURE_TRUE(IsBlockNode(aNode.AsDOMNode()), NS_ERROR_NULL_POINTER);
-
-  return mHTMLEditor->IsEmptyNode(aNode.AsDOMNode(), aOutIsEmptyBlock,
-                                  aMozBRCounts == MozBRCounts::yes ? false
-                                                                   : true);
+nsHTMLEditRules::IsEmptyBlock(nsIDOMNode *aNode,
+                              bool *outIsEmptyBlock,
+                              bool aMozBRDoesntCount,
+                              bool aListItemsNotEmpty)
+{
+  NS_ENSURE_TRUE(aNode && outIsEmptyBlock, NS_ERROR_NULL_POINTER);
+  *outIsEmptyBlock = true;
+
+//  nsresult res = NS_OK;
+  nsCOMPtr<nsIDOMNode> nodeToTest;
+  if (IsBlockNode(aNode)) nodeToTest = do_QueryInterface(aNode);
+//  else nsCOMPtr<nsIDOMElement> block;
+//  looks like I forgot to finish this.  Wonder what I was going to do?
+
+  NS_ENSURE_TRUE(nodeToTest, NS_ERROR_NULL_POINTER);
+  return mHTMLEditor->IsEmptyNode(nodeToTest, outIsEmptyBlock,
+                     aMozBRDoesntCount, aListItemsNotEmpty);
 }
 
 
 nsresult
-nsHTMLEditRules::WillAlign(Selection& aSelection,
-                           const nsAString& aAlignType,
-                           bool* aCancel,
-                           bool* aHandled)
-{
-  MOZ_ASSERT(aCancel && aHandled);
-
-  NS_ENSURE_STATE(mHTMLEditor);
-  nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
-
-  WillInsert(aSelection, aCancel);
-
-  // Initialize out param.  We want to ignore result of WillInsert().
+nsHTMLEditRules::WillAlign(Selection* aSelection,
+                           const nsAString *alignType,
+                           bool *aCancel,
+                           bool *aHandled)
+{
+  if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
+
+  nsresult res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // initialize out param
+  // we want to ignore result of WillInsert()
   *aCancel = false;
   *aHandled = false;
 
-  nsresult rv = NormalizeSelection(&aSelection);
-  NS_ENSURE_SUCCESS(rv, rv);
-  nsAutoSelectionReset selectionResetter(&aSelection, mHTMLEditor);
-
-  // Convert the selection ranges into "promoted" selection ranges: This
-  // basically just expands the range to include the immediate block parent,
-  // and then further expands to include any ancestors whose children are all
-  // in the range
+  res = NormalizeSelection(aSelection);
+  NS_ENSURE_SUCCESS(res, res);
+  nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
+
+  // convert the selection ranges into "promoted" selection ranges:
+  // this basically just expands the range to include the immediate
+  // block parent, and then further expands to include any ancestors
+  // whose children are all in the range
   *aHandled = true;
   nsTArray<OwningNonNull<nsINode>> nodeArray;
-  rv = GetNodesFromSelection(aSelection, EditAction::align, nodeArray);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // If we don't have any nodes, or we have only a single br, then we are
-  // creating an empty alignment div.  We have to do some different things for
-  // these.
-  bool emptyDiv = nodeArray.IsEmpty();
-  if (nodeArray.Length() == 1) {
-    OwningNonNull<nsINode> node = nodeArray[0];
-
-    if (nsHTMLEditUtils::SupportsAlignAttr(GetAsDOMNode(node))) {
-      // The node is a table element, an hr, a paragraph, a div or a section
-      // header; in HTML 4, it can directly carry the ALIGN attribute and we
-      // don't need to make a div! If we are in CSS mode, all the work is done
-      // in AlignBlock
-      rv = AlignBlock(static_cast<nsIDOMElement*>(node->AsDOMNode()),
-                       &aAlignType, true);
-      NS_ENSURE_SUCCESS(rv, rv);
+  res = GetNodesFromSelection(*aSelection, EditAction::align, nodeArray);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // if we don't have any nodes, or we have only a single br, then we are
+  // creating an empty alignment div.  We have to do some different things for these.
+  bool emptyDiv = false;
+  int32_t listCount = nodeArray.Length();
+  if (!listCount) emptyDiv = true;
+  if (listCount == 1)
+  {
+    OwningNonNull<nsINode> theNode = nodeArray[0];
+
+    if (nsHTMLEditUtils::SupportsAlignAttr(GetAsDOMNode(theNode))) {
+      // the node is a table element, an horiz rule, a paragraph, a div
+      // or a section header; in HTML 4, it can directly carry the ALIGN
+      // attribute and we don't need to make a div! If we are in CSS mode,
+      // all the work is done in AlignBlock
+      nsCOMPtr<nsIDOMElement> theElem = do_QueryInterface(theNode);
+      res = AlignBlock(theElem, alignType, true);
+      NS_ENSURE_SUCCESS(res, res);
       return NS_OK;
     }
 
-    if (nsTextEditUtils::IsBreak(node)) {
-      // The special case emptyDiv code (below) that consumes BRs can cause
-      // tables to split if the start node of the selection is not in a table
-      // cell or caption, for example parent is a <tr>.  Avoid this unnecessary
-      // splitting if possible by leaving emptyDiv FALSE so that we fall
-      // through to the normal case alignment code.
+    if (nsTextEditUtils::IsBreak(theNode))
+    {
+      // The special case emptyDiv code (below) that consumes BRs can
+      // cause tables to split if the start node of the selection is
+      // not in a table cell or caption, for example parent is a <tr>.
+      // Avoid this unnecessary splitting if possible by leaving emptyDiv
+      // FALSE so that we fall through to the normal case alignment code.
       //
-      // XXX: It seems a little error prone for the emptyDiv special case code
-      // to assume that the start node of the selection is the parent of the
-      // single node in the nodeArray, as the paragraph above points out. Do we
-      // rely on the selection start node because of the fact that nodeArray
-      // can be empty?  We should probably revisit this issue. - kin
-
-      NS_ENSURE_STATE(aSelection.GetRangeAt(0) &&
-                      aSelection.GetRangeAt(0)->GetStartParent());
-      OwningNonNull<nsINode> parent =
-        *aSelection.GetRangeAt(0)->GetStartParent();
-
-      emptyDiv = !nsHTMLEditUtils::IsTableElement(parent) ||
-                 nsHTMLEditUtils::IsTableCellOrCaption(parent);
-    }
-  }
-  if (emptyDiv) {
-    NS_ENSURE_STATE(aSelection.GetRangeAt(0) &&
-                    aSelection.GetRangeAt(0)->GetStartParent());
-    nsCOMPtr<nsINode> parent = aSelection.GetRangeAt(0)->GetStartParent();
-    int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
-
-    rv = SplitAsNeeded(*nsGkAtoms::div, parent, offset);
-    NS_ENSURE_SUCCESS(rv, rv);
-    // Consume a trailing br, if any.  This is to keep an alignment from
+      // XXX: It seems a little error prone for the emptyDiv special
+      //      case code to assume that the start node of the selection
+      //      is the parent of the single node in the nodeArray, as
+      //      the paragraph above points out. Do we rely on the selection
+      //      start node because of the fact that nodeArray can be empty?
+      //      We should probably revisit this issue. - kin
+
+      nsCOMPtr<nsIDOMNode> parent;
+      int32_t offset;
+      NS_ENSURE_STATE(mHTMLEditor);
+      res = mHTMLEditor->GetStartNodeAndOffset(aSelection, getter_AddRefs(parent), &offset);
+
+      if (!nsHTMLEditUtils::IsTableElement(parent) || nsHTMLEditUtils::IsTableCellOrCaption(parent))
+        emptyDiv = true;
+    }
+  }
+  if (emptyDiv)
+  {
+    nsCOMPtr<nsIDOMNode> brNode, sib;
+    NS_NAMED_LITERAL_STRING(divType, "div");
+
+    NS_ENSURE_STATE(aSelection->GetRangeAt(0));
+    nsCOMPtr<nsINode> parent = aSelection->GetRangeAt(0)->GetStartParent();
+    int32_t offset = aSelection->GetRangeAt(0)->StartOffset();
+    NS_ENSURE_STATE(parent);
+
+    res = SplitAsNeeded(*nsGkAtoms::div, parent, offset);
+    NS_ENSURE_SUCCESS(res, res);
+    // consume a trailing br, if any.  This is to keep an alignment from
     // creating extra lines, if possible.
-    nsCOMPtr<nsIContent> brContent =
-      mHTMLEditor->GetNextHTMLNode(parent, offset);
-    if (brContent && nsTextEditUtils::IsBreak(brContent)) {
-      // Making use of html structure... if next node after where we are
-      // putting our div is not a block, then the br we found is in same block
-      // we are, so it's safe to consume it.
-      nsCOMPtr<nsIContent> sibling = mHTMLEditor->GetNextHTMLSibling(parent,
-                                                                     offset);
-      if (!IsBlockNode(GetAsDOMNode(sibling))) {
-        rv = mHTMLEditor->DeleteNode(brContent);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-    }
-    nsCOMPtr<Element> div = mHTMLEditor->CreateNode(nsGkAtoms::div, parent,
-                                                    offset);
-    NS_ENSURE_STATE(div);
-    // Remember our new block for postprocessing
-    mNewBlock = div->AsDOMNode();
-    // Set up the alignment on the div, using HTML or CSS
-    rv = AlignBlock(static_cast<nsIDOMElement*>(GetAsDOMNode(div)),
-                    &aAlignType, true);
-    NS_ENSURE_SUCCESS(rv, rv);
+    NS_ENSURE_STATE(mHTMLEditor);
+    res = mHTMLEditor->GetNextHTMLNode(GetAsDOMNode(parent), offset,
+                                       address_of(brNode));
+    NS_ENSURE_SUCCESS(res, res);
+    if (brNode && nsTextEditUtils::IsBreak(brNode))
+    {
+      // making use of html structure... if next node after where
+      // we are putting our div is not a block, then the br we
+      // found is in same block we are, so its safe to consume it.
+      NS_ENSURE_STATE(mHTMLEditor);
+      res = mHTMLEditor->GetNextHTMLSibling(GetAsDOMNode(parent), offset,
+                                            address_of(sib));
+      NS_ENSURE_SUCCESS(res, res);
+      if (!IsBlockNode(sib))
+      {
+        NS_ENSURE_STATE(mHTMLEditor);
+        res = mHTMLEditor->DeleteNode(brNode);
+        NS_ENSURE_SUCCESS(res, res);
+      }
+    }
+    NS_ENSURE_STATE(mHTMLEditor);
+    nsCOMPtr<Element> theDiv = mHTMLEditor->CreateNode(nsGkAtoms::div, parent,
+                                                       offset);
+    NS_ENSURE_STATE(theDiv);
+    // remember our new block for postprocessing
+    mNewBlock = GetAsDOMNode(theDiv);
+    // set up the alignment on the div, using HTML or CSS
+    nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(theDiv);
+    res = AlignBlock(divElem, alignType, true);
+    NS_ENSURE_SUCCESS(res, res);
     *aHandled = true;
-    // Put in a moz-br so that it won't get deleted
-    rv = CreateMozBR(div->AsDOMNode(), 0);
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = aSelection.Collapse(div, 0);
-    // Don't reset our selection in this case.
-    selectionResetter.Abort();
-    NS_ENSURE_SUCCESS(rv, rv);
-    return NS_OK;
+    // put in a moz-br so that it won't get deleted
+    res = CreateMozBR(GetAsDOMNode(theDiv), 0);
+    NS_ENSURE_SUCCESS(res, res);
+    res = aSelection->Collapse(theDiv, 0);
+    selectionResetter.Abort();  // don't reset our selection in this case.
+    return res;
   }
 
   // Next we detect all the transitions in the array, where a transition
   // means that adjacent nodes in the array don't have the same parent.
 
   nsTArray<bool> transitionList;
   MakeTransitionList(nodeArray, transitionList);
 
-  // Okay, now go through all the nodes and give them an align attrib or put
-  // them in a div, or whatever is appropriate.  Woohoo!
-
+  // Ok, now go through all the nodes and give them an align attrib or put them in a div,
+  // or whatever is appropriate.  Wohoo!
+
+  nsCOMPtr<nsINode> curParent;
   nsCOMPtr<Element> curDiv;
   bool useCSS = mHTMLEditor->IsCSSEnabled();
-  for (size_t i = 0; i < nodeArray.Length(); i++) {
-    auto& curNode = nodeArray[i];
-    // Here's where we actually figure out what to do
+  for (int32_t i = 0; i < listCount; ++i) {
+    // here's where we actually figure out what to do
+    nsCOMPtr<nsIDOMNode> curNode = nodeArray[i]->AsDOMNode();
+    nsCOMPtr<nsIContent> curContent = do_QueryInterface(curNode);
+    NS_ENSURE_STATE(curContent);
 
     // Ignore all non-editable nodes.  Leave them be.
-    if (!mHTMLEditor->IsEditable(curNode)) {
+    if (!mHTMLEditor->IsEditable(curNode)) continue;
+
+    curParent = curContent->GetParentNode();
+    int32_t offset = curParent ? curParent->IndexOf(curContent) : -1;
+
+    // the node is a table element, an horiz rule, a paragraph, a div
+    // or a section header; in HTML 4, it can directly carry the ALIGN
+    // attribute and we don't need to nest it, just set the alignment.
+    // In CSS, assign the corresponding CSS styles in AlignBlock
+    if (nsHTMLEditUtils::SupportsAlignAttr(curNode))
+    {
+      nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(curNode);
+      res = AlignBlock(curElem, alignType, false);
+      NS_ENSURE_SUCCESS(res, res);
+      // clear out curDiv so that we don't put nodes after this one into it
+      curDiv = 0;
       continue;
     }
 
-    // The node is a table element, an hr, a paragraph, a div or a section
-    // header; in HTML 4, it can directly carry the ALIGN attribute and we
-    // don't need to nest it, just set the alignment.  In CSS, assign the
-    // corresponding CSS styles in AlignBlock
-    if (nsHTMLEditUtils::SupportsAlignAttr(GetAsDOMNode(curNode))) {
-      rv = AlignBlock(static_cast<nsIDOMElement*>(curNode->AsDOMNode()),
-                      &aAlignType, false);
-      NS_ENSURE_SUCCESS(rv, rv);
-      // Clear out curDiv so that we don't put nodes after this one into it
-      curDiv = nullptr;
-      continue;
-    }
-
-    nsCOMPtr<nsINode> curParent = curNode->GetParentNode();
-    int32_t offset = curParent ? curParent->IndexOf(curNode) : -1;
-
-    // Skip insignificant formatting text nodes to prevent unnecessary
-    // structure splitting!
+    // Skip insignificant formatting text nodes to prevent
+    // unnecessary structure splitting!
     bool isEmptyTextNode = false;
-    if (curNode->GetAsText() &&
-        ((nsHTMLEditUtils::IsTableElement(curParent) &&
-          !nsHTMLEditUtils::IsTableCellOrCaption(*curParent)) ||
-         nsHTMLEditUtils::IsList(curParent) ||
-         (NS_SUCCEEDED(mHTMLEditor->IsEmptyNode(curNode, &isEmptyTextNode)) &&
-          isEmptyTextNode))) {
+    if (nsEditor::IsTextNode(curNode) &&
+       ((nsHTMLEditUtils::IsTableElement(curParent) &&
+         !nsHTMLEditUtils::IsTableCellOrCaption(GetAsDOMNode(curParent))) ||
+        nsHTMLEditUtils::IsList(curParent) ||
+        (NS_SUCCEEDED(mHTMLEditor->IsEmptyNode(curNode, &isEmptyTextNode)) && isEmptyTextNode)))
       continue;
-    }
-
-    // If it's a list item, or a list inside a list, forget any "current" div,
-    // and instead put divs inside the appropriate block (td, li, etc)
-    if (nsHTMLEditUtils::IsListItem(curNode) ||
-        nsHTMLEditUtils::IsList(curNode)) {
-      rv = RemoveAlignment(GetAsDOMNode(curNode), aAlignType, true);
-      NS_ENSURE_SUCCESS(rv, rv);
+
+    // if it's a list item, or a list
+    // inside a list, forget any "current" div, and instead put divs inside
+    // the appropriate block (td, li, etc)
+    if ( nsHTMLEditUtils::IsListItem(curNode)
+         || nsHTMLEditUtils::IsList(curNode))
+    {
+      res = RemoveAlignment(curNode, *alignType, true);
+      NS_ENSURE_SUCCESS(res, res);
       if (useCSS) {
-        mHTMLEditor->mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(
-            curNode->AsElement(), nullptr, &NS_LITERAL_STRING("align"),
-            &aAlignType, false);
-        curDiv = nullptr;
+        nsCOMPtr<nsIDOMElement> curElem = do_QueryInterface(curNode);
+        NS_NAMED_LITERAL_STRING(attrName, "align");
+        int32_t count;
+        mHTMLEditor->mHTMLCSSUtils->SetCSSEquivalentToHTMLStyle(curNode, nullptr,
+                                                                &attrName, alignType,
+                                                                &count, false);
+        curDiv = 0;
         continue;
-      } else if (nsHTMLEditUtils::IsList(curParent)) {
-        // If we don't use CSS, add a contraint to list element: they have to
-        // be inside another list, i.e., >= second level of nesting
-        rv = AlignInnerBlocks(*curNode, &aAlignType);
-        NS_ENSURE_SUCCESS(rv, rv);
-        curDiv = nullptr;
+      }
+      else if (nsHTMLEditUtils::IsList(curParent)) {
+        // if we don't use CSS, add a contraint to list element : they have
+        // to be inside another list, ie >= second level of nesting
+        res = AlignInnerBlocks(*curContent, alignType);
+        NS_ENSURE_SUCCESS(res, res);
+        curDiv = 0;
         continue;
       }
-      // Clear out curDiv so that we don't put nodes after this one into it
-    }
-
-    // Need to make a div to put things in if we haven't already, or if this
-    // node doesn't go in div we used earlier.
-    if (!curDiv || transitionList[i]) {
+      // clear out curDiv so that we don't put nodes after this one into it
+    }
+
+    // need to make a div to put things in if we haven't already,
+    // or if this node doesn't go in div we used earlier.
+    if (!curDiv || transitionList[i])
+    {
       // First, check that our element can contain a div.
       if (!mEditor->CanContainTag(*curParent, *nsGkAtoms::div)) {
-        // Cancelled
-        return NS_OK;
-      }
-
-      rv = SplitAsNeeded(*nsGkAtoms::div, curParent, offset);
-      NS_ENSURE_SUCCESS(rv, rv);
+        return NS_OK; // cancelled
+      }
+
+      res = SplitAsNeeded(*nsGkAtoms::div, curParent, offset);
+      NS_ENSURE_SUCCESS(res, res);
+      NS_ENSURE_STATE(mHTMLEditor);
       curDiv = mHTMLEditor->CreateNode(nsGkAtoms::div, curParent, offset);
       NS_ENSURE_STATE(curDiv);
-      // Remember our new block for postprocessing
-      mNewBlock = curDiv->AsDOMNode();
-      // Set up the alignment on the div
-      rv = AlignBlock(static_cast<nsIDOMElement*>(curDiv->AsDOMNode()),
-                       &aAlignType, true);
-    }
-
-    NS_ENSURE_STATE(curNode->IsContent());
-
-    // Tuck the node into the end of the active div
-    rv = mHTMLEditor->MoveNode(curNode->AsContent(), curDiv, -1);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return NS_OK;
+      // remember our new block for postprocessing
+      mNewBlock = GetAsDOMNode(curDiv);
+      // set up the alignment on the div
+      nsCOMPtr<nsIDOMElement> divElem = do_QueryInterface(curDiv);
+      res = AlignBlock(divElem, alignType, true);
+      //nsAutoString attr(NS_LITERAL_STRING("align"));
+      //res = mHTMLEditor->SetAttribute(divElem, attr, *alignType);
+      //NS_ENSURE_SUCCESS(res, res);
+      // curDiv is now the correct thing to put curNode in
+    }
+
+    // tuck the node into the end of the active div
+    NS_ENSURE_STATE(mHTMLEditor);
+    res = mHTMLEditor->MoveNode(curContent, curDiv, -1);
+    NS_ENSURE_SUCCESS(res, res);
+  }
+
+  return res;
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // AlignInnerBlocks: Align inside table cells or list items
 //
 nsresult
 nsHTMLEditRules::AlignInnerBlocks(nsINode& aNode, const nsAString* alignType)
@@ -4995,148 +5163,190 @@ nsHTMLEditRules::GetInnerContent(nsINode
       GetInnerContent(*node, aOutArrayOfNodes, aIndex, aLists, aTables);
     } else {
       aOutArrayOfNodes.InsertElementAt(*aIndex, *node);
       (*aIndex)++;
     }
   }
 }
 
-/**
- * Promotes selection to include blocks that have all their children selected.
- */
+///////////////////////////////////////////////////////////////////////////
+// ExpandSelectionForDeletion: this promotes our selection to include blocks
+// that have all their children selected.
+//
 nsresult
-nsHTMLEditRules::ExpandSelectionForDeletion(Selection& aSelection)
-{
-  // Don't need to touch collapsed selections
-  if (aSelection.Collapsed()) {
-    return NS_OK;
-  }
-
-  // We don't need to mess with cell selections, and we assume multirange
-  // selections are those.
-  if (aSelection.RangeCount() != 1) {
+nsHTMLEditRules::ExpandSelectionForDeletion(Selection* aSelection)
+{
+  NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
+
+  // don't need to touch collapsed selections
+  if (aSelection->Collapsed()) {
     return NS_OK;
   }
 
-  // Find current sel start and end
-  NS_ENSURE_TRUE(aSelection.GetRangeAt(0), NS_ERROR_NULL_POINTER);
-  OwningNonNull<nsRange> range = *aSelection.GetRangeAt(0);
-
-  nsCOMPtr<nsINode> selStartNode = range->GetStartParent();
-  int32_t selStartOffset = range->StartOffset();
-  nsCOMPtr<nsINode> selEndNode = range->GetEndParent();
-  int32_t selEndOffset = range->EndOffset();
-
-  // Find current selection common block parent
-  nsCOMPtr<Element> selCommon =
-    nsHTMLEditor::GetBlock(*range->GetCommonAncestor());
+  int32_t rangeCount;
+  nsresult res = aSelection->GetRangeCount(&rangeCount);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // we don't need to mess with cell selections, and we assume multirange selections are those.
+  if (rangeCount != 1) return NS_OK;
+
+  // find current sel start and end
+  RefPtr<nsRange> range = aSelection->GetRangeAt(0);
+  NS_ENSURE_TRUE(range, NS_ERROR_NULL_POINTER);
+  nsCOMPtr<nsIDOMNode> selStartNode, selEndNode, selCommon;
+  int32_t selStartOffset, selEndOffset;
+
+  res = range->GetStartContainer(getter_AddRefs(selStartNode));
+  NS_ENSURE_SUCCESS(res, res);
+  res = range->GetStartOffset(&selStartOffset);
+  NS_ENSURE_SUCCESS(res, res);
+  res = range->GetEndContainer(getter_AddRefs(selEndNode));
+  NS_ENSURE_SUCCESS(res, res);
+  res = range->GetEndOffset(&selEndOffset);
+  NS_ENSURE_SUCCESS(res, res);
+
+  // find current selection common block parent
+  res = range->GetCommonAncestorContainer(getter_AddRefs(selCommon));
+  NS_ENSURE_SUCCESS(res, res);
+  if (!IsBlockNode(selCommon))
+    selCommon = nsHTMLEditor::GetBlockNodeParent(selCommon);
   NS_ENSURE_STATE(selCommon);
 
-  // Set up for loops and cache our root element
-  nsCOMPtr<nsINode> firstBRParent;
+  // set up for loops and cache our root element
+  bool stillLooking = true;
+  nsCOMPtr<nsIDOMNode> firstBRParent;
   nsCOMPtr<nsINode> unused;
-  int32_t visOffset = 0, firstBROffset = 0;
+  int32_t visOffset=0, firstBROffset=0;
   WSType wsType;
-  nsCOMPtr<Element> root = mHTMLEditor->GetActiveEditingHost();
-  NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
-
-  // Find previous visible thingy before start of selection
-  if (selStartNode != selCommon && selStartNode != root) {
-    while (true) {
+  nsCOMPtr<nsIContent> rootContent = mHTMLEditor->GetActiveEditingHost();
+  nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent);
+  NS_ENSURE_TRUE(rootElement, NS_ERROR_FAILURE);
+
+  // find previous visible thingy before start of selection
+  if ((selStartNode!=selCommon) && (selStartNode!=rootElement))
+  {
+    while (stillLooking)
+    {
       nsWSRunObject wsObj(mHTMLEditor, selStartNode, selStartOffset);
-      wsObj.PriorVisibleNode(selStartNode, selStartOffset, address_of(unused),
+      nsCOMPtr<nsINode> selStartNode_(do_QueryInterface(selStartNode));
+      wsObj.PriorVisibleNode(selStartNode_, selStartOffset, address_of(unused),
                              &visOffset, &wsType);
-      if (wsType != WSType::thisBlock) {
-        break;
-      }
-      // We want to keep looking up.  But stop if we are crossing table
-      // element boundaries, or if we hit the root.
-      if (nsHTMLEditUtils::IsTableElement(wsObj.mStartReasonNode) ||
-          selCommon == wsObj.mStartReasonNode ||
-          root == wsObj.mStartReasonNode) {
-        break;
-      }
-      selStartNode = wsObj.mStartReasonNode->GetParentNode();
-      selStartOffset = selStartNode ?
-        selStartNode->IndexOf(wsObj.mStartReasonNode) : -1;
-    }
-  }
-
-  // Find next visible thingy after end of selection
-  if (selEndNode != selCommon && selEndNode != root) {
-    while (true) {
+      if (wsType == WSType::thisBlock) {
+        // we want to keep looking up.  But stop if we are crossing table element
+        // boundaries, or if we hit the root.
+        if (nsHTMLEditUtils::IsTableElement(wsObj.mStartReasonNode) ||
+            selCommon == GetAsDOMNode(wsObj.mStartReasonNode) ||
+            rootElement == GetAsDOMNode(wsObj.mStartReasonNode)) {
+          stillLooking = false;
+        }
+        else
+        {
+          selStartNode = nsEditor::GetNodeLocation(GetAsDOMNode(wsObj.mStartReasonNode),
+                                                   &selStartOffset);
+        }
+      }
+      else
+      {
+        stillLooking = false;
+      }
+    }
+  }
+
+  stillLooking = true;
+  // find next visible thingy after end of selection
+  if ((selEndNode!=selCommon) && (selEndNode!=rootElement))
+  {
+    while (stillLooking)
+    {
       nsWSRunObject wsObj(mHTMLEditor, selEndNode, selEndOffset);
-      wsObj.NextVisibleNode(selEndNode, selEndOffset, address_of(unused),
+      nsCOMPtr<nsINode> selEndNode_(do_QueryInterface(selEndNode));
+      wsObj.NextVisibleNode(selEndNode_, selEndOffset, address_of(unused),
                             &visOffset, &wsType);
       if (wsType == WSType::br) {
-        if (mHTMLEditor->IsVisBreak(wsObj.mEndReasonNode)) {
-          break;
-        }
-        if (!firstBRParent) {
-          firstBRParent = selEndNode;
-          firstBROffset = selEndOffset;
+        if (mHTMLEditor->IsVisBreak(wsObj.mEndReasonNode))
+        {
+          stillLooking = false;
         }
-        selEndNode = wsObj.mEndReasonNode->GetParentNode();
-        selEndOffset = selEndNode
-          ? selEndNode->IndexOf(wsObj.mEndReasonNode) + 1 : 0;
+        else
+        {
+          if (!firstBRParent)
+          {
+            firstBRParent = selEndNode;
+            firstBROffset = selEndOffset;
+          }
+          selEndNode = nsEditor::GetNodeLocation(GetAsDOMNode(wsObj.mEndReasonNode), &selEndOffset);
+          ++selEndOffset;
+        }
       } else if (wsType == WSType::thisBlock) {
-        // We want to keep looking up.  But stop if we are crossing table
-        // element boundaries, or if we hit the root.
+        // we want to keep looking up.  But stop if we are crossing table element
+        // boundaries, or if we hit the root.
         if (nsHTMLEditUtils::IsTableElement(wsObj.mEndReasonNode) ||
-            selCommon == wsObj.mEndReasonNode ||
-            root == wsObj.mEndReasonNode) {
-          break;
+            selCommon == GetAsDOMNode(wsObj.mEndReasonNode) ||
+            rootElement == GetAsDOMNode(wsObj.mEndReasonNode)) {
+          stillLooking = false;
+        }
+        else
+        {
+          selEndNode = nsEditor::GetNodeLocation(GetAsDOMNode(wsObj.mEndReasonNode), &selEndOffset);
+          ++selEndOffset;
         }
-        selEndNode = wsObj.mEndReasonNode->GetParentNode();
-        selEndOffset = 1 + selEndNode->IndexOf(wsObj.mEndReasonNode);
-      } else {
-        break;
-      }
-    }
-  }
-  // Now set the selection to the new range
-  aSelection.Collapse(selStartNode, selStartOffset);
-
-  // Expand selection endpoint only if we didn't pass a br, or if we really
-  // needed to pass that br (i.e., its block is now totally selected)
-  nsresult res;
+       }
+      else
+      {
+        stillLooking = false;
+      }
+    }
+  }
+  // now set the selection to the new range
+  aSelection->Collapse(selStartNode, selStartOffset);
+
+  // expand selection endpoint only if we didnt pass a br,
+  // or if we really needed to pass that br (ie, its block is now
+  // totally selected)
   bool doEndExpansion = true;
-  if (firstBRParent) {
-    // Find block node containing br
-    nsCOMPtr<Element> brBlock = nsHTMLEditor::GetBlock(*firstBRParent);
-    bool nodeBefore = false, nodeAfter = false;
-
-    // Create a range that represents expanded selection
-    RefPtr<nsRange> range = new nsRange(selStartNode);
+  if (firstBRParent)
+  {
+    // find block node containing br
+    nsCOMPtr<nsIDOMNode> brBlock = firstBRParent;
+    if (!IsBlockNode(brBlock))
+      brBlock = nsHTMLEditor::GetBlockNodeParent(brBlock);
+    bool nodeBefore=false, nodeAfter=false;
+
+    // create a range that represents expanded selection
+    nsCOMPtr<nsINode> node = do_QueryInterface(selStartNode);
+    NS_ENSURE_STATE(node);
+    RefPtr<nsRange> range = new nsRange(node);
     res = range->SetStart(selStartNode, selStartOffset);
     NS_ENSURE_SUCCESS(res, res);
     res = range->SetEnd(selEndNode, selEndOffset);
     NS_ENSURE_SUCCESS(res, res);
 
-    // Check if block is entirely inside range
-    if (brBlock) {
-      nsRange::CompareNodeToRange(brBlock, range, &nodeBefore, &nodeAfter);
-    }
-
-    // If block isn't contained, forgo grabbing the br in expanded selection
-    if (nodeBefore || nodeAfter) {
+    // check if block is entirely inside range
+    nsCOMPtr<nsIContent> brContentBlock = do_QueryInterface(brBlock);
+    if (brContentBlock) {
+      res = nsRange::CompareNodeToRange(brContentBlock, range, &nodeBefore,
+                                        &nodeAfter);
+    }
+
+    // if block isn't contained, forgo grabbing the br in the expanded selection
+    if (nodeBefore || nodeAfter)
       doEndExpansion = false;
-    }
-  }
-  if (doEndExpansion) {
-    res = aSelection.Extend(selEndNode, selEndOffset);
-    NS_ENSURE_SUCCESS(res, res);
-  } else {
-    // Only expand to just before br
-    res = aSelection.Extend(firstBRParent, firstBROffset);
-    NS_ENSURE_SUCCESS(res, res);
-  }
-
-  return NS_OK;
+  }
+  if (doEndExpansion)
+  {
+    res = aSelection->Extend(selEndNode, selEndOffset);
+  }
+  else
+  {
+    // only expand to just before br
+    res = aSelection->Extend(firstBRParent, firstBROffset);
+  }
+
+  return res;
 }
 
 
 ///////////////////////////////////////////////////////////////////////////
 // NormalizeSelection:  tweak non-collapsed selections to be more "natural".
 //    Idea here is to adjust selection endpoint so that they do not cross
 //    breaks or block boundaries unless something editable beyond that boundary
 //    is also selected.  This adjustment makes it much easier for the various
@@ -6175,16 +6385,24 @@ nsHTMLEditRules::MakeTransitionList(nsTA
  ********************************************************/
 
 ///////////////////////////////////////////////////////////////////////////
 // IsInListItem: if aNode is the descendant of a listitem, return that li.
 //               But table element boundaries are stoppers on the search.
 //               Also stops on the active editor host (contenteditable).
 //               Also test if aNode is an li itself.
 //
+already_AddRefed<nsIDOMNode>
+nsHTMLEditRules::IsInListItem(nsIDOMNode* aNode)
+{
+  nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
+  nsCOMPtr<nsIDOMNode> retval = do_QueryInterface(IsInListItem(node));
+  return retval.forget();
+}
+
 Element*
 nsHTMLEditRules::IsInListItem(nsINode* aNode)
 {
   NS_ENSURE_TRUE(aNode, nullptr);
   if (nsHTMLEditUtils::IsListItem(aNode)) {
     return aNode->AsElement();
   }
 
@@ -6195,95 +6413,111 @@ nsHTMLEditRules::IsInListItem(nsINode* a
       return parent;
     }
     parent = parent->GetParentElement();
   }
   return nullptr;
 }
 
 
-/**
- * ReturnInHeader: do the right thing for returns pressed in headers
- */
+///////////////////////////////////////////////////////////////////////////
+// ReturnInHeader: do the right thing for returns pressed in headers
+//
 nsresult
-nsHTMLEditRules::ReturnInHeader(Selection& aSelection,
-                                Element& aHeader,
-                                nsINode& aNode,
+nsHTMLEditRules::ReturnInHeader(Selection* aSelection,
+                                nsIDOMNode *aHeader,
+                                nsIDOMNode *aNode,
                                 int32_t aOffset)
 {
+  nsCOMPtr<Element> header = do_QueryInterface(aHeader);
+  nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
+  NS_ENSURE_TRUE(aSelection && header && node, NS_ERROR_NULL_POINTER);
+
+  // remeber where the header is
+  int32_t offset;
+  nsCOMPtr<nsIDOMNode> headerParent = nsEditor::GetNodeLocation(aHeader, &offset);
+
+  // get ws code to adjust any ws
   NS_ENSURE_STATE(mHTMLEditor);
-  nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
-
-  // Remember where the header is
-  nsCOMPtr<nsINode> headerParent = aHeader.GetParentNode();
-  int32_t offset = headerParent ? headerParent->IndexOf(&aHeader) : -1;
-
-  // Get ws code to adjust any ws
-  nsCOMPtr<nsINode> node = &aNode;
   nsresult res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor,
                                                            address_of(node),
                                                            &aOffset);
   NS_ENSURE_SUCCESS(res, res);
 
-  // Split the header
+  // split the header
   NS_ENSURE_STATE(node->IsContent());
-  mHTMLEditor->SplitNodeDeep(aHeader, *node->AsContent(), aOffset);
-
-  // If the left-hand heading is empty, put a mozbr in it
-  nsCOMPtr<nsIContent> prevItem = mHTMLEditor->GetPriorHTMLSibling(&aHeader);
-  if (prevItem && nsHTMLEditUtils::IsHeader(*prevItem)) {
-    bool isEmptyNode;
-    res = mHTMLEditor->IsEmptyNode(prevItem, &isEmptyNode);
+  NS_ENSURE_STATE(mHTMLEditor);
+  mHTMLEditor->SplitNodeDeep(*header, *node->AsContent(), aOffset);
+
+  // if the leftand heading is empty, put a mozbr in it
+  nsCOMPtr<nsIDOMNode> prevItem;
+  NS_ENSURE_STATE(mHTMLEditor);
+  mHTMLEditor->GetPriorHTMLSibling(aHeader, address_of(prevItem));
+  if (prevItem && nsHTMLEditUtils::IsHeader(prevItem))
+  {
+    bool bIsEmptyNode;
+    NS_ENSURE_STATE(mHTMLEditor);
+    res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
     NS_ENSURE_SUCCESS(res, res);
-    if (isEmptyNode) {
-      res = CreateMozBR(prevItem->AsDOMNode(), 0);
+    if (bIsEmptyNode) {
+      res = CreateMozBR(prevItem, 0);
       NS_ENSURE_SUCCESS(res, res);
     }
   }
 
-  // If the new (righthand) header node is empty, delete it
+  // if the new (righthand) header node is empty, delete it
   bool isEmpty;
-  res = IsEmptyBlock(aHeader, &isEmpty, MozBRCounts::no);
+  res = IsEmptyBlock(aHeader, &isEmpty, true);
   NS_ENSURE_SUCCESS(res, res);
-  if (isEmpty) {
-    res = mHTMLEditor->DeleteNode(&aHeader);
+  if (isEmpty)
+  {
+    NS_ENSURE_STATE(mHTMLEditor);
+    res = mHTMLEditor->DeleteNode(aHeader);
+    NS_ENSURE_SUCCESS(res, res);
+    // layout tells the caret to blink in a weird place
+    // if we don't place a break after the header.
+    nsCOMPtr<nsIDOMNode> sibling;
+    NS_ENSURE_STATE(mHTMLEditor);
+    res = mHTMLEditor->GetNextHTMLSibling(headerParent, offset+1, address_of(sibling));
     NS_ENSURE_SUCCESS(res, res);
-    // Layout tells the caret to blink in a weird place if we don't place a
-    // break after the header.
-    nsCOMPtr<nsIContent> sibling =
-      mHTMLEditor->GetNextHTMLSibling(headerParent, offset + 1);
-    if (!sibling || !sibling->IsHTMLElement(nsGkAtoms::br)) {
+    if (!sibling || !nsTextEditUtils::IsBreak(sibling))
+    {
       ClearCachedStyles();
+      NS_ENSURE_STATE(mHTMLEditor);
       mHTMLEditor->mTypeInState->ClearAllProps();
 
-      // Create a paragraph
-      nsCOMPtr<Element> pNode =
-        mHTMLEditor->CreateNode(nsGkAtoms::p, headerParent, offset + 1);
-      NS_ENSURE_STATE(pNode);
-
-      // Append a <br> to it
-      nsCOMPtr<Element> brNode = mHTMLEditor->CreateBR(pNode, 0);
-      NS_ENSURE_STATE(brNode);
-
-      // Set selection to before the break
-      res = aSelection.Collapse(pNode, 0);
+      // create a paragraph
+      NS_NAMED_LITERAL_STRING(pType, "p");
+      nsCOMPtr<nsIDOMNode> pNode;
+      NS_ENSURE_STATE(mHTMLEditor);
+      res = mHTMLEditor->CreateNode(pType, headerParent, offset+1, getter_AddRefs(pNode));
+      NS_ENSURE_SUCCESS(res, res);
+
+      // append a <br> to it
+      nsCOMPtr<nsIDOMNode> brNode;
+      NS_ENSURE_STATE(mHTMLEditor);
+      res = mHTMLEditor->CreateBR(pNode, 0, address_of(brNode));
       NS_ENSURE_SUCCESS(res, res);
-    } else {
-      headerParent = sibling->GetParentNode();
-      offset = headerParent ? headerParent->IndexOf(sibling) : -1;
-      // Put selection after break
-      res = aSelection.Collapse(headerParent, offset + 1);
-      NS_ENSURE_SUCCESS(res, res);
-    }
-  } else {
-    // Put selection at front of righthand heading
-    res = aSelection.Collapse(&aHeader, 0);
-    NS_ENSURE_SUCCESS(res, res);
-  }
-  return NS_OK;
+
+      // set selection to before the break
+      res = aSelection->Collapse(pNode, 0);
+    }
+    else
+    {
+      headerParent = nsEditor::GetNodeLocation(sibling, &offset);
+      // put selection after break
+      res = aSelection->Collapse(headerParent,offset+1);
+    }
+  }
+  else
+  {
+    // put selection at front of righthand heading
+    res = aSelection->Collapse(aHeader,0);
+  }
+  return res;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // ReturnInParagraph: do the right thing for returns pressed in paragraphs
 //
 nsresult
 nsHTMLEditRules::ReturnInParagraph(Selection* aSelection,
                                    nsIDOMNode* aPara,
@@ -6465,170 +6699,188 @@ nsHTMLEditRules::SplitParagraph(nsIDOMNo
     int32_t offset;
     nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(child, &offset);
     aSelection->Collapse(parent,offset);
   }
   return res;
 }
 
 
-/**
- * ReturnInListItem: do the right thing for returns pressed in list items
- */
+///////////////////////////////////////////////////////////////////////////
+// ReturnInListItem: do the right thing for returns pressed in list items
+//
 nsresult
-nsHTMLEditRules::ReturnInListItem(Selection& aSelection,
-                                  Element& aListItem,
-                                  nsINode& aNode,
+nsHTMLEditRules::ReturnInListItem(Selection* aSelection,
+                                  nsIDOMNode *aListItem,
+                                  nsIDOMNode *aNode,
                                   int32_t aOffset)
 {
-  MOZ_ASSERT(nsHTMLEditUtils::IsListItem(&aListItem));
-
+  nsCOMPtr<Element> listItem = do_QueryInterface(aListItem);
+  NS_ENSURE_TRUE(aSelection && listItem && aNode, NS_ERROR_NULL_POINTER);
+  nsresult res = NS_OK;
+
+  nsCOMPtr<nsIDOMNode> listitem;
+
+  // sanity check
+  NS_PRECONDITION(true == nsHTMLEditUtils::IsListItem(aListItem),
+                  "expected a list item and didn't get one");
+
+  // get the listitem parent and the active editing host.
   NS_ENSURE_STATE(mHTMLEditor);
-  nsCOMPtr<nsIEditor> kungFuDeathGrip(mHTMLEditor);
-
-  // Get the item parent and the active editing host.
-  nsCOMPtr<Element> root = mHTMLEditor->GetActiveEditingHost();
-
-  nsCOMPtr<Element> list = aListItem.GetParentElement();
-  int32_t itemOffset = list ? list->IndexOf(&aListItem) : -1;
-
-  // If we are in an empty item, then we want to pop up out of the list, but
-  // only if prefs say it's okay and if the parent isn't the active editing
-  // host.
+  nsIContent* rootContent = mHTMLEditor->GetActiveEditingHost();
+  nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(rootContent);
+  nsCOMPtr<nsINode> list = listItem->GetParentNode();
+  int32_t itemOffset = list ? list->IndexOf(listItem) : -1;
+
+  // if we are in an empty listitem, then we want to pop up out of the list
+  // but only if prefs says it's ok and if the parent isn't the active editing host.
   bool isEmpty;
-  nsresult res = IsEmptyBlock(aListItem, &isEmpty, MozBRCounts::no);
+  res = IsEmptyBlock(aListItem, &isEmpty, true, false);
   NS_ENSURE_SUCCESS(res, res);
-  if (isEmpty && root != list && mReturnInEmptyLIKillsList) {
-    // Get the list offset now -- before we might eventually split the list
+  if (isEmpty && (rootNode != GetAsDOMNode(list)) &&
+      mReturnInEmptyLIKillsList) {
+    // get the list offset now -- before we might eventually split the list
     nsCOMPtr<nsINode> listParent = list->GetParentNode();
     int32_t offset = listParent ? listParent->IndexOf(list) : -1;
 
-    // Are we the last list item in the list?
-    bool isLast;
-    res = mHTMLEditor->IsLastEditableChild(aListItem.AsDOMNode(), &isLast);
+    // are we the last list item in the list?
+    bool bIsLast;
+    NS_ENSURE_STATE(mHTMLEditor);
+    res = mHTMLEditor->IsLastEditableChild(aListItem, &bIsLast);
     NS_ENSURE_SUCCESS(res, res);
-    if (!isLast) {
-      // We need to split the list!
-      ErrorResult rv;
-      mHTMLEditor->SplitNode(*list, itemOffset, rv);
-      NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
-    }
-
-    // Are we in a sublist?
+    if (!bIsLast)
+    {
+      // we need to split the list!
+      nsCOMPtr<nsIDOMNode> tempNode;
+      NS_ENSURE_STATE(mHTMLEditor);
+      res = mHTMLEditor->SplitNode(GetAsDOMNode(list), itemOffset,
+                                   getter_AddRefs(tempNode));
+      NS_ENSURE_SUCCESS(res, res);
+    }
+
+    // are we in a sublist?
     if (nsHTMLEditUtils::IsList(listParent)) {
-      // If so, move item out of this list and into the grandparent list
-      res = mHTMLEditor->MoveNode(&aListItem, listParent, offset + 1);
+      // if so, move this list item out of this list and into the grandparent list
+      NS_ENSURE_STATE(mHTMLEditor);
+      res = mHTMLEditor->MoveNode(listItem, listParent, offset + 1);
+      NS_ENSURE_SUCCESS(res, res);
+      res = aSelection->Collapse(aListItem,0);
+    }
+    else
+    {
+      // otherwise kill this listitem
+      NS_ENSURE_STATE(mHTMLEditor);
+      res = mHTMLEditor->DeleteNode(aListItem);
+      NS_ENSURE_SUCCESS(res, res);
+
+      // time to insert a paragraph
+      NS_NAMED_LITERAL_STRING(pType, "p");
+      nsCOMPtr<nsIDOMNode> pNode;
+      NS_ENSURE_STATE(mHTMLEditor);
+      res = mHTMLEditor->CreateNode(pType, GetAsDOMNode(listParent),
+                                    offset + 1, getter_AddRefs(pNode));
+      NS_ENSURE_SUCCESS(res, res);
+
+      // append a <br> to it
+      nsCOMPtr<nsIDOMNode> brNode;
+      NS_ENSURE_STATE(mHTMLEditor);
+      res = mHTMLEditor->CreateBR(pNode, 0, address_of(brNode));
       NS_ENSURE_SUCCESS(res, res);
-      res = aSelection.Collapse(&aListItem, 0);
+
+      // set selection to before the break
+      res = aSelection->Collapse(pNode, 0);
+    }
+    return res;
+  }
+
+  // else we want a new list item at the same list level.
+  // get ws code to adjust any ws
+  nsCOMPtr<nsINode> selNode(do_QueryInterface(aNode));
+  NS_ENSURE_STATE(mHTMLEditor);
+  res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor, address_of(selNode), &aOffset);
+  NS_ENSURE_SUCCESS(res, res);
+  // now split list item
+  NS_ENSURE_STATE(mHTMLEditor);
+  NS_ENSURE_STATE(selNode->IsContent());
+  mHTMLEditor->SplitNodeDeep(*listItem, *selNode->AsContent(), aOffset);
+  // hack: until I can change the damaged doc range code back to being
+  // extra inclusive, I have to manually detect certain list items that
+  // may be left empty.
+  nsCOMPtr<nsIDOMNode> prevItem;
+  NS_ENSURE_STATE(mHTMLEditor);
+  mHTMLEditor->GetPriorHTMLSibling(aListItem, address_of(prevItem));
+
+  if (prevItem && nsHTMLEditUtils::IsListItem(prevItem))
+  {
+    bool bIsEmptyNode;
+    NS_ENSURE_STATE(mHTMLEditor);
+    res = mHTMLEditor->IsEmptyNode(prevItem, &bIsEmptyNode);
+    NS_ENSURE_SUCCESS(res, res);
+    if (bIsEmptyNode) {
+      res = CreateMozBR(prevItem, 0);
       NS_ENSURE_SUCCESS(res, res);
     } else {
-      // Otherwise kill this item
-      res = mHTMLEditor->DeleteNode(&aListItem);
-      NS_ENSURE_SUCCESS(res, res);
-
-      // Time to insert a paragraph
-      nsCOMPtr<Element> pNode =
-        mHTMLEditor->CreateNode(nsGkAtoms::p, listParent, offset + 1);
-      NS_ENSURE_STATE(pNode);
-
-      // Append a <br> to it
-      nsCOMPtr<Element> brNode = mHTMLEditor->CreateBR(pNode, 0);
-      NS_ENSURE_STATE(brNode);
-
-      // Set selection to before the break
-      res = aSelection.Collapse(pNode, 0);
+      NS_ENSURE_STATE(mHTMLEditor);
+      res = mHTMLEditor->IsEmptyNode(aListItem, &bIsEmptyNode, true);
       NS_ENSURE_SUCCESS(res, res);
-    }
-    return NS_OK;
-  }
-
-  // Else we want a new list item at the same list level.  Get ws code to
-  // adjust any ws.
-  nsCOMPtr<nsINode> selNode = &aNode;
-  res = nsWSRunObject::PrepareToSplitAcrossBlocks(mHTMLEditor,
-                                                  address_of(selNode),
-                                                  &aOffset);
-  NS_ENSURE_SUCCESS(res, res);
-  // Now split list item
-  NS_ENSURE_STATE(selNode->IsContent());
-  mHTMLEditor->SplitNodeDeep(aListItem, *selNode->AsContent(), aOffset);
-
-  // Hack: until I can change the damaged doc range code back to being
-  // extra-inclusive, I have to manually detect certain list items that may be
-  // left empty.
-  nsCOMPtr<nsIContent> prevItem = mHTMLEditor->GetPriorHTMLSibling(&aListItem);
-  if (prevItem && nsHTMLEditUtils::IsListItem(prevItem)) {
-    bool isEmptyNode;
-    res = mHTMLEditor->IsEmptyNode(prevItem, &isEmptyNode);
-    NS_ENSURE_SUCCESS(res, res);
-    if (isEmptyNode) {
-      res = CreateMozBR(prevItem->AsDOMNode(), 0);
-      NS_ENSURE_SUCCESS(res, res);
-    } else {
-      res = mHTMLEditor->IsEmptyNode(&aListItem, &isEmptyNode, true);
-      NS_ENSURE_SUCCESS(res, res);
-      if (isEmptyNode) {
-        nsCOMPtr<nsIAtom> nodeAtom = aListItem.NodeInfo()->NameAtom();
+      if (bIsEmptyNode)
+      {
+        nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aListItem);
         if (nodeAtom == nsGkAtoms::dd || nodeAtom == nsGkAtoms::dt) {
-          nsCOMPtr<nsINode> list = aListItem.GetParentNode();
-          int32_t itemOffset = list ? list->IndexOf(&aListItem) : -1;
-
-          nsIAtom* listAtom = nodeAtom == nsGkAtoms::dt ? nsGkAtoms::dd
-                                                        : nsGkAtoms::dt;
-          nsCOMPtr<Element> newListItem =
-            mHTMLEditor->CreateNode(listAtom, list, itemOffset + 1);
-          NS_ENSURE_STATE(newListItem);
-          res = mEditor->DeleteNode(&aListItem);
+          int32_t itemOffset;
+          nsCOMPtr<nsIDOMNode> list = nsEditor::GetNodeLocation(aListItem, &itemOffset);
+
+          nsAutoString listTag(nodeAtom == nsGkAtoms::dt
+            ? NS_LITERAL_STRING("dd") : NS_LITERAL_STRING("dt"));
+          nsCOMPtr<nsIDOMNode> newListItem;
+          NS_ENSURE_STATE(mHTMLEditor);
+          res = mHTMLEditor->CreateNode(listTag, list, itemOffset+1, getter_AddRefs(newListItem));
           NS_ENSURE_SUCCESS(res, res);
-          res = aSelection.Collapse(newListItem, 0);
+          res = mEditor->DeleteNode(aListItem);
           NS_ENSURE_SUCCESS(res, res);
-
-          return NS_OK;
+          return aSelection->Collapse(newListItem, 0);
         }
 
-        nsCOMPtr<Element> brNode;
-        res = mHTMLEditor->CopyLastEditableChildStyles(GetAsDOMNode(prevItem),
-          GetAsDOMNode(&aListItem), getter_AddRefs(brNode));
+        nsCOMPtr<nsIDOMNode> brNode;
+        NS_ENSURE_STATE(mHTMLEditor);
+        res = mHTMLEditor->CopyLastEditableChildStyles(prevItem, aListItem, getter_AddRefs(brNode));
         NS_ENSURE_SUCCESS(res, res);
-        if (brNode) {
-          nsCOMPtr<nsINode> brParent = brNode->GetParentNode();
-          int32_t offset = brParent ? brParent->IndexOf(brNode) : -1;
-          res = aSelection.Collapse(brParent, offset);
-          NS_ENSURE_SUCCESS(res, res);
-
-          return NS_OK;
+        if (brNode)
+        {
+          int32_t offset;
+          nsCOMPtr<nsIDOMNode> brParent = nsEditor::GetNodeLocation(brNode, &offset);
+          return aSelection->Collapse(brParent, offset);
         }
-      } else {
-        nsWSRunObject wsObj(mHTMLEditor, &aListItem, 0);
-        nsCOMPtr<nsINode> visNode;
+      }
+      else
+      {
+        NS_ENSURE_STATE(mHTMLEditor);
+        nsWSRunObject wsObj(mHTMLEditor, aListItem, 0);
+        nsCOMPtr<nsINode> visNode_;
         int32_t visOffset = 0;
         WSType wsType;
-        wsObj.NextVisibleNode(&aListItem, 0, address_of(visNode),
+        nsCOMPtr<nsINode> aListItem_(do_QueryInterface(aListItem));
+        wsObj.NextVisibleNode(aListItem_, 0, address_of(visNode_),
                               &visOffset, &wsType);
+        nsCOMPtr<nsIDOMNode> visNode(GetAsDOMNode(visNode_));
         if (wsType == WSType::special || wsType == WSType::br ||
-            visNode->IsHTMLElement(nsGkAtoms::hr)) {
-          nsCOMPtr<nsINode> parent = visNode->GetParentNode();
-          int32_t offset = parent ? parent->IndexOf(visNode) : -1;
-          res = aSelection.Collapse(parent, offset);
-          NS_ENSURE_SUCCESS(res, res);
-
-          return NS_OK;
-        } else {
-          res = aSelection.Collapse(visNode, visOffset);
-          NS_ENSURE_SUCCESS(res, res);
-
-          return NS_OK;
+            nsHTMLEditUtils::IsHR(visNode)) {
+          int32_t offset;
+          nsCOMPtr<nsIDOMNode> parent = nsEditor::GetNodeLocation(visNode, &offset);
+          return aSelection->Collapse(parent, offset);
         }
-      }
-    }
-  }
-  res = aSelection.Collapse(&aListItem, 0);
-  NS_ENSURE_SUCCESS(res, res);
-
-  return NS_OK;
+        else
+        {
+          return aSelection->Collapse(visNode, visOffset);
+        }
+      }
+    }
+  }
+  res = aSelection->Collapse(aListItem,0);
+  return res;
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // MakeBlockquote: Put the list of nodes into one or more blockquotes.
 //
 nsresult
 nsHTMLEditRules::MakeBlockquote(nsTArray<OwningNonNull<nsINode>>& aNodeArray)
@@ -8483,21 +8735,22 @@ nsHTMLEditRules::RelativeChangeIndentati
   }
 
   nsCOMPtr<Element> element = do_QueryInterface(aNode);
   if (!element) {
     return NS_OK;
   }
 
   NS_ENSURE_STATE(mHTMLEditor);
-  nsIAtom& marginProperty =
-    MarginPropertyAtomForIndent(*mHTMLEditor->mHTMLCSSUtils, *element);
+  nsIAtom* marginProperty =
+    MarginPropertyAtomForIndent(mHTMLEditor->mHTMLCSSUtils,
+                                GetAsDOMNode(element));
   nsAutoString value;
   NS_ENSURE_STATE(mHTMLEditor);
-  mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(*element, marginProperty,
+  mHTMLEditor->mHTMLCSSUtils->GetSpecifiedProperty(*element, *marginProperty,
                                                    value);
   float f;
   nsCOMPtr<nsIAtom> unit;
   NS_ENSURE_STATE(mHTMLEditor);
   mHTMLEditor->mHTMLCSSUtils->ParseLength(value, &f, getter_AddRefs(unit));
   if (0 == f) {
     nsAutoString defaultLengthUnit;
     NS_ENSURE_STATE(mHTMLEditor);
@@ -8524,23 +8777,23 @@ nsHTMLEditRules::RelativeChangeIndentati
             f += NS_EDITOR_INDENT_INCREMENT_PERCENT * aRelativeChange;
   }
 
   if (0 < f) {
     nsAutoString newValue;
     newValue.AppendFloat(f);
     newValue.Append(nsDependentAtomString(unit));
     NS_ENSURE_STATE(mHTMLEditor);
-    mHTMLEditor->mHTMLCSSUtils->SetCSSProperty(*element, marginProperty,
+    mHTMLEditor->mHTMLCSSUtils->SetCSSProperty(*element, *marginProperty,
                                                newValue);
     return NS_OK;
   }
 
   NS_ENSURE_STATE(mHTMLEditor);
-  mHTMLEditor->mHTMLCSSUtils->RemoveCSSProperty(*element, marginProperty,
+  mHTMLEditor->mHTMLCSSUtils->RemoveCSSProperty(*element, *marginProperty,
                                                 value);
 
   // remove unnecessary DIV blocks:
   // we could skip this section but that would cause a FAIL in
   // editor/libeditor/tests/browserscope/richtext.html, which expects
   // to unapply a CSS "indent" (<div style="margin-left: 40px;">) by
   // removing the DIV container instead of just removing the CSS property.
   nsCOMPtr<dom::Element> node = do_QueryInterface(aNode);
@@ -8561,27 +8814,27 @@ nsHTMLEditRules::RelativeChangeIndentati
 // Support for Absolute Positioning
 //
 
 nsresult
 nsHTMLEditRules::WillAbsolutePosition(Selection* aSelection,
                                       bool* aCancel, bool* aHandled)
 {
   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
-  WillInsert(*aSelection, aCancel);
+  nsresult res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
 
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = false;
   *aHandled = true;
 
   nsCOMPtr<nsIDOMElement> focusElement;
   NS_ENSURE_STATE(mHTMLEditor);
-  nsresult res =
-    mHTMLEditor->GetSelectionContainer(getter_AddRefs(focusElement));
+  res = mHTMLEditor->GetSelectionContainer(getter_AddRefs(focusElement));
   if (focusElement) {
     nsCOMPtr<nsIDOMNode> node = do_QueryInterface(focusElement);
     if (nsHTMLEditUtils::IsImage(node)) {
       mNewBlock = node;
       return NS_OK;
     }
   }
 
@@ -8790,27 +9043,27 @@ nsHTMLEditRules::DidAbsolutePosition()
   nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(mNewBlock);
   return absPosHTMLEditor->AbsolutelyPositionElement(elt, true);
 }
 
 nsresult
 nsHTMLEditRules::WillRemoveAbsolutePosition(Selection* aSelection,
                                             bool* aCancel, bool* aHandled) {
   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
-  WillInsert(*aSelection, aCancel);
+  nsresult res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
 
   // initialize out param
   // we want to ignore aCancel from WillInsert()
   *aCancel = false;
   *aHandled = true;
 
   nsCOMPtr<nsIDOMElement>  elt;
   NS_ENSURE_STATE(mHTMLEditor);
-  nsresult res =
-    mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
+  res = mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
   NS_ENSURE_SUCCESS(res, res);
 
   NS_ENSURE_STATE(mHTMLEditor);
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
 
   NS_ENSURE_STATE(mHTMLEditor);
   nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
   return absPosHTMLEditor->AbsolutelyPositionElement(elt, false);
@@ -8818,27 +9071,27 @@ nsHTMLEditRules::WillRemoveAbsolutePosit
 
 nsresult
 nsHTMLEditRules::WillRelativeChangeZIndex(Selection* aSelection,
                                           int32_t aChange,
                                           bool *aCancel,
                                           bool * aHandled)
 {
   if (!aSelection || !aCancel || !aHandled) { return NS_ERROR_NULL_POINTER; }
-  WillInsert(*aSelection, aCancel);
+  nsresult res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
 
   // initialize out param
   // we want to ignore aCancel from WillInsert()
   *aCancel = false;
   *aHandled = true;
 
   nsCOMPtr<nsIDOMElement>  elt;
   NS_ENSURE_STATE(mHTMLEditor);
-  nsresult res =
-    mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
+  res = mHTMLEditor->GetAbsolutelyPositionedSelectionContainer(getter_AddRefs(elt));
   NS_ENSURE_SUCCESS(res, res);
 
   NS_ENSURE_STATE(mHTMLEditor);
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
 
   NS_ENSURE_STATE(mHTMLEditor);
   nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
   int32_t zIndex;
--- a/editor/libeditor/nsHTMLEditRules.h
+++ b/editor/libeditor/nsHTMLEditRules.h
@@ -101,16 +101,17 @@ public:
   NS_IMETHOD WillJoinNodes(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent) override;
   NS_IMETHOD DidJoinNodes(nsIDOMNode  *aLeftNode, nsIDOMNode *aRightNode, nsIDOMNode *aParent, nsresult aResult) override;
   NS_IMETHOD WillInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString) override;
   NS_IMETHOD DidInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString, nsresult aResult) override;
   NS_IMETHOD WillDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength) override;
   NS_IMETHOD DidDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength, nsresult aResult) override;
   NS_IMETHOD WillDeleteSelection(nsISelection *aSelection) override;
   NS_IMETHOD DidDeleteSelection(nsISelection *aSelection) override;
+  void DeleteNodeIfCollapsedText(nsINode& aNode);
 
 protected:
   virtual ~nsHTMLEditRules();
 
   enum RulesEndpoint
   {
     kStart,
     kEnd
@@ -120,44 +121,43 @@ protected:
   {
     kBeforeBlock,
     kBlockEnd
   };
 
   void InitFields();
 
   // nsHTMLEditRules implementation methods
-  void WillInsert(mozilla::dom::Selection& aSelection, bool* aCancel);
+  nsresult WillInsert(mozilla::dom::Selection* aSelection, bool* aCancel);
   nsresult WillInsertText(  EditAction aAction,
                             mozilla::dom::Selection* aSelection,
                             bool            *aCancel,
                             bool            *aHandled,
                             const nsAString *inString,
                             nsAString       *outString,
                             int32_t          aMaxLength);
   nsresult WillLoadHTML(mozilla::dom::Selection* aSelection, bool* aCancel);
-  nsresult WillInsertBreak(mozilla::dom::Selection& aSelection,
+  nsresult WillInsertBreak(mozilla::dom::Selection* aSelection,
                            bool* aCancel, bool* aHandled);
-  nsresult StandardBreakImpl(nsINode& aNode, int32_t aOffset,
-                             mozilla::dom::Selection& aSelection);
+  nsresult StandardBreakImpl(nsIDOMNode* aNode, int32_t aOffset,
+                             mozilla::dom::Selection* aSelection);
   nsresult DidInsertBreak(mozilla::dom::Selection* aSelection,
                           nsresult aResult);
   nsresult SplitMailCites(mozilla::dom::Selection* aSelection, bool* aHandled);
   nsresult WillDeleteSelection(mozilla::dom::Selection* aSelection,
                                nsIEditor::EDirection aAction,
                                nsIEditor::EStripWrappers aStripWrappers,
                                bool* aCancel, bool* aHandled);
   nsresult DidDeleteSelection(mozilla::dom::Selection* aSelection,
                               nsIEditor::EDirection aDir,
                               nsresult aResult);
   nsresult InsertBRIfNeeded(mozilla::dom::Selection* aSelection);
   ::DOMPoint GetGoodSelPointForNode(nsINode& aNode,
                                     nsIEditor::EDirection aAction);
-  nsresult JoinBlocks(nsIContent& aLeftNode, nsIContent& aRightNode,
-                      bool* aCanceled);
+  nsresult JoinBlocks(nsIDOMNode *aLeftNode, nsIDOMNode *aRightNode, bool *aCanceled);
   nsresult MoveBlock(nsIDOMNode *aLeft, nsIDOMNode *aRight, int32_t aLeftOffset, int32_t aRightOffset);
   nsresult MoveNodeSmart(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset);
   nsresult MoveContents(nsIDOMNode *aSource, nsIDOMNode *aDest, int32_t *aOffset);
   nsresult DeleteNonTableElements(nsINode* aNode);
   nsresult WillMakeList(mozilla::dom::Selection* aSelection,
                         const nsAString* aListType,
                         bool aEntireList,
                         const nsAString* aBulletType,
@@ -168,18 +168,18 @@ protected:
   nsresult WillIndent(mozilla::dom::Selection* aSelection,
                       bool* aCancel, bool* aHandled);
   nsresult WillCSSIndent(mozilla::dom::Selection* aSelection,
                          bool* aCancel, bool* aHandled);
   nsresult WillHTMLIndent(mozilla::dom::Selection* aSelection,
                           bool* aCancel, bool* aHandled);
   nsresult WillOutdent(mozilla::dom::Selection* aSelection,
                        bool* aCancel, bool* aHandled);
-  nsresult WillAlign(mozilla::dom::Selection& aSelection,
-                     const nsAString& aAlignType,
+  nsresult WillAlign(mozilla::dom::Selection* aSelection,
+                     const nsAString* alignType,
                      bool* aCancel, bool* aHandled);
   nsresult WillAbsolutePosition(mozilla::dom::Selection* aSelection,
                                 bool* aCancel, bool* aHandled);
   nsresult WillRemoveAbsolutePosition(mozilla::dom::Selection* aSelection,
                                       bool* aCancel, bool* aHandled);
   nsresult WillRelativeChangeZIndex(mozilla::dom::Selection* aSelection,
                                     int32_t aChange,
                                     bool* aCancel, bool* aHandled);
@@ -198,31 +198,31 @@ protected:
                                   nsINode* aNode);
   nsresult GetFormatString(nsIDOMNode *aNode, nsAString &outFormat);
   enum class Lists { no, yes };
   enum class Tables { no, yes };
   void GetInnerContent(nsINode& aNode,
                        nsTArray<mozilla::OwningNonNull<nsINode>>& aOutArrayOfNodes,
                        int32_t* aIndex, Lists aLists = Lists::yes,
                        Tables aTables = Tables::yes);
+  already_AddRefed<nsIDOMNode> IsInListItem(nsIDOMNode* aNode);
   mozilla::dom::Element* IsInListItem(nsINode* aNode);
-  nsresult ReturnInHeader(mozilla::dom::Selection& aSelection,
-                          mozilla::dom::Element& aHeader, nsINode& aNode,
+  nsresult ReturnInHeader(mozilla::dom::Selection* aSelection,
+                          nsIDOMNode* aHeader, nsIDOMNode* aTextNode,
                           int32_t aOffset);
   nsresult ReturnInParagraph(mozilla::dom::Selection* aSelection,
                              nsIDOMNode* aHeader, nsIDOMNode* aTextNode,
                              int32_t aOffset, bool* aCancel, bool* aHandled);
   nsresult SplitParagraph(nsIDOMNode *aPara,
                           nsIDOMNode *aBRNode,
                           mozilla::dom::Selection* aSelection,
                           nsCOMPtr<nsIDOMNode> *aSelNode,
                           int32_t *aOffset);
-  nsresult ReturnInListItem(mozilla::dom::Selection& aSelection,
-                            mozilla::dom::Element& aHeader,
-                            nsINode& aNode,
+  nsresult ReturnInListItem(mozilla::dom::Selection* aSelection,
+                            nsIDOMNode* aHeader, nsIDOMNode* aTextNode,
                             int32_t aOffset);
   nsresult AfterEditInner(EditAction action,
                           nsIEditor::EDirection aDirection);
   nsresult RemovePartOfBlock(mozilla::dom::Element& aBlock,
                              nsIContent& aStartChild,
                              nsIContent& aEndChild);
   nsresult SplitBlock(nsIDOMNode *aBlock,
                       nsIDOMNode *aStartChild,
@@ -241,30 +241,30 @@ protected:
                            nsCOMPtr<nsIDOMNode>* outList,
                            nsIAtom* aListType,
                            nsIAtom* aItemType);
   nsresult ConvertListType(mozilla::dom::Element* aList,
                            mozilla::dom::Element** aOutList,
                            nsIAtom* aListType,
                            nsIAtom* aItemType);
 
-  nsresult CreateStyleForInsertText(mozilla::dom::Selection& aSelection,
-                                    nsIDocument& aDoc);
-  enum class MozBRCounts { yes, no };
-  nsresult IsEmptyBlock(mozilla::dom::Element& aNode,
-                        bool* aOutIsEmptyBlock,
-                        MozBRCounts aMozBRCounts = MozBRCounts::yes);
+  nsresult CreateStyleForInsertText(mozilla::dom::Selection* aSelection,
+                                    nsIDOMDocument* aDoc);
+  nsresult IsEmptyBlock(nsIDOMNode *aNode,
+                        bool *outIsEmptyBlock,
+                        bool aMozBRDoesntCount = false,
+                        bool aListItemsNotEmpty = false);
   nsresult CheckForEmptyBlock(nsINode* aStartNode,
                               mozilla::dom::Element* aBodyNode,
                               mozilla::dom::Selection* aSelection,
                               nsIEditor::EDirection aAction,
                               bool* aHandled);
   nsresult CheckForInvisibleBR(nsIDOMNode *aBlock, nsHTMLEditRules::BRLocation aWhere,
                                nsCOMPtr<nsIDOMNode> *outBRNode, int32_t aOffset=0);
-  nsresult ExpandSelectionForDeletion(mozilla::dom::Selection& aSelection);
+  nsresult ExpandSelectionForDeletion(mozilla::dom::Selection* aSelection);
   bool IsFirstNode(nsIDOMNode *aNode);
   bool IsLastNode(nsIDOMNode *aNode);
   nsresult NormalizeSelection(mozilla::dom::Selection* aSelection);
   void GetPromotedPoint(RulesEndpoint aWhere, nsIDOMNode* aNode,
                         int32_t aOffset, EditAction actionID,
                         nsCOMPtr<nsIDOMNode>* outNode, int32_t* outOffset);
   void GetPromotedRanges(mozilla::dom::Selection& aSelection,
                          nsTArray<RefPtr<nsRange>>& outArrayOfRanges,
--- a/editor/libeditor/nsHTMLEditUtils.cpp
+++ b/editor/libeditor/nsHTMLEditUtils.cpp
@@ -254,23 +254,26 @@ bool
 nsHTMLEditUtils::IsTableCell(nsINode* aNode)
 {
   MOZ_ASSERT(aNode);
   return aNode->IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th);
 }
 
 
 ///////////////////////////////////////////////////////////////////////////
-// IsTableCellOrCaption: true if node an html td or th or caption
+// IsTableCell: true if node an html td or th
 //
 bool
-nsHTMLEditUtils::IsTableCellOrCaption(nsINode& aNode)
+nsHTMLEditUtils::IsTableCellOrCaption(nsIDOMNode* aNode)
 {
-  return aNode.IsAnyOfHTMLElements(nsGkAtoms::td, nsGkAtoms::th,
-                                   nsGkAtoms::caption);
+  NS_PRECONDITION(aNode, "null parent passed to nsHTMLEditUtils::IsTableCell");
+  nsCOMPtr<nsIAtom> nodeAtom = nsEditor::GetTag(aNode);
+  return (nodeAtom == nsGkAtoms::td)
+      || (nodeAtom == nsGkAtoms::th)
+      || (nodeAtom == nsGkAtoms::caption);
 }
 
 
 ///////////////////////////////////////////////////////////////////////////
 // IsList: true if node an html list
 //
 bool
 nsHTMLEditUtils::IsList(nsIDOMNode* aNode)
--- a/editor/libeditor/nsHTMLEditUtils.h
+++ b/editor/libeditor/nsHTMLEditUtils.h
@@ -30,17 +30,17 @@ public:
   static bool IsTable(nsINode* aNode);
   static bool IsTableRow(nsIDOMNode *aNode);
   static bool IsTableElement(nsINode* aNode);
   static bool IsTableElement(nsIDOMNode *aNode);
   static bool IsTableElementButNotTable(nsINode* aNode);
   static bool IsTableElementButNotTable(nsIDOMNode *aNode);
   static bool IsTableCell(nsINode* node);
   static bool IsTableCell(nsIDOMNode *aNode);
-  static bool IsTableCellOrCaption(nsINode& aNode);
+  static bool IsTableCellOrCaption(nsIDOMNode *aNode);
   static bool IsList(nsINode* aNode);
   static bool IsList(nsIDOMNode *aNode);
   static bool IsOrderedList(nsIDOMNode *aNode);
   static bool IsUnorderedList(nsIDOMNode *aNode);
   static bool IsBlockquote(nsIDOMNode *aNode);
   static bool IsPre(nsIDOMNode *aNode);
   static bool IsAnchor(nsIDOMNode *aNode);
   static bool IsImage(nsINode* aNode);
--- a/editor/libeditor/nsHTMLEditor.cpp
+++ b/editor/libeditor/nsHTMLEditor.cpp
@@ -1129,26 +1129,26 @@ nsHTMLEditor::TabInTable(bool inIsShift,
     if (cell) {
       selection->Collapse(cell, 0);
     }
   }
 
   return NS_OK;
 }
 
-Element*
+already_AddRefed<Element>
 nsHTMLEditor::CreateBR(nsINode* aNode, int32_t aOffset, EDirection aSelect)
 {
   nsCOMPtr<nsIDOMNode> parent = GetAsDOMNode(aNode);
   int32_t offset = aOffset;
   nsCOMPtr<nsIDOMNode> outBRNode;
   // We assume everything is fine if the br is not null, irrespective of retval
   CreateBRImpl(address_of(parent), &offset, address_of(outBRNode), aSelect);
   nsCOMPtr<Element> ret = do_QueryInterface(outBRNode);
-  return ret;
+  return ret.forget();
 }
 
 NS_IMETHODIMP nsHTMLEditor::CreateBR(nsIDOMNode *aNode, int32_t aOffset, nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect)
 {
   nsCOMPtr<nsIDOMNode> parent = aNode;
   int32_t offset = aOffset;
   return CreateBRImpl(address_of(parent), &offset, outBRNode, aSelect);
 }
@@ -4714,17 +4714,17 @@ nsHTMLEditor::AreNodesSameType(nsIConten
 
   // If CSS is enabled, we are stricter about span nodes.
   return mHTMLCSSUtils->ElementsSameStyle(aNode1->AsDOMNode(),
                                           aNode2->AsDOMNode());
 }
 
 nsresult
 nsHTMLEditor::CopyLastEditableChildStyles(nsIDOMNode * aPreviousBlock, nsIDOMNode * aNewBlock,
-                                          Element** aOutBrNode)
+                                          nsIDOMNode **aOutBrNode)
 {
   nsCOMPtr<nsINode> newBlock = do_QueryInterface(aNewBlock);
   NS_ENSURE_STATE(newBlock || !aNewBlock);
   *aOutBrNode = nullptr;
   nsCOMPtr<nsIDOMNode> child, tmp;
   nsresult res;
   // first, clear out aNewBlock.  Contract is that we want only the styles from previousBlock.
   res = aNewBlock->GetFirstChild(getter_AddRefs(child));
@@ -4768,17 +4768,17 @@ nsHTMLEditor::CopyLastEditableChildStyle
           CreateNode(childElement->NodeInfo()->NameAtom(), newBlock, 0);
         NS_ENSURE_STATE(newStyles);
       }
       CloneAttributes(newStyles, childElement);
     }
     childElement = childElement->GetParentElement();
   }
   if (deepestStyle) {
-    *aOutBrNode = CreateBR(deepestStyle, 0);
+    *aOutBrNode = GetAsDOMNode(CreateBR(deepestStyle, 0).take());
     NS_ENSURE_STATE(*aOutBrNode);
   }
   return NS_OK;
 }
 
 nsresult
 nsHTMLEditor::GetElementOrigin(nsIDOMElement * aElement, int32_t & aX, int32_t & aY)
 {
--- a/editor/libeditor/nsHTMLEditor.h
+++ b/editor/libeditor/nsHTMLEditor.h
@@ -138,17 +138,17 @@ public:
   NS_DECL_NSIHTMLABSPOSEDITOR
 
   /* ------------ nsIHTMLInlineTableEditor methods -------------- */
   /* ------- Implemented in nsHTMLInlineTableEditor.cpp --------- */
   NS_DECL_NSIHTMLINLINETABLEEDITOR
 
   /* ------------ nsIHTMLEditor methods -------------- */
   nsresult CopyLastEditableChildStyles(nsIDOMNode *aPreviousBlock, nsIDOMNode *aNewBlock,
-                                       mozilla::dom::Element** aOutBrNode);
+                                         nsIDOMNode **aOutBrNode);
 
   nsresult LoadHTML(const nsAString &aInputString);
 
   nsresult GetCSSBackgroundColorState(bool *aMixed, nsAString &aOutColor,
                                       bool aBlockLevel);
   NS_IMETHOD GetHTMLBackgroundColorState(bool *aMixed, nsAString &outColor);
 
   /* ------------ nsIEditorStyleSheets methods -------------- */
@@ -421,18 +421,18 @@ protected:
   //            Otherwise, returns null.
   already_AddRefed<nsINode> GetFocusedNode();
 
   // Return TRUE if aElement is a table-related elemet and caret was set
   bool SetCaretInTableCell(nsIDOMElement* aElement);
 
   // key event helpers
   NS_IMETHOD TabInTable(bool inIsShift, bool *outHandled);
-  mozilla::dom::Element* CreateBR(nsINode* aNode, int32_t aOffset,
-                                  EDirection aSelect = eNone);
+  already_AddRefed<mozilla::dom::Element> CreateBR(nsINode* aNode,
+      int32_t aOffset, EDirection aSelect = eNone);
   NS_IMETHOD CreateBR(nsIDOMNode *aNode, int32_t aOffset,
                       nsCOMPtr<nsIDOMNode> *outBRNode, nsIEditor::EDirection aSelect = nsIEditor::eNone) override;
 
 // Table Editing (implemented in nsTableEditor.cpp)
 
   // Table utilities
 
   // Insert a new cell after or before supplied aCell.
@@ -635,56 +635,63 @@ protected:
   nsresult MakeDefinitionItem(const nsAString & aItemType);
   nsresult InsertBasicBlock(const nsAString & aBlockType);
 
   /* increase/decrease the font size of selection */
   enum class FontSize { incr, decr };
   nsresult RelativeFontChange(FontSize aDir);
 
   /* helper routines for font size changing */
-  nsresult RelativeFontChangeOnTextNode(FontSize aDir,
-                                        mozilla::dom::Text& aTextNode,
-                                        int32_t aStartOffset,
-                                        int32_t aEndOffset);
+  nsresult RelativeFontChangeOnTextNode( int32_t aSizeChange,
+                                         nsIDOMCharacterData *aTextNode,
+                                         int32_t aStartOffset,
+                                         int32_t aEndOffset);
   nsresult RelativeFontChangeOnNode(int32_t aSizeChange, nsIContent* aNode);
   nsresult RelativeFontChangeHelper(int32_t aSizeChange, nsINode* aNode);
 
   /* helper routines for inline style */
   nsresult SetInlinePropertyOnTextNode(mozilla::dom::Text& aData,
                                        int32_t aStartOffset,
                                        int32_t aEndOffset,
                                        nsIAtom& aProperty,
                                        const nsAString* aAttribute,
                                        const nsAString& aValue);
   nsresult SetInlinePropertyOnNode(nsIContent& aNode,
                                    nsIAtom& aProperty,
                                    const nsAString* aAttribute,
                                    const nsAString& aValue);
 
-  nsresult PromoteInlineRange(nsRange& aRange);
-  nsresult PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange);
+  nsresult PromoteInlineRange(nsRange* aRange);
+  nsresult PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange* aRange);
   nsresult SplitStyleAboveRange(nsRange* aRange,
                                 nsIAtom *aProperty,
                                 const nsAString *aAttribute);
-  nsresult SplitStyleAbovePoint(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
-                                nsIAtom* aProperty,
-                                const nsAString* aAttribute,
-                                nsIContent** aOutLeftNode = nullptr,
-                                nsIContent** aOutRightNode = nullptr);
+  nsresult SplitStyleAbovePoint(nsCOMPtr<nsIDOMNode> *aNode,
+                                int32_t *aOffset,
+                                nsIAtom *aProperty,
+                                const nsAString *aAttribute,
+                                nsCOMPtr<nsIDOMNode> *outLeftNode = nullptr,
+                                nsCOMPtr<nsIDOMNode> *outRightNode = nullptr);
   nsresult ApplyDefaultProperties();
+  nsresult RemoveStyleInside(nsIDOMNode *aNode,
+                             nsIAtom *aProperty,
+                             const nsAString *aAttribute,
+                             const bool aChildrenOnly = false);
   nsresult RemoveStyleInside(nsIContent& aNode,
                              nsIAtom* aProperty,
                              const nsAString* aAttribute,
                              const bool aChildrenOnly = false);
   nsresult RemoveInlinePropertyImpl(nsIAtom* aProperty,
                                     const nsAString* aAttribute);
 
-  bool NodeIsProperty(nsINode& aNode);
-  bool IsAtFrontOfNode(nsINode& aNode, int32_t aOffset);
-  bool IsAtEndOfNode(nsINode& aNode, int32_t aOffset);
+  bool NodeIsProperty(nsIDOMNode *aNode);
+  bool HasAttr(nsIDOMNode *aNode, const nsAString *aAttribute);
+  bool IsAtFrontOfNode(nsIDOMNode *aNode, int32_t aOffset);
+  bool IsAtEndOfNode(nsIDOMNode *aNode, int32_t aOffset);
+  bool IsOnlyAttribute(nsIDOMNode *aElement, const nsAString *aAttribute);
   bool IsOnlyAttribute(const nsIContent* aElement, const nsAString& aAttribute);
 
   nsresult RemoveBlockContainer(nsIDOMNode *inNode);
 
   nsIContent* GetPriorHTMLSibling(nsINode* aNode);
   nsresult GetPriorHTMLSibling(nsIDOMNode *inNode, nsCOMPtr<nsIDOMNode> *outNode);
   nsIContent* GetPriorHTMLSibling(nsINode* aParent, int32_t aOffset);
   nsresult GetPriorHTMLSibling(nsIDOMNode *inParent, int32_t inOffset, nsCOMPtr<nsIDOMNode> *outNode);
@@ -744,17 +751,17 @@ protected:
                                    const nsAString& aFlavor,
                                    nsIDOMDocument* aSourceDoc,
                                    nsIDOMNode* aDestNode,
                                    int32_t aDestOffset,
                                    bool aDeleteSelection,
                                    bool aTrustedInput,
                                    bool aClearStyle = true);
 
-  nsresult ClearStyle(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
+  nsresult ClearStyle(nsCOMPtr<nsIDOMNode>* aNode, int32_t* aOffset,
                       nsIAtom* aProperty, const nsAString* aAttribute);
 
   void SetElementPosition(mozilla::dom::Element& aElement,
                           int32_t aX, int32_t aY);
 
 // Data members
 protected:
 
--- a/editor/libeditor/nsHTMLEditorStyle.cpp
+++ b/editor/libeditor/nsHTMLEditorStyle.cpp
@@ -20,17 +20,19 @@
 #include "nsError.h"
 #include "nsGkAtoms.h"
 #include "nsHTMLCSSUtils.h"
 #include "nsHTMLEditUtils.h"
 #include "nsHTMLEditor.h"
 #include "nsIAtom.h"
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
+#include "nsIDOMCharacterData.h"
 #include "nsIDOMElement.h"
+#include "nsIDOMNode.h"
 #include "nsIEditor.h"
 #include "nsIEditorIMESupport.h"
 #include "nsNameSpaceManager.h"
 #include "nsINode.h"
 #include "nsISupportsImpl.h"
 #include "nsLiteralString.h"
 #include "nsRange.h"
 #include "nsReadableUtils.h"
@@ -138,17 +140,17 @@ nsHTMLEditor::SetInlineProperty(nsIAtom*
   if (!cancel && !handled) {
     // Loop through the ranges in the selection
     uint32_t rangeCount = selection->RangeCount();
     for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) {
       RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
 
       // Adjust range to include any ancestors whose children are entirely
       // selected
-      res = PromoteInlineRange(*range);
+      res = PromoteInlineRange(range);
       NS_ENSURE_SUCCESS(res, res);
 
       // Check for easy case: both range endpoints in same text node
       nsCOMPtr<nsINode> startNode = range->GetStartParent();
       nsCOMPtr<nsINode> endNode = range->GetEndParent();
       if (startNode && startNode == endNode && startNode->GetAsText()) {
         res = SetInlinePropertyOnTextNode(*startNode->GetAsText(),
                                           range->StartOffset(),
@@ -516,141 +518,156 @@ nsHTMLEditor::SetInlinePropertyOnNode(ns
 
 
 nsresult
 nsHTMLEditor::SplitStyleAboveRange(nsRange* inRange, nsIAtom* aProperty,
                                    const nsAString* aAttribute)
 {
   NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
   nsresult res;
+  nsCOMPtr<nsIDOMNode> startNode, endNode, origStartNode;
+  int32_t startOffset, endOffset;
 
-  nsCOMPtr<nsINode> startNode = inRange->GetStartParent();
-  int32_t startOffset = inRange->StartOffset();
-  nsCOMPtr<nsINode> endNode = inRange->GetEndParent();
-  int32_t endOffset = inRange->EndOffset();
+  res = inRange->GetStartContainer(getter_AddRefs(startNode));
+  NS_ENSURE_SUCCESS(res, res);
+  res = inRange->GetStartOffset(&startOffset);
+  NS_ENSURE_SUCCESS(res, res);
+  res = inRange->GetEndContainer(getter_AddRefs(endNode));
+  NS_ENSURE_SUCCESS(res, res);
+  res = inRange->GetEndOffset(&endOffset);
+  NS_ENSURE_SUCCESS(res, res);
 
-  nsCOMPtr<nsINode> origStartNode = startNode;
+  origStartNode = startNode;
 
   // split any matching style nodes above the start of range
   {
     nsAutoTrackDOMPoint tracker(mRangeUpdater, address_of(endNode), &endOffset);
-    res = SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty,
-                               aAttribute);
+    res = SplitStyleAbovePoint(address_of(startNode), &startOffset, aProperty, aAttribute);
     NS_ENSURE_SUCCESS(res, res);
   }
 
   // second verse, same as the first...
-  res = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty,
-                             aAttribute);
+  res = SplitStyleAbovePoint(address_of(endNode), &endOffset, aProperty, aAttribute);
   NS_ENSURE_SUCCESS(res, res);
 
   // reset the range
   res = inRange->SetStart(startNode, startOffset);
   NS_ENSURE_SUCCESS(res, res);
   res = inRange->SetEnd(endNode, endOffset);
   return res;
 }
 
-nsresult
-nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsINode>* aNode,
-                                   int32_t* aOffset,
-                                   // null here means we split all properties
-                                   nsIAtom* aProperty,
-                                   const nsAString* aAttribute,
-                                   nsIContent** aOutLeftNode,
-                                   nsIContent** aOutRightNode)
+nsresult nsHTMLEditor::SplitStyleAbovePoint(nsCOMPtr<nsIDOMNode> *aNode,
+                                           int32_t *aOffset,
+                                           nsIAtom *aProperty,          // null here means we split all properties
+                                           const nsAString *aAttribute,
+                                           nsCOMPtr<nsIDOMNode> *outLeftNode,
+                                           nsCOMPtr<nsIDOMNode> *outRightNode)
 {
-  MOZ_ASSERT(aNode && *aNode && aOffset);
-  NS_ENSURE_TRUE((*aNode)->IsContent(), NS_OK);
-
-  // Split any matching style nodes above the node/offset
-  OwningNonNull<nsIContent> node = *(*aNode)->AsContent();
+  NS_ENSURE_TRUE(aNode && *aNode && aOffset, NS_ERROR_NULL_POINTER);
+  if (outLeftNode)  *outLeftNode  = nullptr;
+  if (outRightNode) *outRightNode = nullptr;
+  // split any matching style nodes above the node/offset
+  nsCOMPtr<nsIContent> node = do_QueryInterface(*aNode);
+  NS_ENSURE_STATE(node);
+  int32_t offset;
 
   bool useCSS = IsCSSEnabled();
 
   bool isSet;
-  while (!IsBlockNode(node) && node->GetParent() &&
-         IsEditable(node->GetParent())) {
+  while (node && !IsBlockNode(node) && node->GetParentNode() &&
+         IsEditable(node->GetParentNode())) {
     isSet = false;
-    if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty,
-                                                       aAttribute)) {
-      // The HTML style defined by aProperty/aAttribute has a CSS equivalence
-      // in this implementation for the node; let's check if it carries those
-      // CSS styles
+    if (useCSS && mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty, aAttribute)) {
+      // the HTML style defined by aProperty/aAttribute has a CSS equivalence
+      // in this implementation for the node; let's check if it carries those css styles
       nsAutoString firstValue;
       mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(GetAsDOMNode(node),
         aProperty, aAttribute, isSet, firstValue, nsHTMLCSSUtils::eSpecified);
     }
     if (// node is the correct inline prop
         (aProperty && node->IsHTMLElement(aProperty)) ||
         // node is href - test if really <a href=...
         (aProperty == nsGkAtoms::href && nsHTMLEditUtils::IsLink(node)) ||
         // or node is any prop, and we asked to split them all
-        (!aProperty && NodeIsProperty(node)) ||
+        (!aProperty && NodeIsProperty(GetAsDOMNode(node))) ||
         // or the style is specified in the style attribute
         isSet) {
-      // Found a style node we need to split
-      int32_t offset = SplitNodeDeep(*node, *(*aNode)->AsContent(), *aOffset,
-                                     EmptyContainers::yes, aOutLeftNode,
-                                     aOutRightNode);
+      // found a style node we need to split
+      nsCOMPtr<nsIContent> outLeftContent, outRightContent;
+      nsCOMPtr<nsIContent> nodeParam = do_QueryInterface(*aNode);
+      NS_ENSURE_STATE(nodeParam || !*aNode);
+      offset = SplitNodeDeep(*node, *nodeParam, *aOffset, EmptyContainers::yes,
+                             getter_AddRefs(outLeftContent),
+                             getter_AddRefs(outRightContent));
       NS_ENSURE_TRUE(offset != -1, NS_ERROR_FAILURE);
       // reset startNode/startOffset
-      *aNode = node->GetParent();
+      *aNode = GetAsDOMNode(node->GetParent());
       *aOffset = offset;
+      if (outLeftNode) {
+        *outLeftNode = GetAsDOMNode(outLeftContent);
+      }
+      if (outRightNode) {
+        *outRightNode = GetAsDOMNode(outRightContent);
+      }
     }
     node = node->GetParent();
   }
-
   return NS_OK;
 }
 
 nsresult
-nsHTMLEditor::ClearStyle(nsCOMPtr<nsINode>* aNode, int32_t* aOffset,
+nsHTMLEditor::ClearStyle(nsCOMPtr<nsIDOMNode>* aNode, int32_t* aOffset,
                          nsIAtom* aProperty, const nsAString* aAttribute)
 {
-  nsCOMPtr<nsIContent> leftNode, rightNode;
-  nsresult res = SplitStyleAbovePoint(aNode, aOffset, aProperty,
-                                      aAttribute, getter_AddRefs(leftNode),
-                                      getter_AddRefs(rightNode));
+  nsCOMPtr<nsIDOMNode> leftNode, rightNode, tmp;
+  nsresult res = SplitStyleAbovePoint(aNode, aOffset, aProperty, aAttribute,
+                                      address_of(leftNode),
+                                      address_of(rightNode));
   NS_ENSURE_SUCCESS(res, res);
-
   if (leftNode) {
     bool bIsEmptyNode;
     IsEmptyNode(leftNode, &bIsEmptyNode, false, true);
     if (bIsEmptyNode) {
       // delete leftNode if it became empty
       res = DeleteNode(leftNode);
       NS_ENSURE_SUCCESS(res, res);
     }
   }
   if (rightNode) {
-    nsCOMPtr<nsINode> secondSplitParent = GetLeftmostChild(rightNode);
+    nsCOMPtr<nsINode> rightNode_ = do_QueryInterface(rightNode);
+    NS_ENSURE_STATE(rightNode_);
+    nsCOMPtr<nsIDOMNode> secondSplitParent =
+      GetAsDOMNode(GetLeftmostChild(rightNode_));
     // don't try to split non-containers (br's, images, hr's, etc)
     if (!secondSplitParent) {
       secondSplitParent = rightNode;
     }
     nsCOMPtr<Element> savedBR;
     if (!IsContainer(secondSplitParent)) {
       if (nsTextEditUtils::IsBreak(secondSplitParent)) {
         savedBR = do_QueryInterface(secondSplitParent);
         NS_ENSURE_STATE(savedBR);
       }
 
-      secondSplitParent = secondSplitParent->GetParentNode();
+      secondSplitParent->GetParentNode(getter_AddRefs(tmp));
+      secondSplitParent = tmp;
     }
     *aOffset = 0;
     res = SplitStyleAbovePoint(address_of(secondSplitParent),
                                aOffset, aProperty, aAttribute,
-                               getter_AddRefs(leftNode),
-                               getter_AddRefs(rightNode));
+                               address_of(leftNode), address_of(rightNode));
     NS_ENSURE_SUCCESS(res, res);
     // should be impossible to not get a new leftnode here
-    nsCOMPtr<nsINode> newSelParent = GetLeftmostChild(leftNode);
+    nsCOMPtr<nsINode> leftNode_ = do_QueryInterface(leftNode);
+    NS_ENSURE_TRUE(leftNode_, NS_ERROR_FAILURE);
+    nsCOMPtr<nsINode> newSelParent = GetLeftmostChild(leftNode_);
     if (!newSelParent) {
-      newSelParent = leftNode;
+      newSelParent = do_QueryInterface(leftNode);
+      NS_ENSURE_STATE(newSelParent);
     }
     // If rightNode starts with a br, suck it out of right node and into
     // leftNode.  This is so we you don't revert back to the previous style
     // if you happen to click at the end of a line.
     if (savedBR) {
       res = MoveNode(savedBR, newSelParent, 0);
       NS_ENSURE_SUCCESS(res, res);
     }
@@ -666,55 +683,73 @@ nsHTMLEditor::ClearStyle(nsCOMPtr<nsINod
     {
       // Track the point at the new hierarchy.  This is so we can know where
       // to put the selection after we call RemoveStyleInside().
       // RemoveStyleInside() could remove any and all of those nodes, so I
       // have to use the range tracking system to find the right spot to put
       // selection.
       nsAutoTrackDOMPoint tracker(mRangeUpdater,
                                   address_of(newSelParent), &newSelOffset);
-      res = RemoveStyleInside(*leftNode, aProperty, aAttribute);
+      res = RemoveStyleInside(leftNode, aProperty, aAttribute);
       NS_ENSURE_SUCCESS(res, res);
     }
     // reset our node offset values to the resulting new sel point
-    *aNode = newSelParent;
+    *aNode = GetAsDOMNode(newSelParent);
     *aOffset = newSelOffset;
   }
 
   return NS_OK;
 }
 
-bool
-nsHTMLEditor::NodeIsProperty(nsINode& aNode)
+bool nsHTMLEditor::NodeIsProperty(nsIDOMNode *aNode)
 {
-  return IsContainer(&aNode) && IsEditable(&aNode) && !IsBlockNode(&aNode) &&
-         !aNode.IsHTMLElement(nsGkAtoms::a);
+  NS_ENSURE_TRUE(aNode, false);
+  if (!IsContainer(aNode))  return false;
+  if (!IsEditable(aNode))   return false;
+  if (IsBlockNode(aNode))   return false;
+  if (NodeIsType(aNode, nsGkAtoms::a)) {
+    return false;
+  }
+  return true;
 }
 
 nsresult nsHTMLEditor::ApplyDefaultProperties()
 {
   nsresult res = NS_OK;
   uint32_t j, defcon = mDefaultStyles.Length();
   for (j=0; j<defcon; j++)
   {
     PropItem *propItem = mDefaultStyles[j];
     NS_ENSURE_TRUE(propItem, NS_ERROR_NULL_POINTER);
     res = SetInlineProperty(propItem->tag, propItem->attr, propItem->value);
     NS_ENSURE_SUCCESS(res, res);
   }
   return res;
 }
 
+nsresult nsHTMLEditor::RemoveStyleInside(nsIDOMNode *aNode,
+                                         // null here means remove all properties
+                                         nsIAtom *aProperty,
+                                         const nsAString *aAttribute,
+                                         const bool aChildrenOnly)
+{
+  NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
+  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+  NS_ENSURE_STATE(content);
+
+  return RemoveStyleInside(*content, aProperty, aAttribute, aChildrenOnly);
+}
+
 nsresult
 nsHTMLEditor::RemoveStyleInside(nsIContent& aNode,
                                 nsIAtom* aProperty,
                                 const nsAString* aAttribute,
                                 const bool aChildrenOnly /* = false */)
 {
-  if (aNode.GetAsText()) {
+  if (aNode.NodeType() == nsIDOMNode::TEXT_NODE) {
     return NS_OK;
   }
 
   // first process the children
   RefPtr<nsIContent> child = aNode.GetFirstChild();
   while (child) {
     // cache next sibling since we might remove child
     nsCOMPtr<nsIContent> next = child->GetNextSibling();
@@ -728,17 +763,17 @@ nsHTMLEditor::RemoveStyleInside(nsIConte
     (
       // node is prop we asked for
       (aProperty && aNode.NodeInfo()->NameAtom() == aProperty) ||
       // but check for link (<a href=...)
       (aProperty == nsGkAtoms::href && nsHTMLEditUtils::IsLink(&aNode)) ||
       // and for named anchors
       (aProperty == nsGkAtoms::name && nsHTMLEditUtils::IsNamedAnchor(&aNode)) ||
       // or node is any prop and we asked for that
-      (!aProperty && NodeIsProperty(aNode))
+      (!aProperty && NodeIsProperty(aNode.AsDOMNode()))
     )
   ) {
     nsresult res;
     // if we weren't passed an attribute, then we want to
     // remove any matching inlinestyles entirely
     if (!aAttribute || aAttribute->IsEmpty()) {
       NS_NAMED_LITERAL_STRING(styleAttr, "style");
       NS_NAMED_LITERAL_STRING(classAttr, "class");
@@ -809,16 +844,27 @@ nsHTMLEditor::RemoveStyleInside(nsIConte
     )
   ) {
     // if we are setting font size, remove any nested bigs and smalls
     return RemoveContainer(&aNode);
   }
   return NS_OK;
 }
 
+bool nsHTMLEditor::IsOnlyAttribute(nsIDOMNode *aNode,
+                                     const nsAString *aAttribute)
+{
+  NS_ENSURE_TRUE(aNode && aAttribute, false);  // ooops
+
+  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+  NS_ENSURE_TRUE(content, false);  // ooops
+
+  return IsOnlyAttribute(content, *aAttribute);
+}
+
 bool
 nsHTMLEditor::IsOnlyAttribute(const nsIContent* aContent,
                               const nsAString& aAttribute)
 {
   MOZ_ASSERT(aContent);
 
   uint32_t attrCount = aContent->GetAttrCount();
   for (uint32_t i = 0; i < attrCount; ++i) {
@@ -836,127 +882,176 @@ nsHTMLEditor::IsOnlyAttribute(const nsIC
       return false;
     }
   }
   // if we made it through all of them without finding a real attribute
   // other than aAttribute, then return true
   return true;
 }
 
-nsresult
-nsHTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange& aRange)
+bool nsHTMLEditor::HasAttr(nsIDOMNode* aNode,
+                           const nsAString* aAttribute)
 {
-  // We assume that <a> is not nested.
-  nsCOMPtr<nsINode> startNode = aRange.GetStartParent();
-  int32_t startOffset = aRange.StartOffset();
-  nsCOMPtr<nsINode> endNode = aRange.GetEndParent();
-  int32_t endOffset = aRange.EndOffset();
-
-  nsCOMPtr<nsINode> parent = startNode;
-
-  while (parent && !parent->IsHTMLElement(nsGkAtoms::body) &&
-         !nsHTMLEditUtils::IsNamedAnchor(parent)) {
-    parent = parent->GetParentNode();
-  }
-  NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
-
-  if (nsHTMLEditUtils::IsNamedAnchor(parent)) {
-    startNode = parent->GetParentNode();
-    startOffset = startNode ? startNode->IndexOf(parent) : -1;
+  NS_ENSURE_TRUE(aNode, false);
+  if (!aAttribute || aAttribute->IsEmpty()) {
+    // everybody has the 'null' attribute
+    return true;
   }
 
-  parent = endNode;
-  while (parent && !parent->IsHTMLElement(nsGkAtoms::body) &&
-         !nsHTMLEditUtils::IsNamedAnchor(parent)) {
-    parent = parent->GetParentNode();
+  // get element
+  nsCOMPtr<dom::Element> element = do_QueryInterface(aNode);
+  NS_ENSURE_TRUE(element, false);
+
+  nsCOMPtr<nsIAtom> atom = NS_Atomize(*aAttribute);
+  NS_ENSURE_TRUE(atom, false);
+
+  return element->HasAttr(kNameSpaceID_None, atom);
+}
+
+
+nsresult
+nsHTMLEditor::PromoteRangeIfStartsOrEndsInNamedAnchor(nsRange* inRange)
+{
+  NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
+  nsresult res;
+  nsCOMPtr<nsIDOMNode> startNode, endNode, parent, tmp;
+  int32_t startOffset, endOffset, tmpOffset;
+
+  res = inRange->GetStartContainer(getter_AddRefs(startNode));
+  NS_ENSURE_SUCCESS(res, res);
+  res = inRange->GetStartOffset(&startOffset);
+  NS_ENSURE_SUCCESS(res, res);
+  res = inRange->GetEndContainer(getter_AddRefs(endNode));
+  NS_ENSURE_SUCCESS(res, res);
+  res = inRange->GetEndOffset(&endOffset);
+  NS_ENSURE_SUCCESS(res, res);
+
+  tmp = startNode;
+  while ( tmp &&
+          !nsTextEditUtils::IsBody(tmp) &&
+          !nsHTMLEditUtils::IsNamedAnchor(tmp))
+  {
+    parent = GetNodeLocation(tmp, &tmpOffset);
+    tmp = parent;
   }
-  NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
-
-  if (nsHTMLEditUtils::IsNamedAnchor(parent)) {
-    endNode = parent->GetParentNode();
-    endOffset = endNode ? endNode->IndexOf(parent) + 1 : 0;
+  NS_ENSURE_TRUE(tmp, NS_ERROR_NULL_POINTER);
+  if (nsHTMLEditUtils::IsNamedAnchor(tmp))
+  {
+    parent = GetNodeLocation(tmp, &tmpOffset);
+    startNode = parent;
+    startOffset = tmpOffset;
   }
 
-  nsresult res = aRange.SetStart(startNode, startOffset);
+  tmp = endNode;
+  while ( tmp &&
+          !nsTextEditUtils::IsBody(tmp) &&
+          !nsHTMLEditUtils::IsNamedAnchor(tmp))
+  {
+    parent = GetNodeLocation(tmp, &tmpOffset);
+    tmp = parent;
+  }
+  NS_ENSURE_TRUE(tmp, NS_ERROR_NULL_POINTER);
+  if (nsHTMLEditUtils::IsNamedAnchor(tmp))
+  {
+    parent = GetNodeLocation(tmp, &tmpOffset);
+    endNode = parent;
+    endOffset = tmpOffset + 1;
+  }
+
+  res = inRange->SetStart(startNode, startOffset);
   NS_ENSURE_SUCCESS(res, res);
-  res = aRange.SetEnd(endNode, endOffset);
-  NS_ENSURE_SUCCESS(res, res);
-
-  return NS_OK;
+  res = inRange->SetEnd(endNode, endOffset);
+  return res;
 }
 
 nsresult
-nsHTMLEditor::PromoteInlineRange(nsRange& aRange)
+nsHTMLEditor::PromoteInlineRange(nsRange* inRange)
 {
-  nsCOMPtr<nsINode> startNode = aRange.GetStartParent();
-  int32_t startOffset = aRange.StartOffset();
-  nsCOMPtr<nsINode> endNode = aRange.GetEndParent();
-  int32_t endOffset = aRange.EndOffset();
+  NS_ENSURE_TRUE(inRange, NS_ERROR_NULL_POINTER);
+  nsresult res;
+  nsCOMPtr<nsIDOMNode> startNode, endNode, parent;
+  int32_t startOffset, endOffset;
 
-  while (startNode && !startNode->IsHTMLElement(nsGkAtoms::body) &&
-         IsEditable(startNode) && IsAtFrontOfNode(*startNode, startOffset)) {
-    nsCOMPtr<nsINode> parent = startNode->GetParentNode();
-    NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
-    startOffset = parent->IndexOf(startNode);
+  res = inRange->GetStartContainer(getter_AddRefs(startNode));
+  NS_ENSURE_SUCCESS(res, res);
+  res = inRange->GetStartOffset(&startOffset);
+  NS_ENSURE_SUCCESS(res, res);
+  res = inRange->GetEndContainer(getter_AddRefs(endNode));
+  NS_ENSURE_SUCCESS(res, res);
+  res = inRange->GetEndOffset(&endOffset);
+  NS_ENSURE_SUCCESS(res, res);
+
+  while ( startNode &&
+          !nsTextEditUtils::IsBody(startNode) &&
+          IsEditable(startNode) &&
+          IsAtFrontOfNode(startNode, startOffset) )
+  {
+    parent = GetNodeLocation(startNode, &startOffset);
     startNode = parent;
   }
+  NS_ENSURE_TRUE(startNode, NS_ERROR_NULL_POINTER);
 
-  while (endNode && !endNode->IsHTMLElement(nsGkAtoms::body) &&
-         IsEditable(endNode) && IsAtEndOfNode(*endNode, endOffset)) {
-    nsCOMPtr<nsINode> parent = endNode->GetParentNode();
-    NS_ENSURE_TRUE(parent, NS_ERROR_NULL_POINTER);
-    // We are AFTER this node
-    endOffset = 1 + parent->IndexOf(endNode);
+  while ( endNode &&
+          !nsTextEditUtils::IsBody(endNode) &&
+          IsEditable(endNode) &&
+          IsAtEndOfNode(endNode, endOffset) )
+  {
+    parent = GetNodeLocation(endNode, &endOffset);
     endNode = parent;
+    endOffset++;  // we are AFTER this node
   }
+  NS_ENSURE_TRUE(endNode, NS_ERROR_NULL_POINTER);
 
-  nsresult res = aRange.SetStart(startNode, startOffset);
+  res = inRange->SetStart(startNode, startOffset);
   NS_ENSURE_SUCCESS(res, res);
-  res = aRange.SetEnd(endNode, endOffset);
-  NS_ENSURE_SUCCESS(res, res);
-
-  return NS_OK;
+  res = inRange->SetEnd(endNode, endOffset);
+  return res;
 }
 
-bool
-nsHTMLEditor::IsAtFrontOfNode(nsINode& aNode, int32_t aOffset)
+bool nsHTMLEditor::IsAtFrontOfNode(nsIDOMNode *aNode, int32_t aOffset)
 {
+  nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
+  NS_ENSURE_TRUE(node, false);
   if (!aOffset) {
     return true;
   }
 
-  if (IsTextNode(&aNode)) {
+  if (IsTextNode(aNode))
+  {
     return false;
   }
-
-  nsCOMPtr<nsIContent> firstNode = GetFirstEditableChild(aNode);
-  NS_ENSURE_TRUE(firstNode, true);
-  if (aNode.IndexOf(firstNode) < aOffset) {
-    return false;
+  else
+  {
+    nsCOMPtr<nsIContent> firstNode = GetFirstEditableChild(*node);
+    NS_ENSURE_TRUE(firstNode, true);
+    int32_t offset = node->IndexOf(firstNode);
+    if (offset < aOffset) return false;
+    return true;
   }
-  return true;
 }
 
-bool
-nsHTMLEditor::IsAtEndOfNode(nsINode& aNode, int32_t aOffset)
+bool nsHTMLEditor::IsAtEndOfNode(nsIDOMNode *aNode, int32_t aOffset)
 {
-  if (aOffset == (int32_t)aNode.Length()) {
-    return true;
-  }
+  nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
+  NS_ENSURE_TRUE(node, false);
+  uint32_t len = node->Length();
+  if (aOffset == (int32_t)len) return true;
 
-  if (IsTextNode(&aNode)) {
+  if (IsTextNode(aNode))
+  {
     return false;
   }
-
-  nsCOMPtr<nsIContent> lastNode = GetLastEditableChild(aNode);
-  NS_ENSURE_TRUE(lastNode, true);
-  if (aNode.IndexOf(lastNode) < aOffset) {
-    return true;
+  else
+  {
+    nsCOMPtr<nsIContent> lastNode = GetLastEditableChild(*node);
+    NS_ENSURE_TRUE(lastNode, true);
+    int32_t offset = node->IndexOf(lastNode);
+    if (offset < aOffset) return true;
+    return false;
   }
-  return false;
 }
 
 
 nsresult
 nsHTMLEditor::GetInlinePropertyBase(nsIAtom& aProperty,
                                     const nsAString* aAttribute,
                                     const nsAString* aValue,
                                     bool* aFirst,
@@ -1221,17 +1316,17 @@ nsHTMLEditor::RemoveInlinePropertyImpl(n
   // Protect the edit rules object from dying
   nsCOMPtr<nsIEditRules> kungFuDeathGrip(mRules);
   nsresult res = mRules->WillDoAction(selection, &ruleInfo, &cancel, &handled);
   NS_ENSURE_SUCCESS(res, res);
   if (!cancel && !handled) {
     // Loop through the ranges in the selection
     uint32_t rangeCount = selection->RangeCount();
     for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
-      OwningNonNull<nsRange> range = *selection->GetRangeAt(rangeIdx);
+      RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
       if (aProperty == nsGkAtoms::name) {
         // Promote range if it starts or end in a named anchor and we want to
         // remove named anchors
         res = PromoteRangeIfStartsOrEndsInNamedAnchor(range);
       } else {
         // Adjust range to include any ancestors whose children are entirely
         // selected
         res = PromoteInlineRange(range);
@@ -1268,46 +1363,47 @@ nsHTMLEditor::RemoveInlinePropertyImpl(n
                                           aAttribute, value);
             }
           }
         }
       } else {
         // Not the easy case.  Range not contained in single text node.
         nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator();
 
-        nsTArray<OwningNonNull<nsIContent>> arrayOfNodes;
+        nsTArray<nsCOMPtr<nsINode>> arrayOfNodes;
 
         // Iterate range and build up array
         for (iter->Init(range); !iter->IsDone(); iter->Next()) {
           nsCOMPtr<nsINode> node = iter->GetCurrentNode();
           NS_ENSURE_TRUE(node, NS_ERROR_FAILURE);
 
-          if (IsEditable(node) && node->IsContent()) {
-            arrayOfNodes.AppendElement(*node->AsContent());
+          if (IsEditable(node)) {
+            arrayOfNodes.AppendElement(node);
           }
         }
 
         // Loop through the list, remove the property on each node
         for (auto& node : arrayOfNodes) {
-          res = RemoveStyleInside(node, aProperty, aAttribute);
+          res = RemoveStyleInside(GetAsDOMNode(node), aProperty, aAttribute);
           NS_ENSURE_SUCCESS(res, res);
           if (IsCSSEnabled() &&
               mHTMLCSSUtils->IsCSSEditableProperty(node, aProperty,
                                                    aAttribute) &&
               mHTMLCSSUtils->IsCSSEquivalentToHTMLInlineStyleSet(node,
                   aProperty, aAttribute, EmptyString(),
                   nsHTMLCSSUtils::eComputed) &&
               // startNode's computed style indicates the CSS equivalence to
               // the HTML style to remove is applied; but we found no element
               // in the ancestors of startNode carrying specified styles;
               // assume it comes from a rule and let's try to insert a span
               // "inverting" the style
               mHTMLCSSUtils->IsCSSInvertible(*aProperty, aAttribute)) {
             NS_NAMED_LITERAL_STRING(value, "-moz-editor-invert-value");
-            SetInlinePropertyOnNode(node, *aProperty, aAttribute, value);
+            SetInlinePropertyOnNode(*node->AsContent(), *aProperty,
+                                    aAttribute, value);
           }
         }
       }
     }
   }
   if (!cancel) {
     // Post-process
     res = mRules->DidDoAction(selection, &ruleInfo, res);
@@ -1366,26 +1462,26 @@ nsHTMLEditor::RelativeFontChange(FontSiz
   nsAutoTxnsConserveSelection dontSpazMySelection(this);
 
   // Loop through the ranges in the selection
   uint32_t rangeCount = selection->RangeCount();
   for (uint32_t rangeIdx = 0; rangeIdx < rangeCount; ++rangeIdx) {
     RefPtr<nsRange> range = selection->GetRangeAt(rangeIdx);
 
     // Adjust range to include any ancestors with entirely selected children
-    nsresult res = PromoteInlineRange(*range);
+    nsresult res = PromoteInlineRange(range);
     NS_ENSURE_SUCCESS(res, res);
 
     // Check for easy case: both range endpoints in same text node
     nsCOMPtr<nsINode> startNode = range->GetStartParent();
     nsCOMPtr<nsINode> endNode = range->GetEndParent();
     if (startNode == endNode && IsTextNode(startNode)) {
-      res = RelativeFontChangeOnTextNode(aDir, *startNode->GetAsText(),
-                                         range->StartOffset(),
-                                         range->EndOffset());
+      res = RelativeFontChangeOnTextNode(aDir == FontSize::incr ? +1 : -1,
+          static_cast<nsIDOMCharacterData*>(startNode->AsDOMNode()),
+          range->StartOffset(), range->EndOffset());
       NS_ENSURE_SUCCESS(res, res);
     } else {
       // Not the easy case.  Range not contained in single text node.  There
       // are up to three phases here.  There are all the nodes reported by the
       // subtree iterator to be processed.  And there are potentially a
       // starting textnode and an ending textnode which are only partially
       // contained by the range.
 
@@ -1415,88 +1511,96 @@ nsHTMLEditor::RelativeFontChange(FontSiz
                                          node);
           NS_ENSURE_SUCCESS(res, res);
         }
       }
       // Now check the start and end parents of the range to see if they need
       // to be separately handled (they do if they are text nodes, due to how
       // the subtree iterator works - it will not have reported them).
       if (IsTextNode(startNode) && IsEditable(startNode)) {
-        res = RelativeFontChangeOnTextNode(aDir, *startNode->GetAsText(),
-                                           range->StartOffset(),
-                                           startNode->Length());
+        res = RelativeFontChangeOnTextNode(aDir == FontSize::incr ? +1 : -1,
+            static_cast<nsIDOMCharacterData*>(startNode->AsDOMNode()),
+            range->StartOffset(), startNode->Length());
         NS_ENSURE_SUCCESS(res, res);
       }
       if (IsTextNode(endNode) && IsEditable(endNode)) {
-        res = RelativeFontChangeOnTextNode(aDir, *endNode->GetAsText(), 0,
-                                           range->EndOffset());
+        res = RelativeFontChangeOnTextNode(aDir == FontSize::incr ? +1 : -1,
+            static_cast<nsIDOMCharacterData*>(endNode->AsDOMNode()),
+            0, range->EndOffset());
         NS_ENSURE_SUCCESS(res, res);
       }
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-nsHTMLEditor::RelativeFontChangeOnTextNode(FontSize aDir,
-                                           Text& aTextNode,
-                                           int32_t aStartOffset,
-                                           int32_t aEndOffset)
+nsHTMLEditor::RelativeFontChangeOnTextNode( int32_t aSizeChange,
+                                            nsIDOMCharacterData *aTextNode,
+                                            int32_t aStartOffset,
+                                            int32_t aEndOffset)
 {
-  // Don't need to do anything if no characters actually selected
-  if (aStartOffset == aEndOffset) {
-    return NS_OK;
-  }
+  // Can only change font size by + or - 1
+  if ( !( (aSizeChange==1) || (aSizeChange==-1) ) )
+    return NS_ERROR_ILLEGAL_VALUE;
+  nsCOMPtr<nsIContent> textNode = do_QueryInterface(aTextNode);
+  NS_ENSURE_TRUE(textNode, NS_ERROR_NULL_POINTER);
 
-  if (!aTextNode.GetParentNode() ||
-      !CanContainTag(*aTextNode.GetParentNode(), *nsGkAtoms::big)) {
+  // don't need to do anything if no characters actually selected
+  if (aStartOffset == aEndOffset) return NS_OK;
+
+  if (!textNode->GetParentNode() ||
+      !CanContainTag(*textNode->GetParentNode(), *nsGkAtoms::big)) {
     return NS_OK;
   }
 
-  OwningNonNull<nsIContent> node = aTextNode;
+  nsCOMPtr<nsIDOMNode> tmp;
+  nsCOMPtr<nsIContent> node = do_QueryInterface(aTextNode);
+  NS_ENSURE_STATE(node);
 
-  // Do we need to split the text node?
+  // do we need to split the text node?
+  uint32_t textLen;
+  aTextNode->GetLength(&textLen);
 
   // -1 is a magic value meaning to the end of node
-  if (aEndOffset == -1) {
-    aEndOffset = aTextNode.Length();
+  if (aEndOffset == -1) aEndOffset = textLen;
+
+  nsresult res = NS_OK;
+  if ( (uint32_t)aEndOffset != textLen )
+  {
+    // we need to split off back of text node
+    res = SplitNode(GetAsDOMNode(node), aEndOffset, getter_AddRefs(tmp));
+    NS_ENSURE_SUCCESS(res, res);
+    // remember left node
+    node = do_QueryInterface(tmp);
+  }
+  if ( aStartOffset )
+  {
+    // we need to split off front of text node
+    res = SplitNode(GetAsDOMNode(node), aStartOffset, getter_AddRefs(tmp));
+    NS_ENSURE_SUCCESS(res, res);
   }
 
-  ErrorResult rv;
-  if ((uint32_t)aEndOffset != aTextNode.Length()) {
-    // We need to split off back of text node
-    node = SplitNode(node, aEndOffset, rv);
-    NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
-  }
-  if (aStartOffset) {
-    // We need to split off front of text node
-    SplitNode(node, aStartOffset, rv);
-    NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
-  }
-
-  // Look for siblings that are correct type of node
-  nsIAtom* nodeType = aDir == FontSize::incr ? nsGkAtoms::big
-                                             : nsGkAtoms::small;
+  // look for siblings that are correct type of node
+  nsIAtom* nodeType = aSizeChange == 1 ? nsGkAtoms::big : nsGkAtoms::small;
   nsCOMPtr<nsIContent> sibling = GetPriorHTMLSibling(node);
   if (sibling && sibling->IsHTMLElement(nodeType)) {
-    // Previous sib is already right kind of inline node; slide this over
-    nsresult res = MoveNode(node, sibling, -1);
-    NS_ENSURE_SUCCESS(res, res);
-    return NS_OK;
+    // previous sib is already right kind of inline node; slide this over into it
+    res = MoveNode(node, sibling, -1);
+    return res;
   }
   sibling = GetNextHTMLSibling(node);
   if (sibling && sibling->IsHTMLElement(nodeType)) {
-    // Following sib is already right kind of inline node; slide this over
-    nsresult res = MoveNode(node, sibling, 0);
-    NS_ENSURE_SUCCESS(res, res);
-    return NS_OK;
+    // following sib is already right kind of inline node; slide this over into it
+    res = MoveNode(node, sibling, 0);
+    return res;
   }
 
-  // Else reparent the node inside font node with appropriate relative size
+  // else reparent the node inside font node with appropriate relative size
   nsCOMPtr<Element> newElement = InsertContainerAbove(node, nodeType);
   NS_ENSURE_STATE(newElement);
 
   return NS_OK;
 }
 
 
 nsresult
--- a/editor/libeditor/nsPlaintextEditor.cpp
+++ b/editor/libeditor/nsPlaintextEditor.cpp
@@ -431,28 +431,28 @@ nsPlaintextEditor::TypedText(const nsASt
     case eTypedBreak:
       return InsertLineBreak();
     default:
       // eTypedBR is only for HTML
       return NS_ERROR_FAILURE;
   }
 }
 
-Element*
+already_AddRefed<Element>
 nsPlaintextEditor::CreateBRImpl(nsCOMPtr<nsINode>* aInOutParent,
                                 int32_t* aInOutOffset,
                                 EDirection aSelect)
 {
   nsCOMPtr<nsIDOMNode> parent(GetAsDOMNode(*aInOutParent));
   nsCOMPtr<nsIDOMNode> br;
   // We ignore the retval, and assume it's fine if the br is non-null
   CreateBRImpl(address_of(parent), aInOutOffset, address_of(br), aSelect);
   *aInOutParent = do_QueryInterface(parent);
   nsCOMPtr<Element> ret(do_QueryInterface(br));
-  return ret;
+  return ret.forget();
 }
 
 nsresult
 nsPlaintextEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode>* aInOutParent,
                                 int32_t* aInOutOffset,
                                 nsCOMPtr<nsIDOMNode>* outBRNode,
                                 EDirection aSelect)
 {
--- a/editor/libeditor/nsPlaintextEditor.h
+++ b/editor/libeditor/nsPlaintextEditor.h
@@ -175,19 +175,19 @@ protected:
   NS_IMETHOD GetAndInitDocEncoder(const nsAString& aFormatType,
                                   uint32_t aFlags,
                                   const nsACString& aCharset,
                                   nsIDocumentEncoder** encoder);
 
   // key event helpers
   NS_IMETHOD CreateBR(nsIDOMNode *aNode, int32_t aOffset,
                       nsCOMPtr<nsIDOMNode> *outBRNode, EDirection aSelect = eNone);
-  mozilla::dom::Element* CreateBRImpl(nsCOMPtr<nsINode>* aInOutParent,
-                                      int32_t* aInOutOffset,
-                                      EDirection aSelect);
+  already_AddRefed<mozilla::dom::Element>
+      CreateBRImpl(nsCOMPtr<nsINode>* aInOutParent, int32_t* aInOutOffset,
+                   EDirection aSelect);
   nsresult CreateBRImpl(nsCOMPtr<nsIDOMNode>* aInOutParent,
                         int32_t* aInOutOffset,
                         nsCOMPtr<nsIDOMNode>* outBRNode,
                         EDirection aSelect);
   nsresult InsertBR(nsCOMPtr<nsIDOMNode>* outBRNode);
 
   // factored methods for handling insertion of data from transferables (drag&drop or clipboard)
   NS_IMETHOD PrepareTransferable(nsITransferable **transferable);
--- a/editor/libeditor/nsTextEditRules.cpp
+++ b/editor/libeditor/nsTextEditRules.cpp
@@ -280,18 +280,17 @@ nsTextEditRules::WillDoAction(Selection*
     case EditAction::removeTextProperty:
       return WillRemoveTextProperty(aSelection, aCancel, aHandled);
     case EditAction::outputText:
       return WillOutputText(aSelection, info->outputFormat, info->outString,
                             aCancel, aHandled);
     case EditAction::insertElement:
       // i had thought this would be html rules only.  but we put pre elements
       // into plaintext mail when doing quoting for reply!  doh!
-      WillInsert(*aSelection, aCancel);
-      return NS_OK;
+      return WillInsert(aSelection, aCancel);
     default:
       return NS_ERROR_FAILURE;
   }
 }
 
 NS_IMETHODIMP
 nsTextEditRules::DidDoAction(Selection* aSelection,
                              nsRulesInfo *aInfo, nsresult aResult)
@@ -341,35 +340,35 @@ nsTextEditRules::DocumentIsEmpty(bool *a
   return NS_OK;
 }
 
 /********************************************************
  *  Protected methods
  ********************************************************/
 
 
-void
-nsTextEditRules::WillInsert(Selection& aSelection, bool* aCancel)
+nsresult
+nsTextEditRules::WillInsert(Selection* aSelection, bool* aCancel)
 {
-  MOZ_ASSERT(aCancel);
+  NS_ENSURE_TRUE(aSelection && aCancel, NS_ERROR_NULL_POINTER);
 
-  if (IsReadonly() || IsDisabled()) {
-    *aCancel = true;
-    return;
-  }
+  CANCEL_OPERATION_IF_READONLY_OR_DISABLED
 
   // initialize out param
   *aCancel = false;
 
   // check for the magic content node and delete it if it exists
-  if (mBogusNode) {
-    NS_ENSURE_TRUE_VOID(mEditor);
+  if (mBogusNode)
+  {
+    NS_ENSURE_STATE(mEditor);
     mEditor->DeleteNode(mBogusNode);
     mBogusNode = nullptr;
   }
+
+  return NS_OK;
 }
 
 nsresult
 nsTextEditRules::DidInsert(Selection* aSelection, nsresult aResult)
 {
   return NS_OK;
 }
 
@@ -408,17 +407,18 @@ nsTextEditRules::WillInsertBreak(Selecti
     NS_ENSURE_SUCCESS(res, res);
     if (!bCollapsed)
     {
       NS_ENSURE_STATE(mEditor);
       res = mEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
       NS_ENSURE_SUCCESS(res, res);
     }
 
-    WillInsert(*aSelection, aCancel);
+    res = WillInsert(aSelection, aCancel);
+    NS_ENSURE_SUCCESS(res, res);
     // initialize out param
     // we want to ignore result of WillInsert()
     *aCancel = false;
 
   }
   return NS_OK;
 }
 
@@ -646,17 +646,18 @@ nsTextEditRules::WillInsertText(EditActi
   NS_ENSURE_SUCCESS(res, res);
   if (!bCollapsed)
   {
     NS_ENSURE_STATE(mEditor);
     res = mEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
     NS_ENSURE_SUCCESS(res, res);
   }
 
-  WillInsert(*aSelection, aCancel);
+  res = WillInsert(aSelection, aCancel);
+  NS_ENSURE_SUCCESS(res, res);
   // initialize out param
   // we want to ignore result of WillInsert()
   *aCancel = false;
 
   // handle password field data
   // this has the side effect of changing all the characters in aOutString
   // to the replacement character
   if (IsPasswordEditor())
--- a/editor/libeditor/nsTextEditRules.h
+++ b/editor/libeditor/nsTextEditRules.h
@@ -118,17 +118,17 @@ protected:
                          nsresult aResult);
   nsresult GetTopEnclosingPre(nsIDOMNode *aNode, nsIDOMNode** aOutPreNode);
 
   nsresult WillInsertBreak(mozilla::dom::Selection* aSelection, bool* aCancel,
                            bool *aHandled, int32_t aMaxLength);
   nsresult DidInsertBreak(mozilla::dom::Selection* aSelection,
                           nsresult aResult);
 
-  void WillInsert(mozilla::dom::Selection& aSelection, bool* aCancel);
+  nsresult WillInsert(mozilla::dom::Selection* aSelection, bool* aCancel);
   nsresult DidInsert(mozilla::dom::Selection* aSelection, nsresult aResult);
 
   nsresult WillDeleteSelection(mozilla::dom::Selection* aSelection,
                                nsIEditor::EDirection aCollapsedAction,
                                bool *aCancel,
                                bool *aHandled);
   nsresult DidDeleteSelection(mozilla::dom::Selection* aSelection,
                               nsIEditor::EDirection aCollapsedAction,
--- a/editor/libeditor/nsWSRunObject.cpp
+++ b/editor/libeditor/nsWSRunObject.cpp
@@ -188,17 +188,17 @@ nsWSRunObject::PrepareToSplitAcrossBlock
 
   return wsObj.PrepareToSplitAcrossBlocksPriv();
 }
 
 //--------------------------------------------------------------------------------------------
 //   public instance methods
 //--------------------------------------------------------------------------------------------
 
-Element*
+already_AddRefed<Element>
 nsWSRunObject::InsertBreak(nsCOMPtr<nsINode>* aInOutParent,
                            int32_t* aInOutOffset,
                            nsIEditor::EDirection aSelect)
 {
   // MOOSE: for now, we always assume non-PRE formatting.  Fix this later.
   // meanwhile, the pre case is handled in WillInsertText in
   // nsHTMLEditRules.cpp
   NS_ENSURE_TRUE(aInOutParent && aInOutOffset, nullptr);
--- a/editor/libeditor/nsWSRunObject.h
+++ b/editor/libeditor/nsWSRunObject.h
@@ -198,19 +198,19 @@ class MOZ_STACK_CLASS nsWSRunObject
     static nsresult PrepareToSplitAcrossBlocks(nsHTMLEditor* aHTMLEd,
                                                nsCOMPtr<nsINode>* aSplitNode,
                                                int32_t* aSplitOffset);
 
     // InsertBreak inserts a br node at {aInOutParent,aInOutOffset}
     // and makes any needed adjustments to ws around that point.
     // example of fixup: normalws after {aInOutParent,aInOutOffset}
     //                   needs to begin with nbsp.
-    mozilla::dom::Element* InsertBreak(nsCOMPtr<nsINode>* aInOutParent,
-                                       int32_t* aInOutOffset,
-                                       nsIEditor::EDirection aSelect);
+    already_AddRefed<mozilla::dom::Element>
+      InsertBreak(nsCOMPtr<nsINode>* aInOutParent, int32_t* aInOutOffset,
+                  nsIEditor::EDirection aSelect);
 
     // InsertText inserts a string at {aInOutParent,aInOutOffset} and makes any
     // needed adjustments to ws around that point.  Example of fixup:
     // trailingws before {aInOutParent,aInOutOffset} needs to be removed.
     nsresult InsertText(const nsAString& aStringToInsert,
                         nsCOMPtr<nsINode>* aInOutNode,
                         int32_t* aInOutOffset,
                         nsIDocument* aDoc);
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -163,8 +163,9 @@ skip-if = toolkit == 'android'
 [test_bug1186799.html]
 [test_bug1181130-1.html]
 [test_bug1181130-2.html]
 [test_backspace_vs.html]
 [test_css_chrome_load_access.html]
 skip-if = toolkit == 'android' # chrome urls not available due to packaging
 [test_bug1247483.html]
 skip-if = toolkit == 'android'
+[test_bug1258085.html]
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/tests/test_bug1258085.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<title>Test for Bug 1186799</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<div contenteditable></div>
+<script>
+var div = document.querySelector("div");
+
+function reset() {
+  div.innerHTML = "x<br> y";
+  div.focus();
+  synthesizeKey("VK_DOWN", {});
+}
+
+function checks(msg) {
+  is(div.innerHTML, "x<br><br>",
+     msg + ": Should add a second <br> to prevent collapse of first");
+  is(div.childNodes.length, 3, msg + ": No empty text nodes allowed");
+  ok(getSelection().isCollapsed, msg + ": Selection must be collapsed");
+  is(getSelection().focusNode, div, msg + ": Focus must be in div");
+  is(getSelection().focusOffset, 2,
+     msg + ": Focus must be between the two <br>s");
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+  // Put selection after the "y" and backspace
+  reset();
+  synthesizeKey("VK_RIGHT", {});
+  synthesizeKey("VK_BACK_SPACE", {});
+  checks("Collapsed backspace");
+
+  // Now do the same with delete
+  reset();
+  synthesizeKey("VK_DELETE", {});
+  checks("Collapsed delete");
+
+  // Forward selection
+  reset();
+  synthesizeKey("VK_RIGHT", {shiftKey: true});
+  synthesizeKey("VK_BACK_SPACE", {});
+  checks("Forward-selected backspace");
+
+  // Backward selection
+  reset();
+  synthesizeKey("VK_RIGHT", {});
+  synthesizeKey("VK_LEFT", {shiftKey: true});
+  synthesizeKey("VK_BACK_SPACE", {});
+  checks("Backward-selected backspace");
+
+  // Make sure we're not deleting if the whitespace isn't actually collapsed
+  div.style.whiteSpace = "pre-wrap";
+  reset();
+  synthesizeKey("VK_RIGHT", {});
+  synthesizeKey("VK_RIGHT", {});
+  synthesizeKey("VK_BACK_SPACE", {});
+  if (div.innerHTML, "x<br> ", "pre-wrap: Don't delete uncollapsed space");
+  ok(getSelection().isCollapsed, "pre-wrap: Selection must be collapsed");
+  is(getSelection().focusNode, div.lastChild,
+     "pre-wrap: Focus must be in final text node");
+  is(getSelection().focusOffset, 1, "pre-wrap: Focus must be at end of node");
+
+  SimpleTest.finish();
+});
+</script>
--- a/editor/libeditor/tests/test_bug772796.html
+++ b/editor/libeditor/tests/test_bug772796.html
@@ -133,17 +133,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 /*66*/[ "<ul><pre><li>test</li><b>foobar\nbaz</b></pre></ul>",   "<ul><pre><li>test<b>foobar\n</b></li><b>baz</b></pre></ul>" ],
 /*67*/[ "<ul><pre><li>test</li><b>foo</b>bar\nbaz</pre></ul>",   "<ul><pre><li>test<b>foo</b>bar\n</li>baz</pre></ul>" ],
 /*68*/[ "<ul><pre><li>test</li><b>foo</b>\nbar</pre></ul>",      "<ul><pre><li>test<b>foo</b>\n</li>bar</pre></ul>" ],
 /*69*/[ "<ul><pre><li>test</li><b>foo\n</b>bar\nbaz</pre></ul>", "<ul><pre><li>test<b>foo\n</b></li>bar\nbaz</pre></ul>" ],
 
       /* Last not least, some simple edge case tests. */
 /*70*/[ "<div>test</div><pre>foobar\n</pre>baz", "<div>testfoobar\n</div>baz" ],
 /*71*/[ "<div>test</div><pre>\nfoo\nbar</pre>", "<div>testfoo\n</div><pre>bar</pre>" ],
-/*72*/[ "<div>test</div><pre>\n\nfoo\nbar</pre>", "<div>test\n</div><pre>foo\nbar</pre>" ],
+/*72*/[ "<div>test</div><pre>\n\nfoo\nbar</pre>", "<div>test</div><pre>foo\nbar</pre>", "<div>test\n</div><pre>foo\nbar</pre>" ],
     ];
 
     /** Test for Bug 772796 **/
 
     SimpleTest.waitForExplicitFinish();
 
     SimpleTest.waitForFocus(function() {
 
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -138,18 +138,20 @@ GetAndInitWARPDisplay(GLLibraryEGL& egl,
 
     return display;
 }
 
 static bool
 IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
 {
     int32_t angleSupport;
+    nsCString discardFailureId;
     gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
                                          nsIGfxInfo::FEATURE_WEBGL_ANGLE,
+                                         discardFailureId,
                                          &angleSupport);
     return (angleSupport == nsIGfxInfo::FEATURE_STATUS_OK);
 }
 
 static EGLDisplay
 GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
 {
     EGLDisplay display = egl.fGetDisplay(displayType);
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -13,18 +13,18 @@
 #include "gfxPoint.h"                   // for gfxPoint
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/EventForwards.h"      // for WidgetInputEvent, nsEventStatus
 #include "mozilla/gfx/Logging.h"        // for gfx::TreeLog
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4
 #include "mozilla/layers/APZUtils.h"    // for HitTestResult
 #include "mozilla/layers/TouchCounter.h"// for TouchCounter
 #include "mozilla/Mutex.h"              // for Mutex
+#include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/TimeStamp.h"          // for mozilla::TimeStamp
-#include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsTArrayForwardDeclare.h"     // for nsTArray, nsTArray_Impl, etc
 #include "Units.h"                      // for CSSPoint, CSSRect, etc
 
 namespace mozilla {
 class InputData;
 class MultiTouchInput;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -30,16 +30,17 @@
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/BasicEvents.h"        // for Modifiers, MODIFIER_*
 #include "mozilla/ClearOnShutdown.h"    // for ClearOnShutdown
 #include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
 #include "mozilla/EventForwards.h"      // for nsEventStatus_*
 #include "mozilla/MouseEvents.h"        // for WidgetWheelEvent
 #include "mozilla/Preferences.h"        // for Preferences
 #include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitorAutoEnter, etc
+#include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/StaticPtr.h"          // for StaticAutoPtr
 #include "mozilla/Telemetry.h"          // for Telemetry
 #include "mozilla/TimeStamp.h"          // for TimeDuration, TimeStamp
 #include "mozilla/dom/CheckerboardReportService.h" // for CheckerboardEventStorage
              // note: CheckerboardReportService.h actually lives in gfx/layers/apz/util/
 #include "mozilla/dom/Touch.h"          // for Touch
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
@@ -53,17 +54,16 @@
 #include "mozilla/layers/AxisPhysicsMSDModel.h" // for AxisPhysicsMSDModel
 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
 #include "mozilla/layers/LayerTransactionParent.h" // for LayerTransactionParent
 #include "mozilla/layers/ScrollInputMethods.h" // for ScrollInputMethod
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/unused.h"             // for unused
 #include "mozilla/FloatingPoint.h"      // for FuzzyEquals*
 #include "nsAlgorithm.h"                // for clamped
-#include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsIDOMWindowUtils.h"          // for nsIDOMWindowUtils
 #include "nsMathUtils.h"                // for NS_hypot
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsStyleConsts.h"
 #include "nsStyleStruct.h"              // for nsTimingFunction
 #include "nsTArray.h"                   // for nsTArray, nsTArray_Impl, etc
@@ -3961,17 +3961,19 @@ Maybe<CSSPoint> AsyncPanZoomController::
     return Some(scrollRange.ClampPoint(cssSnapPoint));
   }
   return Nothing();
 }
 
 void AsyncPanZoomController::ScrollSnapNear(const CSSPoint& aDestination) {
   if (Maybe<CSSPoint> snapPoint =
         FindSnapPointNear(aDestination, nsIScrollableFrame::DEVICE_PIXELS)) {
-    SmoothScrollTo(*snapPoint);
+    if (*snapPoint != mFrameMetrics.GetScrollOffset()) {
+      SmoothScrollTo(*snapPoint);
+    }
   }
 }
 
 void AsyncPanZoomController::ScrollSnap() {
   ReentrantMonitorAutoEnter lock(mMonitor);
   ScrollSnapNear(mFrameMetrics.GetScrollOffset());
 }
 
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -454,17 +454,17 @@ bool Axis::CanScroll() const {
 }
 
 bool Axis::CanScroll(ParentLayerCoord aDelta) const
 {
   if (!CanScroll() || mAxisLocked) {
     return false;
   }
 
-  return DisplacementWillOverscrollAmount(aDelta) != aDelta;
+  return fabs(DisplacementWillOverscrollAmount(aDelta) - aDelta) > COORDINATE_EPSILON;
 }
 
 CSSCoord Axis::ClampOriginToScrollableRect(CSSCoord aOrigin) const
 {
   CSSToParentLayerScale zoom = GetScaleForAxis(GetFrameMetrics().GetZoom());
   ParentLayerCoord origin = aOrigin * zoom;
 
   ParentLayerCoord result;
--- a/gfx/layers/apz/src/GestureEventListener.h
+++ b/gfx/layers/apz/src/GestureEventListener.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_GestureEventListener_h
 #define mozilla_layers_GestureEventListener_h
 
 #include "InputData.h"                  // for MultiTouchInput, etc
 #include "Units.h"
 #include "mozilla/EventForwards.h"      // for nsEventStatus
-#include "nsAutoPtr.h"                  // for nsRefPtr
+#include "mozilla/RefPtr.h"             // for RefPtr
 #include "nsISupportsImpl.h"
 #include "nsTArray.h"                   // for nsTArray
 
 class CancelableTask;
 
 namespace mozilla {
 namespace layers {
 
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -118,17 +118,17 @@ int32_t
 HitTestingTreeNode::GetScrollSize() const
 {
   return mScrollSize;
 }
 
 bool
 HitTestingTreeNode::IsScrollbarNode() const
 {
-  return mIsScrollbarContainer;
+  return mIsScrollbarContainer || (mScrollDir != Layer::NONE);
 }
 
 void
 HitTestingTreeNode::SetPrevSibling(HitTestingTreeNode* aSibling)
 {
   mPrevSibling = aSibling;
   if (aSibling) {
     aSibling->mParent = mParent;
--- a/gfx/layers/apz/src/InputBlockState.h
+++ b/gfx/layers/apz/src/InputBlockState.h
@@ -3,21 +3,21 @@
 /* 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_layers_InputBlockState_h
 #define mozilla_layers_InputBlockState_h
 
 #include "InputData.h"                      // for MultiTouchInput
+#include "mozilla/RefPtr.h"                 // for RefPtr
 #include "mozilla/gfx/Matrix.h"             // for Matrix4x4
 #include "mozilla/layers/APZUtils.h"        // for TouchBehaviorFlags
 #include "mozilla/layers/AsyncDragMetrics.h"
 #include "mozilla/TimeStamp.h"              // for TimeStamp
-#include "nsAutoPtr.h"                      // for nsRefPtr
 #include "nsTArray.h"                       // for nsTArray
 #include "TouchCounter.h"
 
 namespace mozilla {
 namespace layers {
 
 class AsyncPanZoomController;
 class OverscrollHandoffChain;
--- a/gfx/layers/apz/src/InputQueue.h
+++ b/gfx/layers/apz/src/InputQueue.h
@@ -5,18 +5,18 @@
 
 #ifndef mozilla_layers_InputQueue_h
 #define mozilla_layers_InputQueue_h
 
 #include "APZUtils.h"
 #include "DragTracker.h"
 #include "InputData.h"
 #include "mozilla/EventForwards.h"
+#include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
-#include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "TouchCounter.h"
 
 namespace mozilla {
 
 class InputData;
 class MultiTouchInput;
 class ScrollWheelInput;
--- a/gfx/layers/apz/src/OverscrollHandoffState.h
+++ b/gfx/layers/apz/src/OverscrollHandoffState.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_OverscrollHandoffChain_h
 #define mozilla_layers_OverscrollHandoffChain_h
 
 #include <vector>
-#include "nsAutoPtr.h"
+#include "mozilla/RefPtr.h"   // for RefPtr
 #include "nsISupportsImpl.h"  // for NS_INLINE_DECL_THREADSAFE_REFCOUNTING
 #include "APZUtils.h"         // for CancelAnimationFlags
 #include "Layers.h"           // for Layer::ScrollDirection
 #include "Units.h"            // for ScreenPoint
 
 namespace mozilla {
 
 class InputData;
--- a/gfx/layers/composite/ContainerLayerComposite.cpp
+++ b/gfx/layers/composite/ContainerLayerComposite.cpp
@@ -468,16 +468,32 @@ ContainerPrepare(ContainerT* aContainer,
       aContainer->mPrepared->mNeedsSurfaceCopy = true;
       aContainer->mLastIntermediateSurface = nullptr;
     }
   } else {
     aContainer->mLastIntermediateSurface = nullptr;
   }
 }
 
+template <typename RectPainter> void
+DrawRegion(CSSIntRegion* aRegion,
+           gfx::Color aColor,
+           const RectPainter& aRectPainter)
+{
+  MOZ_ASSERT(aRegion);
+
+  // Iterate through and draw the rects in the region using the provided lambda.
+  for (CSSIntRegion::RectIterator iterator = aRegion->RectIter();
+       !iterator.Done();
+       iterator.Next())
+  {
+    aRectPainter(iterator.Get(), aColor);
+  }
+}
+
 template<class ContainerT> void
 RenderMinimap(ContainerT* aContainer, LayerManagerComposite* aManager,
                    const RenderTargetIntRect& aClipRect, Layer* aLayer)
 {
   Compositor* compositor = aManager->GetCompositor();
 
   if (aLayer->GetScrollMetadataCount() < 1) {
     return;
@@ -495,17 +511,18 @@ RenderMinimap(ContainerT* aContainer, La
   const int horizontalPadding = 5;
   gfx::Color backgroundColor(0.3f, 0.3f, 0.3f, 0.3f);
   gfx::Color tileActiveColor(1, 1, 1, 0.4f);
   gfx::Color tileBorderColor(0, 0, 0, 0.1f);
   gfx::Color pageBorderColor(0, 0, 0);
   gfx::Color criticalDisplayPortColor(1.f, 1.f, 0);
   gfx::Color displayPortColor(0, 1.f, 0);
   gfx::Color viewPortColor(0, 0, 1.f, 0.3f);
-  gfx::Color visibilityColor(1.f, 0, 0);
+  gfx::Color approxVisibilityColor(1.f, 0, 0);
+  gfx::Color inDisplayPortVisibilityColor(1.f, 1.f, 0);
 
   // Rects
   const FrameMetrics& fm = aLayer->GetFrameMetrics(0);
   ParentLayerRect compositionBounds = fm.GetCompositionBounds();
   LayerRect scrollRect = fm.GetScrollableRect() * fm.LayersPixelsPerCSSPixel();
   LayerRect viewRect = ParentLayerRect(scrollOffset, compositionBounds.Size()) / LayerToParentLayerScale(1);
   LayerRect dp = (fm.GetDisplayPort() + fm.GetScrollOffset()) * fm.LayersPixelsPerCSSPixel();
   Maybe<LayerRect> cdp;
@@ -554,33 +571,31 @@ RenderMinimap(ContainerT* aContainer, La
   if (gfxPrefs::APZMinimapVisibilityEnabled()) {
     // Retrieve the APZC scrollable layer guid, which we'll use to get the
     // appropriate visibility information from the layer manager.
     AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(0);
     MOZ_ASSERT(controller);
 
     ScrollableLayerGuid guid = controller->GetGuid();
 
-    // Get the approximately visible region.
-    static CSSIntRegion emptyRegion;
-    CSSIntRegion* visibleRegion = aManager->GetApproximatelyVisibleRegion(guid);
-    if (!visibleRegion) {
-      visibleRegion = &emptyRegion;
-    }
+    auto rectPainter = [&](const CSSIntRect& aRect, const gfx::Color& aColor) {
+      LayerRect scaledRect = aRect * fm.LayersPixelsPerCSSPixel();
+      Rect r = transform.TransformBounds(scaledRect.ToUnknownRect());
+      compositor->FillRect(r, aColor, clipRect, aContainer->GetEffectiveTransform());
+    };
 
-    // Iterate through and draw the rects in the region.
-    for (CSSIntRegion::RectIterator iterator = visibleRegion->RectIter();
-         !iterator.Done();
-         iterator.Next())
-    {
-      CSSIntRect rect = iterator.Get();
-      LayerRect scaledRect = rect * fm.LayersPixelsPerCSSPixel();
-      Rect r = transform.TransformBounds(scaledRect.ToUnknownRect());
-      compositor->FillRect(r, visibilityColor, clipRect, aContainer->GetEffectiveTransform());
-    }
+    // Draw the approximately visible region.
+    CSSIntRegion* approxVisibleRegion =
+      aManager->GetVisibleRegion(VisibilityCounter::MAY_BECOME_VISIBLE, guid);
+    DrawRegion(approxVisibleRegion, approxVisibilityColor, rectPainter);
+
+    // Draw the in-displayport visible region.
+    CSSIntRegion* inDisplayPortVisibleRegion =
+      aManager->GetVisibleRegion(VisibilityCounter::IN_DISPLAYPORT, guid);
+    DrawRegion(inDisplayPortVisibleRegion, inDisplayPortVisibilityColor, rectPainter);
   }
 
   // Render the displayport.
   Rect r = transform.TransformBounds(dp.ToUnknownRect());
   compositor->FillRect(r, tileActiveColor, clipRect, aContainer->GetEffectiveTransform());
   compositor->SlowDrawRect(r, displayPortColor, clipRect, aContainer->GetEffectiveTransform());
 
   // Render the critical displayport if there is one
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -28,16 +28,17 @@
 #include "mozilla/RefPtr.h"                   // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nscore.h"                     // for nsAString, etc
 #include "LayerTreeInvalidation.h"
+#include "Visibility.h"
 
 class gfxContext;
 
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 
 namespace mozilla {
@@ -214,39 +215,63 @@ public:
 
   static void PlatformSyncBeforeReplyUpdate();
 
   void AddInvalidRegion(const nsIntRegion& aRegion)
   {
     mInvalidRegion.Or(mInvalidRegion, aRegion);
   }
 
-  void ClearApproximatelyVisibleRegions(uint64_t aLayersId,
-                                        const Maybe<uint32_t>& aPresShellId)
+  void ClearVisibleRegions(uint64_t aLayersId,
+                           const Maybe<uint32_t>& aPresShellId)
   {
-    for (auto iter = mVisibleRegions.Iter(); !iter.Done(); iter.Next()) {
+    for (auto iter = mApproximatelyVisibleRegions.Iter(); !iter.Done(); iter.Next()) {
+      if (iter.Key().mLayersId == aLayersId &&
+          (!aPresShellId || iter.Key().mPresShellId == *aPresShellId)) {
+        iter.Remove();
+      }
+    }
+
+    for (auto iter = mInDisplayPortVisibleRegions.Iter(); !iter.Done(); iter.Next()) {
       if (iter.Key().mLayersId == aLayersId &&
           (!aPresShellId || iter.Key().mPresShellId == *aPresShellId)) {
         iter.Remove();
       }
     }
   }
 
-  void UpdateApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
-                                        const CSSIntRegion& aRegion)
+  void UpdateVisibleRegion(VisibilityCounter aCounter,
+                           const ScrollableLayerGuid& aGuid,
+                           const CSSIntRegion& aRegion)
   {
-    CSSIntRegion* regionForScrollFrame = mVisibleRegions.LookupOrAdd(aGuid);
+    VisibleRegions& regions = aCounter == VisibilityCounter::MAY_BECOME_VISIBLE
+                            ? mApproximatelyVisibleRegions
+                            : mInDisplayPortVisibleRegions;
+
+    CSSIntRegion* regionForScrollFrame = regions.LookupOrAdd(aGuid);
     MOZ_ASSERT(regionForScrollFrame);
 
     *regionForScrollFrame = aRegion;
   }
 
-  CSSIntRegion* GetApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid)
+  CSSIntRegion* GetVisibleRegion(VisibilityCounter aCounter,
+                                 const ScrollableLayerGuid& aGuid)
   {
-    return mVisibleRegions.Get(aGuid);
+    static CSSIntRegion emptyRegion;
+
+    VisibleRegions& regions = aCounter == VisibilityCounter::MAY_BECOME_VISIBLE
+                            ? mApproximatelyVisibleRegions
+                            : mInDisplayPortVisibleRegions;
+
+    CSSIntRegion* region = regions.Get(aGuid);
+    if (!region) {
+      region = &emptyRegion;
+    }
+
+    return region;
   }
 
   Compositor* GetCompositor() const
   {
     return mCompositor;
   }
 
   // Called by CompositorBridgeParent when a new compositor has been created due
@@ -377,17 +402,18 @@ private:
    */
   RefPtr<gfx::DrawTarget> mTarget;
   gfx::IntRect mTargetBounds;
 
   nsIntRegion mInvalidRegion;
 
   typedef nsClassHashtable<nsGenericHashKey<ScrollableLayerGuid>,
                            CSSIntRegion> VisibleRegions;
-  VisibleRegions mVisibleRegions;
+  VisibleRegions mApproximatelyVisibleRegions;
+  VisibleRegions mInDisplayPortVisibleRegions;
 
   UniquePtr<FPSState> mFPS;
 
   bool mInTransaction;
   bool mIsCompositorReady;
   bool mDebugOverlayWantsNextFrame;
 
   RefPtr<CompositingRenderTarget> mTwoPassTmpTarget;
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -724,34 +724,33 @@ CompositorBridgeChild::SendRequestNotify
   MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
     return true;
   }
   return PCompositorBridgeChild::SendRequestNotifyAfterRemotePaint();
 }
 
 bool
-CompositorBridgeChild::SendClearApproximatelyVisibleRegions(uint64_t aLayersId,
-                                                            uint32_t aPresShellId)
+CompositorBridgeChild::SendClearVisibleRegions(uint64_t aLayersId,
+                                               uint32_t aPresShellId)
 {
   MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
     return true;
   }
-  return PCompositorBridgeChild::SendClearApproximatelyVisibleRegions(aLayersId,
-                                                                aPresShellId);
+  return PCompositorBridgeChild::SendClearVisibleRegions(aLayersId, aPresShellId);
 }
 
 bool
-CompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
-                                                            const CSSIntRegion& aRegion)
+CompositorBridgeChild::SendUpdateVisibleRegion(VisibilityCounter aCounter,
+                                               const ScrollableLayerGuid& aGuid,
+                                               const CSSIntRegion& aRegion)
 {
   MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
     return true;
   }
-  return PCompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(aGuid, aRegion);
+  return PCompositorBridgeChild::SendUpdateVisibleRegion(aCounter, aGuid, aRegion);
 }
 
-
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -121,19 +121,20 @@ public:
   bool SendAdoptChild(const uint64_t& id);
   bool SendMakeSnapshot(const SurfaceDescriptor& inSnapshot, const gfx::IntRect& dirtyRect);
   bool SendFlushRendering();
   bool SendGetTileSize(int32_t* tileWidth, int32_t* tileHeight);
   bool SendStartFrameTimeRecording(const int32_t& bufferSize, uint32_t* startIndex);
   bool SendStopFrameTimeRecording(const uint32_t& startIndex, nsTArray<float>* intervals);
   bool SendNotifyRegionInvalidated(const nsIntRegion& region);
   bool SendRequestNotifyAfterRemotePaint();
-  bool SendClearApproximatelyVisibleRegions(uint64_t aLayersId, uint32_t aPresShellId);
-  bool SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
-                                            const mozilla::CSSIntRegion& aRegion);
+  bool SendClearVisibleRegions(uint64_t aLayersId, uint32_t aPresShellId);
+  bool SendUpdateVisibleRegion(VisibilityCounter aCounter,
+                               const ScrollableLayerGuid& aGuid,
+                               const mozilla::CSSIntRegion& aRegion);
   bool IsSameProcess() const;
 
   static void ShutDown();
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CompositorBridgeChild();
 
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -892,46 +892,55 @@ CompositorBridgeParent::RecvStopFrameTim
 {
   if (mLayerManager) {
     mLayerManager->StopFrameTimeRecording(aStartIndex, *intervals);
   }
   return true;
 }
 
 bool
-CompositorBridgeParent::RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
-                                                            const uint32_t& aPresShellId)
+CompositorBridgeParent::RecvClearVisibleRegions(const uint64_t& aLayersId,
+                                                const uint32_t& aPresShellId)
 {
-  ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId));
+  ClearVisibleRegions(aLayersId, Some(aPresShellId));
   return true;
 }
 
 void
-CompositorBridgeParent::ClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
-                                                         const Maybe<uint32_t>& aPresShellId)
+CompositorBridgeParent::ClearVisibleRegions(const uint64_t& aLayersId,
+                                            const Maybe<uint32_t>& aPresShellId)
 {
   if (mLayerManager) {
-    mLayerManager->ClearApproximatelyVisibleRegions(aLayersId, aPresShellId);
+    mLayerManager->ClearVisibleRegions(aLayersId, aPresShellId);
 
     // We need to recomposite to update the minimap.
     ScheduleComposition();
   }
 }
 
 bool
-CompositorBridgeParent::RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
-                                                             const CSSIntRegion& aRegion)
+CompositorBridgeParent::RecvUpdateVisibleRegion(const VisibilityCounter& aCounter,
+                                                const ScrollableLayerGuid& aGuid,
+                                                const CSSIntRegion& aRegion)
+{
+  UpdateVisibleRegion(aCounter, aGuid, aRegion);
+  return true;
+}
+
+void
+CompositorBridgeParent::UpdateVisibleRegion(const VisibilityCounter& aCounter,
+                                            const ScrollableLayerGuid& aGuid,
+                                            const CSSIntRegion& aRegion)
 {
   if (mLayerManager) {
-    mLayerManager->UpdateApproximatelyVisibleRegion(aGuid, aRegion);
+    mLayerManager->UpdateVisibleRegion(aCounter, aGuid, aRegion);
 
     // We need to recomposite to update the minimap.
     ScheduleComposition();
   }
-  return true;
 }
 
 void
 CompositorBridgeParent::ActorDestroy(ActorDestroyReason why)
 {
   CancelCurrentCompositeTask();
   if (mForceCompositionTask) {
     mForceCompositionTask->Cancel();
@@ -1742,17 +1751,17 @@ static void
 EraseLayerState(uint64_t aId)
 {
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
 
   auto iter = sIndirectLayerTrees.find(aId);
   if (iter != sIndirectLayerTrees.end()) {
     CompositorBridgeParent* parent = iter->second.mParent;
     if (parent) {
-      parent->ClearApproximatelyVisibleRegions(aId, Nothing());
+      parent->ClearVisibleRegions(aId, Nothing());
     }
 
     sIndirectLayerTrees.erase(iter);
   }
 }
 
 /*static*/ void
 CompositorBridgeParent::DeallocateLayerTreeId(uint64_t aId)
@@ -1924,41 +1933,48 @@ public:
                                 const gfx::IntRect& aRect) override
   { return true; }
   virtual bool RecvFlushRendering() override { return true; }
   virtual bool RecvForcePresent() override { return true; }
   virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override { return true; }
   virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override { return true; }
   virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override  { return true; }
 
-  virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
-                                                    const uint32_t& aPresShellId) override
+  virtual bool RecvClearVisibleRegions(const uint64_t& aLayersId,
+                                       const uint32_t& aPresShellId) override
   {
     CompositorBridgeParent* parent;
     { // scope lock
       MonitorAutoLock lock(*sIndirectLayerTreesLock);
       parent = sIndirectLayerTrees[aLayersId].mParent;
     }
-    if (parent) {
-      parent->ClearApproximatelyVisibleRegions(aLayersId, Some(aPresShellId));
+
+    if (!parent) {
+      return false;
     }
+
+    parent->ClearVisibleRegions(aLayersId, Some(aPresShellId));
     return true;
   }
 
-  virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
-                                                    const CSSIntRegion& aRegion) override
+  virtual bool RecvUpdateVisibleRegion(const VisibilityCounter& aCounter,
+                                       const ScrollableLayerGuid& aGuid,
+                                       const CSSIntRegion& aRegion) override
   {
     CompositorBridgeParent* parent;
     { // scope lock
       MonitorAutoLock lock(*sIndirectLayerTreesLock);
       parent = sIndirectLayerTrees[aGuid.mLayersId].mParent;
     }
-    if (parent) {
-      return parent->RecvNotifyApproximatelyVisibleRegion(aGuid, aRegion);
+
+    if (!parent) {
+      return false;
     }
+
+    parent->UpdateVisibleRegion(aCounter, aGuid, aRegion);
     return true;
   }
 
   virtual bool RecvGetTileSize(int32_t* aWidth, int32_t* aHeight) override
   {
     *aWidth = gfxPlatform::GetPlatform()->GetTileWidth();
     *aHeight = gfxPlatform::GetPlatform()->GetTileHeight();
     return true;
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -249,22 +249,26 @@ public:
   virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override;
   virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override;
   virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override;
 
   // Unused for chrome <-> compositor communication (which this class does).
   // @see CrossProcessCompositorBridgeParent::RecvRequestNotifyAfterRemotePaint
   virtual bool RecvRequestNotifyAfterRemotePaint() override { return true; };
 
-  virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
-                                                    const uint32_t& aPresShellId) override;
-  void ClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
-                                        const Maybe<uint32_t>& aPresShellId);
-  virtual bool RecvNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
-                                                    const CSSIntRegion& aRegion) override;
+  virtual bool RecvClearVisibleRegions(const uint64_t& aLayersId,
+                                       const uint32_t& aPresShellId) override;
+  void ClearVisibleRegions(const uint64_t& aLayersId,
+                           const Maybe<uint32_t>& aPresShellId);
+  virtual bool RecvUpdateVisibleRegion(const VisibilityCounter& aCounter,
+                                       const ScrollableLayerGuid& aGuid,
+                                       const CSSIntRegion& aRegion) override;
+  void UpdateVisibleRegion(const VisibilityCounter& aCounter,
+                           const ScrollableLayerGuid& aGuid,
+                           const CSSIntRegion& aRegion);
 
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                    const uint64_t& aTransactionId,
                                    const TargetConfig& aTargetConfig,
                                    const InfallibleTArray<PluginWindowData>& aPlugins,
                                    bool aIsFirstPaint,
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -20,16 +20,17 @@ using mozilla::layers::MaybeZoomConstrai
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
 using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
 using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
 using mozilla::CSSIntRegion from "Units.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using mozilla::LayoutDeviceIntRegion from "Units.h";
+using mozilla::VisibilityCounter from "VisibilityIPC.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h";
 
 namespace mozilla {
 namespace layers {
 
 
 /**
@@ -168,25 +169,28 @@ parent:
   /**
    * The child (content/chrome thread) requests that the parent inform it when
    * the graphics objects are ready to display.
    * @see PBrowser
    * @see RemotePaintIsReady
    */
   async RequestNotifyAfterRemotePaint();
 
-  // The child clears the 'approximately visible' regions associated with the
-  // provided layers ID and pres shell ID (i.e., the regions for all view IDs
-  // associated with those IDs).
-  async ClearApproximatelyVisibleRegions(uint64_t layersId, uint32_t presShellId);
+  // The child sends a request to clear the visible regions (approximate,
+  // in-displayport, etc.) associated with the provided layers ID and pres shell
+  // ID (i.e., the regions for all view IDs associated with those IDs).
+  async ClearVisibleRegions(uint64_t layersId, uint32_t presShellId);
 
   // The child sends a region containing rects associated with the provided
-  // scrollable layer GUID that the child considers 'approximately visible'.
+  // scrollable layer GUID that the child considers visible in the sense
+  // specified by |counter|.
   // We visualize this information in the APZ minimap.
-  async NotifyApproximatelyVisibleRegion(ScrollableLayerGuid guid, CSSIntRegion region);
+  async UpdateVisibleRegion(VisibilityCounter counter,
+                            ScrollableLayerGuid guid,
+                            CSSIntRegion region);
 
 child:
   // Send back Compositor Frame Metrics from APZCs so tiled layers can
   // update progressively.
   async SharedCompositorFrameMetrics(Handle metrics, CrossProcessMutexHandle mutex, uint64_t aLayersId, uint32_t aAPZCId);
   async ReleaseSharedCompositorFrameMetrics(ViewID aId, uint32_t aAPZCId);
 };
 
--- a/gfx/src/DriverCrashGuard.cpp
+++ b/gfx/src/DriverCrashGuard.cpp
@@ -293,17 +293,18 @@ DriverCrashGuard::UpdateBaseEnvironment(
 
 bool
 DriverCrashGuard::FeatureEnabled(int aFeature, bool aDefault)
 {
   if (!mGfxInfo) {
     return aDefault;
   }
   int32_t status;
-  if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, &status))) {
+  nsCString discardFailureId;
+  if (!NS_SUCCEEDED(mGfxInfo->GetFeatureStatus(aFeature, discardFailureId, &status))) {
     return false;
   }
   return status == nsIGfxInfo::FEATURE_STATUS_OK;
 }
 
 bool
 DriverCrashGuard::CheckAndUpdateBoolPref(const char* aPrefName, bool aCurrentValue)
 {
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1743,24 +1743,16 @@ gfxFont::CalcXScale(DrawTarget* aDrawTar
     if (m == 0.0) {
         return 0.0; // effectively disables offset
     }
 
     // scale factor so that offsets are 1px in device pixels
     return 1.0 / m;
 }
 
-static DrawMode
-ForcePaintingDrawMode(DrawMode aDrawMode)
-{
-    return aDrawMode == DrawMode::GLYPH_PATH ?
-        DrawMode(int(DrawMode::GLYPH_FILL) | int(DrawMode::GLYPH_STROKE)) :
-        aDrawMode;
-}
-
 // Draw an individual glyph at a specific location.
 // *aPt is the glyph position in appUnits; it is converted to device
 // coordinates (devPt) here.
 void
 gfxFont::DrawOneGlyph(uint32_t aGlyphID, double aAdvance, gfxPoint *aPt,
                       GlyphBufferAzure& aBuffer, bool *aEmittedGlyphs) const
 {
     const TextRunDrawParams& runParams(aBuffer.mRunParams);
@@ -1788,18 +1780,19 @@ gfxFont::DrawOneGlyph(uint32_t aGlyphID,
     }
     gfxPoint devPt(ToDeviceUnits(glyphX, runParams.devPerApp),
                    ToDeviceUnits(glyphY, runParams.devPerApp));
 
     if (fontParams.haveSVGGlyphs) {
         if (!runParams.paintSVGGlyphs) {
             return;
         }
-        DrawMode mode = ForcePaintingDrawMode(runParams.drawMode);
-        if (RenderSVGGlyph(runParams.context, devPt, mode,
+        NS_WARN_IF_FALSE(runParams.drawMode != DrawMode::GLYPH_PATH,
+                         "Rendering SVG glyph despite request for glyph path");
+        if (RenderSVGGlyph(runParams.context, devPt,
                            aGlyphID, fontParams.contextPaint,
                            runParams.callbacks, *aEmittedGlyphs)) {
             return;
         }
     }
 
     if (fontParams.haveColorGlyphs &&
         RenderColorGlyph(runParams.dt,
@@ -2141,17 +2134,17 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
             *aPt = gfxPoint(origPt.x, origPt.y - advance);
         } else {
             *aPt = gfxPoint(origPt.x, origPt.y + advance);
         }
     }
 }
 
 bool
-gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
+gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
                         uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) const
 {
     if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
         return false;
     }
 
     const gfxFloat devUnitsPerSVGUnit =
         GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
@@ -2160,33 +2153,33 @@ gfxFont::RenderSVGGlyph(gfxContext *aCon
     aContext->Save();
     aContext->SetMatrix(
       aContext->CurrentMatrix().Translate(aPoint.x, aPoint.y).
                                 Scale(devUnitsPerSVGUnit, devUnitsPerSVGUnit));
 
     aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit);
 
     bool rv = GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId,
-                                             int(aDrawMode), aContextPaint);
+                                             aContextPaint);
     aContext->Restore();
     aContext->NewPath();
     return rv;
 }
 
 bool
-gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
+gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
                         uint32_t aGlyphId, gfxTextContextPaint *aContextPaint,
                         gfxTextRunDrawCallbacks *aCallbacks,
                         bool& aEmittedGlyphs) const
 {
     if (aCallbacks && aEmittedGlyphs) {
         aCallbacks->NotifyGlyphPathEmitted();
         aEmittedGlyphs = false;
     }
-    return RenderSVGGlyph(aContext, aPoint, aDrawMode, aGlyphId, aContextPaint);
+    return RenderSVGGlyph(aContext, aPoint, aGlyphId, aContextPaint);
 }
 
 bool
 gfxFont::RenderColorGlyph(DrawTarget* aDrawTarget,
                           mozilla::gfx::ScaledFont* scaledFont,
                           GlyphRenderingOptions* aRenderingOptions,
                           mozilla::gfx::DrawOptions aDrawOptions,
                           const mozilla::gfx::Point& aPoint,
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -2129,19 +2129,19 @@ protected:
     // Helper to calculate various derived metrics from the results of
     // InitMetricsFromSfntTables or equivalent platform code
     void CalculateDerivedMetrics(Metrics& aMetrics);
 
     // some fonts have bad metrics, this method sanitize them.
     // if this font has bad underline offset, aIsBadUnderlineFont should be true.
     void SanitizeMetrics(Metrics *aMetrics, bool aIsBadUnderlineFont);
 
-    bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
+    bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
                         uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) const;
-    bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
+    bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint,
                         uint32_t aGlyphId, gfxTextContextPaint *aContextPaint,
                         gfxTextRunDrawCallbacks *aCallbacks,
                         bool& aEmittedGlyphs) const;
 
     bool RenderColorGlyph(DrawTarget* aDrawTarget,
                           mozilla::gfx::ScaledFont* scaledFont,
                           mozilla::gfx::GlyphRenderingOptions* renderingOptions,
                           mozilla::gfx::DrawOptions drawOptions,
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -347,21 +347,20 @@ gfxFontEntry::GetSVGGlyphExtents(DrawTar
                             fontMatrix.x0, fontMatrix.y0);
     svgToAppSpace.Scale(1.0f / mUnitsPerEm, 1.0f / mUnitsPerEm);
 
     return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToAppSpace, aResult);
 }
 
 bool
 gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
-                             int aDrawMode, gfxTextContextPaint *aContextPaint)
+                             gfxTextContextPaint *aContextPaint)
 {
     NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
-    return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, DrawMode(aDrawMode),
-                                   aContextPaint);
+    return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, aContextPaint);
 }
 
 bool
 gfxFontEntry::TryGetSVGData(gfxFont* aFont)
 {
     if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
         return false;
     }
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -10,17 +10,16 @@
 #include "nsString.h"
 #include "gfxFontConstants.h"
 #include "gfxFontFeatures.h"
 #include "gfxFontUtils.h"
 #include "nsTArray.h"
 #include "nsTHashtable.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/MemoryReporting.h"
-#include "DrawMode.h"
 #include "nsUnicodeScriptCodes.h"
 #include "nsDataHashtable.h"
 #include "harfbuzz/hb.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/UniquePtr.h"
 
 typedef struct gr_face gr_face;
 
@@ -179,17 +178,17 @@ public:
     // gfxCharacterMap, even if empty, as other code assumes this pointer
     // can be safely dereferenced.
     virtual nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr);
 
     bool TryGetSVGData(gfxFont* aFont);
     bool HasSVGGlyph(uint32_t aGlyphId);
     bool GetSVGGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphId,
                             gfxRect *aResult);
-    bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, int aDrawMode,
+    bool RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
                         gfxTextContextPaint *aContextPaint);
     // Call this when glyph geometry or rendering has changed
     // (e.g. animated SVG glyphs)
     void NotifyGlyphsChanged();
 
     enum MathConstant {
         // The order of the constants must match the order of the fields
         // defined in the MATH table.
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1238,18 +1238,20 @@ gfxPlatform::SupportsAzureContentForDraw
 }
 
 bool gfxPlatform::UseAcceleratedCanvas()
 {
   // Allow acceleration on Skia if the preference is set, unless it's blocked
   if (mPreferredCanvasBackend == BackendType::SKIA && gfxPrefs::CanvasAzureAccelerated()) {
     nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
     int32_t status;
+    nsCString discardFailureId;
     return !gfxInfo ||
       (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION,
+                                              discardFailureId,
                                               &status)) &&
        status == nsIGfxInfo::FEATURE_STATUS_OK);
   }
   return false;
 }
 
 void
 gfxPlatform::InitializeSkiaCacheLimits()
@@ -2069,51 +2071,52 @@ InitLayersAccelerationPrefs()
     // this before the compositor thread, but let's at least make the assumption
     // explicit.
     MOZ_ASSERT(NS_IsMainThread(), "can only initialize prefs on the main thread");
 
     gfxPrefs::GetSingleton();
     sPrefBrowserTabsRemoteAutostart = BrowserTabsRemoteAutostart();
 
     nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+    nsCString discardFailureId;
     int32_t status;
 #ifdef XP_WIN
     if (gfxPrefs::LayersAccelerationForceEnabled()) {
       sLayersSupportsD3D9 = true;
       sLayersSupportsD3D11 = true;
     } else if (!gfxPrefs::LayersAccelerationDisabled() && gfxInfo) {
-      if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &status))) {
+      if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, discardFailureId, &status))) {
         if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
           MOZ_ASSERT(!sPrefBrowserTabsRemoteAutostart || IsVistaOrLater());
           sLayersSupportsD3D9 = true;
         }
       }
-      if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
+      if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, discardFailureId, &status))) {
         if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
           sLayersSupportsD3D11 = true;
         }
       }
       if (!gfxPrefs::LayersD3D11DisableWARP()) {
         // Always support D3D11 when WARP is allowed.
         sLayersSupportsD3D11 = true;
       }
-      if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, &status))) {
+      if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, discardFailureId, &status))) {
         if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
           gANGLESupportsD3D11 = true;
         }
       }
     }
 #endif
 
     if (Preferences::GetBool("media.hardware-video-decoding.enabled", false) &&
 #ifdef XP_WIN
         Preferences::GetBool("media.windows-media-foundation.use-dxva", true) &&
 #endif
         NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
-                                               &status))) {
+                                               discardFailureId, &status))) {
         if (status == nsIGfxInfo::FEATURE_STATUS_OK || gfxPrefs::HardwareVideoDecodingForceEnabled()) {
            sLayersSupportsHardwareVideoDecoding = true;
       }
     }
 
     Preferences::AddBoolVarCache(&sLayersHardwareVideoDecodingFailed,
                                  "media.hardware-video-decoding.failed",
                                  false);
@@ -2346,17 +2349,18 @@ AllowOpenGL(bool* aWhitelisted)
   if (gfxInfo) {
     // bug 655578: on X11 at least, we must always call GetData (even if we don't need that information)
     // as that's what causes GfxInfo initialization which kills the zombie 'glxtest' process.
     // initially we relied on the fact that GetFeatureStatus calls GetData for us, but bug 681026 showed
     // that assumption to be unsafe.
     gfxInfo->GetData();
 
     int32_t status;
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_OPENGL_LAYERS, &status))) {
+    nsCString discardFailureId;
+    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_OPENGL_LAYERS, discardFailureId, &status))) {
       if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
         *aWhitelisted = true;
         return true;
       }
     }
   }
 
   return gfxPrefs::LayersAccelerationForceEnabled();
--- a/gfx/thebes/gfxSVGGlyphs.cpp
+++ b/gfx/thebes/gfxSVGGlyphs.cpp
@@ -204,33 +204,28 @@ gfxSVGGlyphsDocument::FindGlyphElements(
     InsertGlyphId(aElem);
 }
 
 /**
  * If there exists an SVG glyph with the specified glyph id, render it and return true
  * If no such glyph exists, or in the case of an error return false
  * @param aContext The thebes aContext to draw to
  * @param aGlyphId The glyph id
- * @param aDrawMode Whether to fill or stroke or both (see |DrawMode|)
  * @return true iff rendering succeeded
  */
 bool
 gfxSVGGlyphs::RenderGlyph(gfxContext *aContext, uint32_t aGlyphId,
-                          DrawMode aDrawMode, gfxTextContextPaint *aContextPaint)
+                          gfxTextContextPaint *aContextPaint)
 {
-    if (aDrawMode == DrawMode::GLYPH_PATH) {
-        return false;
-    }
-
     gfxContextAutoSaveRestore aContextRestorer(aContext);
 
     Element *glyph = mGlyphIdMap.Get(aGlyphId);
     NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
 
-    return nsSVGUtils::PaintSVGGlyph(glyph, aContext, aDrawMode, aContextPaint);
+    return nsSVGUtils::PaintSVGGlyph(glyph, aContext, aContextPaint);
 }
 
 bool
 gfxSVGGlyphs::GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace,
                               gfxRect *aResult)
 {
     Element *glyph = mGlyphIdMap.Get(aGlyphId);
     NS_ASSERTION(glyph, "No glyph element. Should check with HasSVGGlyph() first!");
--- a/gfx/thebes/gfxSVGGlyphs.h
+++ b/gfx/thebes/gfxSVGGlyphs.h
@@ -9,17 +9,16 @@
 #include "mozilla/gfx/2D.h"
 #include "nsString.h"
 #include "nsClassHashtable.h"
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
 #include "gfxPattern.h"
 #include "mozilla/gfx/UserData.h"
 #include "nsRefreshDriver.h"
-#include "DrawMode.h"
 
 class nsIDocument;
 class nsIContentViewer;
 class nsIPresShell;
 class gfxSVGGlyphs;
 class gfxTextContextPaint;
 
 namespace mozilla {
@@ -110,21 +109,20 @@ public:
 
     /**
      * Return true iff there is an SVG glyph for |aGlyphId|
      */
     bool HasSVGGlyph(uint32_t aGlyphId);
 
     /**
      * Render the SVG glyph for |aGlyphId|
-     * @param aDrawMode Whether to fill or stroke or both; see DrawMode
      * @param aContextPaint Information on text context paints.
      *   See |gfxTextContextPaint|.
      */
-    bool RenderGlyph(gfxContext *aContext, uint32_t aGlyphId, DrawMode aDrawMode,
+    bool RenderGlyph(gfxContext *aContext, uint32_t aGlyphId,
                      gfxTextContextPaint *aContextPaint);
 
     /**
      * Get the extents for the SVG glyph associated with |aGlyphId|
      * @param aSVGToAppSpace The matrix mapping the SVG glyph space to the
      *   target context space
      */
     bool GetGlyphExtents(uint32_t aGlyphId, const gfxMatrix& aSVGToAppSpace,
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -1495,71 +1495,77 @@ gfxUtils::GetInputStream(gfx::DataSource
 }
 
 class GetFeatureStatusRunnable final : public dom::workers::WorkerMainThreadRunnable
 {
 public:
     GetFeatureStatusRunnable(dom::workers::WorkerPrivate* workerPrivate,
                              const nsCOMPtr<nsIGfxInfo>& gfxInfo,
                              int32_t feature,
+                             nsACString& failureId,
                              int32_t* status)
       : WorkerMainThreadRunnable(workerPrivate)
       , mGfxInfo(gfxInfo)
       , mFeature(feature)
       , mStatus(status)
+      , mFailureId(failureId)
       , mNSResult(NS_OK)
     {
     }
 
     bool MainThreadRun() override
     {
       if (mGfxInfo) {
-        mNSResult = mGfxInfo->GetFeatureStatus(mFeature, mStatus);
+        mNSResult = mGfxInfo->GetFeatureStatus(mFeature, mFailureId, mStatus);
       }
       return true;
     }
 
     nsresult GetNSResult() const
     {
       return mNSResult;
     }
 
 protected:
     ~GetFeatureStatusRunnable() {}
 
 private:
     nsCOMPtr<nsIGfxInfo> mGfxInfo;
     int32_t mFeature;
     int32_t* mStatus;
+    nsACString& mFailureId;
     nsresult mNSResult;
 };
 
 /* static */ nsresult
 gfxUtils::ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
-                                     int32_t feature, int32_t* status)
+                                     int32_t feature, nsACString& failureId,
+                                     int32_t* status)
 {
   if (!NS_IsMainThread()) {
     dom::workers::WorkerPrivate* workerPrivate =
       dom::workers::GetCurrentThreadWorkerPrivate();
+
     RefPtr<GetFeatureStatusRunnable> runnable =
-      new GetFeatureStatusRunnable(workerPrivate, gfxInfo, feature, status);
+      new GetFeatureStatusRunnable(workerPrivate, gfxInfo, feature, failureId,
+                                   status);
 
     ErrorResult rv;
     runnable->Dispatch(rv);
     if (rv.Failed()) {
         // XXXbz This is totally broken, since we're supposed to just abort
         // everything up the callstack but the callers basically eat the
         // exception.  Ah, well.
         return rv.StealNSResult();
     }
 
     return runnable->GetNSResult();
   }
 
-  return gfxInfo->GetFeatureStatus(feature, status);
+  return gfxInfo->GetFeatureStatus(feature, failureId, status);
 }
 
 /* static */ bool
 gfxUtils::DumpDisplayList() {
   return gfxPrefs::LayoutDumpDisplayList() ||
          (gfxPrefs::LayoutDumpDisplayListContent() && XRE_IsContentProcess());
 }
 
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -286,16 +286,17 @@ public:
     static nsresult GetInputStream(DataSourceSurface* aSurface,
                                    bool aIsAlphaPremultiplied,
                                    const char* aMimeType,
                                    const char16_t* aEncoderOptions,
                                    nsIInputStream** outStream);
 
     static nsresult ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
                                                int32_t feature,
+                                               nsACString& failureId,
                                                int32_t* status);
 
     /**
      * Copy to the clipboard as a PNG encoded Data URL.
      */
     static void CopyAsDataURI(SourceSurface* aSourceSurface);
     static void CopyAsDataURI(DrawTarget* aDT);
 
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1961,17 +1961,18 @@ gfxWindowsPlatform::CheckD3D11Support(bo
   }
   if (gfxPrefs::LayersAccelerationForceEnabled()) {
     *aCanUseHardware = true;
     return FeatureStatus::Available;
   }
 
   if (nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo()) {
     int32_t status;
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) {
+    nsCString discardFailureId;
+    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, discardFailureId, &status))) {
       if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
         if (CanUseWARP()) {
           *aCanUseHardware = false;
           return FeatureStatus::Available;
         }
         return FeatureStatus::Blacklisted;
       }
     }
@@ -2440,17 +2441,18 @@ gfxWindowsPlatform::ResetD3D11Devices()
 }
 
 static bool
 IsD2DBlacklisted()
 {
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   if (gfxInfo) {
     int32_t status;
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) {
+    nsCString discardFailureId;
+    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, discardFailureId, &status))) {
       if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
         return true;
       }
     }
   }
   return false;
 }
 
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -16,16 +16,21 @@
 #include "mozilla/ipc/Transport.h"
 #include "mozilla/StaticMutex.h"
 
 #if defined(MOZ_SANDBOX) && defined(XP_WIN)
 #define TARGET_SANDBOX_EXPORTS
 #include "mozilla/sandboxTarget.h"
 #endif
 
+#if defined(MOZ_CRASHREPORTER) && defined(XP_WIN)
+#include "Aclapi.h"
+#include "Sddl.h"
+#endif
+
 using namespace IPC;
 
 using base::GetCurrentProcId;
 using base::ProcessHandle;
 using base::ProcessId;
 
 namespace mozilla {
 namespace ipc {
@@ -295,28 +300,111 @@ bool DuplicateHandle(HANDLE aSourceHandl
 
   return !!::DuplicateHandle(::GetCurrentProcess(), aSourceHandle,
                               targetProcess, aTargetHandle,
                               aDesiredAccess, FALSE, aOptions);
 }
 #endif
 
 #ifdef MOZ_CRASHREPORTER
-void AnnotateSystemError()
+void
+AnnotateSystemError()
 {
   int64_t error = 0;
 #if defined(XP_WIN)
   error = ::GetLastError();
 #elif defined(OS_POSIX)
   error = errno;
 #endif
   if (error) {
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("SystemError"),
-                                       nsPrintfCString("%lld", error));
+    CrashReporter::AnnotateCrashReport(
+      NS_LITERAL_CSTRING("IPCSystemError"),
+      nsPrintfCString("%lld", error));
+  }
+}
+
+void
+AnnotateProcessInformation(base::ProcessId aPid)
+{
+#ifdef XP_WIN
+  HANDLE processHandle = OpenProcess(READ_CONTROL|PROCESS_QUERY_INFORMATION, FALSE, aPid);
+  if (!processHandle) {
+    CrashReporter::AnnotateCrashReport(
+      NS_LITERAL_CSTRING("IPCExtraSystemError"),
+      nsPrintfCString("Failed to get information of process %d, error(%d)",
+                      aPid,
+                      ::GetLastError()));
+    return;
+  }
+
+  DWORD exitCode = 0;
+  if (!::GetExitCodeProcess(processHandle, &exitCode)) {
+    CrashReporter::AnnotateCrashReport(
+      NS_LITERAL_CSTRING("IPCExtraSystemError"),
+      nsPrintfCString("Failed to get exit information of process %d, error(%d)",
+                      aPid,
+                      ::GetLastError()));
+    return;
+  }
+
+  if (exitCode != STILL_ACTIVE) {
+    CrashReporter::AnnotateCrashReport(
+      NS_LITERAL_CSTRING("IPCExtraSystemError"),
+      nsPrintfCString("Process %d is not alive. Exit code: %d",
+                      aPid,
+                      exitCode));
+    return;
   }
+
+  PSECURITY_DESCRIPTOR secDesc = nullptr;
+  PSID ownerSid = nullptr;
+  DWORD rv = ::GetSecurityInfo(processHandle,
+                               SE_KERNEL_OBJECT,
+                               OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
+                               &ownerSid,
+                               nullptr,
+                               nullptr,
+                               nullptr,
+                               &secDesc);
+  if (rv != ERROR_SUCCESS) {
+    // GetSecurityInfo() failed.
+    CrashReporter::AnnotateCrashReport(
+      NS_LITERAL_CSTRING("IPCExtraSystemError"),
+      nsPrintfCString("Failed to get security information of process %d,"
+                      " error(%d)",
+                      aPid,
+                      rv));
+    return;
+  }
+
+  LPTSTR ownerSidStr = nullptr;
+  nsString annotation{};
+  annotation.AppendLiteral("Owner: ");
+  if (::ConvertSidToStringSid(ownerSid, &ownerSidStr)) {
+    annotation.Append(ownerSidStr);
+  }
+  ::LocalFree(ownerSidStr);
+
+  LPTSTR secDescStr = nullptr;
+  annotation.AppendLiteral(", Security Descriptor: ");
+  if (::ConvertSecurityDescriptorToStringSecurityDescriptor(secDesc,
+                                                            SDDL_REVISION_1,
+                                                            DACL_SECURITY_INFORMATION,
+                                                            &secDescStr,
+                                                            nullptr)) {
+    annotation.Append(secDescStr);
+  }
+
+  CrashReporter::AnnotateCrashReport(
+    NS_LITERAL_CSTRING("IPCExtraSystemError"),
+    NS_ConvertUTF16toUTF8(annotation));
+
+  ::LocalFree(secDescStr);
+  ::LocalFree(secDesc);
+#endif
 }
 #endif
 
 void
 LogMessageForProtocol(const char* aTopLevelProtocol, base::ProcessId aOtherPid,
                       const char* aContextDescription,
                       const char* aMessageDescription,
                       MessageDirection aDirection)
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -349,23 +349,25 @@ bool
 DuplicateHandle(HANDLE aSourceHandle,
                 DWORD aTargetProcessId,
                 HANDLE* aTargetHandle,
                 DWORD aDesiredAccess,
                 DWORD aOptions);
 #endif
 
 /**
- * Annotate the crash reporter with the error error code from the most recent
- * system call.
+ * Annotate the crash reporter with the error code from the most recent system
+ * call. Returns the system error.
  */
 #ifdef MOZ_CRASHREPORTER
 void AnnotateSystemError();
+void AnnotateProcessInformation(base::ProcessId aPid);
 #else
 #define AnnotateSystemError() do { } while (0)
+#define AnnotateProcessInformation(...) do { } while (0)
 #endif
 
 /**
  * An endpoint represents one end of a partially initialized IPDL channel. To
  * set up a new top-level protocol:
  *
  * Endpoint<PFooParent> parentEp;
  * Endpoint<PFooChild> childEp;
--- a/ipc/glue/Transport_win.cpp
+++ b/ipc/glue/Transport_win.cpp
@@ -61,16 +61,17 @@ TransferHandleToProcess(HANDLE source, b
     return source;
   }
   HANDLE handleDup;
   DWORD access = 0;
   DWORD options = DUPLICATE_SAME_ACCESS;
   bool ok = DuplicateHandle(source, pid, &handleDup, access, options);
   if (!ok) {
     AnnotateSystemError();
+    AnnotateProcessInformation(pid);
   }
   MOZ_RELEASE_ASSERT(ok);
 
   // Now close our own copy of the handle (we're supposed to be transferring,
   // not copying).
   CloseHandle(source);
 
   return handleDup;
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -1558,16 +1558,43 @@ class HashTable : private AllocPolicy
 
         // TODO: this algorithm leaves collision bits on *all* elements, even if
         // they are on no collision path. We have the option of setting the
         // collision bits correctly on a subsequent pass or skipping the rehash
         // unless we are totally filled with tombstones: benchmark to find out
         // which approach is best.
     }
 
+    // Note: |l| may be a reference to a piece of |u|, so this function
+    // must take care not to use |l| after moving |u|.
+    //
+    // Prefer to use putNewInfallible; this function does not check
+    // invariants.
+    template <typename... Args>
+    void putNewInfallibleInternal(const Lookup& l, Args&&... args)
+    {
+        MOZ_ASSERT(table);
+
+        HashNumber keyHash = prepareHash(l);
+        Entry* entry = &findFreeEntry(keyHash);
+        MOZ_ASSERT(entry);
+
+        if (entry->isRemoved()) {
+            METER(stats.addOverRemoved++);
+            removedCount--;
+            keyHash |= sCollisionBit;
+        }
+
+        entry->setLive(keyHash, mozilla::Forward<Args>(args)...);
+        entryCount++;
+#ifdef JS_DEBUG
+        mutationCount++;
+#endif
+    }
+
   public:
     void clear()
     {
         if (mozilla::IsPod<Entry>::value) {
             memset(table, 0, sizeof(*table) * capacity());
         } else {
             uint32_t tableCapacity = capacity();
             Entry* end = table + tableCapacity;
@@ -1698,33 +1725,19 @@ class HashTable : private AllocPolicy
         return true;
     }
 
     // Note: |l| may be a reference to a piece of |u|, so this function
     // must take care not to use |l| after moving |u|.
     template <typename... Args>
     void putNewInfallible(const Lookup& l, Args&&... args)
     {
-        MOZ_ASSERT(table);
-
-        HashNumber keyHash = prepareHash(l);
-        Entry* entry = &findFreeEntry(keyHash);
-        MOZ_ASSERT(entry);
-
-        if (entry->isRemoved()) {
-            METER(stats.addOverRemoved++);
-            removedCount--;
-            keyHash |= sCollisionBit;
-        }
-
-        entry->setLive(keyHash, mozilla::Forward<Args>(args)...);
-        entryCount++;
-#ifdef JS_DEBUG
-        mutationCount++;
-#endif
+        MOZ_ASSERT(!lookup(l).found());
+        mozilla::ReentrancyGuard g(*this);
+        putNewInfallibleInternal(l, mozilla::Forward<Args>(args)...);
     }
 
     // Note: |l| may be alias arguments in |args|, so this function must take
     // care not to use |l| after moving |args|.
     template <typename... Args>
     MOZ_WARN_UNUSED_RESULT bool putNew(const Lookup& l, Args&&... args)
     {
         if (!this->checkSimulatedOOM())
@@ -1766,17 +1779,17 @@ class HashTable : private AllocPolicy
     void rekeyWithoutRehash(Ptr p, const Lookup& l, const Key& k)
     {
         MOZ_ASSERT(table);
         mozilla::ReentrancyGuard g(*this);
         MOZ_ASSERT(p.found());
         typename HashTableEntry<T>::NonConstT t(mozilla::Move(*p));
         HashPolicy::setKey(t, const_cast<Key&>(k));
         remove(*p.entry_);
-        putNewInfallible(l, mozilla::Move(t));
+        putNewInfallibleInternal(l, mozilla::Move(t));
     }
 
     void rekeyAndMaybeRehash(Ptr p, const Lookup& l, const Key& k)
     {
         rekeyWithoutRehash(p, l, k);
         checkOverRemoved();
     }
 
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -2348,16 +2348,17 @@ intl_toSource(JSContext* cx, unsigned ar
     return true;
 }
 #endif
 
 static const JSFunctionSpec intl_static_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,  intl_toSource,        0, 0),
 #endif
+    JS_SELF_HOSTED_FN("getCanonicalLocales", "Intl_getCanonicalLocales", 1, 0),
     JS_FS_END
 };
 
 /**
  * Initializes the Intl Object and its standard built-in properties.
  * Spec: ECMAScript Internationalization API Specification, 8.0, 8.1
  */
 JSObject*
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -2879,8 +2879,22 @@ function resolveICUPattern(pattern, resu
                 _DefineDataProperty(result, icuPatternCharToComponent[c], value);
             if (c === "h" || c === "K")
                 _DefineDataProperty(result, "hour12", true);
             else if (c === "H" || c === "k")
                 _DefineDataProperty(result, "hour12", false);
         }
     }
 }
+
+function Intl_getCanonicalLocales(locales) {
+  let codes = CanonicalizeLocaleList(locales);
+  let result = [];
+
+  let len = codes.length;
+  let k = 0;
+
+  while (k < len) {
+    _DefineDataProperty(result, k, codes[k]);
+    k++;
+  }
+  return result;
+}
--- a/js/src/builtin/RegExp.js
+++ b/js/src/builtin/RegExp.js
@@ -180,17 +180,22 @@ function RegExpReplace(string, replaceVa
     // Step 5.
     var functionalReplace = IsCallable(replaceValue);
 
     // Step 6.
     var firstDollarIndex = -1;
     if (!functionalReplace) {
         // Step 6.a.
         replaceValue = ToString(replaceValue);
-        firstDollarIndex = callFunction(std_String_indexOf, replaceValue, "$");
+
+        // Skip if replaceValue is an empty string or a single character.
+        // A single character string may contain "$", but that cannot be a
+        // substitution.
+        if (replaceValue.length > 1)
+            firstDollarIndex = callFunction(std_String_indexOf, replaceValue, "$");
     }
 
     // Step 7.
     var global = !!rx.global;
 
     // Optimized paths for simple cases.
     if (!functionalReplace && firstDollarIndex === -1 && IsRegExpMethodOptimizable(rx)) {
         if (global) {
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -400,19 +400,16 @@ FillLanes(JSContext* cx, Handle<TypedObj
 }
 
 bool
 SimdTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>());
-    MOZ_ASSERT(size_t(static_cast<TypeDescr*>(descr)->size()) <= InlineTypedObject::MaximumSize,
-               "inline storage is needed for using InternalHandle belows");
-
     Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, descr, 0));
     if (!result)
         return false;
 
 #define CASE_CALL_(Type) \
       case SimdType::Type:   return FillLanes< ::Type>(cx, result, args);
 
     switch (descr->type()) {
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -639,16 +639,19 @@ ArrayMetaTypeDescr::create(JSContext* cx
     obj->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
 
     if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
         return nullptr;
 
     if (!CreateTraceList(cx, obj))
         return nullptr;
 
+    if (!cx->zone()->typeDescrObjects.put(obj))
+        return nullptr;
+
     return obj;
 }
 
 bool
 ArrayMetaTypeDescr::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -935,42 +938,37 @@ StructMetaTypeDescr::create(JSContext* c
     // Construct for internal use an array with the name for each field.
     {
         RootedObject fieldNamesVec(cx);
         fieldNamesVec = NewDenseCopiedArray(cx, fieldNames.length(),
                                             fieldNames.begin(), nullptr,
                                             TenuredObject);
         if (!fieldNamesVec)
             return nullptr;
-        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES,
-                                     ObjectValue(*fieldNamesVec));
+        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_NAMES, ObjectValue(*fieldNamesVec));
     }
 
     // Construct for internal use an array with the type object for each field.
-    {
-        RootedObject fieldTypeVec(cx);
-        fieldTypeVec = NewDenseCopiedArray(cx, fieldTypeObjs.length(),
-                                           fieldTypeObjs.begin(), nullptr,
-                                           TenuredObject);
-        if (!fieldTypeVec)
-            return nullptr;
-        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES,
-                                     ObjectValue(*fieldTypeVec));
-    }
+    RootedObject fieldTypeVec(cx);
+    fieldTypeVec = NewDenseCopiedArray(cx, fieldTypeObjs.length(),
+                                       fieldTypeObjs.begin(), nullptr,
+                                       TenuredObject);
+    if (!fieldTypeVec)
+        return nullptr;
+    descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_TYPES, ObjectValue(*fieldTypeVec));
 
     // Construct for internal use an array with the offset for each field.
     {
         RootedObject fieldOffsetsVec(cx);
         fieldOffsetsVec = NewDenseCopiedArray(cx, fieldOffsets.length(),
                                               fieldOffsets.begin(), nullptr,
                                               TenuredObject);
         if (!fieldOffsetsVec)
             return nullptr;
-        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS,
-                                     ObjectValue(*fieldOffsetsVec));
+        descr->initReservedSlot(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS, ObjectValue(*fieldOffsetsVec));
     }
 
     // Create data properties fieldOffsets and fieldTypes
     if (!FreezeObject(cx, userFieldOffsets))
         return nullptr;
     if (!FreezeObject(cx, userFieldTypes))
         return nullptr;
     RootedValue userFieldOffsetsValue(cx, ObjectValue(*userFieldOffsets));
@@ -997,16 +995,22 @@ StructMetaTypeDescr::create(JSContext* c
     descr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*prototypeObj));
 
     if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
         return nullptr;
 
     if (!CreateTraceList(cx, descr))
         return nullptr;
 
+    if (!cx->zone()->typeDescrObjects.put(descr) ||
+        !cx->zone()->typeDescrObjects.put(fieldTypeVec))
+    {
+        return nullptr;
+    }
+
     return descr;
 }
 
 bool
 StructMetaTypeDescr::construct(JSContext* cx, unsigned int argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -1029,23 +1033,16 @@ StructMetaTypeDescr::construct(JSContext
 }
 
 size_t
 StructTypeDescr::fieldCount() const
 {
     return fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES).getDenseInitializedLength();
 }
 
-size_t
-StructTypeDescr::maybeForwardedFieldCount() const
-{
-    ArrayObject& fieldInfo = maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES);
-    return fieldInfo.getDenseInitializedLength();
-}
-
 bool
 StructTypeDescr::fieldIndex(jsid id, size_t* out) const
 {
     ArrayObject& fieldNames = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_NAMES);
     size_t l = fieldNames.getDenseInitializedLength();
     for (size_t i = 0; i < l; i++) {
         JSAtom& a = fieldNames.getDenseElement(i).toString()->asAtom();
         if (JSID_IS_ATOM(id, &a)) {
@@ -1065,40 +1062,24 @@ StructTypeDescr::fieldName(size_t index)
 size_t
 StructTypeDescr::fieldOffset(size_t index) const
 {
     ArrayObject& fieldOffsets = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
     MOZ_ASSERT(index < fieldOffsets.getDenseInitializedLength());
     return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32());
 }
 
-size_t
-StructTypeDescr::maybeForwardedFieldOffset(size_t index) const
-{
-    ArrayObject& fieldOffsets = maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS);
-    MOZ_ASSERT(index < fieldOffsets.getDenseInitializedLength());
-    return AssertedCast<size_t>(fieldOffsets.getDenseElement(index).toInt32());
-}
-
 TypeDescr&
 StructTypeDescr::fieldDescr(size_t index) const
 {
     ArrayObject& fieldDescrs = fieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
     MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
     return fieldDescrs.getDenseElement(index).toObject().as<TypeDescr>();
 }
 
-TypeDescr&
-StructTypeDescr::maybeForwardedFieldDescr(size_t index) const
-{
-    ArrayObject& fieldDescrs = maybeForwardedFieldInfoObject(JS_DESCR_SLOT_STRUCT_FIELD_TYPES);
-    MOZ_ASSERT(index < fieldDescrs.getDenseInitializedLength());
-    return MaybeForwarded(&fieldDescrs.getDenseElement(index).toObject())->as<TypeDescr>();
-}
-
 /******************************************************************************
  * Creating the TypedObject "module"
  *
  * We create one global, `TypedObject`, which contains the following
  * members:
  *
  * 1. uint8, uint16, etc
  * 2. ArrayType
@@ -1186,16 +1167,19 @@ DefineSimpleTypeDescr(JSContext* cx,
 
     RootedValue descrValue(cx, ObjectValue(*descr));
     if (!DefineProperty(cx, module, className, descrValue, nullptr, nullptr, 0))
         return false;
 
     if (!CreateTraceList(cx, descr))
         return false;
 
+    if (!cx->zone()->typeDescrObjects.put(descr))
+        return false;
+
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 
 template<typename T>
 static JSObject*
 DefineMetaTypeDescr(JSContext* cx,
@@ -1401,29 +1385,16 @@ TypedObject::isAttached() const
     if (!as<OutlineTypedObject>().outOfLineTypedMem())
         return false;
     JSObject& owner = as<OutlineTypedObject>().owner();
     if (owner.is<ArrayBufferObject>() && owner.as<ArrayBufferObject>().isDetached())
         return false;
     return true;
 }
 
-bool
-TypedObject::maybeForwardedIsAttached() const
-{
-    if (is<InlineTypedObject>())
-        return true;
-    if (!as<OutlineTypedObject>().outOfLineTypedMem())
-        return false;
-    JSObject& owner = *MaybeForwarded(&as<OutlineTypedObject>().owner());
-    if (owner.is<ArrayBufferObject>() && owner.as<ArrayBufferObject>().isDetached())
-        return false;
-    return true;
-}
-
 /* static */ bool
 TypedObject::GetBuffer(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSObject& obj = args[0].toObject();
     ArrayBufferObject* buffer;
     if (obj.is<OutlineTransparentTypedObject>())
         buffer = obj.as<OutlineTransparentTypedObject>().getOrCreateBuffer(cx);
@@ -1673,17 +1644,17 @@ OutlineTypedObject::obj_trace(JSTracer* 
          owner->as<ArrayBufferObject>().hasInlineData()))
     {
         newData += reinterpret_cast<uint8_t*>(owner) - reinterpret_cast<uint8_t*>(oldOwner);
         typedObj.setData(newData);
 
         trc->runtime()->gc.nursery.maybeSetForwardingPointer(trc, oldData, newData, /* direct = */ false);
     }
 
-    if (!descr.opaque() || !typedObj.maybeForwardedIsAttached())
+    if (!descr.opaque() || !typedObj.isAttached())
         return;
 
     descr.traceInstances(trc, newData, 1);
 }
 
 bool
 TypeDescr::hasProperty(const JSAtomState& names, jsid id)
 {
@@ -2826,30 +2797,30 @@ visitReferences(TypeDescr& descr,
 
       case type::Reference:
         visitor.visitReference(descr.as<ReferenceTypeDescr>(), mem);
         return;
 
       case type::Array:
       {
         ArrayTypeDescr& arrayDescr = descr.as<ArrayTypeDescr>();
-        TypeDescr& elementDescr = arrayDescr.maybeForwardedElementType();
+        TypeDescr& elementDescr = arrayDescr.elementType();
         for (int32_t i = 0; i < arrayDescr.length(); i++) {
             visitReferences(elementDescr, mem, visitor);
             mem += elementDescr.size();
         }
         return;
       }
 
       case type::Struct:
       {
         StructTypeDescr& structDescr = descr.as<StructTypeDescr>();
-        for (size_t i = 0; i < structDescr.maybeForwardedFieldCount(); i++) {
-            TypeDescr& descr = structDescr.maybeForwardedFieldDescr(i);
-            size_t offset = structDescr.maybeForwardedFieldOffset(i);
+        for (size_t i = 0; i < structDescr.fieldCount(); i++) {
+            TypeDescr& descr = structDescr.fieldDescr(i);
+            size_t offset = structDescr.fieldOffset(i);
             visitReferences(descr, mem + offset, visitor);
         }
         return;
       }
     }
 
     MOZ_CRASH("Invalid type repr kind");
 }
@@ -3052,11 +3023,12 @@ CreateTraceList(JSContext* cx, HandleTyp
 
     descr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(list));
     return true;
 }
 
 /* static */ void
 TypeDescr::finalize(FreeOp* fop, JSObject* obj)
 {
-    if (obj->as<TypeDescr>().hasTraceList())
-        js_free(const_cast<int32_t*>(obj->as<TypeDescr>().traceList()));
+    TypeDescr& descr = obj->as<TypeDescr>();
+    if (descr.hasTraceList())
+        js_free(const_cast<int32_t*>(descr.traceList()));
 }
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -397,20 +397,16 @@ class ArrayTypeDescr : public ComplexTyp
 {
   public:
     static const Class class_;
     static const type::Kind Kind = type::Array;
 
     TypeDescr& elementType() const {
         return getReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE).toObject().as<TypeDescr>();
     }
-    TypeDescr& maybeForwardedElementType() const {
-        JSObject* obj = MaybeForwarded(&getReservedSlot(JS_DESCR_SLOT_ARRAY_ELEM_TYPE).toObject());
-        return obj->as<TypeDescr>();
-    }
 
     int32_t length() const {
         return getReservedSlot(JS_DESCR_SLOT_ARRAY_LENGTH).toInt32();
     }
 
     static int32_t offsetOfLength() {
         return getFixedSlotOffset(JS_DESCR_SLOT_ARRAY_LENGTH);
     }
@@ -445,40 +441,34 @@ class StructMetaTypeDescr : public Nativ
 
 class StructTypeDescr : public ComplexTypeDescr
 {
   public:
     static const Class class_;
 
     // Returns the number of fields defined in this struct.
     size_t fieldCount() const;
-    size_t maybeForwardedFieldCount() const;
 
     // Set `*out` to the index of the field named `id` and returns true,
     // or return false if no such field exists.
     bool fieldIndex(jsid id, size_t* out) const;
 
     // Return the name of the field at index `index`.
     JSAtom& fieldName(size_t index) const;
 
     // Return the type descr of the field at index `index`.
     TypeDescr& fieldDescr(size_t index) const;
-    TypeDescr& maybeForwardedFieldDescr(size_t index) const;
 
     // Return the offset of the field at index `index`.
     size_t fieldOffset(size_t index) const;
-    size_t maybeForwardedFieldOffset(size_t index) const;
 
   private:
     ArrayObject& fieldInfoObject(size_t slot) const {
         return getReservedSlot(slot).toObject().as<ArrayObject>();
     }
-    ArrayObject& maybeForwardedFieldInfoObject(size_t slot) const {
-        return MaybeForwarded(&getReservedSlot(slot).toObject())->as<ArrayObject>();
-    }
 };
 
 typedef Handle<StructTypeDescr*> HandleStructTypeDescr;
 
 /*
  * This object exists in order to encapsulate the typed object types
  * somewhat, rather than sticking them all into the global object.
  * Eventually it will go away and become a module.
@@ -547,17 +537,16 @@ class TypedObject : public JSObject
         return group()->typeDescr();
     }
 
     int32_t offset() const;
     int32_t length() const;
     uint8_t* typedMem() const;
     uint8_t* typedMemBase() const;
     bool isAttached() const;
-    bool maybeForwardedIsAttached() const;
 
     int32_t size() const {
         return typeDescr().size();
     }
 
     uint8_t* typedMem(size_t offset) const {
         // It seems a bit surprising that one might request an offset
         // == size(), but it can happen when taking the "address of" a
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4014,17 +4014,17 @@ Parser<ParseHandler>::PossibleError::tra
 
 template <typename ParseHandler>
 static inline bool
 HasOuterLexicalBinding(ParseContext<ParseHandler>* pc, StmtInfoPC* stmt, HandleAtom atom)
 {
     while (stmt->enclosingScope) {
         stmt = LexicalLookup(pc, atom, stmt->enclosingScope);
         if (!stmt)
-            return false;
+            break;
         if (stmt->type == StmtType::BLOCK)
             return true;
     }
 
     // Even if the binding doesn't appear in any blocks, it might still be a
     // body-level lexical.
     return pc->isBodyLevelLexicallyDeclaredName(atom);
 }
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -964,16 +964,17 @@ class GCRuntime
     bool shouldCompact();
     void beginCompactPhase();
     IncrementalProgress compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget);
     void endCompactPhase(JS::gcreason::Reason reason);
     void sweepTypesAfterCompacting(Zone* zone);
     void sweepZoneAfterCompacting(Zone* zone);
     bool relocateArenas(Zone* zone, JS::gcreason::Reason reason, Arena*& relocatedListOut,
                         SliceBudget& sliceBudget);
+    void updateTypeDescrObjects(MovingTracer* trc, Zone* zone);
     void updateAllCellPointers(MovingTracer* trc, Zone* zone);
     void updatePointersToRelocatedCells(Zone* zone);
     void protectAndHoldArenas(Arena* arenaList);
     void unprotectHeldRelocatedArenas();
     void releaseRelocatedArenas(Arena* arenaList);
     void releaseRelocatedArenasWithoutUnlocking(Arena* arenaList, const AutoLockGC& lock);
     void finishCollection(JS::gcreason::Reason reason);
 
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gc/Zone.h"
 
 #include "jsgc.h"
 
+#include "gc/Policy.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/JitCompartment.h"
 #include "vm/Debugger.h"
 #include "vm/Runtime.h"
 
 #include "jscompartmentinlines.h"
 #include "jsgcinlines.h"
@@ -25,16 +26,17 @@ Zone * const Zone::NotOnList = reinterpr
 JS::Zone::Zone(JSRuntime* rt)
   : JS::shadow::Zone(rt, &rt->gc.marker),
     debuggers(nullptr),
     suppressAllocationMetadataBuilder(false),
     arenas(rt),
     types(this),
     compartments(),
     gcGrayRoots(),
+    typeDescrObjects(this, SystemAllocPolicy()),
     gcMallocBytes(0),
     gcMallocGCTriggered(false),
     usage(&rt->gc.usage),
     gcDelayBytes(0),
     data(nullptr),
     isSystem(false),
     usedByExclusiveThread(false),
     active(false),
@@ -51,28 +53,33 @@ JS::Zone::Zone(JSRuntime* rt)
 
     AutoLockGC lock(rt);
     threshold.updateAfterGC(8192, GC_NORMAL, rt->gc.tunables, rt->gc.schedulingState, lock);
     setGCMaxMallocBytes(rt->gc.maxMallocBytesAllocated() * 0.9);
 }
 
 Zone::~Zone()
 {
+    MOZ_ASSERT_IF(typeDescrObjects.initialized(), typeDescrObjects.empty());
+
     JSRuntime* rt = runtimeFromMainThread();
     if (this == rt->gc.systemZone)
         rt->gc.systemZone = nullptr;
 
     js_delete(debuggers);
     js_delete(jitZone_);
 }
 
 bool Zone::init(bool isSystemArg)
 {
     isSystem = isSystemArg;
-    return uniqueIds_.init() && gcZoneGroupEdges.init() && gcWeakKeys.init();
+    return uniqueIds_.init() &&
+           gcZoneGroupEdges.init() &&
+           gcWeakKeys.init() &&
+           typeDescrObjects.init();
 }
 
 void
 Zone::setNeedsIncrementalBarrier(bool needs, ShouldUpdateJit updateJit)
 {
     if (updateJit == UpdateJit && needs != jitUsingBarriers_) {
         jit::ToggleBarriers(this, needs);
         jitUsingBarriers_ = needs;
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -322,16 +322,25 @@ struct Zone : public JS::shadow::Zone,
     js::gc::WeakKeyTable gcWeakKeys;
 
     // A set of edges from this zone to other zones.
     //
     // This is used during GC while calculating zone groups to record edges that
     // can't be determined by examining this zone by itself.
     ZoneSet gcZoneGroupEdges;
 
+    // Keep track of all TypeDescr and related objects in this compartment.
+    // This is used by the GC to trace them all first when compacting, since the
+    // TypedObject trace hook may access these objects.
+    using TypeDescrObjectSet = js::GCHashSet<js::RelocatablePtrObject,
+                                             js::MovableCellHasher<js::RelocatablePtrObject>,
+                                             js::SystemAllocPolicy>;
+    JS::WeakCache<TypeDescrObjectSet> typeDescrObjects;
+
+
     // Malloc counter to measure memory pressure for GC scheduling. It runs from
     // gcMaxMallocBytes down to zero. This counter should be used only when it's
     // not possible to know the size of a free.
     mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> gcMallocBytes;
 
     // GC trigger threshold for allocations on the C heap.
     size_t gcMaxMallocBytes;
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Frame-eval-31.js
@@ -0,0 +1,9 @@
+// evalInFrame with non-syntactic scopes.
+
+load(libdir + "asserts.js");
+load(libdir + "evalInFrame.js");
+
+evalReturningScope(`
+  var x = 42;
+  assertEq(evalInFrame(0, "x"), 42);
+`);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1263899.js
@@ -0,0 +1,29 @@
+try {
+  evaluate(` 
+    function runTestCase() $ERROR()
+    function $ERROR() {
+      throw Error
+    }
+    Object.defineProperty(this, "x", { value: 0 });
+    setJitCompilerOption("ion.warmup.trigger", 0)
+  `)
+  evaluate(`function f() {} f(x)`)
+  runTestCase()
+} catch (exc) {}
+evaluate(`
+  g = newGlobal()
+  g.parent = this
+  g.eval("(" + function() {
+    Debugger(parent).onExceptionUnwind = function(frame) {
+      frame.older
+    }
+  } + ")()")
+  try { $ERROR() } catch(e){}
+`)
+try {
+evaluate(`
+  x ^= null;
+  if (x = 1)
+    $ERROR()
+`);
+} catch(e) {}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1264961.js
@@ -0,0 +1,28 @@
+// |jit-test| slow;
+
+if (!('oomTest' in this))
+  quit();
+
+loadFile(`
+  var o = {}
+  var global = this;
+  var p = new Proxy(o, {
+    "deleteProperty": function (await , key) {
+      var g = newGlobal();
+      g.parent = global;
+      g.eval("var dbg = new Debugger(parent); dbg.onEnterFrame = function(frame) {};");
+    }
+  })
+  for (var i=0; i<100; i++);
+  assertEq(delete p.foo, true);
+`);
+function loadFile(lfVarx) {
+    var k = 0;
+    oomTest(function() {
+        // In practice a crash occurs before iteration 4000.
+        if (k++ > 4000)
+          quit();
+        eval(lfVarx);
+    })
+}
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/testIsCallable.js
@@ -0,0 +1,134 @@
+var IsCallable = getSelfHostedValue("IsCallable");
+
+setJitCompilerOption("ion.warmup.trigger", 50);
+
+function testSinglePrimitive() {
+  var f1 = function() { assertEq(IsCallable(undefined), false); };
+  do { f1(); } while (!inIon());
+
+  var f2 = function() { assertEq(IsCallable(null), false); };
+  do { f2(); } while (!inIon());
+
+  var f3 = function() { assertEq(IsCallable(true), false); };
+  do { f3(); } while (!inIon());
+
+  var f4 = function() { assertEq(IsCallable(1), false); };
+  do { f4(); } while (!inIon());
+
+  var f5 = function() { assertEq(IsCallable(1.2), false); };
+  do { f5(); } while (!inIon());
+
+  var f6 = function() { assertEq(IsCallable("foo"), false); };
+  do { f6(); } while (!inIon());
+
+  var f7 = function() { assertEq(IsCallable(Symbol.iterator), false); };
+  do { f7(); } while (!inIon());
+}
+testSinglePrimitive();
+
+function testMixedPrimitive() {
+  var list = [
+    undefined,
+    null,
+    true,
+    1,
+    1.2,
+    "foo",
+    Symbol.iterator,
+  ];
+
+  var f1 = function() {
+    for (let x of list) {
+      assertEq(IsCallable(x), false);
+    }
+  };
+  do { f1(); } while (!inIon());
+}
+testMixedPrimitive();
+
+function testSingleObject() {
+  var obj = [];
+  var arr = [];
+
+  var f1 = function() { assertEq(IsCallable(obj), false); };
+  do { f1(); } while (!inIon());
+
+  var f2 = function() { assertEq(IsCallable(arr), false); };
+  do { f2(); } while (!inIon());
+}
+testSingleObject();
+
+function testMixedPrimitiveAndObject() {
+  var list = [
+    undefined,
+    null,
+    true,
+    1,
+    1.2,
+    "foo",
+    Symbol.iterator,
+
+    {},
+    [],
+  ];
+
+  var f1 = function() {
+    for (let x of list) {
+      assertEq(IsCallable(x), false);
+    }
+  };
+  do { f1(); } while (!inIon());
+}
+testMixedPrimitiveAndObject();
+
+function testFunction() {
+  var f1 = function() { assertEq(IsCallable(Function), true); };
+  do { f1(); } while (!inIon());
+
+  var f2 = function() { assertEq(IsCallable(parseInt), true); };
+  do { f2(); } while (!inIon());
+}
+testFunction();
+
+function testProxy() {
+  var p1 = new Proxy({}, {});
+  var f1 = function() { assertEq(IsCallable(p1), false); };
+  do { f1(); } while (!inIon());
+
+  var p2 = new Proxy(function() {}, {});
+  var f2 = function() { assertEq(IsCallable(p2), true); };
+  do { f2(); } while (!inIon());
+}
+testProxy();
+
+function testMixed() {
+  var p1 = new Proxy({}, {});
+  var p2 = new Proxy(function() {}, {});
+
+  var list = [
+    [undefined, false],
+    [null, false],
+    [true, false],
+    [1, false],
+    [1.2, false],
+    ["foo", false],
+    [Symbol.iterator, false],
+
+    [{}, false],
+    [[], false],
+
+    [Function, true],
+    [parseInt, true],
+
+    [p1, false],
+    [p2, true],
+  ];
+
+  var f1 = function() {
+    for (let [x, expected] of list) {