Bug 1041200. Ensure nsDisplayZoom and friends compute mVisibleRect relative to their reference frame. r=mattwoodrow
authorRobert O'Callahan <robert@ocallahan.org>
Tue, 22 Jul 2014 17:50:11 +1200
changeset 217069 d2a81ea71ea757e30b21d826d855e0bfc454b623
parent 217068 dd2018a5f894c967702595dbaed6c4d17503bbc7
child 217070 a0ffaeafbe7eb919f05505c8c9d7b4fb1d73fc2a
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmattwoodrow
bugs1041200
milestone34.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 1041200. Ensure nsDisplayZoom and friends compute mVisibleRect relative to their reference frame. r=mattwoodrow
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/tests/chrome/bug1041200_window.html
layout/base/tests/chrome/chrome.ini
layout/base/tests/chrome/test_bug1041200.xul
layout/generic/nsIFrame.h
layout/generic/nsSubDocumentFrame.cpp
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1001,16 +1001,41 @@ nsDisplayListBuilder::AllocateDisplayIte
     return static_cast<DisplayItemClip*>(p);
   }
 
   DisplayItemClip* c = new (p) DisplayItemClip(aOriginal);
   mDisplayItemClipsToDestroy.AppendElement(c);
   return c;
 }
 
+const nsIFrame*
+nsDisplayListBuilder::FindReferenceFrameFor(const nsIFrame *aFrame,
+                                            nsPoint* aOffset)
+{
+  if (aFrame == mCurrentFrame) {
+    if (aOffset) {
+      *aOffset = mCurrentOffsetToReferenceFrame;
+    }
+    return mCurrentReferenceFrame;
+  }
+  for (const nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f))
+  {
+    if (f == mReferenceFrame || f->IsTransformed()) {
+      if (aOffset) {
+        *aOffset = aFrame->GetOffsetToCrossDoc(f);
+      }
+      return f;
+    }
+  }
+  if (aOffset) {
+    *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
+  }
+  return mReferenceFrame;
+}
+
 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const
 {
   aDestination.BorderBackground()->AppendToTop(BorderBackground());
   aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
   aDestination.Floats()->AppendToTop(Floats());
   aDestination.Content()->AppendToTop(Content());
   aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
   aDestination.Outlines()->AppendToTop(Outlines());
@@ -1551,16 +1576,32 @@ void nsDisplayList::SortByContentOrder(n
   Sort(aBuilder, IsContentLEQ, aCommonAncestor);
 }
 
 void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder,
                          SortLEQ aCmp, void* aClosure) {
   ::Sort(this, Count(), aCmp, aClosure);
 }
 
+nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
+  : mFrame(aFrame)
+  , mClip(aBuilder->ClipState().GetCurrentCombinedClip(aBuilder))
+#ifdef MOZ_DUMP_PAINTING
+  , mPainted(false)
+#endif
+{
+  mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
+  NS_ASSERTION(aBuilder->GetDirtyRect().width >= 0 ||
+               !aBuilder->IsForPainting(), "dirty rect not set");
+  // The dirty rect is for mCurrentFrame, so we have to use
+  // mCurrentOffsetToReferenceFrame
+  mVisibleRect = aBuilder->GetDirtyRect() +
+      aBuilder->GetCurrentFrameOffsetToReferenceFrame();
+}
+
 void
 nsDisplayItem::AddInvalidRegionForSyncDecodeBackgroundImages(
   nsDisplayListBuilder* aBuilder,
   const nsDisplayItemGeometry* aGeometry,
   nsRegion* aInvalidRegion)
 {
   if (aBuilder->ShouldSyncDecodeImages()) {
     if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(mFrame)) {
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -197,38 +197,17 @@ public:
    */
   nsISelection* GetBoundingSelection() { return mBoundingSelection; }
 
   /**
    * @return the root of given frame's (sub)tree, whose origin
    * establishes the coordinate system for the child display items.
    */
   const nsIFrame* FindReferenceFrameFor(const nsIFrame *aFrame,
-                                        nsPoint* aOffset = nullptr)
-  {
-    if (aFrame == mCurrentFrame) {
-      if (aOffset) {
-        *aOffset = mCurrentOffsetToReferenceFrame;
-      }
-      return mCurrentReferenceFrame;
-    }
-    for (const nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f))
-    {
-      if (f == mReferenceFrame || f->IsTransformed()) {
-        if (aOffset) {
-          *aOffset = aFrame->GetOffsetToCrossDoc(f);
-        }
-        return f;
-      }
-    }
-    if (aOffset) {
-      *aOffset = aFrame->GetOffsetToCrossDoc(mReferenceFrame);
-    }
-    return mReferenceFrame;
-  }
+                                        nsPoint* aOffset = nullptr);
   
   /**
    * @return the root of the display list's frame (sub)tree, whose origin
    * establishes the coordinate system for the display list
    */
   nsIFrame* RootReferenceFrame() 
   {
     return mReferenceFrame;
@@ -315,16 +294,17 @@ public:
 
   /**
    * Get dirty rect relative to current frame (the frame that we're calling
    * BuildDisplayList on right now).
    */
   const nsRect& GetDirtyRect() { return mDirtyRect; }
   const nsIFrame* GetCurrentFrame() { return mCurrentFrame; }
   const nsIFrame* GetCurrentReferenceFrame() { return mCurrentReferenceFrame; }
+  const nsPoint& GetCurrentFrameOffsetToReferenceFrame() { return mCurrentOffsetToReferenceFrame; }
 
   /**
    * Returns true if merging and flattening of display lists should be
    * performed while computing visibility.
    */
   bool AllowMergingAndFlattening() { return mAllowMergingAndFlattening; }
   void SetAllowMergingAndFlattening(bool aAllow) { mAllowMergingAndFlattening = aAllow; }
 
@@ -818,28 +798,17 @@ public:
   typedef mozilla::DisplayItemClip DisplayItemClip;
   typedef mozilla::layers::FrameMetrics::ViewID ViewID;
   typedef mozilla::layers::Layer Layer;
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::LayerState LayerState;
 
   // This is never instantiated directly (it has pure virtual methods), so no
   // need to count constructors and destructors.
-  nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
-    : mFrame(aFrame)
-    , mClip(aBuilder->ClipState().GetCurrentCombinedClip(aBuilder))
-#ifdef MOZ_DUMP_PAINTING
-    , mPainted(false)
-#endif
-  {
-    mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
-    NS_ASSERTION(aBuilder->GetDirtyRect().width >= 0 ||
-                 !aBuilder->IsForPainting(), "dirty rect not set");
-    mVisibleRect = aBuilder->GetDirtyRect() + mToReferenceFrame;
-  }
+  nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
   /**
    * This constructor is only used in rare cases when we need to construct
    * temporary items.
    */
   nsDisplayItem(nsIFrame* aFrame)
     : mFrame(aFrame)
     , mClip(nullptr)
     , mReferenceFrame(nullptr)
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/chrome/bug1041200_window.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 1041200</title>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+  <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js"></script>
+</head>
+<body>
+<iframe style="width:700px; height:500px; margin-top:200px;" id="ourFrame"></iframe>
+<script>
+var SpecialPowers = window.opener.wrappedJSObject.SpecialPowers;
+var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
+var ok = window.opener.wrappedJSObject.ok;
+var info = window.opener.wrappedJSObject.info;
+
+var viewer =
+  SpecialPowers.wrap(ourFrame).contentWindow
+               .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
+               .getInterface(SpecialPowers.Ci.nsIWebNavigation)
+               .QueryInterface(SpecialPowers.Ci.nsIDocShell)
+               .contentViewer
+               .QueryInterface(SpecialPowers.Ci.nsIMarkupDocumentViewer);
+viewer.fullZoom = 2;
+
+SimpleTest.waitForExplicitFinish();
+
+window.onload = function() {
+  window.waitForAllPaintsFlushed(function () {
+    // Supply random key to ensure load actually happens
+    ourFrame.src = "data:text/html,<body onload='parent.childLoaded()' style='background:lime'><p>Hello<p>Hello<p>Hello<p>Hello<p>Hello<p>" + Math.random();
+  }, document.getElementById("ourFrame").contentDocument);
+};
+
+window.childLoaded = function() {
+  setTimeout(function() {
+    window.waitForAllPaintsFlushed(function(x1, y1, x2, y2) {
+      ok(x2 - x1 >= 700 && y2 - y1 >= 500,
+         "expected to see invalidate of entire frame, got " + [x1,y1,x2,y2].join(','));
+      SimpleTest.finish();
+      window.close();
+    });
+  }, 0);
+};
+</script>
+
--- a/layout/base/tests/chrome/chrome.ini
+++ b/layout/base/tests/chrome/chrome.ini
@@ -26,16 +26,18 @@ support-files =
 [test_bug504311.xul]
 [test_bug514660.xul]
 [test_bug533845.xul]
 [test_bug551434.html]
 [test_bug708062.html]
 [test_bug812817.xul]
 [test_bug847890_paintFlashing.html]
 [test_bug1018265.xul]
+[test_bug1041200.xul]
+support-files=bug1041200_window.html
 [test_chrome_content_integration.xul]
 [test_chrome_over_plugin.xul]
 [test_default_background.xul]
 [test_dialog_with_positioning.html]
 [test_fixed_bg_scrolling_repaints.html]
 [test_leaf_layers_partition_browser_window.xul]
 skip-if = (!debug) || (toolkit == "cocoa") # Disabled on Mac because of Bug 748219
 [test_no_clip_iframe.xul]
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug1041200.xul
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+    SimpleTest.waitForExplicitFinish();
+    // Run the test in a separate window so that the test runs as a chrome
+    // window
+    window.open("bug1041200_window.html", "bug1041200",
+                "chrome,width=800,height=800");
+  ]]>
+  </script>
+</window>
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1891,16 +1891,22 @@ public:
    *
    * NOTE: this actually returns the offset from aOther to |this|, but
    * that offset is added to transform _coordinates_ from |this| to
    * aOther.
    */
   nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther) const;
 
   /**
+   * Like GetOffsetToCrossDoc, but the caller can specify which appunits
+   * to return the result in.
+   */
+  nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther, const int32_t aAPD) const;
+
+  /**
    * Get the screen rect of the frame in pixels.
    * @return the pixel rect of the frame in screen coordinates.
    */
   nsIntRect GetScreenRect() const;
   virtual nsIntRect GetScreenRectExternal() const;
 
   /**
    * Get the screen rect of the frame in app units.
@@ -3075,17 +3081,16 @@ private:
                                 mOverflow.mVisualDeltas.mLeft,
                   mRect.height + mOverflow.mVisualDeltas.mBottom +
                                  mOverflow.mVisualDeltas.mTop);
   }
   /**
    * Returns true if any overflow changed.
    */
   bool SetOverflowAreas(const nsOverflowAreas& aOverflowAreas);
-  nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther, const int32_t aAPD) const;
 
   // Helper-functions for SortFrameList():
   template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
   static nsIFrame* SortedMerge(nsIFrame *aLeft, nsIFrame *aRight);
 
   template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
   static nsIFrame* MergeSort(nsIFrame *aSource);
 
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -494,16 +494,24 @@ nsSubDocumentFrame::BuildDisplayList(nsD
         uint32_t flags = nsIPresShell::FORCE_DRAW;
         presShell->AddCanvasBackgroundColorItem(
           *aBuilder, childItems, subdocRootFrame ? subdocRootFrame : this,
           bounds, NS_RGBA(0,0,0,0), flags);
       }
     }
   }
 
+  if (subdocRootFrame) {
+    aBuilder->LeavePresShell(subdocRootFrame, dirty);
+
+    if (ignoreViewportScrolling) {
+      aBuilder->SetIgnoreScrollFrame(savedIgnoreScrollFrame);
+    }
+  }
+
   // Generate a resolution and/or zoom item if needed. If one or both of those is
   // created, we don't need to create a separate nsDisplaySubDocument.
 
   uint32_t flags = nsDisplayOwnLayer::GENERATE_SUBDOC_INVALIDATIONS;
   // If ignoreViewportScrolling is true then the top most layer we create here
   // is going to become the scrollable layer for the root scroll frame, so we
   // want to add nsDisplayOwnLayer::GENERATE_SCROLLABLE_LAYER to whatever layer
   // becomes the topmost. We do this below.
@@ -533,24 +541,16 @@ nsSubDocumentFrame::BuildDisplayList(nsD
   if (needsOwnLayer) {
     // We always want top level content documents to be in their own layer.
     nsDisplaySubDocument* layerItem = new (aBuilder) nsDisplaySubDocument(
       aBuilder, subdocRootFrame ? subdocRootFrame : this,
       &childItems, flags);
     childItems.AppendToTop(layerItem);
   }
 
-  if (subdocRootFrame) {
-    aBuilder->LeavePresShell(subdocRootFrame, dirty);
-
-    if (ignoreViewportScrolling) {
-      aBuilder->SetIgnoreScrollFrame(savedIgnoreScrollFrame);
-    }
-  }
-
   if (aBuilder->IsForImageVisibility()) {
     // We don't add the childItems to the return list as we're dealing with them here.
     presShell->RebuildImageVisibilityDisplayList(childItems);
     childItems.DeleteAll();
   } else {
     aLists.Content()->AppendToTop(&childItems);
   }
 }