Bug 1342800 - ComputeSize should not treat iframe as object r=firefox-svg-reviewers,longsonr
authorviolet <violet.bugreport@gmail.com>
Mon, 22 Apr 2019 16:32:14 +0000
changeset 470452 6ab85d4927b51b449d4d3c8ef3be3679fd2f04e7
parent 470451 821c7955f890e693d8c6ce79039ca7b7c0f83024
child 470453 29ad5178399d2ae1743ff24838818e4f77ccfe39
push id35906
push useraciure@mozilla.com
push dateTue, 23 Apr 2019 22:14:56 +0000
treeherdermozilla-central@0ce3633f8b80 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfirefox-svg-reviewers, longsonr
bugs1342800, 843480
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 1342800 - ComputeSize should not treat iframe as object r=firefox-svg-reviewers,longsonr We should not treat <iframe> as <object> for SVG, otherwise there will be unexpected sizing and other webcompat problem (e.g. getScreenCTM). It behaved correctly before, the change was introduced to solve a double zooming problem (bug 843480) for <iframe>. Actually the zooming problem can be solved by avoiding zooming when the window frame is an <iframe>. Differential Revision: https://phabricator.services.mozilla.com/D28313
dom/svg/test/test_getCTM.html
layout/svg/nsSVGOuterSVGFrame.cpp
layout/svg/nsSVGOuterSVGFrame.h
--- a/dom/svg/test/test_getCTM.html
+++ b/dom/svg/test/test_getCTM.html
@@ -65,41 +65,41 @@ function runTest() {
   is((function() { try { return fO.getCTM().e; } catch (e) { return e; } })(), 2, "fO.getCTM().e");
   is((function() { try { return fO.getCTM().f; } catch (e) { return e; } })(), 3, "fO.getCTM().f");
   // g5.nearestViewportElement == inner-2
   is((function() { try { return g5.getCTM(); } catch (e) { return e; } })(), null, "g5.getCTM()");
 
   /* Tests the consistency with farthestViewportElement
      (code is from test_viewport.html) */
   // root.farthestViewportElement == null (but actually == root)
-  is((function() { try { return root.getScreenCTM().e; } catch (e) { return e; } })(), 91.5, "root.getScreenCTM().e");
-  is((function() { try { return root.getScreenCTM().f; } catch (e) { return e; } })(), 33, "root.getScreenCTM().f");
+  is((function() { try { return root.getScreenCTM().e; } catch (e) { return e; } })(), 11, "root.getScreenCTM().e");
+  is((function() { try { return root.getScreenCTM().f; } catch (e) { return e; } })(), 22, "root.getScreenCTM().f");
   // inner.farthestViewportElement == root
-  is((function() { try { return inner.getScreenCTM().e; } catch (e) { return e; } })(), 97.5, "inner.getScreenCTM().e");
-  is((function() { try { return inner.getScreenCTM().f; } catch (e) { return e; } })(), 42, "inner.getScreenCTM().f");
+  is((function() { try { return inner.getScreenCTM().e; } catch (e) { return e; } })(), 15, "inner.getScreenCTM().e");
+  is((function() { try { return inner.getScreenCTM().f; } catch (e) { return e; } })(), 28, "inner.getScreenCTM().f");
   // g1.farthestViewportElement == root
-  is((function() { try { return g1.getScreenCTM().e; } catch (e) { return e; } })(), 142.5, "g1.getScreenCTM().e");
-  is((function() { try { return g1.getScreenCTM().f; } catch (e) { return e; } })(), 102, "g1.getScreenCTM().f");
+  is((function() { try { return g1.getScreenCTM().e; } catch (e) { return e; } })(), 45, "g1.getScreenCTM().e");
+  is((function() { try { return g1.getScreenCTM().f; } catch (e) { return e; } })(), 68, "g1.getScreenCTM().f");
   // outer.farthestViewportElement == null (but actually == root)
-  is((function() { try { return outer.getScreenCTM().e; } catch (e) { return e; } })(), 144, "outer.getScreenCTM().e");
-  is((function() { try { return outer.getScreenCTM().f; } catch (e) { return e; } })(), 103.5, "outer.getScreenCTM().f");
+  is((function() { try { return outer.getScreenCTM().e; } catch (e) { return e; } })(), 46, "outer.getScreenCTM().e");
+  is((function() { try { return outer.getScreenCTM().f; } catch (e) { return e; } })(), 69, "outer.getScreenCTM().f");
   // outer.farthestViewportElement == null (but actually == root)
   is((function() { try { return outer2.getScreenCTM().e; } catch (e) { return e; } })(), -19, "outer2.getScreenCTM().e");
   is((function() { try { return outer2.getScreenCTM().f; } catch (e) { return e; } })(), -8, "outer2.getScreenCTM().f");
   // g2.farthestViewportElement == outer (but actually == root)
-  is((function() { try { return g2.getScreenCTM().e; } catch (e) { return e; } })(), 1044, "g2.getScreenCTM().e");
-  is((function() { try { return g2.getScreenCTM().f; } catch (e) { return e; } })(), 1153.5, "g2.getScreenCTM().f");
+  is((function() { try { return g2.getScreenCTM().e; } catch (e) { return e; } })(), 646, "g2.getScreenCTM().e");
+  is((function() { try { return g2.getScreenCTM().f; } catch (e) { return e; } })(), 769, "g2.getScreenCTM().f");
   // g3.farthestViewportElement == null (but actually == null)
   is((function() { try { return g3.getScreenCTM(); } catch (e) { return e; } })(), null, "g3.getScreenCTM()");
   // symbolRect.farthestViewportElement == root
-  is((function() { try { return symbolRect.getScreenCTM().e; } catch (e) { return e; } })(), 202.5, "symbolRect.getScreenCTM().e");
-  is((function() { try { return symbolRect.getScreenCTM().f; } catch (e) { return e; } })(), 162, "symbolRect.getScreenCTM().f");
+  is((function() { try { return symbolRect.getScreenCTM().e; } catch (e) { return e; } })(), 85, "symbolRect.getScreenCTM().e");
+  is((function() { try { return symbolRect.getScreenCTM().f; } catch (e) { return e; } })(), 108, "symbolRect.getScreenCTM().f");
   // fO.farthestViewportElement == root
-  is((function() { try { return fO.getScreenCTM().e; } catch (e) { return e; } })(), 99, "symbolRect.getScreenCTM().e");
-  is((function() { try { return fO.getScreenCTM().f; } catch (e) { return e; } })(), 43.5, "symbolRect.getScreenCTM().f");
+  is((function() { try { return fO.getScreenCTM().e; } catch (e) { return e; } })(), 16, "symbolRect.getScreenCTM().e");
+  is((function() { try { return fO.getScreenCTM().f; } catch (e) { return e; } })(), 29, "symbolRect.getScreenCTM().f");
   // g5.farthestViewportElement == root
   is((function() { try { return g5.getScreenCTM(); } catch (e) { return e; } })(), null, "g5.getScreenCTM()");
 
   SimpleTest.finish();
 }
 
 window.addEventListener("load", runTest);
 </script>
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -303,18 +303,21 @@ LogicalSize nsSVGOuterSVGFrame::ComputeS
 
   if (!mContent->GetParent()) {
     // We're the root of the outermost browsing context, so we need to scale
     // cbSize by the full-zoom so that SVGs with percentage width/height zoom:
 
     NS_ASSERTION(aCBSize.ISize(aWM) != NS_AUTOHEIGHT &&
                      aCBSize.BSize(aWM) != NS_AUTOHEIGHT,
                  "root should not have auto-width/height containing block");
-    cbSize.ISize(aWM) *= PresContext()->GetFullZoom();
-    cbSize.BSize(aWM) *= PresContext()->GetFullZoom();
+
+    if (!IsContainingWindowElementOfType(nullptr, nsGkAtoms::iframe)) {
+      cbSize.ISize(aWM) *= PresContext()->GetFullZoom();
+      cbSize.BSize(aWM) *= PresContext()->GetFullZoom();
+    }
 
     // We also need to honour the width and height attributes' default values
     // of 100% when we're the root of a browsing context.  (GetIntrinsicSize()
     // doesn't report these since there's no such thing as a percentage
     // intrinsic size.  Also note that explicit percentage values are mapped
     // into style, so the following isn't for them.)
 
     SVGSVGElement* content = static_cast<SVGSVGElement*>(GetContent());
@@ -422,17 +425,18 @@ void nsSVGOuterSVGFrame::Reflow(nsPresCo
       anonChild->AddStateBits(NS_FRAME_IS_DIRTY);
       for (nsIFrame* child : anonChild->PrincipalChildList()) {
         child->AddStateBits(NS_FRAME_IS_DIRTY);
       }
     }
     changeBits |= COORD_CONTEXT_CHANGED;
     svgElem->SetViewportSize(newViewportSize);
   }
-  if (mFullZoom != PresContext()->GetFullZoom()) {
+  if (mFullZoom != PresContext()->GetFullZoom() &&
+      !IsContainingWindowElementOfType(nullptr, nsGkAtoms::iframe)) {
     changeBits |= FULL_ZOOM_CHANGED;
     mFullZoom = PresContext()->GetFullZoom();
   }
   if (changeBits) {
     NotifyViewportOrTransformChanged(changeBits);
   }
   mViewportInitialized = true;
 
@@ -868,46 +872,50 @@ gfxMatrix nsSVGOuterSVGFrame::GetCanvasT
     mCanvasTM = new gfxMatrix(tm);
   }
   return *mCanvasTM;
 }
 
 //----------------------------------------------------------------------
 // Implementation helpers
 
-bool nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(
-    nsIFrame** aEmbeddingFrame) {
+template <typename... Args>
+bool nsSVGOuterSVGFrame::IsContainingWindowElementOfType(
+    nsIFrame** aContainingWindowFrame, Args... aArgs) const {
   if (!mContent->GetParent()) {
     // Our content is the document element
     nsCOMPtr<nsIDocShell> docShell = PresContext()->GetDocShell();
     nsCOMPtr<nsPIDOMWindowOuter> window;
     if (docShell) {
       window = docShell->GetWindow();
     }
 
     if (window) {
       RefPtr<Element> frameElement = window->GetFrameElement();
-      if (frameElement &&
-          frameElement->IsAnyOfHTMLElements(nsGkAtoms::object, nsGkAtoms::embed,
-                                            nsGkAtoms::iframe)) {
-        // Our document is inside an HTML 'object', 'embed' or 'iframe' element
-        if (aEmbeddingFrame) {
-          *aEmbeddingFrame = frameElement->GetPrimaryFrame();
-          NS_ASSERTION(*aEmbeddingFrame, "Yikes, no embedding frame!");
+      if (frameElement && frameElement->IsAnyOfHTMLElements(aArgs...)) {
+        if (aContainingWindowFrame) {
+          *aContainingWindowFrame = frameElement->GetPrimaryFrame();
+          NS_ASSERTION(*aContainingWindowFrame, "Yikes, no frame!");
         }
         return true;
       }
     }
   }
-  if (aEmbeddingFrame) {
-    *aEmbeddingFrame = nullptr;
+  if (aContainingWindowFrame) {
+    *aContainingWindowFrame = nullptr;
   }
   return false;
 }
 
+bool nsSVGOuterSVGFrame::IsRootOfReplacedElementSubDoc(
+    nsIFrame** aEmbeddingFrame) {
+  return IsContainingWindowElementOfType(aEmbeddingFrame, nsGkAtoms::object,
+                                         nsGkAtoms::embed);
+}
+
 bool nsSVGOuterSVGFrame::IsRootOfImage() {
   if (!mContent->GetParent()) {
     // Our content is the document element
     Document* doc = mContent->GetUncomposedDoc();
     if (doc && doc->IsBeingUsedAsImage()) {
       // Our document is being used as an image
       return true;
     }
--- a/layout/svg/nsSVGOuterSVGFrame.h
+++ b/layout/svg/nsSVGOuterSVGFrame.h
@@ -186,16 +186,21 @@ class nsSVGOuterSVGFrame final : public 
       mForeignObjectHash;
 
   nsRegion mInvalidRegion;
 
   float mFullZoom;
 
   bool mViewportInitialized;
   bool mIsRootContent;
+
+ private:
+  template <typename... Args>
+  bool IsContainingWindowElementOfType(nsIFrame** aContainingWindowFrame,
+                                       Args... aArgs) const;
 };
 
 ////////////////////////////////////////////////////////////////////////
 // nsSVGOuterSVGAnonChildFrame class
 
 /**
  * nsSVGOuterSVGFrames have a single direct child that is an instance of this
  * class, and which is used to wrap their real child frames. Such anonymous