merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Wed, 20 Sep 2017 11:48:02 +0200
changeset 431381 a20de99fa3c1ba6287fe47d493a859a4e95120b0
parent 431374 c0769d228dd3ba666d6e9b02896bebef7e996ad5 (current diff)
parent 431380 46c0aa7327b3a5035e8678def3e2d216c4fb634a (diff)
child 431382 4bfb0b8fb6b1da31cb6c22b243bb071b6db01984
child 431408 6d8d80beb8c82d1617b9fe939fe6035df3044738
child 431423 0c49ebf4195ce51072b24aacf01eaaffdcff9be6
child 431481 5db1536c77f3d7d9eb6e3d9f512abe95259f6748
push id7782
push userryanvm@gmail.com
push dateWed, 20 Sep 2017 11:51:24 +0000
treeherdermozilla-beta@4bfb0b8fb6b1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone57.0a1
first release with
nightly linux64
a20de99fa3c1 / 57.0a1 / 20170920100426 / files
nightly mac
a20de99fa3c1 / 57.0a1 / 20170920100426 / files
nightly win32
a20de99fa3c1 / 57.0a1 / 20170920100426 / files
nightly win64
a20de99fa3c1 / 57.0a1 / 20170920100426 / files
nightly linux32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: 9Z3Ly8icnvh
layout/generic/nsFrame.cpp
layout/generic/nsIFrame.h
layout/painting/nsDisplayList.cpp
layout/svg/nsSVGEffects.cpp
layout/svg/nsSVGEffects.h
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -1302,19 +1302,21 @@ nsAccessibilityService::Init()
     MOZ_ASSERT(contentChild);
     // If we were instantiated by the chrome process, GetMsaaID() will return
     // a non-zero value and we may safely continue with initialization.
     if (!contentChild->GetMsaaID()) {
       // Since we were not instantiated by chrome, we need to synchronously
       // obtain a MSAA content process id.
       contentChild->SendGetA11yContentId();
     }
+
+    gApplicationAccessible = new ApplicationAccessibleWrap();
+#else
+    gApplicationAccessible = new ApplicationAccessible();
 #endif // defined(XP_WIN)
-
-    gApplicationAccessible = new ApplicationAccessible();
   }
 
   NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
   gApplicationAccessible->Init();
 
 #ifdef MOZ_CRASHREPORTER
   CrashReporter::
     AnnotateCrashReport(NS_LITERAL_CSTRING("Accessibility"),
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -464,16 +464,17 @@ class ViewPopup extends BasePopup {
    * @returns {Promise<boolean>}
    *        Resolves when the browser is ready. Resolves to `false` if the
    *        browser was destroyed before it was fully loaded, and the popup
    *        should be closed, or `true` otherwise.
    */
   async attach(viewNode) {
     this.viewNode = viewNode;
     this.viewNode.addEventListener(this.DESTROY_EVENT, this);
+    this.viewNode.setAttribute("closemenu", "none");
 
     if (this.extension.remote) {
       this.panel.setAttribute("remote", "true");
     }
 
     // Wait until the browser element is fully initialized, and give it at least
     // a short grace period to finish loading its initial content, if necessary.
     //
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -102,16 +102,20 @@ if (AppConstants.E10S_TESTING_ONLY) {
     "resource://gre/modules/UpdateUtils.jsm");
 }
 
 if (AppConstants.MOZ_DEV_EDITION) {
   XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
     "resource://gre/modules/FxAccounts.jsm");
 }
 
+// A promise that resolves when the list of application handlers is loaded.
+// We store this in a global so tests can await it.
+var promiseLoadHandlersList;
+
 var gMainPane = {
   // The set of types the app knows how to handle.  A hash of HandlerInfoWrapper
   // objects, indexed by type.
   _handledTypes: {},
 
   // The list of types we can show, sorted by the sort column/direction.
   // An array of HandlerInfoWrapper objects.  We build this list when we first
   // load the data and then rebuild it when users change a pref that affects
@@ -438,21 +442,28 @@ var gMainPane = {
       document.getElementById("typeColumn").removeAttribute("sortDirection");
     } else {
       this._sortColumn = document.getElementById("typeColumn");
     }
 
     // Load the data and build the list of handlers.
     // By doing this after pageshow, we ensure it doesn't delay painting
     // of the preferences page.
-    window.addEventListener("pageshow", () => {
-      this._loadData();
-      this._rebuildVisibleTypes();
-      this._sortVisibleTypes();
-      this._rebuildView();
+    promiseLoadHandlersList = new Promise((resolve, reject) => {
+      window.addEventListener("pageshow", async () => {
+        try {
+          this._loadData();
+          await this._rebuildVisibleTypes();
+          this._sortVisibleTypes();
+          this._rebuildView();
+          resolve();
+        } catch (ex) {
+          reject(ex);
+        }
+      }, {once: true});
     });
 
     let browserBundle = document.getElementById("browserBundle");
     appendSearchKeywords("browserContainersSettings", [
       browserBundle.getString("userContextPersonal.label"),
       browserBundle.getString("userContextWork.label"),
       browserBundle.getString("userContextBanking.label"),
       browserBundle.getString("userContextShopping.label"),
@@ -1386,30 +1397,30 @@ var gMainPane = {
       return this;
 
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
 
 
   // nsIObserver
 
-  observe(aSubject, aTopic, aData) {
+  async observe(aSubject, aTopic, aData) {
     if (aTopic == "nsPref:changed") {
       if (aData == PREF_CONTAINERS_EXTENSION) {
         this.readBrowserContainersCheckbox();
         return;
       }
       // Rebuild the list when there are changes to preferences that influence
       // whether or not to show certain entries in the list.
       if (!this._storingAction) {
         // These two prefs alter the list of visible types, so we have to rebuild
         // that list when they change.
         if (aData == PREF_SHOW_PLUGINS_IN_LIST ||
           aData == PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS) {
-          this._rebuildVisibleTypes();
+          await this._rebuildVisibleTypes();
           this._sortVisibleTypes();
         }
 
         // All the prefs we observe can affect what we display, so we rebuild
         // the view when any of them changes.
         this._rebuildView();
       }
       if (AppConstants.MOZ_UPDATER) {
@@ -1519,27 +1530,32 @@ var gMainPane = {
 
       handlerInfoWrapper.handledOnlyByPlugin = false;
     }
   },
 
 
   // View Construction
 
-  _rebuildVisibleTypes() {
+  async _rebuildVisibleTypes() {
     // Reset the list of visible types and the visible type description counts.
     this._visibleTypes = [];
     this._visibleTypeDescriptionCount = {};
 
     // Get the preferences that help determine what types to show.
     var showPlugins = this._prefSvc.getBoolPref(PREF_SHOW_PLUGINS_IN_LIST);
     var hidePluginsWithoutExtensions =
       this._prefSvc.getBoolPref(PREF_HIDE_PLUGINS_WITHOUT_EXTENSIONS);
 
     for (let type in this._handledTypes) {
+      // Yield before processing each handler info object to avoid monopolizing
+      // the main thread, as the objects are retrieved lazily, and retrieval
+      // can be expensive on Windows.
+      await new Promise(resolve => Services.tm.dispatchToMainThread(resolve));
+
       let handlerInfo = this._handledTypes[type];
 
       // Hide plugins without associated extensions if so prefed so we don't
       // show a whole bunch of obscure types handled by plugins on Mac.
       // Note: though protocol types don't have extensions, we still show them;
       // the pref is only meant to be applied to MIME types, since plugins are
       // only associated with MIME types.
       // FIXME: should we also check the "suffixes" property of the plugin?
--- a/browser/components/preferences/in-content/tests/browser_applications_selection.js
+++ b/browser/components/preferences/in-content/tests/browser_applications_selection.js
@@ -3,16 +3,19 @@ var feedItem;
 var container;
 
 SimpleTest.requestCompleteLog();
 
 add_task(async function setup() {
   await openPreferencesViaOpenPreferencesAPI("general", { leaveOpen: true });
   info("Preferences page opened on the general pane.");
 
+  await gBrowser.selectedBrowser.contentWindow.promiseLoadHandlersList;
+  info("Apps list loaded.");
+
   registerCleanupFunction(() => {
     gBrowser.removeCurrentTab();
   });
 });
 
 add_task(async function getFeedItem() {
   win = gBrowser.selectedBrowser.contentWindow;
 
--- a/browser/components/preferences/in-content/tests/browser_bug410900.js
+++ b/browser/components/preferences/in-content/tests/browser_bug410900.js
@@ -17,19 +17,19 @@ function test() {
               getService(Ci.nsIExternalProtocolService);
   var info = extps.getProtocolHandlerInfo("apppanetest");
   info.possibleApplicationHandlers.appendElement(handler);
 
   var hserv = Cc["@mozilla.org/uriloader/handler-service;1"].
               getService(Ci.nsIHandlerService);
   hserv.store(info);
 
-  openPreferencesViaOpenPreferencesAPI("general", {leaveOpen: true}).then(
-      () => runTest(gBrowser.selectedBrowser.contentWindow)
-  );
+  openPreferencesViaOpenPreferencesAPI("general", {leaveOpen: true})
+  .then(() => gBrowser.selectedBrowser.contentWindow.promiseLoadHandlersList)
+  .then(() => runTest(gBrowser.selectedBrowser.contentWindow));
 }
 
 function runTest(win) {
   var rbox = win.document.getElementById("handlersView");
   ok(rbox, "handlersView is present");
 
   var items = rbox && rbox.getElementsByTagName("richlistitem");
   ok(items && items.length > 0, "App handler list populated");
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -901,18 +901,16 @@ KeyframeEffectReadOnly::ConstructKeyfram
                            aSource.mEffectOptions);
   // Copy cumulative change hint. mCumulativeChangeHint should be the same as
   // the source one because both of targets are the same.
   effect->mCumulativeChangeHint = aSource.mCumulativeChangeHint;
 
   // Copy aSource's keyframes and animation properties.
   // Note: We don't call SetKeyframes directly, which might revise the
   //       computed offsets and rebuild the animation properties.
-  // FIXME: Bug 1314537: We have to make sure SharedKeyframeList is handled
-  //        properly.
   effect->mKeyframes = aSource.mKeyframes;
   effect->mProperties = aSource.mProperties;
   return effect.forget();
 }
 
 template<typename StyleType>
 nsTArray<AnimationProperty>
 KeyframeEffectReadOnly::BuildProperties(StyleType* aStyle)
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -34,17 +34,17 @@
 #include "nsIStreamListener.h"
 
 #include "nsIFrame.h"
 #include "nsIDOMNode.h"
 
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsIContentPolicy.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 
 #include "gfxPrefs.h"
 
 #include "mozAutoDocUpdate.h"
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
@@ -251,17 +251,17 @@ nsImageLoadingContent::OnLoadComplete(im
       FireEvent(NS_LITERAL_STRING("loadend"));
     }
   } else {
     FireEvent(NS_LITERAL_STRING("error"));
     FireEvent(NS_LITERAL_STRING("loadend"));
   }
 
   nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
-  nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement());
+  SVGObserverUtils::InvalidateDirectRenderingObservers(thisNode->AsElement());
 
   return NS_OK;
 }
 
 static bool
 ImageIsAnimated(imgIRequest* aRequest)
 {
   if (!aRequest) {
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -618,18 +618,18 @@ skip-if = toolkit == 'android'
 [test_bug1295852.html]
 [test_bug1307730.html]
 [test_bug1308069.html]
 [test_bug1314032.html]
 [test_bug1318303.html]
 [test_bug1375050.html]
 [test_bug1381710.html]
 [test_bug1384658.html]
+skip-if = toolkit == 'android'
 [test_bug1399605.html]
-skip-if = toolkit == 'android'
 [test_caretPositionFromPoint.html]
 [test_change_policy.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_constructor-assignment.html]
 [test_constructor.html]
 [test_copyimage.html]
 subsuite = clipboard
 skip-if = toolkit == 'android' #bug 904183
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -12,17 +12,17 @@
 #include "nsIServiceManager.h"
 #include "nsMathUtils.h"
 #include "SVGImageContext.h"
 
 #include "nsContentUtils.h"
 
 #include "nsIDocument.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIFrame.h"
 #include "nsError.h"
 
 #include "nsCSSParser.h"
@@ -1295,17 +1295,17 @@ CanvasRenderingContext2D::Redraw()
 
   mIsEntireFrameInvalid = true;
 
   if (!mCanvasElement) {
     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
     return NS_OK;
   }
 
-  nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
+  SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
 
   mCanvasElement->InvalidateCanvasContent(nullptr);
 
   return NS_OK;
 }
 
 void
 CanvasRenderingContext2D::Redraw(const gfx::Rect& aR)
@@ -1324,17 +1324,17 @@ CanvasRenderingContext2D::Redraw(const g
     return;
   }
 
   if (!mCanvasElement) {
     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
     return;
   }
 
-  nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
+  SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
 
   mCanvasElement->InvalidateCanvasContent(&aR);
 }
 
 void
 CanvasRenderingContext2D::DidRefresh()
 {
   if (IsTargetValid() && mIsSkiaGL) {
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -22,17 +22,17 @@
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/UniquePtr.h"
 #include "gfx2DGlue.h"
 #include "imgIEncoder.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/EnumeratedArray.h"
 #include "FilterSupport.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "Layers.h"
 #include "nsBidi.h"
 
 class nsGlobalWindow;
 class nsXULElement;
 
 namespace mozilla {
 namespace gl {
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -40,17 +40,17 @@
 #include "nsIConsoleService.h"
 #include "nsIDOMEvent.h"
 #include "nsIGfxInfo.h"
 #include "nsIObserverService.h"
 #include "nsIVariant.h"
 #include "nsIWidget.h"
 #include "nsIXPConnect.h"
 #include "nsServiceManagerUtils.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "prenv.h"
 #include "ScopedGLHelpers.h"
 #include "VRManagerChild.h"
 #include "mozilla/layers/TextureClientSharedSurface.h"
 
 // Local
 #include "CanvasUtils.h"
 #include "WebGL1Context.h"
@@ -311,17 +311,17 @@ WebGLContext::Invalidate()
     if (!mCanvasElement)
         return;
 
     mCapturedFrameInvalidated = true;
 
     if (mInvalidated)
         return;
 
-    nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
 
     mInvalidated = true;
     mCanvasElement->InvalidateCanvasContent(nullptr);
 }
 
 void
 WebGLContext::OnVisibilityChange()
 {
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -327,20 +327,20 @@ URLCreateObjectURL_MediaStreamWarning=UR
 # LOCALIZATION NOTE: Do not translate MozAutoGainControl or autoGainControl.
 MozAutoGainControlWarning=mozAutoGainControl is deprecated. Use autoGainControl instead.
 # LOCALIZATION NOTE: Do not translate mozNoiseSuppression or noiseSuppression.
 MozNoiseSuppressionWarning=mozNoiseSuppression is deprecated. Use noiseSuppression instead.
 # LOCALIZATION NOTE: Do not translate xml:base.
 XMLBaseAttributeWarning=Use of xml:base attribute is deprecated and will be removed soon. Please remove any use of it.
 # LOCALIZATION NOTE: Do not translate "content", "Window", and "window.top"
 WindowContentUntrustedWarning=The ‘content’ attribute of Window objects is deprecated.  Please use ‘window.top’ instead.
-# LOCALIZATION NOTE: %S is the tag name of the element that starts the loop
-SVGReferenceLoopWarning=There is an SVG <%S> reference loop in this document, which will prevent the document rendering correctly.
-# LOCALIZATION NOTE: %S is the tag name of the element that starts the chain
-SVGReferenceChainLengthExceededWarning=There is an SVG <%S> reference chain which is too long in this document, which will prevent the document rendering correctly.
+# LOCALIZATION NOTE: The first %S is the tag name of the element that starts the loop, the second %S is the element's ID.
+SVGReferenceLoopWarning=The SVG <%S> with ID “%S” has a reference loop.
+# LOCALIZATION NOTE: The first %S is the tag name of the element in the chain where the chain was broken, the second %S is the element's ID.
+SVGReferenceChainLengthExceededWarning=An SVG <%S> reference chain which is too long was abandoned at the element with ID “%S”.
 # LOCALIZATION NOTE: Do not translate "<script>".
 ScriptSourceEmpty=‘%S’ attribute of <script> element is empty.
 # LOCALIZATION NOTE: Do not translate "<script>".
 ScriptSourceInvalidUri=‘%S’ attribute of <script> element is not a valid URI: “%S”
 # LOCALIZATION NOTE: Do not translate "<script>".
 ScriptSourceLoadFailed=Loading failed for the <script> with source “%S”.
 # LOCALIZATION NOTE: Do not translate "<script>".
 ScriptSourceMalformed=<script> source URI is malformed: “%S”.
--- a/dom/media/VideoFrameContainer.cpp
+++ b/dom/media/VideoFrameContainer.cpp
@@ -6,17 +6,17 @@
 
 #include "VideoFrameContainer.h"
 
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/Telemetry.h"
 
 #include "nsIFrame.h"
 #include "nsDisplayList.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 
 using namespace mozilla::layers;
 
 namespace mozilla {
 static LazyLogModule gVideoFrameContainerLog("VideoFrameContainer");
 #define CONTAINER_LOG(type, msg) MOZ_LOG(gVideoFrameContainerLog, type, msg)
 
 #define NS_DispatchToMainThread(...) CompileError_UseAbstractMainThreadInstead
@@ -384,14 +384,14 @@ void VideoFrameContainer::InvalidateWith
     if (invalidateFrame) {
       frame->InvalidateFrame();
     } else {
       frame->InvalidateLayer(DisplayItemType::TYPE_VIDEO, nullptr, nullptr,
                              asyncInvalidate ? nsIFrame::UPDATE_IS_ASYNC : 0);
     }
   }
 
-  nsSVGEffects::InvalidateDirectRenderingObservers(mElement);
+  SVGObserverUtils::InvalidateDirectRenderingObservers(mElement);
 }
 
 } // namespace mozilla
 
 #undef NS_DispatchToMainThread
--- a/dom/svg/SVGTextContentElement.cpp
+++ b/dom/svg/SVGTextContentElement.cpp
@@ -140,17 +140,17 @@ SVGTextContentElement::SelectSubString(u
     return;
 
   rv = textFrame->SelectSubString(this, charnum, nchars);
 }
 
 float
 SVGTextContentElement::GetSubStringLength(uint32_t charnum, uint32_t nchars, ErrorResult& rv)
 {
-  SVGTextFrame* textFrame = GetSVGTextFrame();
+  SVGTextFrame* textFrame = GetSVGTextFrameForNonLayoutDependentQuery();
   if (!textFrame)
     return 0.0f;
 
   float length = 0.0f;
   rv = textFrame->GetSubStringLength(this, charnum, nchars, &length);
   return length;
 }
 
--- a/dom/svg/SVGUseElement.cpp
+++ b/dom/svg/SVGUseElement.cpp
@@ -12,17 +12,17 @@
 #include "nsGkAtoms.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "mozilla/dom/Element.h"
 #include "nsContentUtils.h"
 #include "nsIURI.h"
 #include "mozilla/URLExtraData.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGUseFrame.h"
 
 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(Use)
 
 namespace mozilla {
 namespace dom {
 
 JSObject*
@@ -370,17 +370,17 @@ SVGUseElement::LookupHref()
 
   if (href.IsEmpty()) {
     return;
   }
 
   nsCOMPtr<nsIURI> originURI =
     mOriginal ? mOriginal->GetBaseURI() : GetBaseURI();
   nsCOMPtr<nsIURI> baseURI = nsContentUtils::IsLocalRefURL(href)
-    ? nsSVGEffects::GetBaseURLForLocalRef(this, originURI)
+    ? SVGObserverUtils::GetBaseURLForLocalRef(this, originURI)
     : originURI;
 
   nsCOMPtr<nsIURI> targetURI;
   nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
                                             GetComposedDoc(), baseURI);
   mSource.Reset(this, targetURI);
 }
 
--- a/dom/svg/test/test_text_scaled.html
+++ b/dom/svg/test/test_text_scaled.html
@@ -22,16 +22,24 @@ SimpleTest.waitForExplicitFinish();
 function runTest()
 {
   var doc = $("svg").contentWindow.document;
   var text1 = doc.getElementById("text1");
   var text2 = doc.getElementById("text2");
 
   var charWidth = text1.getSubStringLength(0, 1);
 
+  if (navigator.userAgent.indexOf("Linux") > -1 && charWidth == 241) {
+    // Workaround for a slight difference in 'charWidth' (i.e. the width of
+    // the 'a' char) on Linux build machines after bug 1342951.  The issue
+    // doesn't reproduce locally on Ubuntu 17.04 so is particularly tricky to
+    // debug.
+    charWidth = 240;
+  }
+
   var epsilon = 0.001;
 
   function isClose(a, b, str)
   {
     ok(Math.abs(a - b) < epsilon, str + " - " + b + " should be close to " + a);
   }
 
   function isPoint(pt1, x, y, str)
--- a/dom/xul/nsXULPopupListener.cpp
+++ b/dom/xul/nsXULPopupListener.cpp
@@ -133,18 +133,23 @@ nsXULPopupListener::HandleEvent(nsIDOMEv
       return NS_ERROR_FAILURE;
     }
   }
 
   nsCOMPtr<nsIContent> targetContent = do_QueryInterface(target);
   if (!targetContent) {
     return NS_OK;
   }
-  if (EventStateManager::IsRemoteTarget(targetContent)) {
-    return NS_OK;
+
+  {
+    EventTarget* originalTarget = mouseEvent->AsEvent()->InternalDOMEvent()->GetOriginalTarget();
+    nsCOMPtr<nsIContent> content = do_QueryInterface(originalTarget);
+    if (content && EventStateManager::IsRemoteTarget(content)) {
+      return NS_OK;
+    }
   }
 
   bool preventDefault;
   mouseEvent->AsEvent()->GetDefaultPrevented(&preventDefault);
   if (preventDefault && targetNode && mIsContext) {
     // Someone called preventDefault on a context menu.
     // Let's make sure they are allowed to do so.
     bool eventEnabled =
--- a/extensions/auth/moz.build
+++ b/extensions/auth/moz.build
@@ -19,13 +19,17 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         'nsAuthSSPI.cpp',
     ]
     DEFINES['USE_SSPI'] = True
 else:
     UNIFIED_SOURCES += [
         'nsAuthSambaNTLM.cpp',
     ]
 
+LOCAL_INCLUDES += [
+    '/netwerk/dns', # For nsDNSService2.h
+]
+
 FINAL_LIBRARY = 'xul'
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Networking')
 
--- a/extensions/auth/nsAuthSSPI.cpp
+++ b/extensions/auth/nsAuthSSPI.cpp
@@ -9,16 +9,17 @@
 // Described by IETF Internet draft: draft-brezak-kerberos-http-00.txt
 // (formerly draft-brezak-spnego-http-04.txt)
 //
 // Also described here:
 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnsecure/html/http-sso-1.asp
 //
 
 #include "nsAuthSSPI.h"
+#include "nsDNSService2.h"
 #include "nsIServiceManager.h"
 #include "nsIDNSService.h"
 #include "nsIDNSRecord.h"
 #include "nsNetCID.h"
 #include "nsCOMPtr.h"
 #include "nsICryptoHash.h"
 #include "mozilla/Telemetry.h"
 
@@ -87,48 +88,50 @@ InitSSPI()
         return NS_ERROR_UNEXPECTED;
     }
 
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
-static nsresult
-MakeSN(const char *principal, nsCString &result)
+nsresult
+nsAuthSSPI::MakeSN(const char *principal, nsCString &result)
 {
     nsresult rv;
 
     nsAutoCString buf(principal);
 
     // The service name looks like "protocol@hostname", we need to map
     // this to a value that SSPI expects.  To be consistent with IE, we
     // need to map '@' to '/' and canonicalize the hostname.
     int32_t index = buf.FindChar('@');
     if (index == kNotFound)
         return NS_ERROR_UNEXPECTED;
 
-    nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
+    nsCOMPtr<nsIDNSService> dnsService = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
     if (NS_FAILED(rv))
         return rv;
 
+    auto dns = static_cast<nsDNSService*>(dnsService.get());
+
     // This could be expensive if our DNS cache cannot satisfy the request.
     // However, we should have at least hit the OS resolver once prior to
     // reaching this code, so provided the OS resolver has this information
     // cached, we should not have to worry about blocking on this function call
     // for very long.  NOTE: because we ask for the canonical hostname, we
     // might end up requiring extra network activity in cases where the OS
     // resolver might not have enough information to satisfy the request from
     // its cache.  This is not an issue in versions of Windows up to WinXP.
     nsCOMPtr<nsIDNSRecord> record;
     mozilla::OriginAttributes attrs;
-    rv = dns->ResolveNative(Substring(buf, index + 1),
-                            nsIDNSService::RESOLVE_CANONICAL_NAME,
-                            attrs,
-                            getter_AddRefs(record));
+    rv = dns->DeprecatedSyncResolve(Substring(buf, index + 1),
+                                    nsIDNSService::RESOLVE_CANONICAL_NAME,
+                                    attrs,
+                                    getter_AddRefs(record));
     if (NS_FAILED(rv))
         return rv;
 
     nsAutoCString cname;
     rv = record->GetCanonicalName(cname);
     if (NS_SUCCEEDED(rv)) {
         result = StringHead(buf, index) + NS_LITERAL_CSTRING("/") + cname;
         LOG(("Using SPN of [%s]\n", result.get()));
--- a/extensions/auth/nsAuthSSPI.h
+++ b/extensions/auth/nsAuthSSPI.h
@@ -37,16 +37,18 @@ public:
 private:
     ~nsAuthSSPI();
 
     void Reset();
 
     typedef TimeStamp MS_TimeStamp;
 
 private:
+    nsresult MakeSN(const char *principal, nsCString &result);
+
     CredHandle   mCred;
     CtxtHandle   mCtxt;
     nsCString    mServiceName;
     uint32_t     mServiceFlags;
     uint32_t     mMaxTokenLen;
     pType        mPackage;
     nsString     mDomain;
     nsString     mUsername;
--- a/image/SVGDocumentWrapper.cpp
+++ b/image/SVGDocumentWrapper.cpp
@@ -20,17 +20,17 @@
 #include "nsIRequest.h"
 #include "nsIStreamListener.h"
 #include "nsIXMLContentSink.h"
 #include "nsNetCID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsSMILAnimationController.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/SVGSVGElement.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "mozilla/dom/SVGAnimatedLength.h"
 #include "nsMimeTypes.h"
 #include "DOMSVGLength.h"
 #include "nsDocument.h"
 #include "mozilla/dom/ImageTracker.h"
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 #undef GetCurrentTime
@@ -271,17 +271,17 @@ NS_IMETHODIMP
 SVGDocumentWrapper::Observe(nsISupports* aSubject,
                             const char* aTopic,
                             const char16_t* aData)
 {
   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
     // Sever ties from rendering observers to helper-doc's root SVG node
     SVGSVGElement* svgElem = GetRootSVGElem();
     if (svgElem) {
-      nsSVGEffects::RemoveAllRenderingObservers(svgElem);
+      SVGObserverUtils::RemoveAllRenderingObservers(svgElem);
     }
 
     // Clean up at XPCOM shutdown time.
     DestroyViewer();
     if (mListener) {
       mListener = nullptr;
     }
     if (mLoadGroup) {
--- a/image/VectorImage.cpp
+++ b/image/VectorImage.cpp
@@ -19,17 +19,17 @@
 #include "nsIDOMEvent.h"
 #include "nsIPresShell.h"
 #include "nsIStreamListener.h"
 #include "nsMimeTypes.h"
 #include "nsPresContext.h"
 #include "nsRect.h"
 #include "nsString.h"
 #include "nsStubDocumentObserver.h"
-#include "nsSVGEffects.h" // for nsSVGRenderingObserver
+#include "SVGObserverUtils.h" // for nsSVGRenderingObserver
 #include "nsWindowSizes.h"
 #include "ImageRegion.h"
 #include "ISurfaceProvider.h"
 #include "LookupResult.h"
 #include "Orientation.h"
 #include "SVGDocumentWrapper.h"
 #include "SVGDrawingParameters.h"
 #include "nsIDOMEventListener.h"
@@ -61,17 +61,17 @@ public:
   {
     MOZ_ASSERT(mDocWrapper, "Need a non-null SVG document wrapper");
     MOZ_ASSERT(mVectorImage, "Need a non-null VectorImage");
 
     StartListening();
     Element* elem = GetTarget();
     MOZ_ASSERT(elem, "no root SVG node for us to observe");
 
-    nsSVGEffects::AddRenderingObserver(elem, this);
+    SVGObserverUtils::AddRenderingObserver(elem, this);
     mInObserverList = true;
   }
 
 
   void ResumeHonoringInvalidations()
   {
     mHonoringInvalidations = true;
   }
@@ -103,17 +103,17 @@ protected:
       mHonoringInvalidations = false;
 
       mVectorImage->InvalidateObserversOnNextRefreshDriverTick();
     }
 
     // Our caller might've removed us from rendering-observer list.
     // Add ourselves back!
     if (!mInObserverList) {
-      nsSVGEffects::AddRenderingObserver(elem, this);
+      SVGObserverUtils::AddRenderingObserver(elem, this);
       mInObserverList = true;
     }
   }
 
   // Private data
   const RefPtr<SVGDocumentWrapper> mDocWrapper;
   VectorImage* const mVectorImage;   // Raw pointer because it owns me.
   bool mHonoringInvalidations;
--- a/image/moz.build
+++ b/image/moz.build
@@ -96,17 +96,17 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     # Because SVGDocumentWrapper.cpp includes "mozilla/dom/SVGSVGElement.h"
     '/dom/base',
     '/dom/svg',
     # We need to instantiate the decoders
     '/image/decoders',
-    # Because VectorImage.cpp includes nsSVGUtils.h and nsSVGEffects.h
+    # Because VectorImage.cpp includes nsSVGUtils.h and SVGObserverUtils.h
     '/layout/svg',
     # For URI-related functionality
     '/netwerk/base',
     # DecodePool uses thread-related facilities.
     '/xpcom/threads',
 ]
 
 # Because imgFrame.cpp includes "cairo.h"
--- a/layout/base/GeckoRestyleManager.cpp
+++ b/layout/base/GeckoRestyleManager.cpp
@@ -23,17 +23,17 @@
 #include "GeckoProfiler.h"
 #include "nsAutoPtr.h"
 #include "nsStyleChangeList.h"
 #include "nsRuleProcessorData.h"
 #include "nsStyleContextInlines.h"
 #include "nsStyleSet.h"
 #include "nsStyleUtil.h"
 #include "nsCSSFrameConstructor.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsCSSPseudoElements.h"
 #include "nsCSSRendering.h"
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
 #include "nsViewManager.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsContainerFrame.h"
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -105,17 +105,17 @@
 #include "nsIObjectFrame.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsNetUtil.h"
 #include "nsThreadUtils.h"
 #include "nsStyleSheetService.h"
 #include "gfxUtils.h"
 #include "nsSMILAnimationController.h"
 #include "SVGContentUtils.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "SVGFragmentIdentifier.h"
 #include "nsFrameSelection.h"
 
 #include "mozilla/dom/Performance.h"
 #include "nsRefreshDriver.h"
 #include "nsDOMNavigationTiming.h"
 
 // Drag & Drop, Clipboard
@@ -9303,17 +9303,17 @@ PresShell::DoReflow(nsIFrame* target, bo
     tp->Accumulate();
     tp->reflowCount++;
     timeStart = TimeStamp::Now();
   }
 
   target->SchedulePaint();
   nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(target);
   while (parent) {
-    nsSVGEffects::InvalidateDirectRenderingObservers(parent);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
     parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
   }
 
   nsIURI* uri = mDocument->GetDocumentURI();
   nsCString uriString = uri ? uri->GetSpecOrDefault() : NS_LITERAL_CSTRING("N/A");
   AUTO_PROFILER_LABEL_DYNAMIC("PresShell::DoReflow", GRAPHICS, uriString.get());
 
   nsDocShell* docShell = static_cast<nsDocShell*>(GetPresContext()->GetDocShell());
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1549,24 +1549,24 @@ RestyleManager::ProcessRestyledFrames(ns
         // specify nsChangeHint_UpdateTransformLayer but don't have any
         // transform style, we just drop the unneeded hint here.
         hint &= ~nsChangeHint_UpdateTransformLayer;
       }
 
       if (hint & nsChangeHint_UpdateEffects) {
         for (nsIFrame* cont = frame; cont;
              cont = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(cont)) {
-          nsSVGEffects::UpdateEffects(cont);
+          SVGObserverUtils::UpdateEffects(cont);
         }
       }
       if ((hint & nsChangeHint_InvalidateRenderingObservers) ||
           ((hint & nsChangeHint_UpdateOpacityLayer) &&
            frame->IsFrameOfType(nsIFrame::eSVG) &&
            !(frame->GetStateBits() & NS_STATE_IS_OUTER_SVG))) {
-        nsSVGEffects::InvalidateRenderingObservers(frame);
+        SVGObserverUtils::InvalidateRenderingObservers(frame);
       }
       if (hint & nsChangeHint_NeedReflow) {
         StyleChangeReflow(frame, hint);
         didReflowThisFrame = true;
       }
 
       if ((hint & nsChangeHint_UpdateUsesOpacity) &&
           frame->IsFrameOfType(nsIFrame::eTablePart)) {
--- a/layout/base/nsChangeHint.h
+++ b/layout/base/nsChangeHint.h
@@ -49,18 +49,18 @@ enum nsChangeHint : uint32_t {
   // The currently shown mouse cursor needs to be updated
   nsChangeHint_UpdateCursor = 1 << 6,
 
   /**
    * Used when the computed value (a URI) of one or more of an element's
    * filter/mask/clip/etc CSS properties changes, causing the element's frame
    * to start/stop referencing (or reference different) SVG resource elements.
    * (_Not_ used to handle changes to referenced resource elements.) Using this
-   * hint results in nsSVGEffects::UpdateEffects being called on the element's
-   * frame.
+   * hint results in SVGObserverUtils::UpdateEffects being called on the
+   * element's frame.
    */
   nsChangeHint_UpdateEffects = 1 << 7,
 
   /**
    * Visual change only, but the change can be handled entirely by
    * updating the layer(s) for the frame.
    * Updates all descendants (including following placeholders to out-of-flows).
    */
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -312,17 +312,17 @@ nsTextControlFrame::EnsureEditorInitiali
   return NS_OK;
 }
 
 nsresult
 nsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
 {
   NS_ASSERTION(mContent, "We should have a content!");
 
-  mState |= NS_FRAME_INDEPENDENT_SELECTION;
+  AddStateBits(NS_FRAME_INDEPENDENT_SELECTION);
 
   nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
   NS_ASSERTION(txtCtrl, "Content not a text control element");
 
   // Bind the frame to its text control
   nsresult rv = txtCtrl->BindToFrame(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -43,17 +43,17 @@ ViewportFrame::Init(nsIContent*       aC
   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
   // No need to call CreateView() here - the frame ctor will call SetView()
   // with the ViewManager's root view, so we'll assign it in SetViewInternal().
 
   nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(this);
   if (parent) {
     nsFrameState state = parent->GetStateBits();
 
-    mState |= state & (NS_FRAME_IN_POPUP);
+    AddStateBits(state & (NS_FRAME_IN_POPUP));
   }
 }
 
 void
 ViewportFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsDisplayListSet& aLists)
 {
   AUTO_PROFILER_LABEL("ViewportFrame::BuildDisplayList", GRAPHICS);
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -1224,17 +1224,17 @@ nsBlockFrame::Reflow(nsPresContext*     
       reflowInput->mCBReflowInput->IsIResize() &&
       reflowInput->mStyleText->mTextIndent.HasPercent() &&
       !mLines.empty()) {
     mLines.front()->MarkDirty();
   }
 
   LazyMarkLinesDirty();
 
-  mState &= ~NS_FRAME_FIRST_REFLOW;
+  RemoveStateBits(NS_FRAME_FIRST_REFLOW);
 
   // Now reflow...
   ReflowDirtyLines(state);
 
   // If we have a next-in-flow, and that next-in-flow has pushed floats from
   // this frame from a previous iteration of reflow, then we should not return
   // a status with IsFullyComplete() equals to true, since we actually have
   // overflow, it's just already been handled.
@@ -5263,16 +5263,24 @@ nsBlockFrame::AppendFrames(ChildListID  
   nsFrame::ListTag(stdout, aFrameList);
   if (lastKid) {
     printf(" after ");
     nsFrame::ListTag(stdout, lastKid);
   }
   printf("\n");
 #endif
 
+  if (nsSVGUtils::IsInSVGTextSubtree(this)) {
+    MOZ_ASSERT(GetParent()->IsSVGTextFrame(),
+               "unexpected block frame in SVG text");
+    // Workaround for bug 1399425 in case this bit has been removed from the
+    // SVGTextFrame just before the parser adds more descendant nodes.
+    GetParent()->AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY);
+  }
+
   AddFrames(aFrameList, lastKid);
   if (aListID != kNoReflowPrincipalList) {
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange,
                        NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
   }
 }
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -71,17 +71,17 @@
 #include "imgIContainer.h"
 #include "imgIRequest.h"
 #include "nsError.h"
 #include "nsContainerFrame.h"
 #include "nsBoxLayoutState.h"
 #include "nsBlockFrame.h"
 #include "nsDisplayList.h"
 #include "nsSVGIntegrationUtils.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsChangeHint.h"
 #include "nsDeckFrame.h"
 #include "nsSubDocumentFrame.h"
 #include "SVGTextFrame.h"
 
 #include "gfxContext.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "StickyScrollContainer.h"
@@ -618,46 +618,46 @@ nsFrame::Init(nsIContent*       aContent
 
   if (aPrevInFlow) {
     mWritingMode = aPrevInFlow->GetWritingMode();
 
     // Make sure the general flags bits are the same
     nsFrameState state = aPrevInFlow->GetStateBits();
 
     // Make bits that are currently off (see constructor) the same:
-    mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
-                       NS_FRAME_PART_OF_IBSPLIT |
-                       NS_FRAME_MAY_BE_TRANSFORMED |
-                       NS_FRAME_MAY_HAVE_GENERATED_CONTENT |
-                       NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
+    AddStateBits(state & (NS_FRAME_INDEPENDENT_SELECTION |
+                          NS_FRAME_PART_OF_IBSPLIT |
+                          NS_FRAME_MAY_BE_TRANSFORMED |
+                          NS_FRAME_MAY_HAVE_GENERATED_CONTENT |
+                          NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
   } else {
     PresContext()->ConstructedFrame();
   }
   if (GetParent()) {
     nsFrameState state = GetParent()->GetStateBits();
 
     // Make bits that are currently off (see constructor) the same:
-    mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
-                       NS_FRAME_GENERATED_CONTENT |
-                       NS_FRAME_IS_SVG_TEXT |
-                       NS_FRAME_IN_POPUP |
-                       NS_FRAME_IS_NONDISPLAY);
+    AddStateBits(state & (NS_FRAME_INDEPENDENT_SELECTION |
+                          NS_FRAME_GENERATED_CONTENT |
+                          NS_FRAME_IS_SVG_TEXT |
+                          NS_FRAME_IN_POPUP |
+                          NS_FRAME_IS_NONDISPLAY));
 
     if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
       // Assume all frames in popups are visible.
       IncApproximateVisibleCount();
     }
   }
   const nsStyleDisplay *disp = StyleDisplay();
   if (disp->HasTransform(this) ||
       (IsFrameOfType(eSupportsCSSTransforms) &&
        nsLayoutUtils::HasAnimationOfProperty(this, eCSSProperty_transform))) {
     // The frame gets reconstructed if we toggle the -moz-transform
     // property, so we can set this bit here and then ignore it.
-    mState |= NS_FRAME_MAY_BE_TRANSFORMED;
+    AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
   }
   if (disp->mPosition == NS_STYLE_POSITION_STICKY &&
       !aPrevInFlow &&
       !(mState & NS_FRAME_IS_NONDISPLAY) &&
       !disp->IsInnerTableStyle()) {
     // Note that we only add first continuations, but we really only
     // want to add first continuation-or-ib-split-siblings.  But since we
     // don't yet know if we're a later part of a block-in-inline split,
@@ -710,17 +710,17 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
     "destroy called on frame while scripts not blocked");
   NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
                "Frames should be removed before destruction.");
   NS_ASSERTION(aDestructRoot, "Must specify destruct root");
   MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
   MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),
              "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?");
 
-  nsSVGEffects::InvalidateDirectRenderingObservers(this);
+  SVGObserverUtils::InvalidateDirectRenderingObservers(this);
 
   if (StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY) {
     StickyScrollContainer* ssc =
       StickyScrollContainer::GetStickyScrollContainerForFrame(this);
     if (ssc) {
       ssc->RemoveFrame(this);
     }
   }
@@ -5887,21 +5887,22 @@ nsIFrame::ComputeISizeValue(gfxContext* 
 void
 nsFrame::DidReflow(nsPresContext*           aPresContext,
                    const ReflowInput*  aReflowInput,
                    nsDidReflowStatus         aStatus)
 {
   NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
                      ("nsFrame::DidReflow: aStatus=%d", static_cast<uint32_t>(aStatus)));
 
-  nsSVGEffects::InvalidateDirectRenderingObservers(this, nsSVGEffects::INVALIDATE_REFLOW);
+  SVGObserverUtils::InvalidateDirectRenderingObservers(this,
+                      SVGObserverUtils::INVALIDATE_REFLOW);
 
   if (nsDidReflowStatus::FINISHED == aStatus) {
-    mState &= ~(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
-                NS_FRAME_HAS_DIRTY_CHILDREN);
+    RemoveStateBits(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW |
+                    NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
   }
 
   // Notify the percent bsize observer if there is a percent bsize.
   // The observer may be able to initiate another reflow with a computed
   // bsize. This happens in the case where a table cell has no computed
   // bsize but can fabricate one when the cell bsize is known.
   if (aReflowInput && aReflowInput->mPercentBSizeObserver &&
       !GetPrevInFlow()) {
@@ -6402,22 +6403,22 @@ nsIFrame::GetTransformMatrix(const nsIFr
   return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
                                 NSAppUnitsToFloatPixels(delta.y, scaleFactor),
                                 0.0f);
 }
 
 static void InvalidateRenderingObservers(nsIFrame* aDisplayRoot, nsIFrame* aFrame)
 {
   MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
-  nsSVGEffects::InvalidateDirectRenderingObservers(aFrame);
+  SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
   nsIFrame* parent = aFrame;
   while (parent != aDisplayRoot &&
          (parent = nsLayoutUtils::GetCrossDocParentFrame(parent)) &&
          !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
-    nsSVGEffects::InvalidateDirectRenderingObservers(parent);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
   }
 }
 
 void
 SchedulePaintInternal(nsIFrame* aDisplayRoot, nsIFrame* aFrame,
                       nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT)
 {
   MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
@@ -6446,27 +6447,27 @@ SchedulePaintInternal(nsIFrame* aDisplay
   }
 }
 
 static void InvalidateFrameInternal(nsIFrame *aFrame, bool aHasDisplayItem = true)
 {
   if (aHasDisplayItem) {
     aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
   }
-  nsSVGEffects::InvalidateDirectRenderingObservers(aFrame);
+  SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
   bool needsSchedulePaint = false;
   if (nsLayoutUtils::IsPopup(aFrame)) {
     needsSchedulePaint = true;
   } else {
     nsIFrame *parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
     while (parent && !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
       if (aHasDisplayItem && !parent->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
         parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
       }
-      nsSVGEffects::InvalidateDirectRenderingObservers(parent);
+      SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
 
       // If we're inside a popup, then we need to make sure that we
       // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
       // flag gets added to the popup display root frame.
       if (nsLayoutUtils::IsPopup(parent)) {
         needsSchedulePaint = true;
         break;
       }
@@ -6992,34 +6993,34 @@ nsFrame::UnionChildOverflow(nsOverflowAr
 
 bool
 nsFrame::IsFrameTreeTooDeep(const ReflowInput& aReflowInput,
                             ReflowOutput& aMetrics,
                             nsReflowStatus& aStatus)
 {
   if (aReflowInput.mReflowDepth >  MAX_FRAME_DEPTH) {
     NS_WARNING("frame tree too deep; setting zero size and returning");
-    mState |= NS_FRAME_TOO_DEEP_IN_FRAME_TREE;
+    AddStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE);
     ClearOverflowRects();
     aMetrics.ClearSize();
     aMetrics.SetBlockStartAscent(0);
     aMetrics.mCarriedOutBEndMargin.Zero();
     aMetrics.mOverflowAreas.Clear();
 
     aStatus.Reset();
     if (GetNextInFlow()) {
       // Reflow depth might vary between reflows, so we might have
       // successfully reflowed and split this frame before.  If so, we
       // shouldn't delete its continuations.
       aStatus.SetIncomplete();
     }
 
     return true;
   }
-  mState &= ~NS_FRAME_TOO_DEEP_IN_FRAME_TREE;
+  RemoveStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE);
   return false;
 }
 
 bool
 nsIFrame::IsBlockWrapper() const
 {
   nsIAtom *pseudoType = StyleContext()->GetPseudo();
   return (pseudoType == nsCSSAnonBoxes::mozBlockInsideInlineWrapper ||
@@ -9133,17 +9134,17 @@ nsIFrame::FinishAndStoreOverflow(nsOverf
   bool anyOverflowChanged;
   if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) {
     anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
   } else {
     anyOverflowChanged = ClearOverflowRects();
   }
 
   if (anyOverflowChanged) {
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   }
   return anyOverflowChanged;
 }
 
 void
 nsIFrame::RecomputePerspectiveChildrenOverflow(const nsIFrame* aStartFrame,
                                                EffectSet* aEffectSet)
 {
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -385,16 +385,20 @@ FRAME_STATE_BIT(SVG, 22, NS_STATE_SVG_PO
 // can be true even if a viewport size change will not affect mPositions,
 // determining a completley accurate value for
 // NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES would require extra work that is
 // probably not worth it.
 FRAME_STATE_BIT(SVG, 23, NS_STATE_SVG_POSITIONING_MAY_USE_PERCENTAGES)
 
 FRAME_STATE_BIT(SVG, 24, NS_STATE_SVG_TEXT_IN_REFLOW)
 
+// Set on SVGTextFrame frames when they need a
+// TextNodeCorrespondenceRecorder::RecordCorrespondence call
+// to update the cached nsTextNode indexes that they correspond to.
+FRAME_STATE_BIT(SVG, 25, NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY)
 
 // == Frame state bits that apply to text frames ==============================
 
 FRAME_STATE_GROUP(Text, nsTextFrame)
 
 // -- Flags set during reflow -------------------------------------------------
 
 // nsTextFrame.cpp defines TEXT_REFLOW_FLAGS to be all of these bits.
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -4097,17 +4097,17 @@ protected:
    */
   inline void PropagateRootElementWritingMode(mozilla::WritingMode aRootElemWM);
 
   void MarkInReflow() {
 #ifdef DEBUG_dbaron_off
     // bug 81268
     NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW), "frame is already in reflow");
 #endif
-    mState |= NS_FRAME_IN_REFLOW;
+    AddStateBits(NS_FRAME_IN_REFLOW);
   }
 
   nsFrameState     mState;
 
   /**
    * List of properties attached to the frame.
    */
   FrameProperties  mProperties;
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -986,25 +986,25 @@ nsImageFrame::Reflow(nsPresContext*     
   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                   ("enter nsImageFrame::Reflow: availSize=%d,%d",
                   aReflowInput.AvailableWidth(), aReflowInput.AvailableHeight()));
 
   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
 
   // see if we have a frozen size (i.e. a fixed width and height)
   if (HaveFixedSize(aReflowInput)) {
-    mState |= IMAGE_SIZECONSTRAINED;
+    AddStateBits(IMAGE_SIZECONSTRAINED);
   } else {
-    mState &= ~IMAGE_SIZECONSTRAINED;
+    RemoveStateBits(IMAGE_SIZECONSTRAINED);
   }
 
   // XXXldb These two bits are almost exact opposites (except in the
   // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW.
   if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
-    mState |= IMAGE_GOTINITIALREFLOW;
+    AddStateBits(IMAGE_GOTINITIALREFLOW);
   }
 
   mComputedSize =
     nsSize(aReflowInput.ComputedWidth(), aReflowInput.ComputedHeight());
 
   aMetrics.Width() = mComputedSize.width;
   aMetrics.Height() = mComputedSize.height;
 
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -4544,17 +4544,17 @@ nsContinuingTextFrame::Init(nsIContent* 
         MOZ_ASSERT(nextBidiData.precedingControl == kBidiLevelNone,
                    "There shouldn't be any virtual bidi formatting character "
                    "between continuations");
 #endif
         nextContinuation->mContentOffset = mContentOffset;
         nextContinuation = nextContinuation->GetNextContinuation();
       }
     }
-    mState |= NS_FRAME_IS_BIDI;
+    AddStateBits(NS_FRAME_IS_BIDI);
   } // prev frame is bidi
 }
 
 void
 nsContinuingTextFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   ClearFrameOffsetCache();
 
@@ -4867,17 +4867,17 @@ nsTextFrame::CharacterDataChanged(Charac
   // (For subsequent frames with this same parent, we can just set their
   // dirty bit without bothering to call FrameNeedsReflow again.)
   nsIFrame* lastDirtiedFrameParent = nullptr;
 
   nsIPresShell* shell = PresContext()->GetPresShell();
   do {
     // textFrame contained deleted text (or the insertion point,
     // if this was a pure insertion).
-    textFrame->mState &= ~TEXT_WHITESPACE_FLAGS;
+    textFrame->RemoveStateBits(TEXT_WHITESPACE_FLAGS);
     textFrame->ClearTextRuns();
 
     nsIFrame* parentOfTextFrame = textFrame->GetParent();
     bool areAncestorsAwareOfReflowRequest = false;
     if (lastDirtiedFrameParent == parentOfTextFrame) {
       // An earlier iteration of this loop already called
       // FrameNeedsReflow for a sibling of |textFrame|.
       areAncestorsAwareOfReflowRequest = true;
@@ -10315,17 +10315,17 @@ nsTextFrame::IsEmpty()
 
   if (mState & TEXT_IS_ONLY_WHITESPACE) {
     return true;
   }
 
   bool isEmpty =
     IsAllWhitespace(mContent->GetText(),
                     textStyle->mWhiteSpace != mozilla::StyleWhiteSpace::PreLine);
-  mState |= (isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
+  AddStateBits(isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
   return isEmpty;
 }
 
 #ifdef DEBUG_FRAME_DUMP
 // Translate the mapped content into a string that's printable
 void
 nsTextFrame::ToCString(nsCString& aBuf, int32_t* aTotalContentLength) const
 {
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -41,17 +41,17 @@
 #include "nsITheme.h"
 #include "nsThemeConstants.h"
 #include "nsLayoutUtils.h"
 #include "nsBlockFrame.h"
 #include "nsStyleStructInlines.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsCSSProps.h"
 #include "nsContentUtils.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGIntegrationUtils.h"
 #include "gfxDrawable.h"
 #include "GeckoProfiler.h"
 #include "nsCSSRenderingBorders.h"
 #include "mozilla/css/ImageLoader.h"
 #include "ImageContainer.h"
 #include "mozilla/Telemetry.h"
 #include "gfxUtils.h"
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -38,17 +38,17 @@
 #include "nsThemeConstants.h"
 #include "BorderConsts.h"
 #include "LayerTreeInvalidation.h"
 
 #include "imgIContainer.h"
 #include "BasicLayers.h"
 #include "nsBoxFrame.h"
 #include "nsSubDocumentFrame.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGElement.h"
 #include "nsSVGClipPathFrame.h"
 #include "GeckoProfiler.h"
 #include "nsViewManager.h"
 #include "ImageLayers.h"
 #include "ImageContainer.h"
 #include "nsCanvasFrame.h"
 #include "StickyScrollContainer.h"
@@ -8750,18 +8750,18 @@ ComputeMaskGeometry(PaintFramesParams& a
 {
   // Properties are added lazily and may have been removed by a restyle, so
   // make sure all applicable ones are set again.
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
 
   const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
 
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
   nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
 
   if (maskFrames.Length() == 0) {
     return;
   }
 
   gfxContext& ctx = aParams.ctx;
   nsIFrame* frame = aParams.frame;
@@ -8871,18 +8871,18 @@ nsDisplayMask::BuildLayer(nsDisplayListB
   }
 
   if (mFrame->StyleEffects()->mOpacity == 0.0f && mHandleOpacity) {
     return nullptr;
   }
 
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
 
   if (effectProperties.HasInvalidClipPath() ||
       effectProperties.HasInvalidMask()) {
     return nullptr;
   }
 
   RefPtr<ContainerLayer> container = aManager->GetLayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, &mList,
@@ -9063,18 +9063,18 @@ nsDisplayMask::CreateWebRenderCommands(m
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
 nsDisplayMask::PrintEffects(nsACString& aTo)
 {
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
   bool first = true;
   aTo += " effects=(";
   if (mFrame->StyleEffects()->mOpacity != 1.0f && mHandleOpacity) {
     first = false;
     aTo += nsPrintfCString("opacity(%f)", mFrame->StyleEffects()->mOpacity);
   }
   if (clipPathFrame) {
@@ -9130,18 +9130,18 @@ nsDisplayFilter::BuildLayer(nsDisplayLis
   }
 
   if (mFrame->StyleEffects()->mOpacity == 0.0f && mHandleOpacity) {
     return nullptr;
   }
 
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
 
   if (effectProperties.HasInvalidFilter()) {
     return nullptr;
   }
 
   MOZ_ASSERT(effectProperties.mFilter && mFrame->StyleEffects()->HasFilters(),
              "By getting here, we must have valid CSS filters.");
 
@@ -9295,18 +9295,18 @@ nsDisplayFilter::CreateWebRenderCommands
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
 nsDisplayFilter::PrintEffects(nsACString& aTo)
 {
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(mFrame);
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
   bool first = true;
   aTo += " effects=(";
   if (mFrame->StyleEffects()->mOpacity != 1.0f && mHandleOpacity) {
     first = false;
     aTo += nsPrintfCString("opacity(%f)", mFrame->StyleEffects()->mOpacity);
   }
   if (effectProperties.HasValidFilter()) {
     if (!first) {
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -15,17 +15,17 @@
 #include "ImageOps.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "nsContentUtils.h"
 #include "nsCSSRendering.h"
 #include "nsCSSRenderingGradients.h"
 #include "nsIFrame.h"
 #include "nsStyleStructInlines.h"
 #include "nsSVGDisplayableFrame.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGIntegrationUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 using namespace mozilla::layers;
 
 nsSize
@@ -175,19 +175,19 @@ nsImageRenderer::PrepareImage()
     case eStyleImageType_Element:
     {
       nsAutoString elementId =
         NS_LITERAL_STRING("#") + nsDependentAtomString(mImage->GetElementId());
       nsCOMPtr<nsIURI> targetURI;
       nsCOMPtr<nsIURI> base = mForFrame->GetContent()->GetBaseURI();
       nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), elementId,
                                                 mForFrame->GetContent()->GetUncomposedDoc(), base);
-      nsSVGPaintingProperty* property = nsSVGEffects::GetPaintingPropertyForURI(
+      nsSVGPaintingProperty* property = SVGObserverUtils::GetPaintingPropertyForURI(
           targetURI, mForFrame->FirstContinuation(),
-          nsSVGEffects::BackgroundImageProperty());
+          SVGObserverUtils::BackgroundImageProperty());
       if (!property) {
         mPrepareResult = DrawResult::BAD_IMAGE;
         return false;
       }
 
       // If the referenced element is an <img>, <canvas>, or <video> element,
       // prefer SurfaceFromElement as it's more reliable.
       mImageElementSurface =
--- a/layout/style/ImageLoader.cpp
+++ b/layout/style/ImageLoader.cpp
@@ -8,17 +8,17 @@
 
 #include "mozilla/css/ImageLoader.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsError.h"
 #include "nsDisplayList.h"
 #include "FrameLayerBuilder.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "imgIContainer.h"
 #include "Image.h"
 
 namespace mozilla {
 namespace css {
 
 void
 ImageLoader::DropDocumentReference()
@@ -357,17 +357,17 @@ ImageLoader::DoRedraw(FrameSet* aFrameSe
         // might not find the right display item.
         frame->InvalidateFrame();
       } else {
         FrameLayerBuilder::IterateRetainedDataFor(frame, InvalidateImagesCallback);
 
         // Update ancestor rendering observers (-moz-element etc)
         nsIFrame *f = frame;
         while (f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
-          nsSVGEffects::InvalidateDirectRenderingObservers(f);
+          SVGObserverUtils::InvalidateDirectRenderingObservers(f);
           f = nsLayoutUtils::GetCrossDocParentFrame(f);
         }
 
         if (aForcePaint) {
           frame->SchedulePaint();
         }
       }
     }
--- a/layout/svg/AutoReferenceChainGuard.h
+++ b/layout/svg/AutoReferenceChainGuard.h
@@ -146,19 +146,21 @@ public:
     *mFrameInUse = true;
     (*mChainCounter)--;
 
     return true;
   }
 
 private:
   void ReportErrorToConsole() {
-    nsAutoString tag;
-    mFrame->GetContent()->AsElement()->GetTagName(tag);
-    const char16_t* params[] = { tag.get() };
+    nsAutoString tag, id;
+    dom::Element* element = mFrame->GetContent()->AsElement();
+    element->GetTagName(tag);
+    element->GetId(id);
+    const char16_t* params[] = { tag.get(), id.get() };
     auto doc = mFrame->GetContent()->OwnerDoc();
     auto warning = *mFrameInUse ?
                      nsIDocument::eSVGReferenceLoop :
                      nsIDocument::eSVGReferenceChainLengthExceeded;
     doc->WarnOnceAbout(warning, /* asError */ true,
                        params, ArrayLength(params));
   }
 
--- a/layout/svg/SVGContextPaint.cpp
+++ b/layout/svg/SVGContextPaint.cpp
@@ -5,17 +5,17 @@
 #include "SVGContextPaint.h"
 
 #include "gfxContext.h"
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Preferences.h"
 #include "nsIDocument.h"
 #include "nsSVGPaintServerFrame.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGPaintServerFrame.h"
 
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
 namespace mozilla {
 
 using image::imgDrawingParams;
@@ -86,22 +86,22 @@ SVGContextPaint::IsAllowedForImageFromUR
 static void
 SetupInheritablePaint(const DrawTarget* aDrawTarget,
                       const gfxMatrix& aContextMatrix,
                       nsIFrame* aFrame,
                       float& aOpacity,
                       SVGContextPaint* aOuterContextPaint,
                       SVGContextPaintImpl::Paint& aTargetPaint,
                       nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
-                      nsSVGEffects::PaintingPropertyDescriptor aProperty,
+                      SVGObserverUtils::PaintingPropertyDescriptor aProperty,
                       imgDrawingParams& aImgParams)
 {
   const nsStyleSVG *style = aFrame->StyleSVG();
   nsSVGPaintServerFrame *ps =
-    nsSVGEffects::GetPaintServer(aFrame, aFillOrStroke, aProperty);
+    SVGObserverUtils::GetPaintServer(aFrame, aFillOrStroke, aProperty);
 
   if (ps) {
     RefPtr<gfxPattern> pattern =
       ps->GetPaintServerPattern(aFrame, aDrawTarget, aContextMatrix,
                                 aFillOrStroke, aOpacity, aImgParams);
 
     if (pattern) {
       aTargetPaint.SetPaintServer(aFrame, aContextMatrix, ps);
@@ -152,34 +152,35 @@ SVGContextPaintImpl::Init(const DrawTarg
     SetFillOpacity(0.0f);
   } else {
     float opacity = nsSVGUtils::GetOpacity(style->FillOpacitySource(),
                                            style->mFillOpacity,
                                            aOuterContextPaint);
 
     SetupInheritablePaint(aDrawTarget, aContextMatrix, aFrame, opacity,
                           aOuterContextPaint, mFillPaint, &nsStyleSVG::mFill,
-                          nsSVGEffects::FillProperty(), aImgParams);
+                          SVGObserverUtils::FillProperty(), aImgParams);
 
     SetFillOpacity(opacity);
 
     toDraw |= DrawMode::GLYPH_FILL;
   }
 
   // stroke:
   if (style->mStroke.Type() == eStyleSVGPaintType_None) {
     SetStrokeOpacity(0.0f);
   } else {
     float opacity = nsSVGUtils::GetOpacity(style->StrokeOpacitySource(),
                                            style->mStrokeOpacity,
                                            aOuterContextPaint);
 
     SetupInheritablePaint(aDrawTarget, aContextMatrix, aFrame, opacity,
                           aOuterContextPaint, mStrokePaint,
-                          &nsStyleSVG::mStroke, nsSVGEffects::StrokeProperty(),
+                          &nsStyleSVG::mStroke,
+                          SVGObserverUtils::StrokeProperty(),
                           aImgParams);
 
     SetStrokeOpacity(opacity);
 
     toDraw |= DrawMode::GLYPH_STROKE;
   }
 
   return toDraw;
--- a/layout/svg/SVGFEContainerFrame.cpp
+++ b/layout/svg/SVGFEContainerFrame.cpp
@@ -3,17 +3,17 @@
  * 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/. */
 
 // Keep in (case-insensitive) order:
 #include "nsContainerFrame.h"
 #include "nsGkAtoms.h"
 #include "nsIFrame.h"
 #include "nsLiteralString.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGFilters.h"
 
 /*
  * This frame is used by filter primitive elements that
  * have special child elements that provide parameters.
  */
 class SVGFEContainerFrame : public nsContainerFrame
 {
@@ -84,13 +84,13 @@ nsresult
 SVGFEContainerFrame::AttributeChanged(int32_t  aNameSpaceID,
                                       nsIAtom* aAttribute,
                                       int32_t  aModType)
 {
   nsSVGFE *element = static_cast<nsSVGFE*>(GetContent());
   if (element->AttributeAffectsRendering(aNameSpaceID, aAttribute)) {
     MOZ_ASSERT(GetParent()->IsSVGFilterFrame(),
                "Observers observe the filter, so that's what we must invalidate");
-    nsSVGEffects::InvalidateDirectRenderingObservers(GetParent());
+    SVGObserverUtils::InvalidateDirectRenderingObservers(GetParent());
   }
 
   return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
 }
--- a/layout/svg/SVGFEImageFrame.cpp
+++ b/layout/svg/SVGFEImageFrame.cpp
@@ -5,17 +5,17 @@
 
 // Keep in (case-insensitive) order:
 #include "nsContainerFrame.h"
 #include "nsContentUtils.h"
 #include "nsFrame.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsLiteralString.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGFilters.h"
 #include "mozilla/dom/SVGFEImageElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 class SVGFEImageFrame final : public nsFrame
 {
@@ -120,17 +120,17 @@ nsresult
 SVGFEImageFrame::AttributeChanged(int32_t  aNameSpaceID,
                                   nsIAtom* aAttribute,
                                   int32_t  aModType)
 {
   SVGFEImageElement* element = static_cast<SVGFEImageElement*>(GetContent());
   if (element->AttributeAffectsRendering(aNameSpaceID, aAttribute)) {
     MOZ_ASSERT(GetParent()->IsSVGFilterFrame(),
                "Observers observe the filter, so that's what we must invalidate");
-    nsSVGEffects::InvalidateDirectRenderingObservers(GetParent());
+    SVGObserverUtils::InvalidateDirectRenderingObservers(GetParent());
   }
 
   // Currently our SMIL implementation does not modify the DOM attributes. Once
   // we implement the SVG 2 SMIL behaviour this can be removed
   // SVGFEImageElement::AfterSetAttr's implementation will be sufficient.
   if (aModType == nsIDOMMutationEvent::SMIL &&
       aAttribute == nsGkAtoms::href &&
       (aNameSpaceID == kNameSpaceID_XLink ||
--- a/layout/svg/SVGFELeafFrame.cpp
+++ b/layout/svg/SVGFELeafFrame.cpp
@@ -2,17 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Keep in (case-insensitive) order:
 #include "nsContainerFrame.h"
 #include "nsFrame.h"
 #include "nsGkAtoms.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGFilters.h"
 
 /*
  * This frame is used by filter primitive elements that don't
  * have special child elements that provide parameters.
  */
 class SVGFELeafFrame final : public nsFrame
 {
@@ -82,13 +82,13 @@ nsresult
 SVGFELeafFrame::AttributeChanged(int32_t  aNameSpaceID,
                                  nsIAtom* aAttribute,
                                  int32_t  aModType)
 {
   nsSVGFE *element = static_cast<nsSVGFE*>(GetContent());
   if (element->AttributeAffectsRendering(aNameSpaceID, aAttribute)) {
     MOZ_ASSERT(GetParent()->IsSVGFilterFrame(),
                "Observers observe the filter, so that's what we must invalidate");
-    nsSVGEffects::InvalidateDirectRenderingObservers(GetParent());
+    SVGObserverUtils::InvalidateDirectRenderingObservers(GetParent());
   }
 
   return nsFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
 }
--- a/layout/svg/SVGFEUnstyledLeafFrame.cpp
+++ b/layout/svg/SVGFEUnstyledLeafFrame.cpp
@@ -2,17 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Keep in (case-insensitive) order:
 #include "nsContainerFrame.h"
 #include "nsFrame.h"
 #include "nsGkAtoms.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGFilters.h"
 
 class SVGFEUnstyledLeafFrame : public nsFrame
 {
   friend nsIFrame*
   NS_NewSVGFEUnstyledLeafFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 protected:
   explicit SVGFEUnstyledLeafFrame(nsStyleContext* aContext)
@@ -61,13 +61,13 @@ nsresult
 SVGFEUnstyledLeafFrame::AttributeChanged(int32_t  aNameSpaceID,
                                          nsIAtom* aAttribute,
                                          int32_t  aModType)
 {
   SVGFEUnstyledElement *element = static_cast<SVGFEUnstyledElement*>(GetContent());
   if (element->AttributeAffectsRendering(aNameSpaceID, aAttribute)) {
     MOZ_ASSERT(GetParent()->GetParent()->IsSVGFilterFrame(),
                "Observers observe the filter, so that's what we must invalidate");
-    nsSVGEffects::InvalidateDirectRenderingObservers(GetParent()->GetParent());
+    SVGObserverUtils::InvalidateDirectRenderingObservers(GetParent()->GetParent());
   }
 
   return nsFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
 }
--- a/layout/svg/SVGGeometryFrame.cpp
+++ b/layout/svg/SVGGeometryFrame.cpp
@@ -13,17 +13,17 @@
 #include "gfxUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Helpers.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/SVGContextPaint.h"
 #include "nsDisplayList.h"
 #include "nsGkAtoms.h"
 #include "nsLayoutUtils.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGMarkerFrame.h"
 #include "SVGGeometryElement.h"
 #include "nsSVGUtils.h"
 #include "mozilla/ArrayUtils.h"
 #include "SVGAnimatedTransformList.h"
 #include "SVGContentUtils.h"
 #include "SVGGraphicsElement.h"
@@ -412,25 +412,25 @@ SVGGeometryFrame::ReflowSVG()
   gfxRect extent = GetBBoxContribution(Matrix(), flags).ToThebesRect();
   mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent,
             PresContext()->AppUnitsPerCSSPixel());
 
   if (mState & NS_FRAME_FIRST_REFLOW) {
     // Make sure we have our filter property (if any) before calling
     // FinishAndStoreOverflow (subsequent filter changes are handled off
     // nsChangeHint_UpdateEffects):
-    nsSVGEffects::UpdateEffects(this);
+    SVGObserverUtils::UpdateEffects(this);
   }
 
   nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
   nsOverflowAreas overflowAreas(overflow, overflow);
   FinishAndStoreOverflow(overflowAreas, mRect.Size());
 
-  mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
-              NS_FRAME_HAS_DIRTY_CHILDREN);
+  RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
+                  NS_FRAME_HAS_DIRTY_CHILDREN);
 
   // Invalidate, but only if this is not our first reflow (since if it is our
   // first reflow then we haven't had our first paint yet).
   if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     InvalidateFrame();
   }
 }
 
@@ -697,30 +697,30 @@ SVGGeometryFrame::GetCanvasTM()
 
 SVGGeometryFrame::MarkerProperties
 SVGGeometryFrame::GetMarkerProperties(SVGGeometryFrame *aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
 
   MarkerProperties result;
   nsCOMPtr<nsIURI> markerURL =
-    nsSVGEffects::GetMarkerURI(aFrame, &nsStyleSVG::mMarkerStart);
+    SVGObserverUtils::GetMarkerURI(aFrame, &nsStyleSVG::mMarkerStart);
   result.mMarkerStart =
-    nsSVGEffects::GetMarkerProperty(markerURL, aFrame,
-                                    nsSVGEffects::MarkerBeginProperty());
+    SVGObserverUtils::GetMarkerProperty(markerURL, aFrame,
+                                        SVGObserverUtils::MarkerBeginProperty());
 
-  markerURL = nsSVGEffects::GetMarkerURI(aFrame, &nsStyleSVG::mMarkerMid);
+  markerURL = SVGObserverUtils::GetMarkerURI(aFrame, &nsStyleSVG::mMarkerMid);
   result.mMarkerMid =
-    nsSVGEffects::GetMarkerProperty(markerURL, aFrame,
-                                    nsSVGEffects::MarkerMiddleProperty());
+    SVGObserverUtils::GetMarkerProperty(markerURL, aFrame,
+                                        SVGObserverUtils::MarkerMiddleProperty());
 
-  markerURL = nsSVGEffects::GetMarkerURI(aFrame, &nsStyleSVG::mMarkerEnd);
+  markerURL = SVGObserverUtils::GetMarkerURI(aFrame, &nsStyleSVG::mMarkerEnd);
   result.mMarkerEnd =
-    nsSVGEffects::GetMarkerProperty(markerURL, aFrame,
-                                    nsSVGEffects::MarkerEndProperty());
+    SVGObserverUtils::GetMarkerProperty(markerURL, aFrame,
+                                        SVGObserverUtils::MarkerEndProperty());
   return result;
 }
 
 nsSVGMarkerFrame *
 SVGGeometryFrame::MarkerProperties::GetMarkerStartFrame()
 {
   if (!mMarkerStart)
     return nullptr;
rename from layout/svg/nsSVGEffects.cpp
rename to layout/svg/SVGObserverUtils.cpp
--- a/layout/svg/nsSVGEffects.cpp
+++ b/layout/svg/SVGObserverUtils.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/. */
 
 // Main header first:
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 
 // Keep others in (case-insensitive) order:
 #include "mozilla/RestyleManager.h"
 #include "mozilla/RestyleManagerInlines.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsISupportsImpl.h"
 #include "nsSVGClipPathFrame.h"
 #include "nsSVGPaintServerFrame.h"
@@ -36,17 +36,17 @@ nsSVGRenderingObserver::StartListening()
 void
 nsSVGRenderingObserver::StopListening()
 {
   Element* target = GetTarget();
 
   if (target) {
     target->RemoveMutationObserver(this);
     if (mInObserverList) {
-      nsSVGEffects::RemoveRenderingObserver(target, this);
+      SVGObserverUtils::RemoveRenderingObserver(target, this);
       mInObserverList = false;
     }
   }
   NS_ASSERTION(!mInObserverList, "still in an observer list?");
 }
 
 static nsSVGRenderingObserverList *
 GetObserverList(Element *aElement)
@@ -64,17 +64,17 @@ nsSVGRenderingObserver::GetReferencedEle
     nsSVGRenderingObserverList *observerList = GetObserverList(target);
     bool inObserverList = observerList && observerList->Contains(this);
     NS_ASSERTION(inObserverList == mInObserverList, "failed to track whether we're in our referenced element's observer list!");
   } else {
     NS_ASSERTION(!mInObserverList, "In whose observer list are we, then?");
   }
 #endif
   if (target && !mInObserverList) {
-    nsSVGEffects::AddRenderingObserver(target, this);
+    SVGObserverUtils::AddRenderingObserver(target, this);
     mInObserverList = true;
   }
   return target;
 }
 
 nsIFrame*
 nsSVGRenderingObserver::GetReferencedFrame()
 {
@@ -194,17 +194,17 @@ nsSVGIDRenderingObserver::~nsSVGIDRender
 {
   StopListening();
 }
 
 void
 nsSVGIDRenderingObserver::DoUpdate()
 {
   if (mElement.get() && mInObserverList) {
-    nsSVGEffects::RemoveRenderingObserver(mElement.get(), this);
+    SVGObserverUtils::RemoveRenderingObserver(mElement.get(), this);
     mInObserverList = false;
   }
 }
 
 void
 nsSVGFrameReferenceFromProperty::Detach()
 {
   mFrame = nullptr;
@@ -224,17 +224,18 @@ nsSVGFrameReferenceFromProperty::Get()
 NS_IMPL_ISUPPORTS(nsSVGRenderingObserverProperty, nsIMutationObserver)
 
 void
 nsSVGRenderingObserverProperty::DoUpdate()
 {
   nsSVGIDRenderingObserver::DoUpdate();
 
   nsIFrame* frame = mFrameReference.Get();
-  if (frame && frame->IsFrameOfType(nsIFrame::eSVG)) {
+
+  if (frame && frame->HasAllStateBits(NS_FRAME_SVG_LAYOUT)) {
     // Changes should propagate out to things that might be observing
     // the referencing frame or its ancestors.
     nsLayoutUtils::PostRestyleEvent(
       frame->GetContent()->AsElement(), nsRestyleHint(0),
       nsChangeHint_InvalidateRenderingObservers);
   }
 }
 
@@ -299,17 +300,17 @@ nsSVGFilterChainObserver::nsSVGFilterCha
 {
   for (uint32_t i = 0; i < aFilters.Length(); i++) {
     if (aFilters[i].GetType() != NS_STYLE_FILTER_URL)
       continue;
 
     // aFilteredFrame can be null if this filter belongs to a
     // CanvasRenderingContext2D.
     nsCOMPtr<nsIURI> filterURL = aFilteredFrame
-      ? nsSVGEffects::GetFilterURI(aFilteredFrame, i)
+      ? SVGObserverUtils::GetFilterURI(aFilteredFrame, i)
       : aFilters[i].GetURL()->ResolveLocalRef(aFilteredElement);
 
     RefPtr<nsSVGFilterReference> reference =
       new nsSVGFilterReference(filterURL, aFilteredElement, this);
     mReferences.AppendElement(reference);
   }
 }
 
@@ -344,17 +345,19 @@ nsSVGFilterProperty::DoUpdate()
   nsIFrame* frame = mFrameReference.Get();
   if (!frame)
     return;
 
   // Repaint asynchronously in case the filter frame is being torn down
   nsChangeHint changeHint =
     nsChangeHint(nsChangeHint_RepaintFrame);
 
-  if (frame && frame->IsFrameOfType(nsIFrame::eSVG)) {
+  // Since we don't call nsSVGRenderingObserverProperty::DoUpdate, we have to
+  // add this bit ourselves.
+  if (frame->HasAllStateBits(NS_FRAME_SVG_LAYOUT)) {
     // Changes should propagate out to things that might be observing
     // the referencing frame or its ancestors.
     changeHint |= nsChangeHint_InvalidateRenderingObservers;
   }
 
   // Don't need to request UpdateOverflow if we're being reflowed.
   if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) {
     changeHint |= nsChangeHint_UpdateOverflow;
@@ -369,41 +372,37 @@ nsSVGMarkerProperty::DoUpdate()
   nsSVGRenderingObserverProperty::DoUpdate();
 
   nsIFrame* frame = mFrameReference.Get();
   if (!frame)
     return;
 
   NS_ASSERTION(frame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
 
-  // Repaint asynchronously in case the marker frame is being torn down
-  nsChangeHint changeHint =
-    nsChangeHint(nsChangeHint_RepaintFrame);
-
   // Don't need to request ReflowFrame if we're being reflowed.
   if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) {
-    changeHint |= nsChangeHint_InvalidateRenderingObservers;
     // XXXjwatt: We need to unify SVG into standard reflow so we can just use
     // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here.
     // XXXSDL KILL THIS!!!
     nsSVGUtils::ScheduleReflowSVG(frame);
   }
   frame->PresContext()->RestyleManager()->PostRestyleEvent(
-    frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
+    frame->GetContent()->AsElement(), nsRestyleHint(0),
+    nsChangeHint_RepaintFrame);
 }
 
 NS_IMPL_ISUPPORTS(nsSVGMaskProperty, nsISupports)
 
 nsSVGMaskProperty::nsSVGMaskProperty(nsIFrame* aFrame)
  : mFrame(aFrame)
 {
   const nsStyleSVGReset *svgReset = aFrame->StyleSVGReset();
 
   for (uint32_t i = 0; i < svgReset->mMask.mImageCount; i++) {
-    nsCOMPtr<nsIURI> maskUri = nsSVGEffects::GetMaskURI(aFrame, i);
+    nsCOMPtr<nsIURI> maskUri = SVGObserverUtils::GetMaskURI(aFrame, i);
     bool hasRef = false;
     if (maskUri) {
       maskUri->GetHasRef(&hasRef);
     }
 
     // Accrording to maskUri, nsSVGPaintingProperty's ctor may trigger an
     // external SVG resource download, so we should pass maskUri in only if
     // maskUri has a chance pointing to an SVG mask resource.
@@ -496,52 +495,50 @@ nsSVGPaintingProperty::DoUpdate()
 {
   nsSVGRenderingObserverProperty::DoUpdate();
 
   nsIFrame* frame = mFrameReference.Get();
   if (!frame)
     return;
 
   if (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
-    nsLayoutUtils::PostRestyleEvent(
-      frame->GetContent()->AsElement(), nsRestyleHint(0),
-      nsChangeHint_InvalidateRenderingObservers);
     frame->InvalidateFrameSubtree();
   } else {
     InvalidateAllContinuations(frame);
   }
 }
 
 static nsSVGFilterProperty*
 GetOrCreateFilterProperty(nsIFrame* aFrame)
 {
   const nsStyleEffects* effects = aFrame->StyleEffects();
   if (!effects->HasFilters())
     return nullptr;
 
   nsSVGFilterProperty *prop =
-    aFrame->GetProperty(nsSVGEffects::FilterProperty());
+    aFrame->GetProperty(SVGObserverUtils::FilterProperty());
   if (prop)
     return prop;
   prop = new nsSVGFilterProperty(effects->mFilters, aFrame);
   NS_ADDREF(prop);
-  aFrame->SetProperty(nsSVGEffects::FilterProperty(), prop);
+  aFrame->SetProperty(SVGObserverUtils::FilterProperty(), prop);
   return prop;
 }
 
 static nsSVGMaskProperty*
 GetOrCreateMaskProperty(nsIFrame* aFrame)
 {
-  nsSVGMaskProperty *prop = aFrame->GetProperty(nsSVGEffects::MaskProperty());
+  nsSVGMaskProperty *prop =
+    aFrame->GetProperty(SVGObserverUtils::MaskProperty());
   if (prop)
     return prop;
 
   prop = new nsSVGMaskProperty(aFrame);
   NS_ADDREF(prop);
-  aFrame->SetProperty(nsSVGEffects::MaskProperty(), prop);
+  aFrame->SetProperty(SVGObserverUtils::MaskProperty(), prop);
   return prop;
 }
 
 template<class T>
 static T*
 GetEffectProperty(nsIURI* aURI, nsIFrame* aFrame,
   const mozilla::FramePropertyDescriptor<T>* aProperty)
 {
@@ -553,91 +550,91 @@ GetEffectProperty(nsIURI* aURI, nsIFrame
     return prop;
   prop = new T(aURI, aFrame, false);
   NS_ADDREF(prop);
   aFrame->SetProperty(aProperty, prop);
   return prop;
 }
 
 nsSVGMarkerProperty*
-nsSVGEffects::GetMarkerProperty(nsIURI* aURI, nsIFrame* aFrame,
+SVGObserverUtils::GetMarkerProperty(nsIURI* aURI, nsIFrame* aFrame,
   const mozilla::FramePropertyDescriptor<nsSVGMarkerProperty>* aProperty)
 {
   MOZ_ASSERT(aFrame->IsSVGGeometryFrame() &&
              static_cast<SVGGeometryElement*>(aFrame->GetContent())->IsMarkable(),
              "Bad frame");
   return GetEffectProperty(aURI, aFrame, aProperty);
 }
 
 nsSVGTextPathProperty*
-nsSVGEffects::GetTextPathProperty(nsIURI* aURI, nsIFrame* aFrame,
+SVGObserverUtils::GetTextPathProperty(nsIURI* aURI, nsIFrame* aFrame,
   const mozilla::FramePropertyDescriptor<nsSVGTextPathProperty>* aProperty)
 {
   return GetEffectProperty(aURI, aFrame, aProperty);
 }
 
 nsSVGPaintingProperty*
-nsSVGEffects::GetPaintingProperty(nsIURI* aURI, nsIFrame* aFrame,
+SVGObserverUtils::GetPaintingProperty(nsIURI* aURI, nsIFrame* aFrame,
   const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>* aProperty)
 {
   return GetEffectProperty(aURI, aFrame, aProperty);
 }
 
 nsSVGPaintingProperty*
-nsSVGEffects::GetPaintingPropertyForURI(nsIURI* aURI, nsIFrame* aFrame,
+SVGObserverUtils::GetPaintingPropertyForURI(nsIURI* aURI, nsIFrame* aFrame,
   URIObserverHashtablePropertyDescriptor aProperty)
 {
   if (!aURI)
     return nullptr;
 
-  nsSVGEffects::URIObserverHashtable *hashtable =
+  SVGObserverUtils::URIObserverHashtable *hashtable =
     aFrame->GetProperty(aProperty);
   if (!hashtable) {
-    hashtable = new nsSVGEffects::URIObserverHashtable();
+    hashtable = new SVGObserverUtils::URIObserverHashtable();
     aFrame->SetProperty(aProperty, hashtable);
   }
   nsSVGPaintingProperty* prop =
     static_cast<nsSVGPaintingProperty*>(hashtable->GetWeak(aURI));
   if (!prop) {
-    bool watchImage = aProperty == nsSVGEffects::BackgroundImageProperty();
+    bool watchImage = aProperty == SVGObserverUtils::BackgroundImageProperty();
     prop = new nsSVGPaintingProperty(aURI, aFrame, watchImage);
     hashtable->Put(aURI, prop);
   }
   return prop;
 }
 
-nsSVGEffects::EffectProperties
-nsSVGEffects::GetEffectProperties(nsIFrame* aFrame)
+SVGObserverUtils::EffectProperties
+SVGObserverUtils::GetEffectProperties(nsIFrame* aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
 
   EffectProperties result;
   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
 
   result.mFilter = GetOrCreateFilterProperty(aFrame);
 
   if (style->mClipPath.GetType() == StyleShapeSourceType::URL) {
-    nsCOMPtr<nsIURI> pathURI = nsSVGEffects::GetClipPathURI(aFrame);
+    nsCOMPtr<nsIURI> pathURI = SVGObserverUtils::GetClipPathURI(aFrame);
     result.mClipPath =
       GetPaintingProperty(pathURI, aFrame, ClipPathProperty());
   } else {
     result.mClipPath = nullptr;
   }
 
   MOZ_ASSERT(style->mMask.mImageCount > 0);
   result.mMask = style->HasMask()
                  ? GetOrCreateMaskProperty(aFrame) : nullptr;
 
   return result;
 }
 
 nsSVGPaintServerFrame *
-nsSVGEffects::GetPaintServer(nsIFrame* aTargetFrame,
-                             nsStyleSVGPaint nsStyleSVG::* aPaint,
-                             PaintingPropertyDescriptor aType)
+SVGObserverUtils::GetPaintServer(nsIFrame* aTargetFrame,
+                                 nsStyleSVGPaint nsStyleSVG::* aPaint,
+                                 PaintingPropertyDescriptor aType)
 {
   const nsStyleSVG* svgStyle = aTargetFrame->StyleSVG();
   if ((svgStyle->*aPaint).Type() != eStyleSVGPaintType_Server)
     return nullptr;
 
   // If we're looking at a frame within SVG text, then we need to look up
   // to find the right frame to get the painting property off.  We should at
   // least look up past a text frame, and if the text frame's parent is the
@@ -646,48 +643,49 @@ nsSVGEffects::GetPaintServer(nsIFrame* a
   if (frame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
     frame = frame->GetParent();
     nsIFrame* grandparent = frame->GetParent();
     if (grandparent && grandparent->IsSVGTextFrame()) {
       frame = grandparent;
     }
   }
 
-  nsCOMPtr<nsIURI> paintServerURL = nsSVGEffects::GetPaintURI(frame, aPaint);
+  nsCOMPtr<nsIURI> paintServerURL =
+    SVGObserverUtils::GetPaintURI(frame, aPaint);
   nsSVGPaintingProperty *property =
-    nsSVGEffects::GetPaintingProperty(paintServerURL, frame, aType);
+    SVGObserverUtils::GetPaintingProperty(paintServerURL, frame, aType);
   if (!property)
     return nullptr;
   nsIFrame* result = property->GetReferencedFrame();
   if (!result)
     return nullptr;
 
   LayoutFrameType type = result->Type();
   if (type != LayoutFrameType::SVGLinearGradient &&
       type != LayoutFrameType::SVGRadialGradient &&
       type != LayoutFrameType::SVGPattern)
     return nullptr;
 
   return static_cast<nsSVGPaintServerFrame*>(result);
 }
 
 nsSVGClipPathFrame *
-nsSVGEffects::EffectProperties::GetClipPathFrame()
+SVGObserverUtils::EffectProperties::GetClipPathFrame()
 {
   if (!mClipPath)
     return nullptr;
 
   nsSVGClipPathFrame* frame = static_cast<nsSVGClipPathFrame*>(
     mClipPath->GetReferencedFrame(LayoutFrameType::SVGClipPath, nullptr));
 
   return frame;
 }
 
 nsTArray<nsSVGMaskFrame *>
-nsSVGEffects::EffectProperties::GetMaskFrames()
+SVGObserverUtils::EffectProperties::GetMaskFrames()
 {
   nsTArray<nsSVGMaskFrame *> result;
   if (!mMask)
     return result;
 
   bool ok = true;
   const nsTArray<RefPtr<nsSVGPaintingProperty>>& props = mMask->GetProps();
   for (size_t i = 0; i < props.Length(); i++) {
@@ -705,55 +703,55 @@ nsSVGEffects::EffectProperties::GetMaskF
     }
     result.AppendElement(maskFrame);
   }
 
   return result;
 }
 
 bool
-nsSVGEffects::EffectProperties::HasNoOrValidEffects()
+SVGObserverUtils::EffectProperties::HasNoOrValidEffects()
 {
   return HasNoOrValidClipPath() && HasNoOrValidMask() && HasNoOrValidFilter();
 }
 
 bool
-nsSVGEffects::EffectProperties::HasNoOrValidClipPath()
+SVGObserverUtils::EffectProperties::HasNoOrValidClipPath()
 {
   if (mClipPath) {
     bool ok = true;
     nsSVGClipPathFrame* frame = static_cast<nsSVGClipPathFrame*>(
       mClipPath->GetReferencedFrame(LayoutFrameType::SVGClipPath, &ok));
     if (!ok || (frame && !frame->IsValid())) {
       return false;
     }
   }
 
   return true;
 }
 
 bool
-nsSVGEffects::EffectProperties::HasNoOrValidMask()
+SVGObserverUtils::EffectProperties::HasNoOrValidMask()
 {
   if (mMask) {
     bool ok = true;
     const nsTArray<RefPtr<nsSVGPaintingProperty>>& props = mMask->GetProps();
     for (size_t i = 0; i < props.Length(); i++) {
       props[i]->GetReferencedFrame(LayoutFrameType::SVGMask, &ok);
       if (!ok) {
         return false;
       }
     }
   }
 
   return true;
 }
 
 void
-nsSVGEffects::UpdateEffects(nsIFrame* aFrame)
+SVGObserverUtils::UpdateEffects(nsIFrame* aFrame)
 {
   NS_ASSERTION(aFrame->GetContent()->IsElement(),
                "aFrame's content should be an element");
 
   aFrame->DeleteProperty(FilterProperty());
   aFrame->DeleteProperty(MaskProperty());
   aFrame->DeleteProperty(ClipPathProperty());
   aFrame->DeleteProperty(MarkerBeginProperty());
@@ -776,17 +774,17 @@ nsSVGEffects::UpdateEffects(nsIFrame* aF
     markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerMid);
     GetMarkerProperty(markerURL, aFrame, MarkerMiddleProperty());
     markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerEnd);
     GetMarkerProperty(markerURL, aFrame, MarkerEndProperty());
   }
 }
 
 nsSVGFilterProperty*
-nsSVGEffects::GetFilterProperty(nsIFrame* aFrame)
+SVGObserverUtils::GetFilterProperty(nsIFrame* aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
 
   if (!aFrame->StyleEffects()->HasFilters())
     return nullptr;
 
   return aFrame->GetProperty(FilterProperty());
 }
@@ -843,58 +841,58 @@ nsSVGRenderingObserverList::RemoveAll()
   // Our list is now cleared.  We need to notify the observers we've removed,
   // so they can update their state & remove themselves as mutation-observers.
   for (uint32_t i = 0; i < observers.Length(); ++i) {
     observers[i]->NotifyEvictedFromRenderingObserverList();
   }
 }
 
 void
-nsSVGEffects::AddRenderingObserver(Element* aElement,
-                                   nsSVGRenderingObserver* aObserver)
+SVGObserverUtils::AddRenderingObserver(Element* aElement,
+                                       nsSVGRenderingObserver* aObserver)
 {
   nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
   if (!observerList) {
     observerList = new nsSVGRenderingObserverList();
     if (!observerList)
       return;
     aElement->SetProperty(nsGkAtoms::renderingobserverlist, observerList,
                           nsINode::DeleteProperty<nsSVGRenderingObserverList>);
   }
   aElement->SetHasRenderingObservers(true);
   observerList->Add(aObserver);
 }
 
 void
-nsSVGEffects::RemoveRenderingObserver(Element* aElement,
-                                      nsSVGRenderingObserver* aObserver)
+SVGObserverUtils::RemoveRenderingObserver(Element* aElement,
+                                          nsSVGRenderingObserver* aObserver)
 {
   nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
   if (observerList) {
     NS_ASSERTION(observerList->Contains(aObserver),
                  "removing observer from an element we're not observing?");
     observerList->Remove(aObserver);
     if (observerList->IsEmpty()) {
       aElement->SetHasRenderingObservers(false);
     }
   }
 }
 
 void
-nsSVGEffects::RemoveAllRenderingObservers(Element* aElement)
+SVGObserverUtils::RemoveAllRenderingObservers(Element* aElement)
 {
   nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
   if (observerList) {
     observerList->RemoveAll();
     aElement->SetHasRenderingObservers(false);
   }
 }
 
 void
-nsSVGEffects::InvalidateRenderingObservers(nsIFrame* aFrame)
+SVGObserverUtils::InvalidateRenderingObservers(nsIFrame* aFrame)
 {
   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
 
   nsIContent* content = aFrame->GetContent();
   if (!content || !content->IsElement())
     return;
 
   // If the rendering has changed, the bounds may well have changed too:
@@ -917,17 +915,18 @@ nsSVGEffects::InvalidateRenderingObserve
         observerList->InvalidateAll();
         return;
       }
     }
   }
 }
 
 void
-nsSVGEffects::InvalidateDirectRenderingObservers(Element* aElement, uint32_t aFlags /* = 0 */)
+SVGObserverUtils::InvalidateDirectRenderingObservers(Element* aElement,
+                                                     uint32_t aFlags /* = 0 */)
 {
   nsIFrame* frame = aElement->GetPrimaryFrame();
   if (frame) {
     // If the rendering has changed, the bounds may well have changed too:
     frame->DeleteProperty(nsSVGUtils::ObjectBoundingBoxProperty());
   }
 
   if (aElement->HasRenderingObservers()) {
@@ -938,26 +937,27 @@ nsSVGEffects::InvalidateDirectRenderingO
       } else {
         observerList->InvalidateAll();
       }
     }
   }
 }
 
 void
-nsSVGEffects::InvalidateDirectRenderingObservers(nsIFrame* aFrame, uint32_t aFlags /* = 0 */)
+SVGObserverUtils::InvalidateDirectRenderingObservers(nsIFrame* aFrame,
+                                                     uint32_t aFlags /* = 0 */)
 {
   nsIContent* content = aFrame->GetContent();
   if (content && content->IsElement()) {
     InvalidateDirectRenderingObservers(content->AsElement(), aFlags);
   }
 }
 
 already_AddRefed<nsIURI>
-nsSVGEffects::GetBaseURLForLocalRef(nsIContent* content, nsIURI* aDocURI)
+SVGObserverUtils::GetBaseURLForLocalRef(nsIContent* content, nsIURI* aDocURI)
 {
   MOZ_ASSERT(content);
 
   // For a local-reference URL, resolve that fragment against the current
   // document that relative URLs are resolved against.
   nsCOMPtr<nsIURI> baseURI = content->OwnerDoc()->GetDocumentURI();
 
   if (content->IsInAnonymousSubtree()) {
@@ -1009,71 +1009,72 @@ ResolveURLUsingLocalRef(nsIFrame* aFrame
 
   // Non-local-reference URL.
   if (!aURL->IsLocalRef()) {
     nsCOMPtr<nsIURI> result = aURL->GetURI();
     return result.forget();
   }
 
   nsCOMPtr<nsIURI> baseURI =
-    nsSVGEffects::GetBaseURLForLocalRef(aFrame->GetContent(), aURL->GetURI());
+    SVGObserverUtils::GetBaseURLForLocalRef(aFrame->GetContent(),
+                                            aURL->GetURI());
 
   return aURL->ResolveLocalRef(baseURI);
 }
 
 already_AddRefed<nsIURI>
-nsSVGEffects::GetMarkerURI(nsIFrame* aFrame,
-                           RefPtr<css::URLValue> nsStyleSVG::* aMarker)
+SVGObserverUtils::GetMarkerURI(nsIFrame* aFrame,
+                               RefPtr<css::URLValue> nsStyleSVG::* aMarker)
 {
   return ResolveURLUsingLocalRef(aFrame, aFrame->StyleSVG()->*aMarker);
 }
 
 already_AddRefed<nsIURI>
-nsSVGEffects::GetClipPathURI(nsIFrame* aFrame)
+SVGObserverUtils::GetClipPathURI(nsIFrame* aFrame)
 {
   const nsStyleSVGReset* svgResetStyle = aFrame->StyleSVGReset();
   MOZ_ASSERT(svgResetStyle->mClipPath.GetType() == StyleShapeSourceType::URL);
 
   css::URLValue* url = svgResetStyle->mClipPath.GetURL();
   return ResolveURLUsingLocalRef(aFrame, url);
 }
 
 already_AddRefed<nsIURI>
-nsSVGEffects::GetFilterURI(nsIFrame* aFrame, uint32_t aIndex)
+SVGObserverUtils::GetFilterURI(nsIFrame* aFrame, uint32_t aIndex)
 {
   const nsStyleEffects* effects = aFrame->StyleEffects();
   MOZ_ASSERT(effects->mFilters.Length() > aIndex);
   MOZ_ASSERT(effects->mFilters[aIndex].GetType() == NS_STYLE_FILTER_URL);
 
   return ResolveURLUsingLocalRef(aFrame, effects->mFilters[aIndex].GetURL());
 }
 
 already_AddRefed<nsIURI>
-nsSVGEffects::GetFilterURI(nsIFrame* aFrame, const nsStyleFilter& aFilter)
+SVGObserverUtils::GetFilterURI(nsIFrame* aFrame, const nsStyleFilter& aFilter)
 {
   MOZ_ASSERT(aFrame->StyleEffects()->mFilters.Length());
   MOZ_ASSERT(aFilter.GetType() == NS_STYLE_FILTER_URL);
 
   return ResolveURLUsingLocalRef(aFrame, aFilter.GetURL());
 }
 
 already_AddRefed<nsIURI>
-nsSVGEffects::GetPaintURI(nsIFrame* aFrame,
-                          nsStyleSVGPaint nsStyleSVG::* aPaint)
+SVGObserverUtils::GetPaintURI(nsIFrame* aFrame,
+                              nsStyleSVGPaint nsStyleSVG::* aPaint)
 {
   const nsStyleSVG* svgStyle = aFrame->StyleSVG();
   MOZ_ASSERT((svgStyle->*aPaint).Type() ==
              nsStyleSVGPaintType::eStyleSVGPaintType_Server);
 
   return ResolveURLUsingLocalRef(aFrame,
                                  (svgStyle->*aPaint).GetPaintServer());
 }
 
 already_AddRefed<nsIURI>
-nsSVGEffects::GetMaskURI(nsIFrame* aFrame, uint32_t aIndex)
+SVGObserverUtils::GetMaskURI(nsIFrame* aFrame, uint32_t aIndex)
 {
   const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
   MOZ_ASSERT(svgReset->mMask.mLayers.Length() > aIndex);
 
   mozilla::css::URLValueData* data =
     svgReset->mMask.mLayers[aIndex].mImage.GetURLValue();
   return ResolveURLUsingLocalRef(aFrame, data);
 }
rename from layout/svg/nsSVGEffects.h
rename to layout/svg/SVGObserverUtils.h
--- a/layout/svg/nsSVGEffects.h
+++ b/layout/svg/SVGObserverUtils.h
@@ -421,17 +421,17 @@ public:
    * to them.
    */
   void RemoveAll();
 
 private:
   nsTHashtable<nsPtrHashKey<nsSVGRenderingObserver> > mObservers;
 };
 
-class nsSVGEffects
+class SVGObserverUtils
 {
 public:
   typedef mozilla::dom::Element Element;
   typedef nsInterfaceHashtable<nsURIHashKey, nsIMutationObserver>
     URIObserverHashtable;
 
   using PaintingPropertyDescriptor =
     const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>*;
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -22,17 +22,17 @@
 #include "nsBidiPresUtils.h"
 #include "nsBlockFrame.h"
 #include "nsCaret.h"
 #include "nsContentUtils.h"
 #include "nsGkAtoms.h"
 #include "nsIDOMSVGLength.h"
 #include "nsISelection.h"
 #include "nsQuickSort.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGOuterSVGFrame.h"
 #include "nsSVGPaintServerFrame.h"
 #include "mozilla/dom/SVGRect.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGUtils.h"
 #include "nsTArray.h"
 #include "nsTextFrame.h"
 #include "nsTextNode.h"
@@ -1387,18 +1387,23 @@ private:
    * The index into the current nsTextNode's character content.
    */
   uint32_t mNodeCharIndex;
 };
 
 /* static */ void
 TextNodeCorrespondenceRecorder::RecordCorrespondence(SVGTextFrame* aRoot)
 {
-  TextNodeCorrespondenceRecorder recorder(aRoot);
-  recorder.Record(aRoot);
+  if (aRoot->GetStateBits() & NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY) {
+    // Resolve bidi so that continuation frames are created if necessary:
+    aRoot->MaybeResolveBidiForAnonymousBlockChild();
+    TextNodeCorrespondenceRecorder recorder(aRoot);
+    recorder.Record(aRoot);
+    aRoot->RemoveStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY);
+  }
 }
 
 void
 TextNodeCorrespondenceRecorder::Record(SVGTextFrame* aRoot)
 {
   if (!mNodeIterator.Current()) {
     // If there are no nsTextNodes then there is nothing to do.
     return;
@@ -1723,19 +1728,19 @@ private:
    * The iterator's current position relative to mSubtree.
    */
   SubtreePosition mSubtreePosition;
 };
 
 uint32_t
 TextFrameIterator::UndisplayedCharacters() const
 {
-  MOZ_ASSERT(!(mRootFrame->PrincipalChildList().FirstChild() &&
-               NS_SUBTREE_DIRTY(mRootFrame->PrincipalChildList().FirstChild())),
-             "should have already reflowed the anonymous block child");
+  MOZ_ASSERT(!(mRootFrame->GetStateBits() &
+               NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY),
+             "Text correspondence must be up to date");
 
   if (!mCurrentFrame) {
     return mRootFrame->mTrailingUndisplayedCharacters;
   }
 
   nsTextFrame* frame = do_QueryFrame(mCurrentFrame);
   return GetUndisplayedCharactersBeforeFrame(frame);
 }
@@ -2426,17 +2431,17 @@ private:
   bool mPostReflow;
 };
 
 CharIterator::CharIterator(SVGTextFrame* aSVGTextFrame,
                            CharIterator::CharacterFilter aFilter,
                            nsIContent* aSubtree,
                            bool aPostReflow)
   : mFilter(aFilter),
-    mFrameIterator(FrameIfAnonymousChildReflowed(aSVGTextFrame), aSubtree),
+    mFrameIterator(aSVGTextFrame, aSubtree),
     mFrameForTrimCheck(nullptr),
     mTrimmedOffset(0),
     mTrimmedLength(0),
     mTextElementCharIndex(0),
     mGlyphStartTextElementCharIndex(0),
     mLengthAdjustScaleFactor(aSVGTextFrame->mLengthAdjustScaleFactor)
   , mPostReflow(aPostReflow)
 {
@@ -3402,17 +3407,17 @@ SVGTextFrame::HandleAttributeChangeInDes
       NotifyGlyphMetricsChange();
     } else if ((aNameSpaceID == kNameSpaceID_XLink ||
                 aNameSpaceID == kNameSpaceID_None) &&
                aAttribute == nsGkAtoms::href) {
       // Blow away our reference, if any
       nsIFrame* childElementFrame = aElement->GetPrimaryFrame();
       if (childElementFrame) {
         childElementFrame->DeleteProperty(
-          nsSVGEffects::HrefAsTextPathProperty());
+          SVGObserverUtils::HrefAsTextPathProperty());
         NotifyGlyphMetricsChange();
       }
     }
   } else {
     if (aNameSpaceID == kNameSpaceID_None &&
         IsGlyphPositioningAttribute(aAttribute)) {
       NotifyGlyphMetricsChange();
     }
@@ -3774,17 +3779,19 @@ SVGTextFrame::ReflowSVG()
 {
   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
                "This call is probaby a wasteful mistake");
 
   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
              "ReflowSVG mechanism not designed for this");
 
   if (!nsSVGUtils::NeedsReflowSVG(this)) {
-    NS_ASSERTION(!(mState & NS_STATE_SVG_POSITIONING_DIRTY), "How did this happen?");
+    MOZ_ASSERT(!HasAnyStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
+                                NS_STATE_SVG_POSITIONING_DIRTY),
+               "How did this happen?");
     return;
   }
 
   MaybeReflowAnonymousBlockChild();
   UpdateGlyphPositioning();
 
   nsPresContext* presContext = PresContext();
 
@@ -3830,24 +3837,24 @@ SVGTextFrame::ReflowSVG()
     // covered region.
     mRect.Inflate(presContext->AppUnitsPerDevPixel());
   }
 
   if (mState & NS_FRAME_FIRST_REFLOW) {
     // Make sure we have our filter property (if any) before calling
     // FinishAndStoreOverflow (subsequent filter changes are handled off
     // nsChangeHint_UpdateEffects):
-    nsSVGEffects::UpdateEffects(this);
+    SVGObserverUtils::UpdateEffects(this);
   }
 
   // Now unset the various reflow bits. Do this before calling
   // FinishAndStoreOverflow since FinishAndStoreOverflow can require glyph
   // positions (to resolve transform-origin).
-  mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
-              NS_FRAME_HAS_DIRTY_CHILDREN);
+  RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
+                  NS_FRAME_HAS_DIRTY_CHILDREN);
 
   nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
   nsOverflowAreas overflowAreas(overflow, overflow);
   FinishAndStoreOverflow(overflowAreas, mRect.Size());
 
   // XXX nsSVGContainerFrame::ReflowSVG only looks at its nsSVGDisplayableFrame
   // children, and calls ConsiderChildOverflow on them.  Does it matter
   // that ConsiderChildOverflow won't be called on our children?
@@ -4062,16 +4069,145 @@ SVGTextFrame::SelectSubString(nsIContent
  * Implements the SVG DOM GetSubStringLength method for the specified
  * text content element.
  */
 nsresult
 SVGTextFrame::GetSubStringLength(nsIContent* aContent,
                                  uint32_t charnum, uint32_t nchars,
                                  float* aResult)
 {
+  // For some content we cannot (or currently cannot) compute the length
+  // without reflowing.  In those cases we need to fall back to using
+  // GetSubStringLengthSlowFallback.
+  //
+  // We fall back for textPath since we need glyph positioning in order to
+  // tell if any characters should be ignored due to having fallen off the
+  // end of the textPath.
+  //
+  // We fall back for bidi because GetTrimmedOffsets does not produce the
+  // correct results for bidi continuations when passed aPostReflow = false.
+  // XXX It may be possible to determine which continuations to trim from (and
+  // which sides), but currently we don't do that.  It would require us to
+  // identify the visual (rather than logical) start and end of the line, to
+  // avoid trimming at line-internal frame boundaries.  Maybe nsBidiPresUtils
+  // methods like GetFrameToRightOf and GetFrameToLeftOf would help?
+  //
+  TextFrameIterator frameIter(this);
+  for (nsTextFrame* frame = frameIter.Current(); frame; frame = frameIter.Next()) {
+    if (frameIter.TextPathFrame() ||
+        frame->GetNextContinuation()) {
+      return GetSubStringLengthSlowFallback(aContent, charnum, nchars, aResult);
+    }
+  }
+
+  // We only need our text correspondence to be up to date (no need to call
+  // UpdateGlyphPositioning).
+  TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
+
+  // Convert charnum/nchars from addressable characters relative to
+  // aContent to global character indices.
+  CharIterator chit(this, CharIterator::eAddressable, aContent,
+                    /* aPostReflow */ false);
+  if (!chit.AdvanceToSubtree() ||
+      !chit.Next(charnum) ||
+      chit.IsAfterSubtree()) {
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+  }
+
+  // We do this after the NS_ERROR_DOM_INDEX_SIZE_ERR return so JS calls
+  // correctly throw when necessary.
+  if (nchars == 0) {
+    *aResult = 0.0f;
+    return NS_OK;
+  }
+
+  charnum = chit.TextElementCharIndex();
+  chit.NextWithinSubtree(nchars);
+  nchars = chit.TextElementCharIndex() - charnum;
+
+  // Sum of the substring advances.
+  nscoord textLength = 0;
+
+  TextFrameIterator frit(this); // aSubtree = nullptr
+
+  // Index of the first non-skipped char in the frame, and of a subsequent char
+  // that we're interested in.  Both are relative to the index of the first
+  // non-skipped char in the ancestor <text> element.
+  uint32_t frameStartTextElementCharIndex = 0;
+  uint32_t textElementCharIndex;
+
+  for (nsTextFrame* frame = frit.Current(); frame; frame = frit.Next()) {
+    frameStartTextElementCharIndex += frit.UndisplayedCharacters();
+    textElementCharIndex = frameStartTextElementCharIndex;
+
+    // Offset into frame's nsTextNode:
+    const uint32_t untrimmedOffset = frame->GetContentOffset();
+    const uint32_t untrimmedLength = frame->GetContentEnd() - untrimmedOffset;
+
+    // Trim the offset/length to remove any leading/trailing white space.
+    uint32_t trimmedOffset = untrimmedOffset;
+    uint32_t trimmedLength = untrimmedLength;
+    nsTextFrame::TrimmedOffsets trimmedOffsets =
+      frame->GetTrimmedOffsets(frame->GetContent()->GetText(),
+                               /* aTrimAfter */ true,
+                               /* aPostReflow */ false);
+    TrimOffsets(trimmedOffset, trimmedLength, trimmedOffsets);
+
+    textElementCharIndex += trimmedOffset - untrimmedOffset;
+
+    if (textElementCharIndex >= charnum + nchars) {
+      break; // we're past the end of the substring
+    }
+
+    uint32_t offset = textElementCharIndex;
+
+    // Intersect the substring we are interested in with the range covered by
+    // the nsTextFrame.
+    IntersectInterval(offset, trimmedLength, charnum, nchars);
+
+    if (trimmedLength != 0) {
+      // Convert offset into an index into the frame.
+      offset += trimmedOffset - textElementCharIndex;
+
+      gfxSkipCharsIterator skipCharsIter =
+        frame->EnsureTextRun(nsTextFrame::eInflated);
+      gfxTextRun* textRun = frame->GetTextRun(nsTextFrame::eInflated);
+      Range range =
+        ConvertOriginalToSkipped(skipCharsIter, offset, trimmedLength);
+
+      // Accumulate the advance.
+      textLength += textRun->GetAdvanceWidth(range, nullptr);
+    }
+
+    // Advance, ready for next call:
+    frameStartTextElementCharIndex += untrimmedLength;
+  }
+
+  nsPresContext* presContext = PresContext();
+  float cssPxPerDevPx = presContext->
+    AppUnitsToFloatCSSPixels(presContext->AppUnitsPerDevPixel());
+
+  *aResult = presContext->AppUnitsToGfxUnits(textLength) *
+               cssPxPerDevPx / mFontSizeScaleFactor;
+  return NS_OK;
+}
+
+nsresult
+SVGTextFrame::GetSubStringLengthSlowFallback(nsIContent* aContent,
+                                             uint32_t charnum, uint32_t nchars,
+                                             float* aResult)
+{
+  // We need to make sure that we've been reflowed before updating the glyph
+  // positioning.
+  // XXX perf: It may be possible to limit reflow to just calling ReflowSVG,
+  // but we would still need to resort to full reflow for percentage
+  // positioning attributes.  For now we just do a full reflow regardless since
+  // the cases that would cause us to be called are relatively uncommon.
+  PresContext()->PresShell()->FlushPendingNotifications(FlushType::Layout);
+
   UpdateGlyphPositioning();
 
   // Convert charnum/nchars from addressable characters relative to
   // aContent to global character indices.
   CharIterator chit(this, CharIterator::eAddressable, aContent);
   if (!chit.AdvanceToSubtree() ||
       !chit.Next(charnum) ||
       chit.IsAfterSubtree()) {
@@ -4804,17 +4940,17 @@ SVGTextFrame::AdjustPositionsForClusters
     it.Next();
   }
 }
 
 SVGPathElement*
 SVGTextFrame::GetTextPathPathElement(nsIFrame* aTextPathFrame)
 {
   nsSVGTextPathProperty *property =
-    aTextPathFrame->GetProperty(nsSVGEffects::HrefAsTextPathProperty());
+    aTextPathFrame->GetProperty(SVGObserverUtils::HrefAsTextPathProperty());
 
   if (!property) {
     nsIContent* content = aTextPathFrame->GetContent();
     dom::SVGTextPathElement* tp = static_cast<dom::SVGTextPathElement*>(content);
     nsAutoString href;
     if (tp->mStringAttributes[dom::SVGTextPathElement::HREF].IsExplicitlySet()) {
       tp->mStringAttributes[dom::SVGTextPathElement::HREF]
         .GetAnimValue(href, tp);
@@ -4827,20 +4963,20 @@ SVGTextFrame::GetTextPathPathElement(nsI
       return nullptr; // no URL
     }
 
     nsCOMPtr<nsIURI> targetURI;
     nsCOMPtr<nsIURI> base = content->GetBaseURI();
     nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
                                               content->GetUncomposedDoc(), base);
 
-    property = nsSVGEffects::GetTextPathProperty(
+    property = SVGObserverUtils::GetTextPathProperty(
       targetURI,
       aTextPathFrame,
-      nsSVGEffects::HrefAsTextPathProperty());
+      SVGObserverUtils::HrefAsTextPathProperty());
     if (!property)
       return nullptr;
   }
 
   Element* element = property->GetReferencedElement();
   return (element && element->IsSVGElement(nsGkAtoms::path)) ?
     static_cast<SVGPathElement*>(element) : nullptr;
 }
@@ -5031,16 +5167,20 @@ SVGTextFrame::DoGlyphPositioning()
   RemoveStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
 
   nsIFrame* kid = PrincipalChildList().FirstChild();
   if (kid && NS_SUBTREE_DIRTY(kid)) {
     MOZ_ASSERT(false, "should have already reflowed the kid");
     return;
   }
 
+  // Since we can be called directly via GetBBoxContribution, our correspondence
+  // may not be up to date.
+  TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
+
   // Determine the positions of each character in app units.
   nsTArray<nsPoint> charPositions;
   DetermineCharPositions(charPositions);
 
   if (charPositions.IsEmpty()) {
     // No characters, so nothing to do.
     return;
   }
@@ -5217,17 +5357,21 @@ SVGTextFrame::ScheduleReflowSVG()
   } else {
     nsSVGUtils::ScheduleReflowSVG(this);
   }
 }
 
 void
 SVGTextFrame::NotifyGlyphMetricsChange()
 {
-  AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
+  // TODO: perf - adding NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY is overly
+  // aggressive here.  Ideally we would only set that bit when our descendant
+  // frame tree changes (i.e. after frame construction).
+  AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
+               NS_STATE_SVG_POSITIONING_DIRTY);
   nsLayoutUtils::PostRestyleEvent(
     mContent->AsElement(), nsRestyleHint(0),
     nsChangeHint_InvalidateRenderingObservers);
   ScheduleReflowSVG();
 }
 
 void
 SVGTextFrame::UpdateGlyphPositioning()
@@ -5269,58 +5413,66 @@ SVGTextFrame::MaybeReflowAnonymousBlockC
   if (NS_SUBTREE_DIRTY(this)) {
     if (mState & NS_FRAME_IS_DIRTY) {
       // If we require a full reflow, ensure our kid is marked fully dirty.
       // (Note that our anonymous nsBlockFrame is not an nsSVGDisplayableFrame, so
       // even when we are called via our ReflowSVG this will not be done for us
       // by nsSVGDisplayContainerFrame::ReflowSVG.)
       kid->AddStateBits(NS_FRAME_IS_DIRTY);
     }
+
+    TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
+
     MOZ_ASSERT(nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(this),
                "should be under ReflowSVG");
     nsPresContext::InterruptPreventer noInterrupts(PresContext());
     DoReflow();
   }
 }
 
 void
 SVGTextFrame::DoReflow()
 {
   // Since we are going to reflow the anonymous block frame, we will
   // need to update mPositions.
-  AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
+  // We also mark our text correspondence as dirty since we can end up needing
+  // reflow in ways that do not set NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY.
+  // (We'd then fail the "expected a TextNodeCorrespondenceProperty" assertion
+  // when UpdateGlyphPositioning() is called after we return.)
+  AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
+               NS_STATE_SVG_POSITIONING_DIRTY);
 
   if (mState & NS_FRAME_IS_NONDISPLAY) {
     // Normally, these dirty flags would be cleared in ReflowSVG(), but that
     // doesn't get called for non-display frames. We don't want to reflow our
     // descendants every time SVGTextFrame::PaintSVG makes sure that we have
     // valid positions by calling UpdateGlyphPositioning(), so we need to clear
     // these dirty bits. Note that this also breaks an invalidation loop where
     // our descendants invalidate as they reflow, which invalidates rendering
     // observers, which reschedules the frame that is currently painting by
     // referencing us to paint again. See bug 839958 comment 7. Hopefully we
     // will break that loop more convincingly at some point.
-    mState &= ~(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
+    RemoveStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
   }
 
   nsPresContext *presContext = PresContext();
   nsIFrame* kid = PrincipalChildList().FirstChild();
   if (!kid)
     return;
 
   RefPtr<gfxContext> renderingContext =
     presContext->PresShell()->CreateReferenceRenderingContext();
 
   if (UpdateFontSizeScaleFactor()) {
     // If the font size scale factor changed, we need the block to report
     // an updated preferred width.
     kid->MarkIntrinsicISizesDirty();
   }
 
-  mState |= NS_STATE_SVG_TEXT_IN_REFLOW;
+  AddStateBits(NS_STATE_SVG_TEXT_IN_REFLOW);
 
   nscoord inlineSize = kid->GetPrefISize(renderingContext);
   WritingMode wm = kid->GetWritingMode();
   ReflowInput reflowInput(presContext, kid,
                                 renderingContext,
                                 LogicalSize(wm, inlineSize,
                                             NS_UNCONSTRAINEDSIZE));
   ReflowOutput desiredSize(reflowInput);
@@ -5330,19 +5482,17 @@ SVGTextFrame::DoReflow()
                reflowInput.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
                "style system should ensure that :-moz-svg-text "
                "does not get styled");
 
   kid->Reflow(presContext, desiredSize, reflowInput, status);
   kid->DidReflow(presContext, &reflowInput, nsDidReflowStatus::FINISHED);
   kid->SetSize(wm, desiredSize.Size(wm));
 
-  mState &= ~NS_STATE_SVG_TEXT_IN_REFLOW;
-
-  TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
+  RemoveStateBits(NS_STATE_SVG_TEXT_IN_REFLOW);
 }
 
 // Usable font size range in devpixels / user-units
 #define CLAMP_MIN_SIZE 8.0
 #define CLAMP_MAX_SIZE 200.0
 #define PRECISE_SIZE   200.0
 
 bool
--- a/layout/svg/SVGTextFrame.h
+++ b/layout/svg/SVGTextFrame.h
@@ -198,17 +198,18 @@ class SVGTextFrame final : public nsSVGD
 protected:
   explicit SVGTextFrame(nsStyleContext* aContext)
     : nsSVGDisplayContainerFrame(aContext, kClassID)
     , mTrailingUndisplayedCharacters(0)
     , mFontSizeScaleFactor(1.0f)
     , mLastContextScale(1.0f)
     , mLengthAdjustScaleFactor(1.0f)
   {
-    AddStateBits(NS_STATE_SVG_POSITIONING_DIRTY);
+    AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
+                 NS_STATE_SVG_POSITIONING_DIRTY);
   }
 
   ~SVGTextFrame() {}
 
 public:
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS(SVGTextFrame)
 
@@ -413,16 +414,28 @@ private:
 
   /**
    * Populates mPositions with positioning information for each character
    * within the <text>.
    */
   void DoGlyphPositioning();
 
   /**
+   * This fallback version of GetSubStringLength that flushes layout and takes
+   * into account glyph positioning.  As per the SVG 2 spec, typically glyph
+   * positioning does not affect the results of getSubStringLength, but one
+   * exception is text in a textPath where we need to ignore characters that
+   * fall off the end of the textPath path.
+   */
+  nsresult GetSubStringLengthSlowFallback(nsIContent* aContent,
+                                          uint32_t charnum,
+                                          uint32_t nchars,
+                                          float* aResult);
+
+  /**
    * Converts the specified index into mPositions to an addressable
    * character index (as can be used with the SVG DOM text methods)
    * relative to the specified text child content element.
    *
    * @param aIndex The global character index.
    * @param aContent The descendant text child content element that
    *   the returned addressable index will be relative to; null
    *   means the same as the <text> element.
--- a/layout/svg/moz.build
+++ b/layout/svg/moz.build
@@ -12,38 +12,37 @@ if CONFIG['ENABLE_TESTS']:
         'tests/mochitest.ini',
 ]
     MOCHITEST_CHROME_MANIFESTS += [
         'tests/chrome.ini',
 ]
 
 EXPORTS += [
     'nsFilterInstance.h',
-    'nsSVGEffects.h',
     'nsSVGFilterInstance.h',
     'nsSVGForeignObjectFrame.h',
     'nsSVGImageFrame.h',
     'nsSVGIntegrationUtils.h',
     'nsSVGUseFrame.h',
     'nsSVGUtils.h',
     'SVGImageContext.h',
+    'SVGObserverUtils.h',
 ]
 
 EXPORTS.mozilla += [
     'SVGContextPaint.h',
 ]
 
 UNIFIED_SOURCES += [
     'nsCSSClipPathInstance.cpp',
     'nsCSSFilterInstance.cpp',
     'nsFilterInstance.cpp',
     'nsSVGAFrame.cpp',
     'nsSVGClipPathFrame.cpp',
     'nsSVGContainerFrame.cpp',
-    'nsSVGEffects.cpp',
     'nsSVGFilterFrame.cpp',
     'nsSVGFilterInstance.cpp',
     'nsSVGForeignObjectFrame.cpp',
     'nsSVGGenericContainerFrame.cpp',
     'nsSVGGFrame.cpp',
     'nsSVGGradientFrame.cpp',
     'nsSVGImageFrame.cpp',
     'nsSVGInnerSVGFrame.cpp',
@@ -60,16 +59,17 @@ UNIFIED_SOURCES += [
     'nsSVGViewportFrame.cpp',
     'SVGContextPaint.cpp',
     'SVGFEContainerFrame.cpp',
     'SVGFEImageFrame.cpp',
     'SVGFELeafFrame.cpp',
     'SVGFEUnstyledLeafFrame.cpp',
     'SVGGeometryFrame.cpp',
     'SVGImageContext.cpp',
+    'SVGObserverUtils.cpp',
     'SVGTextFrame.cpp',
     'SVGViewFrame.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '../../widget',
     '../base',
--- a/layout/svg/nsSVGClipPathFrame.cpp
+++ b/layout/svg/nsSVGClipPathFrame.cpp
@@ -7,17 +7,17 @@
 #include "nsSVGClipPathFrame.h"
 
 // Keep others in (case-insensitive) order:
 #include "AutoReferenceChainGuard.h"
 #include "DrawResult.h"
 #include "gfxContext.h"
 #include "mozilla/dom/SVGClipPathElement.h"
 #include "nsGkAtoms.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "SVGGeometryElement.h"
 #include "SVGGeometryFrame.h"
 #include "nsSVGUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
@@ -141,17 +141,17 @@ nsSVGClipPathFrame::PaintClipMask(gfxCon
   // Paint this clipPath's contents into aMaskDT:
   // We need to set mMatrixForChildren here so that under the PaintSVG calls
   // on our children (below) our GetCanvasTM() method will return the correct
   // transform.
   mMatrixForChildren = GetClipPathTransform(aClippedFrame) * aMatrix;
 
   // Check if this clipPath is itself clipped by another clipPath:
   nsSVGClipPathFrame* clipPathThatClipsClipPath =
-    nsSVGEffects::GetEffectProperties(this).GetClipPathFrame();
+    SVGObserverUtils::GetEffectProperties(this).GetClipPathFrame();
   nsSVGUtils::MaskUsage maskUsage;
   nsSVGUtils::DetermineMaskUsage(this, true, maskUsage);
 
   if (maskUsage.shouldApplyClipPath) {
     clipPathThatClipsClipPath->ApplyClipPath(aMaskContext, aClippedFrame,
                                              aMatrix);
   } else if (maskUsage.shouldGenerateClipMaskLayer) {
     Matrix maskTransform;
@@ -197,18 +197,18 @@ nsSVGClipPathFrame::PaintFrameIntoMask(n
   if (!frame) {
     return;
   }
 
   // The CTM of each frame referencing us can be different.
   frame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
 
   // Children of this clipPath may themselves be clipped.
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(aFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(aFrame);
   if (effectProperties.HasInvalidClipPath()) {
     return;
   }
   nsSVGClipPathFrame *clipPathThatClipsChild =
     effectProperties.GetClipPathFrame();
 
   nsSVGUtils::MaskUsage maskUsage;
   nsSVGUtils::DetermineMaskUsage(aFrame, true, maskUsage);
@@ -301,17 +301,17 @@ nsSVGClipPathFrame::PointIsInsideClipPat
   gfxPoint point = matrix.TransformPoint(aPoint);
 
   // clipPath elements can themselves be clipped by a different clip path. In
   // that case the other clip path further clips away the element that is being
   // clipped by the original clipPath. If this clipPath is being clipped by a
   // different clip path we need to check if it prevents the original element
   // from recieving events at aPoint:
   nsSVGClipPathFrame *clipPathFrame =
-    nsSVGEffects::GetEffectProperties(this).GetClipPathFrame();
+    SVGObserverUtils::GetEffectProperties(this).GetClipPathFrame();
   if (clipPathFrame &&
       !clipPathFrame->PointIsInsideClipPath(aClippedFrame, aPoint)) {
     return false;
   }
 
   for (nsIFrame* kid = mFrames.FirstChild(); kid;
        kid = kid->GetNextSibling()) {
     nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
@@ -333,17 +333,17 @@ nsSVGClipPathFrame::PointIsInsideClipPat
 
   return false;
 }
 
 bool
 nsSVGClipPathFrame::IsTrivial(nsSVGDisplayableFrame **aSingleChild)
 {
   // If the clip path is clipped then it's non-trivial
-  if (nsSVGEffects::GetEffectProperties(this).GetClipPathFrame())
+  if (SVGObserverUtils::GetEffectProperties(this).GetClipPathFrame())
     return false;
 
   if (aSingleChild) {
     *aSingleChild = nullptr;
   }
 
   nsSVGDisplayableFrame* foundChild = nullptr;
 
@@ -352,17 +352,17 @@ nsSVGClipPathFrame::IsTrivial(nsSVGDispl
     nsSVGDisplayableFrame* svgChild = do_QueryFrame(kid);
     if (svgChild) {
       // We consider a non-trivial clipPath to be one containing
       // either more than one svg child and/or a svg container
       if (foundChild || svgChild->IsDisplayContainer())
         return false;
 
       // or where the child is itself clipped
-      if (nsSVGEffects::GetEffectProperties(kid).GetClipPathFrame())
+      if (SVGObserverUtils::GetEffectProperties(kid).GetClipPathFrame())
         return false;
 
       foundChild = svgChild;
     }
   }
   if (aSingleChild) {
     *aSingleChild = foundChild;
   }
@@ -378,17 +378,17 @@ nsSVGClipPathFrame::IsValid()
   // that must all be applied.  We re-enter this method for each clipPath in a
   // chain, so we need to protect against reference chain related crashes etc.:
   AutoReferenceChainGuard refChainGuard(this, &mIsBeingProcessed,
                                         &sRefChainLengthCounter);
   if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
     return false; // Break reference chain
   }
 
-  if (nsSVGEffects::GetEffectProperties(this).HasInvalidClipPath()) {
+  if (SVGObserverUtils::GetEffectProperties(this).HasInvalidClipPath()) {
     return false;
   }
 
   for (nsIFrame* kid = mFrames.FirstChild(); kid;
        kid = kid->GetNextSibling()) {
 
     LayoutFrameType kidType = kid->Type();
 
@@ -416,22 +416,22 @@ nsSVGClipPathFrame::IsValid()
 
 nsresult
 nsSVGClipPathFrame::AttributeChanged(int32_t         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      int32_t         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::transform) {
-      nsSVGEffects::InvalidateDirectRenderingObservers(this);
+      SVGObserverUtils::InvalidateDirectRenderingObservers(this);
       nsSVGUtils::NotifyChildrenOfSVGChange(this,
                                             nsSVGDisplayableFrame::TRANSFORM_CHANGED);
     }
     if (aAttribute == nsGkAtoms::clipPathUnits) {
-      nsSVGEffects::InvalidateDirectRenderingObservers(this);
+      SVGObserverUtils::InvalidateDirectRenderingObservers(this);
     }
   }
 
   return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
                                                aAttribute, aModType);
 }
 
 void
@@ -481,33 +481,33 @@ nsSVGClipPathFrame::GetBBoxForClipPathFr
   for (; node; node = node->GetNextSibling()) {
     nsIFrame *frame =
       static_cast<nsSVGElement*>(node)->GetPrimaryFrame();
     if (frame) {
       nsSVGDisplayableFrame* svg = do_QueryFrame(frame);
       if (svg) {
         tmpBBox = svg->GetBBoxContribution(mozilla::gfx::ToMatrix(aMatrix),
                                            nsSVGUtils::eBBoxIncludeFill);
-        nsSVGEffects::EffectProperties effectProperties =
-                              nsSVGEffects::GetEffectProperties(frame);
+        SVGObserverUtils::EffectProperties effectProperties =
+                              SVGObserverUtils::GetEffectProperties(frame);
         if (effectProperties.HasNoOrValidClipPath()) {
           nsSVGClipPathFrame *clipPathFrame =
             effectProperties.GetClipPathFrame();
           if (clipPathFrame) {
             tmpBBox = clipPathFrame->GetBBoxForClipPathFrame(tmpBBox, aMatrix);
           }
         }
         tmpBBox.Intersect(aBBox);
         unionBBox.UnionEdges(tmpBBox);
       }
     }
   }
 
-  nsSVGEffects::EffectProperties props =
-    nsSVGEffects::GetEffectProperties(this);
+  SVGObserverUtils::EffectProperties props =
+    SVGObserverUtils::GetEffectProperties(this);
   if (props.mClipPath) {
     if (props.HasInvalidClipPath()) {
       unionBBox = SVGBBox();
     } else  {
       nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame();
       if (clipPathFrame) {
         tmpBBox = clipPathFrame->GetBBoxForClipPathFrame(aBBox, aMatrix);
         unionBBox.Intersect(tmpBBox);
--- a/layout/svg/nsSVGContainerFrame.cpp
+++ b/layout/svg/nsSVGContainerFrame.cpp
@@ -6,17 +6,17 @@
 // Main header first:
 #include "nsSVGContainerFrame.h"
 
 // Keep others in (case-insensitive) order:
 #include "DrawResult.h"
 #include "mozilla/RestyleManager.h"
 #include "mozilla/RestyleManagerInlines.h"
 #include "nsCSSFrameConstructor.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGElement.h"
 #include "nsSVGUtils.h"
 #include "nsSVGAnimatedTransformList.h"
 #include "SVGTextFrame.h"
 
 using namespace mozilla;
 using namespace mozilla::image;
 
@@ -195,17 +195,17 @@ nsSVGDisplayContainerFrame::InsertFrames
     }
   }
 }
 
 void
 nsSVGDisplayContainerFrame::RemoveFrame(ChildListID aListID,
                                         nsIFrame* aOldFrame)
 {
-  nsSVGEffects::InvalidateRenderingObservers(aOldFrame);
+  SVGObserverUtils::InvalidateRenderingObservers(aOldFrame);
 
   // nsSVGContainerFrame::RemoveFrame doesn't call down into
   // nsContainerFrame::RemoveFrame, so it doesn't call FrameNeedsReflow. We
   // need to schedule a repaint and schedule an update to our overflow rects.
   SchedulePaint();
   PresContext()->RestyleManager()->PostRestyleEvent(
     mContent->AsElement(), nsRestyleHint(0), nsChangeHint_UpdateOverflow);
 
@@ -329,17 +329,17 @@ nsSVGDisplayContainerFrame::ReflowSVG()
   // the initial reflow is currently underway.
 
   bool isFirstReflow = (mState & NS_FRAME_FIRST_REFLOW);
 
   bool outerSVGHasHadFirstReflow =
     (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
 
   if (outerSVGHasHadFirstReflow) {
-    mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
+    RemoveStateBits(NS_FRAME_FIRST_REFLOW); // tell our children
   }
 
   nsOverflowAreas overflowRects;
 
   for (nsIFrame* kid = mFrames.FirstChild(); kid;
        kid = kid->GetNextSibling()) {
     nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
     if (SVGFrame) {
@@ -379,25 +379,25 @@ nsSVGDisplayContainerFrame::ReflowSVG()
               mRect.Size() == nsSize(0,0)) ||
              mRect.IsEqualEdges(nsRect()),
              "Only inner-<svg>/<use> is expected to have mRect set");
 
   if (isFirstReflow) {
     // Make sure we have our filter property (if any) before calling
     // FinishAndStoreOverflow (subsequent filter changes are handled off
     // nsChangeHint_UpdateEffects):
-    nsSVGEffects::UpdateEffects(this);
+    SVGObserverUtils::UpdateEffects(this);
   }
 
   FinishAndStoreOverflow(overflowRects, mRect.Size());
 
   // Remove state bits after FinishAndStoreOverflow so that it doesn't
   // invalidate on first reflow:
-  mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
-              NS_FRAME_HAS_DIRTY_CHILDREN);
+  RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
+                  NS_FRAME_HAS_DIRTY_CHILDREN);
 }
 
 void
 nsSVGDisplayContainerFrame::NotifySVGChanged(uint32_t aFlags)
 {
   MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
              "Invalidation logic may need adjusting");
 
--- a/layout/svg/nsSVGFilterFrame.cpp
+++ b/layout/svg/nsSVGFilterFrame.cpp
@@ -5,17 +5,17 @@
 
 // Main header first:
 #include "nsSVGFilterFrame.h"
 
 // Keep others in (case-insensitive) order:
 #include "AutoReferenceChainGuard.h"
 #include "gfxUtils.h"
 #include "nsGkAtoms.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGElement.h"
 #include "mozilla/dom/SVGFilterElement.h"
 #include "nsSVGFilterInstance.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGUtils.h"
 #include "nsContentUtils.h"
 
 using namespace mozilla;
@@ -112,17 +112,17 @@ nsSVGFilterFrame::GetFilterContent(nsICo
 
 nsSVGFilterFrame *
 nsSVGFilterFrame::GetReferencedFilter()
 {
   if (mNoHRefURI)
     return nullptr;
 
   nsSVGPaintingProperty *property =
-    GetProperty(nsSVGEffects::HrefAsPaintingProperty());
+    GetProperty(SVGObserverUtils::HrefAsPaintingProperty());
 
   if (!property) {
     // Fetch our Filter element's href or xlink:href attribute
     SVGFilterElement *filter = static_cast<SVGFilterElement *>(GetContent());
     nsAutoString href;
     if (filter->mStringAttributes[SVGFilterElement::HREF].IsExplicitlySet()) {
       filter->mStringAttributes[SVGFilterElement::HREF]
         .GetAnimValue(href, filter);
@@ -138,18 +138,18 @@ nsSVGFilterFrame::GetReferencedFilter()
 
     // Convert href to an nsIURI
     nsCOMPtr<nsIURI> targetURI;
     nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
     nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
                                               mContent->GetUncomposedDoc(), base);
 
     property =
-      nsSVGEffects::GetPaintingProperty(targetURI, this,
-                                        nsSVGEffects::HrefAsPaintingProperty());
+      SVGObserverUtils::GetPaintingProperty(targetURI, this,
+                          SVGObserverUtils::HrefAsPaintingProperty());
     if (!property)
       return nullptr;
   }
 
   nsIFrame *result = property->GetReferencedFrame();
   if (!result)
     return nullptr;
 
@@ -167,25 +167,25 @@ nsSVGFilterFrame::AttributeChanged(int32
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       (aAttribute == nsGkAtoms::x ||
        aAttribute == nsGkAtoms::y ||
        aAttribute == nsGkAtoms::width ||
        aAttribute == nsGkAtoms::height ||
        aAttribute == nsGkAtoms::filterUnits ||
        aAttribute == nsGkAtoms::primitiveUnits)) {
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   } else if ((aNameSpaceID == kNameSpaceID_XLink ||
               aNameSpaceID == kNameSpaceID_None) &&
              aAttribute == nsGkAtoms::href) {
     // Blow away our reference, if any
-    DeleteProperty(nsSVGEffects::HrefAsPaintingProperty());
+    DeleteProperty(SVGObserverUtils::HrefAsPaintingProperty());
     mNoHRefURI = false;
     // And update whoever references us
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   }
   return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
                                                aAttribute, aModType);
 }
 
 #ifdef DEBUG
 void
 nsSVGFilterFrame::Init(nsIContent*       aContent,
--- a/layout/svg/nsSVGFilterInstance.cpp
+++ b/layout/svg/nsSVGFilterInstance.cpp
@@ -8,17 +8,17 @@
 
 // Keep others in (case-insensitive) order:
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "nsSVGDisplayableFrame.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
 #include "mozilla/dom/SVGFilterElement.h"
 #include "nsReferencedElement.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGFilterFrame.h"
 #include "nsSVGUtils.h"
 #include "SVGContentUtils.h"
 #include "FilterSupport.h"
 #include "gfx2DGlue.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -121,17 +121,17 @@ nsSVGFilterInstance::GetFilterFrame(nsIF
   // filter element.
   if (!mTargetContent) {
     return nullptr;
   }
 
   // aTargetFrame can be null if this filter belongs to a
   // CanvasRenderingContext2D.
   nsCOMPtr<nsIURI> url = aTargetFrame
-    ? nsSVGEffects::GetFilterURI(aTargetFrame, mFilter)
+    ? SVGObserverUtils::GetFilterURI(aTargetFrame, mFilter)
     : mFilter.GetURL()->ResolveLocalRef(mTargetContent);
 
   if (!url) {
     NS_NOTREACHED("an nsStyleFilter of type URL should have a non-null URL");
     return nullptr;
   }
 
   // Look up the filter element by URL.
--- a/layout/svg/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/nsSVGForeignObjectFrame.cpp
@@ -10,17 +10,17 @@
 #include "DrawResult.h"
 #include "gfxContext.h"
 #include "nsDisplayList.h"
 #include "nsGkAtoms.h"
 #include "nsNameSpaceManager.h"
 #include "nsLayoutUtils.h"
 #include "nsRegion.h"
 #include "nsSVGContainerFrame.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "mozilla/dom/SVGForeignObjectElement.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGOuterSVGFrame.h"
 #include "nsSVGUtils.h"
 #include "mozilla/AutoRestore.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -355,34 +355,34 @@ nsSVGForeignObjectFrame::ReflowSVG()
   nsPresContext::InterruptPreventer noInterrupts(PresContext());
 
   DoReflow();
 
   if (mState & NS_FRAME_FIRST_REFLOW) {
     // Make sure we have our filter property (if any) before calling
     // FinishAndStoreOverflow (subsequent filter changes are handled off
     // nsChangeHint_UpdateEffects):
-    nsSVGEffects::UpdateEffects(this);
+    SVGObserverUtils::UpdateEffects(this);
   }
 
   // If we have a filter, we need to invalidate ourselves because filter
   // output can change even if none of our descendants need repainting.
   if (StyleEffects()->HasFilters()) {
     InvalidateFrame();
   }
 
   // TODO: once we support |overflow:visible| on foreignObject, then we will
   // need to take account of our descendants here.
   nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
   nsOverflowAreas overflowAreas(overflow, overflow);
   FinishAndStoreOverflow(overflowAreas, mRect.Size());
 
   // Now unset the various reflow bits:
-  mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
-              NS_FRAME_HAS_DIRTY_CHILDREN);
+  RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
+                  NS_FRAME_HAS_DIRTY_CHILDREN);
 }
 
 void
 nsSVGForeignObjectFrame::NotifySVGChanged(uint32_t aFlags)
 {
   MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
              "Invalidation logic may need adjusting");
 
--- a/layout/svg/nsSVGGradientFrame.cpp
+++ b/layout/svg/nsSVGGradientFrame.cpp
@@ -8,17 +8,17 @@
 #include <algorithm>
 
 // Keep others in (case-insensitive) order:
 #include "AutoReferenceChainGuard.h"
 #include "gfxPattern.h"
 #include "mozilla/dom/SVGGradientElement.h"
 #include "mozilla/dom/SVGStopElement.h"
 #include "nsContentUtils.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGAnimatedTransformList.h"
 
 // XXX Tight coupling with content classes ahead!
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
@@ -41,25 +41,25 @@ nsresult
 nsSVGGradientFrame::AttributeChanged(int32_t         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      int32_t         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       (aAttribute == nsGkAtoms::gradientUnits ||
        aAttribute == nsGkAtoms::gradientTransform ||
        aAttribute == nsGkAtoms::spreadMethod)) {
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   } else if ((aNameSpaceID == kNameSpaceID_XLink ||
               aNameSpaceID == kNameSpaceID_None) &&
              aAttribute == nsGkAtoms::href) {
     // Blow away our reference, if any
-    DeleteProperty(nsSVGEffects::HrefAsPaintingProperty());
+    DeleteProperty(SVGObserverUtils::HrefAsPaintingProperty());
     mNoHRefURI = false;
     // And update whoever references us
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   }
 
   return nsSVGPaintServerFrame::AttributeChanged(aNameSpaceID,
                                                  aAttribute, aModType);
 }
 
 //----------------------------------------------------------------------
 
@@ -330,17 +330,17 @@ nsSVGGradientFrame::GetPaintServerPatter
 
 nsSVGGradientFrame *
 nsSVGGradientFrame::GetReferencedGradient()
 {
   if (mNoHRefURI)
     return nullptr;
 
   nsSVGPaintingProperty *property =
-    GetProperty(nsSVGEffects::HrefAsPaintingProperty());
+    GetProperty(SVGObserverUtils::HrefAsPaintingProperty());
 
   if (!property) {
     // Fetch our gradient element's href or xlink:href attribute
     dom::SVGGradientElement* grad =
       static_cast<dom::SVGGradientElement*>(GetContent());
     nsAutoString href;
     if (grad->mStringAttributes[dom::SVGGradientElement::HREF]
           .IsExplicitlySet()) {
@@ -358,18 +358,18 @@ nsSVGGradientFrame::GetReferencedGradien
 
     // Convert href to an nsIURI
     nsCOMPtr<nsIURI> targetURI;
     nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
     nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
                                               mContent->GetUncomposedDoc(), base);
 
     property =
-      nsSVGEffects::GetPaintingProperty(targetURI, this,
-                                        nsSVGEffects::HrefAsPaintingProperty());
+      SVGObserverUtils::GetPaintingProperty(targetURI, this,
+                          SVGObserverUtils::HrefAsPaintingProperty());
     if (!property)
       return nullptr;
   }
 
   nsIFrame *result = property->GetReferencedFrame();
   if (!result)
     return nullptr;
 
@@ -435,17 +435,17 @@ nsSVGLinearGradientFrame::AttributeChang
                                            nsIAtom*        aAttribute,
                                            int32_t         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       (aAttribute == nsGkAtoms::x1 ||
        aAttribute == nsGkAtoms::y1 ||
        aAttribute == nsGkAtoms::x2 ||
        aAttribute == nsGkAtoms::y2)) {
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   }
 
   return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
                                               aAttribute, aModType);
 }
 
 //----------------------------------------------------------------------
 
@@ -538,17 +538,17 @@ nsSVGRadialGradientFrame::AttributeChang
                                            int32_t         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       (aAttribute == nsGkAtoms::r ||
        aAttribute == nsGkAtoms::cx ||
        aAttribute == nsGkAtoms::cy ||
        aAttribute == nsGkAtoms::fx ||
        aAttribute == nsGkAtoms::fy)) {
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   }
 
   return nsSVGGradientFrame::AttributeChanged(aNameSpaceID,
                                               aAttribute, aModType);
 }
 
 //----------------------------------------------------------------------
 
--- a/layout/svg/nsSVGImageFrame.cpp
+++ b/layout/svg/nsSVGImageFrame.cpp
@@ -10,17 +10,17 @@
 #include "gfxPlatform.h"
 #include "mozilla/gfx/2D.h"
 #include "imgIContainer.h"
 #include "nsContainerFrame.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIImageLoadingContent.h"
 #include "nsLayoutUtils.h"
 #include "imgINotificationObserver.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "nsSVGUtils.h"
 #include "SVGContentUtils.h"
 #include "SVGGeometryFrame.h"
 #include "SVGImageContext.h"
 #include "mozilla/dom/SVGImageElement.h"
 #include "nsContentUtils.h"
 #include "nsIReflowCallback.h"
@@ -443,31 +443,31 @@ nsSVGImageFrame::ReflowSVG()
   } else {
     mRect.SetEmpty();
   }
 
   if (mState & NS_FRAME_FIRST_REFLOW) {
     // Make sure we have our filter property (if any) before calling
     // FinishAndStoreOverflow (subsequent filter changes are handled off
     // nsChangeHint_UpdateEffects):
-    nsSVGEffects::UpdateEffects(this);
+    SVGObserverUtils::UpdateEffects(this);
 
     if (!mReflowCallbackPosted) {
       nsIPresShell* shell = PresContext()->PresShell();
       mReflowCallbackPosted = true;
       shell->PostReflowCallback(this);
     }
   }
 
   nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
   nsOverflowAreas overflowAreas(overflow, overflow);
   FinishAndStoreOverflow(overflowAreas, mRect.Size());
 
-  mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
-              NS_FRAME_HAS_DIRTY_CHILDREN);
+  RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
+                  NS_FRAME_HAS_DIRTY_CHILDREN);
 
   // Invalidate, but only if this is not our first reflow (since if it is our
   // first reflow then we haven't had our first paint yet).
   if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     InvalidateFrame();
   }
 }
 
--- a/layout/svg/nsSVGImageFrame.h
+++ b/layout/svg/nsSVGImageFrame.h
@@ -11,17 +11,17 @@
 #include "gfxPlatform.h"
 #include "mozilla/gfx/2D.h"
 #include "imgIContainer.h"
 #include "nsContainerFrame.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIImageLoadingContent.h"
 #include "nsLayoutUtils.h"
 #include "imgINotificationObserver.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "mozilla/dom/SVGSVGElement.h"
 #include "nsSVGUtils.h"
 #include "SVGContentUtils.h"
 #include "SVGGeometryFrame.h"
 #include "SVGImageContext.h"
 #include "mozilla/dom/SVGImageElement.h"
 #include "nsContentUtils.h"
 #include "nsIReflowCallback.h"
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -11,17 +11,17 @@
 #include "gfxPrefs.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSClipPathInstance.h"
 #include "nsDisplayList.h"
 #include "nsFilterInstance.h"
 #include "nsLayoutUtils.h"
 #include "gfxContext.h"
 #include "nsSVGClipPathFrame.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGElement.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGMaskFrame.h"
 #include "nsSVGPaintServerFrame.h"
 #include "nsSVGUtils.h"
 #include "FrameLayerBuilder.h"
 #include "BasicLayers.h"
 #include "mozilla/gfx/Point.h"
@@ -279,18 +279,18 @@ nsRect
     ComputePostEffectsVisualOverflowRect(nsIFrame* aFrame,
                                          const nsRect& aPreEffectsOverflowRect)
 {
   NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT),
                  "Don't call this on SVG child frames");
 
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
   if (!effectProperties.HasValidFilter()) {
     return aPreEffectsOverflowRect;
   }
 
   // Create an override bbox - see comment above:
   nsPoint firstFrameToBoundingBox = GetOffsetToBoundingBox(firstFrame);
   // overrideBBox is in "user space", in _CSS_ pixels:
   // XXX Why are we rounding out to pixel boundaries? We don't do that in
@@ -319,17 +319,17 @@ nsSVGIntegrationUtils::AdjustInvalidArea
   if (aInvalidRegion.IsEmpty()) {
     return nsIntRect();
   }
 
   // Don't bother calling GetEffectProperties; the filter property should
   // already have been set up during reflow/ComputeFrameEffectsRect
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
-  nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
+  nsSVGFilterProperty *prop = SVGObserverUtils::GetFilterProperty(firstFrame);
   if (!prop || !prop->IsInObserverLists()) {
     return aInvalidRegion;
   }
 
   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
 
   if (!prop || !prop->ReferencesValidResources()) {
     // The frame is either not there or not currently available,
@@ -359,17 +359,17 @@ nsSVGIntegrationUtils::AdjustInvalidArea
 nsRect
 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
                                                        const nsRect& aDirtyRect)
 {
   // Don't bother calling GetEffectProperties; the filter property should
   // already have been set up during reflow/ComputeFrameEffectsRect
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
-  nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
+  nsSVGFilterProperty *prop = SVGObserverUtils::GetFilterProperty(firstFrame);
   if (!prop || !prop->ReferencesValidResources()) {
     return aDirtyRect;
   }
 
   // Convert aDirtyRect into "user space" in app units:
   nsPoint toUserSpace =
     aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
   nsRect postEffectsRect = aDirtyRect + toUserSpace;
@@ -716,18 +716,18 @@ MoveContextOriginToUserSpace(nsIFrame* a
   return offset;
 }
 
 bool
 nsSVGIntegrationUtils::IsMaskResourceReady(nsIFrame* aFrame)
 {
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
   nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
   const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
 
   for (uint32_t i = 0; i < maskFrames.Length(); i++) {
     // Refers to a valid SVG mask.
     if (maskFrames[i]) {
       continue;
     }
@@ -771,18 +771,18 @@ nsSVGIntegrationUtils::PaintMask(const P
   nsIFrame* frame = aParams.frame;
   if (!ValidateSVGFrame(frame)) {
     return;
   }
 
   gfxContext& ctx = aParams.ctx;
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
 
   RefPtr<DrawTarget> maskTarget = ctx.GetDrawTarget();
 
   if (maskUsage.shouldGenerateMaskLayer &&
       (maskUsage.shouldGenerateClipMaskLayer ||
        maskUsage.shouldApplyClipPath)) {
     // We will paint both mask of positioned mask and clip-path into
     // maskTarget.
@@ -890,18 +890,18 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
 
   gfxContext& context = aParams.ctx;
   gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context);
 
   /* Properties are added lazily and may have been removed by a restyle,
      so make sure all applicable ones are set again. */
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
 
   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
 
   gfxMatrix cssPxToDevPxMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
   nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
 
   bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
                              maskUsage.shouldGenerateClipMaskLayer ||
@@ -1068,18 +1068,18 @@ nsSVGIntegrationUtils::PaintFilter(const
   if (opacity == 0.0f) {
     return;
   }
 
   /* Properties are added lazily and may have been removed by a restyle,
      so make sure all applicable ones are set again. */
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
 
   if (effectProperties.HasInvalidFilter()) {
     return;
   }
 
   gfxContext& context = aParams.ctx;
 
   gfxContextAutoSaveRestore autoSR(&context);
--- a/layout/svg/nsSVGMarkerFrame.cpp
+++ b/layout/svg/nsSVGMarkerFrame.cpp
@@ -3,17 +3,17 @@
  * 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/. */
 
 // Main header first:
 #include "nsSVGMarkerFrame.h"
 
 // Keep others in (case-insensitive) order:
 #include "gfxContext.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "mozilla/dom/SVGMarkerElement.h"
 #include "SVGGeometryElement.h"
 #include "SVGGeometryFrame.h"
 
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
@@ -37,17 +37,17 @@ nsSVGMarkerFrame::AttributeChanged(int32
       (aAttribute == nsGkAtoms::markerUnits ||
        aAttribute == nsGkAtoms::refX ||
        aAttribute == nsGkAtoms::refY ||
        aAttribute == nsGkAtoms::markerWidth ||
        aAttribute == nsGkAtoms::markerHeight ||
        aAttribute == nsGkAtoms::orient ||
        aAttribute == nsGkAtoms::preserveAspectRatio ||
        aAttribute == nsGkAtoms::viewBox)) {
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   }
 
   return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
                                                aAttribute, aModType);
 }
 
 #ifdef DEBUG
 void
--- a/layout/svg/nsSVGMaskFrame.cpp
+++ b/layout/svg/nsSVGMaskFrame.cpp
@@ -7,17 +7,17 @@
 #include "nsSVGMaskFrame.h"
 
 // Keep others in (case-insensitive) order:
 #include "AutoReferenceChainGuard.h"
 #include "gfx2DGlue.h"
 #include "gfxContext.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "mozilla/dom/SVGMaskElement.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
 static LuminanceType
@@ -186,17 +186,17 @@ nsSVGMaskFrame::AttributeChanged(int32_t
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       (aAttribute == nsGkAtoms::x ||
        aAttribute == nsGkAtoms::y ||
        aAttribute == nsGkAtoms::width ||
        aAttribute == nsGkAtoms::height||
        aAttribute == nsGkAtoms::maskUnits ||
        aAttribute == nsGkAtoms::maskContentUnits)) {
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   }
 
   return nsSVGContainerFrame::AttributeChanged(aNameSpaceID,
                                                aAttribute, aModType);
 }
 
 #ifdef DEBUG
 void
--- a/layout/svg/nsSVGPatternFrame.cpp
+++ b/layout/svg/nsSVGPatternFrame.cpp
@@ -13,17 +13,17 @@
 #include "gfxMatrix.h"
 #include "gfxPattern.h"
 #include "gfxPlatform.h"
 #include "mozilla/gfx/2D.h"
 #include "nsContentUtils.h"
 #include "nsGkAtoms.h"
 #include "nsSVGDisplayableFrame.h"
 #include "nsStyleContext.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "SVGGeometryFrame.h"
 #include "mozilla/dom/SVGPatternElement.h"
 #include "nsSVGUtils.h"
 #include "nsSVGAnimatedTransformList.h"
 #include "SVGContentUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -56,27 +56,27 @@ nsSVGPatternFrame::AttributeChanged(int3
        aAttribute == nsGkAtoms::patternContentUnits ||
        aAttribute == nsGkAtoms::patternTransform ||
        aAttribute == nsGkAtoms::x ||
        aAttribute == nsGkAtoms::y ||
        aAttribute == nsGkAtoms::width ||
        aAttribute == nsGkAtoms::height ||
        aAttribute == nsGkAtoms::preserveAspectRatio ||
        aAttribute == nsGkAtoms::viewBox)) {
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   }
 
   if ((aNameSpaceID == kNameSpaceID_XLink ||
        aNameSpaceID == kNameSpaceID_None) &&
       aAttribute == nsGkAtoms::href) {
     // Blow away our reference, if any
-    DeleteProperty(nsSVGEffects::HrefAsPaintingProperty());
+    DeleteProperty(SVGObserverUtils::HrefAsPaintingProperty());
     mNoHRefURI = false;
     // And update whoever references us
-    nsSVGEffects::InvalidateDirectRenderingObservers(this);
+    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
   }
 
   return nsSVGPaintServerFrame::AttributeChanged(aNameSpaceID,
                                                  aAttribute, aModType);
 }
 
 #ifdef DEBUG
 void
@@ -572,17 +572,17 @@ nsSVGPatternFrame::GetLengthValue(uint32
 // Private (helper) methods
 nsSVGPatternFrame *
 nsSVGPatternFrame::GetReferencedPattern()
 {
   if (mNoHRefURI)
     return nullptr;
 
   nsSVGPaintingProperty *property =
-    GetProperty(nsSVGEffects::HrefAsPaintingProperty());
+    GetProperty(SVGObserverUtils::HrefAsPaintingProperty());
 
   if (!property) {
     // Fetch our pattern element's href or xlink:href attribute
     SVGPatternElement *pattern = static_cast<SVGPatternElement *>(GetContent());
     nsAutoString href;
     if (pattern->mStringAttributes[SVGPatternElement::HREF].IsExplicitlySet()) {
       pattern->mStringAttributes[SVGPatternElement::HREF]
         .GetAnimValue(href, pattern);
@@ -598,18 +598,18 @@ nsSVGPatternFrame::GetReferencedPattern(
 
     // Convert href to an nsIURI
     nsCOMPtr<nsIURI> targetURI;
     nsCOMPtr<nsIURI> base = mContent->GetBaseURI();
     nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href,
                                               mContent->GetUncomposedDoc(), base);
 
     property =
-      nsSVGEffects::GetPaintingProperty(targetURI, this,
-                                        nsSVGEffects::HrefAsPaintingProperty());
+      SVGObserverUtils::GetPaintingProperty(targetURI, this,
+                          SVGObserverUtils::HrefAsPaintingProperty());
     if (!property)
       return nullptr;
   }
 
   nsIFrame *result = property->GetReferencedFrame();
   if (!result)
     return nullptr;
 
--- a/layout/svg/nsSVGStopFrame.cpp
+++ b/layout/svg/nsSVGStopFrame.cpp
@@ -3,17 +3,17 @@
  * 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/. */
 
 // Keep in (case-insensitive) order:
 #include "nsContainerFrame.h"
 #include "nsFrame.h"
 #include "nsGkAtoms.h"
 #include "nsStyleContext.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 
 // This is a very simple frame whose only purpose is to capture style change
 // events and propagate them to the parent.  Most of the heavy lifting is done
 // within the nsSVGGradientFrame, which is the parent for this frame
 
 class nsSVGStopFrame : public nsFrame
 {
   friend nsIFrame*
@@ -80,17 +80,17 @@ nsSVGStopFrame::AttributeChanged(int32_t
                                  nsIAtom*        aAttribute,
                                  int32_t         aModType)
 {
   if (aNameSpaceID == kNameSpaceID_None &&
       aAttribute == nsGkAtoms::offset) {
     MOZ_ASSERT(GetParent()->IsSVGLinearGradientFrame() ||
                GetParent()->IsSVGRadialGradientFrame(),
                "Observers observe the gradient, so that's what we must invalidate");
-    nsSVGEffects::InvalidateDirectRenderingObservers(GetParent());
+    SVGObserverUtils::InvalidateDirectRenderingObservers(GetParent());
   }
 
   return nsFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
 }
 
 // -------------------------------------------------------------------------
 // Public functions
 // -------------------------------------------------------------------------
--- a/layout/svg/nsSVGSwitchFrame.cpp
+++ b/layout/svg/nsSVGSwitchFrame.cpp
@@ -1,16 +1,16 @@
 /* -*- 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/. */
 
 // Keep in (case-insensitive) order:
 #include "gfxRect.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGGFrame.h"
 #include "mozilla/dom/SVGSwitchElement.h"
 #include "nsSVGUtils.h"
 
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
 class nsSVGSwitchFrame final : public nsSVGGFrame
@@ -167,17 +167,17 @@ nsSVGSwitchFrame::ReflowSVG()
   // the initial reflow is currently underway.
 
   bool isFirstReflow = (mState & NS_FRAME_FIRST_REFLOW);
 
   bool outerSVGHasHadFirstReflow =
     (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
 
   if (outerSVGHasHadFirstReflow) {
-    mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
+    RemoveStateBits(NS_FRAME_FIRST_REFLOW); // tell our children
   }
 
   nsOverflowAreas overflowRects;
 
   nsIFrame *child = GetActiveChildFrame();
   nsSVGDisplayableFrame* svgChild = do_QueryFrame(child);
   if (svgChild) {
     MOZ_ASSERT(!(child->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
@@ -189,25 +189,25 @@ nsSVGSwitchFrame::ReflowSVG()
     // frame list, and we're iterating over that list now anyway.
     ConsiderChildOverflow(overflowRects, child);
   }
 
   if (isFirstReflow) {
     // Make sure we have our filter property (if any) before calling
     // FinishAndStoreOverflow (subsequent filter changes are handled off
     // nsChangeHint_UpdateEffects):
-    nsSVGEffects::UpdateEffects(this);
+    SVGObserverUtils::UpdateEffects(this);
   }
 
   FinishAndStoreOverflow(overflowRects, mRect.Size());
 
   // Remove state bits after FinishAndStoreOverflow so that it doesn't
   // invalidate on first reflow:
-  mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
-              NS_FRAME_HAS_DIRTY_CHILDREN);
+  RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
+                  NS_FRAME_HAS_DIRTY_CHILDREN);
 }
 
 SVGBBox
 nsSVGSwitchFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
                                       uint32_t aFlags)
 {
   nsIFrame* kid = GetActiveChildFrame();
   nsSVGDisplayableFrame* svgKid = do_QueryFrame(kid);
--- a/layout/svg/nsSVGUseFrame.cpp
+++ b/layout/svg/nsSVGUseFrame.cpp
@@ -2,17 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsSVGUseFrame.h"
 #include "nsContentUtils.h"
 
 #include "mozilla/dom/SVGUseElement.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 
 using namespace mozilla::dom;
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsIFrame*
 NS_NewSVGUseFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -31,17 +31,17 @@
 #include "nsIPresShell.h"
 #include "nsSVGDisplayableFrame.h"
 #include "nsLayoutUtils.h"
 #include "nsPresContext.h"
 #include "nsStyleCoord.h"
 #include "nsStyleStruct.h"
 #include "nsSVGClipPathFrame.h"
 #include "nsSVGContainerFrame.h"
-#include "nsSVGEffects.h"
+#include "SVGObserverUtils.h"
 #include "nsSVGFilterPaintCallback.h"
 #include "nsSVGForeignObjectFrame.h"
 #include "nsSVGInnerSVGFrame.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGLength2.h"
 #include "nsSVGMaskFrame.h"
 #include "nsSVGOuterSVGFrame.h"
 #include "mozilla/dom/SVGClipPathElement.h"
@@ -150,17 +150,17 @@ nsSVGUtils::Init()
 
 nsRect
 nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
                                             const nsRect &aPreFilterRect)
 {
   MOZ_ASSERT(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
              "Called on invalid frame type");
 
-  nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
+  nsSVGFilterProperty *property = SVGObserverUtils::GetFilterProperty(aFrame);
   if (!property || !property->ReferencesValidResources()) {
     return aPreFilterRect;
   }
 
   return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
 }
 
 bool
@@ -189,17 +189,17 @@ nsSVGUtils::ScheduleReflowSVG(nsIFrame *
              "Passed bad frame!");
 
   // If this is triggered, the callers should be fixed to call us before
   // ReflowSVG is called. If we try to mark dirty bits on frames while we're
   // in the process of removing them, things will get messed up.
   NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame),
                "Do not call under nsSVGDisplayableFrame::ReflowSVG!");
 
-  // We don't call nsSVGEffects::InvalidateRenderingObservers here because
+  // We don't call SVGObserverUtils::InvalidateRenderingObservers here because
   // we should only be called under InvalidateAndScheduleReflowSVG (which
   // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
   // (at which point the frame has no observers).
 
   if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
     return;
   }
 
@@ -269,17 +269,17 @@ nsSVGUtils::NotifyAncestorsOfFilterRegio
              "Not expecting to be called on the outer SVG Frame");
 
   aFrame = aFrame->GetParent();
 
   while (aFrame) {
     if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
       return;
 
-    nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
+    nsSVGFilterProperty *property = SVGObserverUtils::GetFilterProperty(aFrame);
     if (property) {
       property->Invalidate();
     }
     aFrame = aFrame->GetParent();
   }
 }
 
 Size
@@ -501,18 +501,18 @@ void
 nsSVGUtils::DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity,
                                MaskUsage& aUsage)
 {
   aUsage.opacity = ComputeOpacity(aFrame, aHandleOpacity);
 
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
 
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(firstFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(firstFrame);
   const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
 
   nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
 
 #ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
   aUsage.shouldGenerateMaskLayer = (maskFrames.Length() > 0);
 #else
   // Since we do not support image mask so far, we should treat any
@@ -731,18 +731,18 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
    * + Use cairo's clipPath when representable natively (single object
    *   clip region).
    *f
    * + Merge opacity and masking if both used together.
    */
 
   /* Properties are added lazily and may have been removed by a restyle,
      so make sure all applicable ones are set again. */
-  nsSVGEffects::EffectProperties effectProperties =
-    nsSVGEffects::GetEffectProperties(aFrame);
+  SVGObserverUtils::EffectProperties effectProperties =
+    SVGObserverUtils::GetEffectProperties(aFrame);
   if (effectProperties.HasInvalidEffects()) {
     // Some resource is invalid. We shouldn't paint anything.
     return;
   }
 
   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
   nsTArray<nsSVGMaskFrame*> masks = effectProperties.GetMaskFrames();
   nsSVGMaskFrame *maskFrame = masks.IsEmpty() ? nullptr : masks[0];
@@ -883,18 +883,18 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
     MOZ_ASSERT(target != &aContext);
     blender.BlendToTarget();
   }
 }
 
 bool
 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const gfxPoint &aPoint)
 {
-  nsSVGEffects::EffectProperties props =
-    nsSVGEffects::GetEffectProperties(aFrame);
+  SVGObserverUtils::EffectProperties props =
+    SVGObserverUtils::GetEffectProperties(aFrame);
   if (!props.mClipPath) {
     const nsStyleSVGReset *style = aFrame->StyleSVGReset();
     if (style->HasClipPath()) {
       return nsCSSClipPathInstance::HitTestBasicShapeClip(aFrame, aPoint);
     }
     return true;
   }
 
@@ -1173,18 +1173,18 @@ nsSVGUtils::GetBBox(nsIFrame* aFrame, ui
     bool hasClip = aFrame->StyleDisplay()->IsScrollableOverflow();
     if (hasClip) {
       clipRect =
         nsSVGUtils::GetClipRectForFrame(aFrame, x, y, width, height);
       if (aFrame->IsSVGForeignObjectFrame() || aFrame->IsSVGUseFrame()) {
         clipRect = matrix.TransformBounds(clipRect);
       }
     }
-    nsSVGEffects::EffectProperties effectProperties =
-      nsSVGEffects::GetEffectProperties(aFrame);
+    SVGObserverUtils::EffectProperties effectProperties =
+      SVGObserverUtils::GetEffectProperties(aFrame);
     if (effectProperties.HasInvalidClipPath()) {
       bbox = gfxRect(0, 0, 0, 0);
     } else {
       nsSVGClipPathFrame *clipPathFrame =
         effectProperties.GetClipPathFrame();
       if (clipPathFrame) {
         SVGClipPathElement *clipContent =
           static_cast<SVGClipPathElement*>(clipPathFrame->GetContent());
@@ -1501,18 +1501,18 @@ nsSVGUtils::MakeFillPatternFor(nsIFrame*
     // Combine the group opacity into the fill opacity (we will have skipped
     // creating an offscreen surface to apply the group opacity).
     fillOpacity *= opacity;
   }
 
   const DrawTarget* dt = aContext->GetDrawTarget();
 
   nsSVGPaintServerFrame *ps =
-    nsSVGEffects::GetPaintServer(aFrame, &nsStyleSVG::mFill,
-                                 nsSVGEffects::FillProperty());
+    SVGObserverUtils::GetPaintServer(aFrame, &nsStyleSVG::mFill,
+                                     SVGObserverUtils::FillProperty());
 
   if (ps) {
     RefPtr<gfxPattern> pattern =
       ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrix(),
                                 &nsStyleSVG::mFill, fillOpacity, aImgParams);
     if (pattern) {
       pattern->CacheColorStops(dt);
       aOutPattern->Init(*pattern->GetPattern(dt));
@@ -1577,18 +1577,18 @@ nsSVGUtils::MakeStrokePatternFor(nsIFram
     // Combine the group opacity into the stroke opacity (we will have skipped
     // creating an offscreen surface to apply the group opacity).
     strokeOpacity *= opacity;
   }
 
   const DrawTarget* dt = aContext->GetDrawTarget();
 
   nsSVGPaintServerFrame *ps =
-    nsSVGEffects::GetPaintServer(aFrame, &nsStyleSVG::mStroke,
-                                 nsSVGEffects::StrokeProperty());
+    SVGObserverUtils::GetPaintServer(aFrame, &nsStyleSVG::mStroke,
+                                     SVGObserverUtils::StrokeProperty());
 
   if (ps) {
     RefPtr<gfxPattern> pattern =
       ps->GetPaintServerPattern(aFrame, dt, aContext->CurrentMatrix(),
                                 &nsStyleSVG::mStroke, strokeOpacity, aImgParams);
     if (pattern) {
       pattern->CacheColorStops(dt);
       aOutPattern->Init(*pattern->GetPattern(dt));
--- a/layout/xul/nsBox.cpp
+++ b/layout/xul/nsBox.cpp
@@ -126,17 +126,17 @@ nsBox::BeginXULLayout(nsBoxLayoutState& 
   XULDumpBox(stdout);
   printf("\n");
   gIndent++;
 #endif
 
   // mark ourselves as dirty so no child under us
   // can post an incremental layout.
   // XXXldb Is this still needed?
-  mState |= NS_FRAME_HAS_DIRTY_CHILDREN;
+  AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
 
   if (GetStateBits() & NS_FRAME_IS_DIRTY)
   {
     // If the parent is dirty, all the children are dirty (ReflowInput
     // does this too).
     nsIFrame* box;
     for (box = GetChildXULBox(this); box; box = GetNextXULBox(box))
       box->AddStateBits(NS_FRAME_IS_DIRTY);
--- a/layout/xul/nsBoxFrame.cpp
+++ b/layout/xul/nsBoxFrame.cpp
@@ -119,21 +119,20 @@ NS_QUERYFRAME_TAIL_INHERITING(nsContaine
 nsBoxFrame::nsBoxFrame(nsStyleContext* aContext,
                        ClassID aID,
                        bool aIsRoot,
                        nsBoxLayout* aLayoutManager)
   : nsContainerFrame(aContext, aID)
   , mFlex(0)
   , mAscent(0)
 {
-  mState |= NS_STATE_IS_HORIZONTAL;
-  mState |= NS_STATE_AUTO_STRETCH;
+  AddStateBits(NS_STATE_IS_HORIZONTAL | NS_STATE_AUTO_STRETCH);
 
   if (aIsRoot)
-     mState |= NS_STATE_IS_ROOT;
+     AddStateBits(NS_STATE_IS_ROOT);
 
   mValign = vAlign_Top;
   mHalign = hAlign_Left;
 
   // if no layout manager specified us the static sprocket layout
   nsCOMPtr<nsBoxLayout> layout = aLayoutManager;
 
   if (layout == nullptr) {
@@ -230,56 +229,56 @@ nsBoxFrame::CacheAttributes()
    */
 
   mValign = vAlign_Top;
   mHalign = hAlign_Left;
 
   bool orient = false;
   GetInitialOrientation(orient);
   if (orient)
-    mState |= NS_STATE_IS_HORIZONTAL;
+    AddStateBits(NS_STATE_IS_HORIZONTAL);
   else
-    mState &= ~NS_STATE_IS_HORIZONTAL;
+    RemoveStateBits(NS_STATE_IS_HORIZONTAL);
 
   bool normal = true;
   GetInitialDirection(normal);
   if (normal)
-    mState |= NS_STATE_IS_DIRECTION_NORMAL;
+    AddStateBits(NS_STATE_IS_DIRECTION_NORMAL);
   else
-    mState &= ~NS_STATE_IS_DIRECTION_NORMAL;
+    RemoveStateBits(NS_STATE_IS_DIRECTION_NORMAL);
 
   GetInitialVAlignment(mValign);
   GetInitialHAlignment(mHalign);
 
   bool equalSize = false;
   GetInitialEqualSize(equalSize);
   if (equalSize)
-        mState |= NS_STATE_EQUAL_SIZE;
+        AddStateBits(NS_STATE_EQUAL_SIZE);
     else
-        mState &= ~NS_STATE_EQUAL_SIZE;
+        RemoveStateBits(NS_STATE_EQUAL_SIZE);
 
   bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
   GetInitialAutoStretch(autostretch);
   if (autostretch)
-        mState |= NS_STATE_AUTO_STRETCH;
+        AddStateBits(NS_STATE_AUTO_STRETCH);
      else
-        mState &= ~NS_STATE_AUTO_STRETCH;
+        RemoveStateBits(NS_STATE_AUTO_STRETCH);
 
 
 #ifdef DEBUG_LAYOUT
   bool debug = mState & NS_STATE_SET_TO_DEBUG;
   bool debugSet = GetInitialDebug(debug);
   if (debugSet) {
-        mState |= NS_STATE_DEBUG_WAS_SET;
+        AddStateBits(NS_STATE_DEBUG_WAS_SET);
         if (debug)
-            mState |= NS_STATE_SET_TO_DEBUG;
+            AddStateBits(NS_STATE_SET_TO_DEBUG);
         else
-            mState &= ~NS_STATE_SET_TO_DEBUG;
+            RemoveStateBits(NS_STATE_SET_TO_DEBUG);
   } else {
-        mState &= ~NS_STATE_DEBUG_WAS_SET;
+        RemoveStateBits(NS_STATE_DEBUG_WAS_SET);
   }
 #endif
 }
 
 #ifdef DEBUG_LAYOUT
 bool
 nsBoxFrame::GetInitialDebug(bool& aDebug)
 {
@@ -561,17 +560,17 @@ nsBoxFrame::GetInitialAutoStretch(bool& 
 void
 nsBoxFrame::DidReflow(nsPresContext*           aPresContext,
                       const ReflowInput*  aReflowInput,
                       nsDidReflowStatus         aStatus)
 {
   nsFrameState preserveBits =
     mState & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
   nsFrame::DidReflow(aPresContext, aReflowInput, aStatus);
-  mState |= preserveBits;
+  AddStateBits(preserveBits);
 }
 
 bool
 nsBoxFrame::HonorPrintBackgroundSettings()
 {
   return (!mContent || !mContent->IsInNativeAnonymousSubtree()) &&
     nsContainerFrame::HonorPrintBackgroundSettings();
 }
@@ -975,19 +974,19 @@ nsBoxFrame::SetXULDebug(nsBoxLayoutState
   // see if our state matches the given debug state
   bool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG;
   bool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet);
 
   // if it doesn't then tell each child below us the new debug state
   if (debugChanged)
   {
      if (aDebug) {
-         mState |= NS_STATE_CURRENTLY_IN_DEBUG;
+         AddStateBits(NS_STATE_CURRENTLY_IN_DEBUG);
      } else {
-         mState &= ~NS_STATE_CURRENTLY_IN_DEBUG;
+         RemoveStateBits(NS_STATE_CURRENTLY_IN_DEBUG);
      }
 
      SetDebugOnChildList(aState, mFirstChild, aDebug);
 
     MarkIntrinsicISizesDirty();
   }
 
   return NS_OK;
@@ -1169,66 +1168,66 @@ nsBoxFrame::AttributeChanged(int32_t aNa
         aAttribute == nsGkAtoms::dir) {
 
       mValign = nsBoxFrame::vAlign_Top;
       mHalign = nsBoxFrame::hAlign_Left;
 
       bool orient = true;
       GetInitialOrientation(orient);
       if (orient)
-        mState |= NS_STATE_IS_HORIZONTAL;
+        AddStateBits(NS_STATE_IS_HORIZONTAL);
       else
-        mState &= ~NS_STATE_IS_HORIZONTAL;
+        RemoveStateBits(NS_STATE_IS_HORIZONTAL);
 
       bool normal = true;
       GetInitialDirection(normal);
       if (normal)
-        mState |= NS_STATE_IS_DIRECTION_NORMAL;
+        AddStateBits(NS_STATE_IS_DIRECTION_NORMAL);
       else
-        mState &= ~NS_STATE_IS_DIRECTION_NORMAL;
+        RemoveStateBits(NS_STATE_IS_DIRECTION_NORMAL);
 
       GetInitialVAlignment(mValign);
       GetInitialHAlignment(mHalign);
 
       bool equalSize = false;
       GetInitialEqualSize(equalSize);
       if (equalSize)
-        mState |= NS_STATE_EQUAL_SIZE;
+        AddStateBits(NS_STATE_EQUAL_SIZE);
       else
-        mState &= ~NS_STATE_EQUAL_SIZE;
+        RemoveStateBits(NS_STATE_EQUAL_SIZE);
 
 #ifdef DEBUG_LAYOUT
       bool debug = mState & NS_STATE_SET_TO_DEBUG;
       bool debugSet = GetInitialDebug(debug);
       if (debugSet) {
-        mState |= NS_STATE_DEBUG_WAS_SET;
+        AddStateBits(NS_STATE_DEBUG_WAS_SET);
 
         if (debug)
-          mState |= NS_STATE_SET_TO_DEBUG;
+          AddStateBits(NS_STATE_SET_TO_DEBUG);
         else
-          mState &= ~NS_STATE_SET_TO_DEBUG;
+          RemoveStateBits(NS_STATE_SET_TO_DEBUG);
       } else {
-        mState &= ~NS_STATE_DEBUG_WAS_SET;
+        RemoveStateBits(NS_STATE_DEBUG_WAS_SET);
       }
 #endif
 
       bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
       GetInitialAutoStretch(autostretch);
       if (autostretch)
-        mState |= NS_STATE_AUTO_STRETCH;
+        AddStateBits(NS_STATE_AUTO_STRETCH);
       else
-        mState &= ~NS_STATE_AUTO_STRETCH;
+        RemoveStateBits(NS_STATE_AUTO_STRETCH);
     }
     else if (aAttribute == nsGkAtoms::left ||
              aAttribute == nsGkAtoms::top ||
              aAttribute == nsGkAtoms::right ||
              aAttribute == nsGkAtoms::bottom ||
              aAttribute == nsGkAtoms::start ||
              aAttribute == nsGkAtoms::end) {
-      mState &= ~NS_STATE_STACK_NOT_POSITIONED;
+      RemoveStateBits(NS_STATE_STACK_NOT_POSITIONED);
     }
     else if (aAttribute == nsGkAtoms::mousethrough) {
       UpdateMouseThrough();
     }
 
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   }
--- a/layout/xul/nsScrollbarFrame.cpp
+++ b/layout/xul/nsScrollbarFrame.cpp
@@ -46,17 +46,17 @@ nsScrollbarFrame::Init(nsIContent*      
                        nsIFrame*         aPrevInFlow)
 {
   nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
 
   // We want to be a reflow root since we use reflows to move the
   // slider.  Any reflow inside the scrollbar frame will be a reflow to
   // move the slider and will thus not change anything outside of the
   // scrollbar or change the size of the scrollbar frame.
-  mState |= NS_FRAME_REFLOW_ROOT;
+  AddStateBits(NS_FRAME_REFLOW_ROOT);
 }
 
 void
 nsScrollbarFrame::Reflow(nsPresContext*          aPresContext,
                          ReflowOutput&     aDesiredSize,
                          const ReflowInput& aReflowInput,
                          nsReflowStatus&          aStatus)
 {
--- a/layout/xul/nsSliderFrame.cpp
+++ b/layout/xul/nsSliderFrame.cpp
@@ -1527,19 +1527,19 @@ nsSliderFrame::GetXULMaxSize(nsBoxLayout
 
 void
 nsSliderFrame::EnsureOrient()
 {
   nsIFrame* scrollbarBox = GetScrollbar();
 
   bool isHorizontal = (scrollbarBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0;
   if (isHorizontal)
-      mState |= NS_STATE_IS_HORIZONTAL;
+      AddStateBits(NS_STATE_IS_HORIZONTAL);
   else
-      mState &= ~NS_STATE_IS_HORIZONTAL;
+      RemoveStateBits(NS_STATE_IS_HORIZONTAL);
 }
 
 
 void
 nsSliderFrame::Notify(void)
 {
     bool stop = false;
 
--- a/layout/xul/nsSplitterFrame.cpp
+++ b/layout/xul/nsSplitterFrame.cpp
@@ -879,19 +879,19 @@ nsSplitterFrameInner::UpdateState()
   mState = newState;
 }
 
 void
 nsSplitterFrameInner::EnsureOrient()
 {
   bool isHorizontal = !(mParentBox->GetStateBits() & NS_STATE_IS_HORIZONTAL);
   if (isHorizontal)
-    mOuter->mState |= NS_STATE_IS_HORIZONTAL;
+    mOuter->AddStateBits(NS_STATE_IS_HORIZONTAL);
   else
-    mOuter->mState &= ~NS_STATE_IS_HORIZONTAL;
+    mOuter->RemoveStateBits(NS_STATE_IS_HORIZONTAL);
 }
 
 void
 nsSplitterFrameInner::AdjustChildren(nsPresContext* aPresContext)
 {
   EnsureOrient();
   bool isHorizontal = !mOuter->IsXULHorizontal();
 
--- a/layout/xul/nsTextBoxFrame.cpp
+++ b/layout/xul/nsTextBoxFrame.cpp
@@ -640,17 +640,17 @@ nsTextBoxFrame::CalculateTitleForWidth(g
     // see if the text will completely fit in the width given
     nscoord titleWidth =
       nsLayoutUtils::AppUnitWidthOfStringBidi(mTitle, this, *fm,
                                               aRenderingContext);
     if (titleWidth <= aWidth) {
         mCroppedTitle = mTitle;
         if (HasRTLChars(mTitle) ||
             StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
-            mState |= NS_FRAME_IS_BIDI;
+            AddStateBits(NS_FRAME_IS_BIDI);
         }
         return titleWidth;  // fits, done.
     }
 
     const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
     if (mCropType != CropNone) {
       // start with an ellipsis
       mCroppedTitle.Assign(kEllipsis);
@@ -699,17 +699,17 @@ nsTextBoxFrame::CalculateTitleForWidth(g
                 charWidth = nsLayoutUtils::AppUnitWidthOfString(pos, length,
                                                                 *fm,
                                                                 drawTarget);
                 if (totalWidth + charWidth > aWidth) {
                     break;
                 }
 
                 if (UCS2_CHAR_IS_BIDI(*pos)) {
-                    mState |= NS_FRAME_IS_BIDI;
+                    AddStateBits(NS_FRAME_IS_BIDI);
                 }
                 pos = nextPos;
                 totalWidth += charWidth;
             }
 
             if (pos == dataBegin) {
                 return titleWidth;
             }
@@ -736,17 +736,17 @@ nsTextBoxFrame::CalculateTitleForWidth(g
                 charWidth = nsLayoutUtils::AppUnitWidthOfString(pos, length,
                                                                 *fm,
                                                                 drawTarget);
                 if (totalWidth + charWidth > aWidth) {
                     break;
                 }
 
                 if (UCS2_CHAR_IS_BIDI(*pos)) {
-                    mState |= NS_FRAME_IS_BIDI;
+                    AddStateBits(NS_FRAME_IS_BIDI);
                 }
                 prevPos = pos;
                 totalWidth += charWidth;
             }
 
             if (prevPos == dataEnd) {
                 return titleWidth;
             }
@@ -788,17 +788,17 @@ nsTextBoxFrame::CalculateTitleForWidth(g
                 charWidth = nsLayoutUtils::AppUnitWidthOfString(leftPos, length,
                                                                 *fm,
                                                                 drawTarget);
                 if (totalWidth + charWidth > aWidth) {
                     break;
                 }
 
                 if (UCS2_CHAR_IS_BIDI(*leftPos)) {
-                    mState |= NS_FRAME_IS_BIDI;
+                    AddStateBits(NS_FRAME_IS_BIDI);
                 }
 
                 leftString.Append(leftPos, length);
                 leftPos = pos;
                 totalWidth += charWidth;
 
                 if (leftPos >= rightPos) {
                     break;
@@ -810,17 +810,17 @@ nsTextBoxFrame::CalculateTitleForWidth(g
                 charWidth = nsLayoutUtils::AppUnitWidthOfString(pos, length,
                                                                 *fm,
                                                                 drawTarget);
                 if (totalWidth + charWidth > aWidth) {
                     break;
                 }
 
                 if (UCS2_CHAR_IS_BIDI(*pos)) {
-                    mState |= NS_FRAME_IS_BIDI;
+                    AddStateBits(NS_FRAME_IS_BIDI);
                 }
 
                 rightString.Insert(pos, 0, length);
                 rightPos = pos;
                 totalWidth += charWidth;
             }
 
             mCroppedTitle = leftString + kEllipsis + rightString;
--- a/memory/build/mozjemalloc.cpp
+++ b/memory/build/mozjemalloc.cpp
@@ -4685,27 +4685,16 @@ MozJemalloc::malloc(size_t aSize)
 RETURN:
   if (!ret) {
     errno = ENOMEM;
   }
 
   return ret;
 }
 
-/*
- * In ELF systems the default visibility allows symbols to be preempted at
- * runtime. This in turn prevents the uses of memalign in this file from being
- * optimized. What we do in here is define two aliasing symbols (they point to
- * the same code): memalign and memalign_internal. The internal version has
- * hidden visibility and is used in every reference from this file.
- *
- * For more information on this technique, see section 2.2.7 (Avoid Using
- * Exported Symbols) in http://www.akkadia.org/drepper/dsohowto.pdf.
- */
-
 template<> inline void*
 MozJemalloc::memalign(size_t aAlignment, size_t aSize)
 {
   void* ret;
 
   MOZ_ASSERT(((aAlignment - 1) & aAlignment) == 0);
 
   if (malloc_init()) {
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewSettings.java
@@ -57,18 +57,18 @@ public final class GeckoViewSettings {
      * active GeckoView session will be ignored.
      */
     public static final Key<Boolean> USE_MULTIPROCESS =
         new Key<Boolean>("useMultiprocess");
 
     /*
      * Key to specify which display-mode we should use
      */
-    public static final Key<Boolean> DISPLAY_MODE =
-        new Key<Boolean>("displayMode");
+    public static final Key<Integer> DISPLAY_MODE =
+        new Key<Integer>("displayMode");
 
 
     private final EventDispatcher mEventDispatcher;
     private final GeckoBundle mBundle;
 
     public GeckoViewSettings() {
         this(null);
     }
@@ -100,28 +100,28 @@ public final class GeckoViewSettings {
     }
 
     public boolean getBoolean(final Key<Boolean> key) {
         synchronized (mBundle) {
             return mBundle.getBoolean(key.text);
         }
     }
 
-    public void setInt(final Key<Boolean> key, int value) {
+    public void setInt(final Key<Integer> key, int value) {
         synchronized (mBundle) {
             final Object old = mBundle.get(key.text);
             if (old != null && old.equals(value)) {
                 return;
             }
             mBundle.putInt(key.text, value);
         }
         dispatchUpdate();
     }
 
-    public int getInt(final Key<Boolean> key) {
+    public int getInt(final Key<Integer> key) {
         synchronized (mBundle) {
             return mBundle.getInt(key.text);
         }
     }
 
     /* package */ GeckoBundle asBundle() {
         return mBundle;
     }
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -987,16 +987,39 @@ nsDNSService::Resolve(const nsACString  
 }
 
 NS_IMETHODIMP
 nsDNSService::ResolveNative(const nsACString        &aHostname,
                             uint32_t                 flags,
                             const OriginAttributes  &aOriginAttributes,
                             nsIDNSRecord           **result)
 {
+    // Synchronous resolution is not available on the main thread.
+    if (NS_IsMainThread()) {
+        return NS_ERROR_NOT_AVAILABLE;
+    }
+
+    return ResolveInternal(aHostname, flags, aOriginAttributes, result);
+}
+
+nsresult
+nsDNSService::DeprecatedSyncResolve(const nsACString        &aHostname,
+                                    uint32_t                 flags,
+                                    const OriginAttributes  &aOriginAttributes,
+                                    nsIDNSRecord           **result)
+{
+    return ResolveInternal(aHostname, flags, aOriginAttributes, result);
+}
+
+nsresult
+nsDNSService::ResolveInternal(const nsACString        &aHostname,
+                              uint32_t                 flags,
+                              const OriginAttributes  &aOriginAttributes,
+                              nsIDNSRecord           **result)
+{
     // grab reference to global host resolver and IDN service.  beware
     // simultaneous shutdown!!
     RefPtr<nsHostResolver> res;
     nsCOMPtr<nsIIDNService> idn;
     bool localDomain = false;
     {
         MutexAutoLock lock(mLock);
         res = mResolver;
--- a/netwerk/dns/nsDNSService2.h
+++ b/netwerk/dns/nsDNSService2.h
@@ -14,16 +14,18 @@
 #include "nsHostResolver.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsTHashtable.h"
 #include "nsHashKeys.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Attributes.h"
 
+class nsAuthSSPI;
+
 class nsDNSService final : public nsPIDNSService
                          , public nsIObserver
                          , public nsIMemoryReporter
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSPIDNSSERVICE
     NS_DECL_NSIDNSSERVICE
@@ -33,28 +35,41 @@ public:
     nsDNSService();
 
     static nsIDNSService* GetXPCOMSingleton();
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
     bool GetOffline() const;
 
+protected:
+    friend class nsAuthSSPI;
+
+    nsresult DeprecatedSyncResolve(const nsACString &aHostname,
+                                   uint32_t flags,
+                                   const mozilla::OriginAttributes &aOriginAttributes,
+                                   nsIDNSRecord **result);
+
 private:
     ~nsDNSService();
 
     static nsDNSService* GetSingleton();
 
     uint16_t GetAFForLookup(const nsACString &host, uint32_t flags);
 
     nsresult PreprocessHostname(bool              aLocalDomain,
                                 const nsACString &aInput,
                                 nsIIDNService    *aIDN,
                                 nsACString       &aACE);
 
+    nsresult ResolveInternal(const nsACString &aHostname,
+                             uint32_t flags,
+                             const mozilla::OriginAttributes &aOriginAttributes,
+                             nsIDNSRecord **result);
+
     RefPtr<nsHostResolver>  mResolver;
     nsCOMPtr<nsIIDNService>   mIDN;
 
     // mLock protects access to mResolver and mIPv4OnlyDomains
     mozilla::Mutex            mLock;
 
     // mIPv4OnlyDomains is a comma-separated list of domains for which only
     // IPv4 DNS lookups are performed. This allows the user to disable IPv6 on
--- a/netwerk/dns/nsIDNSService.idl
+++ b/netwerk/dns/nsIDNSService.idl
@@ -94,31 +94,33 @@ interface nsIDNSService : nsISupports
     [notxpcom]
     nsresult cancelAsyncResolveNative(in AUTF8String       aHostName,
                                       in unsigned long     aFlags,
                                       in nsIDNSListener    aListener,
                                       in nsresult          aReason,
                                       in OriginAttributes  aOriginAttributes);
 
     /**
-     * called to synchronously resolve a hostname.  warning this method may
-     * block the calling thread for a long period of time.  it is extremely
-     * unwise to call this function on the UI thread of an application.
+     * called to synchronously resolve a hostname.
+     *
+     * Since this method may block the calling thread for a long period of
+     * time, it may not be accessed from the main thread.
      *
      * @param aHostName
      *        the hostname or IP-address-literal to resolve.
      * @param aFlags
      *        a bitwise OR of the RESOLVE_ prefixed constants defined below.
      * @param aOriginAttributes
      *        the originAttribute for this resolving, the DNS cache will be
      *        separated according to this originAttributes. This attribute is
      *        optional to avoid breaking add-ons.
      *
      * @return DNS record corresponding to the given hostname.
      * @throws NS_ERROR_UNKNOWN_HOST if host could not be resolved.
+     * @throws NS_ERROR_NOT_AVAILABLE if accessed from the main thread.
      */
     [implicit_jscontext, optional_argc]
     nsIDNSRecord resolve(in AUTF8String   aHostName,
                          in unsigned long aFlags,
               [optional] in jsval         aOriginAttributes);
 
     [notxpcom]
     nsresult resolveNative(in AUTF8String       aHostName,
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-a83094ccf952
+NSS_3_33_RTM
--- a/security/nss/automation/release/nspr-version.txt
+++ b/security/nss/automation/release/nspr-version.txt
@@ -1,9 +1,9 @@
-4.15
+4.17
 
 # The first line of this file must contain the human readable NSPR
 # version number, which is the minimum required version of NSPR
 # that is supported by this version of NSS.
 #
 # This information is used by release automation,
 # when creating an NSS source archive.
 #
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -17,22 +17,22 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION "3.33" _NSS_CUSTOMIZED " Beta"
+#define NSS_VERSION "3.33" _NSS_CUSTOMIZED
 #define NSS_VMAJOR 3
 #define NSS_VMINOR 33
 #define NSS_VPATCH 0
 #define NSS_VBUILD 0
-#define NSS_BETA PR_TRUE
+#define NSS_BETA PR_FALSE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
 typedef struct NSSInitParametersStr NSSInitParameters;
 
 /*
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -12,16 +12,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION "3.33" SOFTOKEN_ECC_STRING " Beta"
+#define SOFTOKEN_VERSION "3.33" SOFTOKEN_ECC_STRING
 #define SOFTOKEN_VMAJOR 3
 #define SOFTOKEN_VMINOR 33
 #define SOFTOKEN_VPATCH 0
 #define SOFTOKEN_VBUILD 0
-#define SOFTOKEN_BETA PR_TRUE
+#define SOFTOKEN_BETA PR_FALSE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,22 +14,22 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION "3.33 Beta"
+#define NSSUTIL_VERSION "3.33"
 #define NSSUTIL_VMAJOR 3
 #define NSSUTIL_VMINOR 33
 #define NSSUTIL_VPATCH 0
 #define NSSUTIL_VBUILD 0
-#define NSSUTIL_BETA PR_TRUE
+#define NSSUTIL_BETA PR_FALSE
 
 SEC_BEGIN_PROTOS
 
 /*
  * Returns a const string of the UTIL library version.
  */
 extern const char *NSSUTIL_GetVersion(void);
 
--- a/uriloader/exthandler/nsHandlerService-json.js
+++ b/uriloader/exthandler/nsHandlerService-json.js
@@ -266,17 +266,39 @@ HandlerService.prototype = {
   enumerate() {
     let handlers = Cc["@mozilla.org/array;1"]
                      .createInstance(Ci.nsIMutableArray);
     for (let type of Object.keys(this._store.data.mimeTypes)) {
       let handler = gMIMEService.getFromTypeAndExtension(type, null);
       handlers.appendElement(handler);
     }
     for (let type of Object.keys(this._store.data.schemes)) {
-      let handler = gExternalProtocolService.getProtocolHandlerInfo(type);
+      // nsIExternalProtocolService.getProtocolHandlerInfo can be expensive
+      // on Windows, so we return a proxy to delay retrieving the nsIHandlerInfo
+      // until one of its properties is accessed.
+      //
+      // Note: our caller still needs to yield periodically when iterating
+      // the enumerator and accessing handler properties to avoid monopolizing
+      // the main thread.
+      //
+      let handler = new Proxy(
+        {
+          QueryInterface: XPCOMUtils.generateQI([Ci.nsIHandlerInfo]),
+          type: type,
+          get _handlerInfo() {
+            delete this._handlerInfo;
+            return this._handlerInfo = gExternalProtocolService.getProtocolHandlerInfo(type);
+          },
+        },
+        {
+          get: function(target, name) {
+            return target[name] || target._handlerInfo[name];
+          },
+        },
+      );
       handlers.appendElement(handler);
     }
     return handlers.enumerate();
   },
 
   // nsIHandlerService
   store(handlerInfo) {
     let handlerList = this._getHandlerListByHandlerInfoType(handlerInfo);
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -87,16 +87,17 @@ enum NSWindowOcclusionState {
 
 enum NSWindowTitleVisibility {
   NSWindowTitleVisible = 0,
   NSWindowTitleHidden  = 1
 };
 
 @interface NSWindow(TitleVisibility)
 - (void)setTitleVisibility:(NSWindowTitleVisibility)visibility;
+- (void)setTitlebarAppearsTransparent:(BOOL)isTitlebarTransparent;
 @end
 
 #endif
 
 #if !defined(MAC_OS_X_VERSION_10_12) || \
     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
 
 @interface NSWindow(AutomaticWindowTabbing)
@@ -485,16 +486,20 @@ nsresult nsCocoaWindow::CreateNativeWind
   // Create the window
   mWindow = [[windowClass alloc] initWithContentRect:contentRect styleMask:features 
                                  backing:NSBackingStoreBuffered defer:YES];
 
   if ([mWindow respondsToSelector:@selector(setTitleVisibility)]) {
     // By default, hide window titles.
     [mWindow setTitleVisibility:NSWindowTitleHidden];
   }
+  if ([mWindow respondsToSelector:@selector(setTitlebarAppearsTransparent)]) {
+    // By default, hide window titlebars.
+    [mWindow setTitlebarAppearsTransparent:YES];
+  }
 
   // setup our notification delegate. Note that setDelegate: does NOT retain.
   mDelegate = [[WindowDelegate alloc] initWithGeckoWindow:this];
   [mWindow setDelegate:mDelegate];
 
   // Make sure that the content rect we gave has been honored.
   NSRect wantedFrame = [mWindow frameRectForContentRect:contentRect];
   if (!NSEqualRects([mWindow frame], wantedFrame)) {
@@ -3152,16 +3157,19 @@ static const NSString* kStateCollectionB
   mDrawsIntoWindowFrame = aState;
   if (changed) {
     [self updateContentViewSize];
     [self reflowTitlebarElements];
     if ([self respondsToSelector:@selector(setTitleVisibility)]) {
       [self setTitleVisibility:mDrawsIntoWindowFrame ? NSWindowTitleHidden :
                                                        NSWindowTitleVisible];
     }
+    if ([self respondsToSelector:@selector(setTitlebarAppearsTransparent)]) {
+      [self setTitlebarAppearsTransparent:mDrawsIntoWindowFrame];
+    }
   }
 }
 
 - (BOOL)drawsContentsIntoWindowFrame
 {
   return mDrawsIntoWindowFrame;
 }