Bug 951904 - SVGTextFrame should always be reflowed even if it is inside a nonactive child of switch element r=longsonr
authorviolet <violet.bugreport@gmail.com>
Thu, 21 Mar 2019 21:17:18 +0000
changeset 465574 806b2a3b5bb196107a751380553fb29a9dd786e1
parent 465573 a2e425c1f7118a03f453c74d2e36f78456d17b82
child 465575 b6ac884371a63090e06d4e481c07801c1f18f73c
push id35744
push userapavel@mozilla.com
push dateFri, 22 Mar 2019 16:44:08 +0000
treeherdermozilla-central@e66a2b59914d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslongsonr
bugs951904
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 951904 - SVGTextFrame should always be reflowed even if it is inside a nonactive child of switch element r=longsonr SVGTextFrame is special, it should always be reflowed to get the correct metrics. Differential Revision: https://phabricator.services.mozilla.com/D22841
layout/svg/crashtests/951904-1.html
layout/svg/crashtests/crashtests.list
layout/svg/nsSVGSwitchFrame.cpp
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/951904-1.html
@@ -0,0 +1,34 @@
+<body onload="go()">
+<svg>
+  <switch>
+    <text id="a">Bonjour</text>
+    <text id="b">Hello</text>
+    <a><text id="c">Hello</text></a>
+    <g>
+      <mask>
+        <text>Lundi</text>
+      </mask>
+    </g>
+    <switch>
+      <text id="d">Au revoir</text>
+      <g>
+        <mask>
+          <text>Mercredi</text>
+        </mask>
+      </g>
+      <text id="e">Goodbye</text>
+      <a><text id="f">Goodbye</text></a>
+    </switch>
+  </switch>
+</svg>
+</body>
+<script>
+function go() {
+  a.getComputedTextLength();
+  b.getComputedTextLength();
+  c.getComputedTextLength();
+  d.getComputedTextLength();
+  e.getComputedTextLength();
+  f.getComputedTextLength();
+}
+</script>
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -177,16 +177,17 @@ load 890783-1.svg
 load 893510-1.svg
 load 895311-1.svg
 load 897342-1.svg
 load 898909-1.svg
 load 898951-1.svg
 load 913990.html
 load 919371-1.xhtml
 load 950324-1.svg
+load 951904-1.html
 load 952270-1.svg
 load 963086-1.svg
 load 974746-1.svg
 load 975773-1.svg
 load 979407-1.svg
 load 979407-2.svg
 load 993443.svg
 load 1016145.svg
--- a/layout/svg/nsSVGSwitchFrame.cpp
+++ b/layout/svg/nsSVGSwitchFrame.cpp
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Keep in (case-insensitive) order:
 #include "gfxRect.h"
 #include "SVGObserverUtils.h"
 #include "nsSVGGFrame.h"
 #include "mozilla/dom/SVGSwitchElement.h"
 #include "nsSVGUtils.h"
+#include "SVGTextFrame.h"
+#include "nsSVGContainerFrame.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::image;
 
 class nsSVGSwitchFrame final : public nsSVGGFrame {
   friend nsIFrame* NS_NewSVGSwitchFrame(nsIPresShell* aPresShell,
                                         ComputedStyle* aStyle);
@@ -46,16 +48,18 @@ class nsSVGSwitchFrame final : public ns
                         const nsIntRect* aDirtyRect = nullptr) override;
   nsIFrame* GetFrameForPoint(const gfxPoint& aPoint) override;
   virtual void ReflowSVG() override;
   virtual SVGBBox GetBBoxContribution(const Matrix& aToBBoxUserspace,
                                       uint32_t aFlags) override;
 
  private:
   nsIFrame* GetActiveChildFrame();
+  void ReflowAllSVGTextFramesInsideNonActiveChildren(nsIFrame* aActiveChild);
+  static void AlwaysReflowSVGTextFrameDoForOneKid(nsIFrame* aKid);
 };
 
 //----------------------------------------------------------------------
 // Implementation
 
 nsIFrame* NS_NewSVGSwitchFrame(nsIPresShell* aPresShell,
                                ComputedStyle* aStyle) {
   return new (aPresShell)
@@ -130,16 +134,57 @@ nsIFrame* nsSVGSwitchFrame::GetFrameForP
       point = m.TransformPoint(point);
     }
     return svgFrame->GetFrameForPoint(point);
   }
 
   return nullptr;
 }
 
+void nsSVGSwitchFrame::AlwaysReflowSVGTextFrameDoForOneKid(nsIFrame* aKid) {
+  if (!NS_SUBTREE_DIRTY(aKid)) {
+    return;
+  }
+
+  LayoutFrameType type = aKid->Type();
+  if (type == LayoutFrameType::SVGText) {
+    MOZ_ASSERT(!aKid->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY),
+               "A non-display SVGTextFrame directly contained in a display "
+               "container?");
+    static_cast<SVGTextFrame*>(aKid)->ReflowSVG();
+  } else if (aKid->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer) ||
+             type == LayoutFrameType::SVGForeignObject ||
+             !aKid->IsFrameOfType(nsIFrame::eSVG)) {
+    if (!aKid->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
+      for (nsIFrame* kid : aKid->PrincipalChildList()) {
+        AlwaysReflowSVGTextFrameDoForOneKid(kid);
+      }
+    } else {
+      // This child is in a nondisplay context, something like:
+      // <switch>
+      //   ...
+      //   <g><mask><text></text></mask></g>
+      // </switch>
+      // We should not call ReflowSVG on it.
+      nsSVGContainerFrame::ReflowSVGNonDisplayText(aKid);
+    }
+  }
+}
+
+void nsSVGSwitchFrame::ReflowAllSVGTextFramesInsideNonActiveChildren(
+    nsIFrame* aActiveChild) {
+  for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) {
+    if (aActiveChild == kid) {
+      continue;
+    }
+
+    AlwaysReflowSVGTextFrameDoForOneKid(kid);
+  }
+}
+
 void nsSVGSwitchFrame::ReflowSVG() {
   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
                "This call is probably a wasteful mistake");
 
   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
              "ReflowSVG mechanism not designed for this");
 
   if (!nsSVGUtils::NeedsReflowSVG(this)) {
@@ -160,16 +205,18 @@ void nsSVGSwitchFrame::ReflowSVG() {
 
   if (outerSVGHasHadFirstReflow) {
     RemoveStateBits(NS_FRAME_FIRST_REFLOW);  // tell our children
   }
 
   nsOverflowAreas overflowRects;
 
   nsIFrame* child = GetActiveChildFrame();
+  ReflowAllSVGTextFramesInsideNonActiveChildren(child);
+
   nsSVGDisplayableFrame* svgChild = do_QueryFrame(child);
   if (svgChild) {
     MOZ_ASSERT(!(child->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
                "Check for this explicitly in the |if|, then");
     svgChild->ReflowSVG();
 
     // We build up our child frame overflows here instead of using
     // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same