Bug 593243: Clip invalidations to the displayport when one is set. r=tn
authorChris Jones <jones.chris.g@gmail.com>
Fri, 11 Mar 2011 17:12:11 -0600
changeset 63392 24c25a9f87fafa41aef81478a29b239e3e13f952
parent 63391 529e0a66b709e6638a49f105349d4af6f79e2ad8
child 63393 79e153479dc87e04765ea3eaac246920533d07d2
push id1
push userroot
push dateTue, 10 Dec 2013 15:46:25 +0000
reviewerstn
bugs593243
milestone2.0b13pre
Bug 593243: Clip invalidations to the displayport when one is set. r=tn
layout/base/nsPresShell.cpp
layout/generic/nsGfxScrollFrame.cpp
view/public/nsIView.h
view/src/nsView.cpp
view/src/nsView.h
view/src/nsViewManager.cpp
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -6057,31 +6057,21 @@ void PresShell::SetRenderingState(const 
   if (UsingDisplayPort()) {
     mDisplayPort = aState.mDisplayPort;
   } else {
     mDisplayPort = nsRect();
   }
   mXResolution = aState.mXResolution;
   mYResolution = aState.mYResolution;
 
-  // FIXME (Bug 593243 should fix this.)
-  //
-  // Invalidated content does not pay any attention to the displayport, so
-  // invalidating the subdocument's root frame could end up not repainting
-  // visible content.
-  //
-  // For instance, imagine the iframe is located at y=1000. Even though the
-  // displayport may intersect the iframe's viewport, the visual overflow
-  // rect of the root content could be (0, 0, 800, 500). Since the dirty region
-  // does not intersect the visible overflow rect, the display list for the
-  // iframe will not even be generated.
-  //
-  // Here, we find the very top presShell and use its root frame for
-  // invalidation instead.
-  //
+  nsIView* rootView;
+  if (NS_SUCCEEDED(mViewManager->GetRootView(rootView)) && rootView) {
+    rootView->SetInvalidationDimensions(&mDisplayPort);
+  }
+
   nsPresContext* rootPresContext = mPresContext->GetRootPresContext();
   if (rootPresContext) {
     nsIPresShell* rootPresShell = rootPresContext->GetPresShell();
     nsIFrame* rootFrame = rootPresShell->FrameManager()->GetRootFrame();
     if (rootFrame) {
       rootFrame->InvalidateWithFlags(rootFrame->GetVisualOverflowRectRelativeToSelf(),
                                      nsIFrame::INVALIDATE_NO_THEBES_LAYERS);
     }
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -190,17 +190,25 @@ nsHTMLScrollFrame::InvalidateInternal(co
                                       nscoord aX, nscoord aY, nsIFrame* aForChild,
                                       PRUint32 aFlags)
 {
   if (aForChild) {
     if (aForChild == mInner.mScrolledFrame) {
       nsRect damage = aDamageRect + nsPoint(aX, aY);
       // This is the damage rect that we're going to pass up to our parent.
       nsRect parentDamage;
-      parentDamage.IntersectRect(damage, mInner.mScrollPort);
+      nsIPresShell* presShell = PresContext()->PresShell();
+      // If we're using a displayport, we might be displaying an area
+      // different than our scroll port and the damage needs to be
+      // clipped to that instead.
+      if (mInner.mIsRoot && presShell->UsingDisplayPort()) {
+        parentDamage.IntersectRect(damage, presShell->GetDisplayPort());
+      } else {
+        parentDamage.IntersectRect(damage, mInner.mScrollPort);
+      }
 
       if (IsScrollingActive()) {
         // This is the damage rect that we're going to pass up and
         // only request invalidation of ThebesLayers for.
         // damage is now in our coordinate system, which means it was
         // translated using the current scroll position. Adjust it to
         // reflect the scroll position at last paint, since that's what
         // the ThebesLayers are currently set up for.
@@ -1099,17 +1107,25 @@ void
 nsXULScrollFrame::InvalidateInternal(const nsRect& aDamageRect,
                                      nscoord aX, nscoord aY, nsIFrame* aForChild,
                                      PRUint32 aFlags)
 {
   if (aForChild == mInner.mScrolledFrame) {
     nsRect damage = aDamageRect + nsPoint(aX, aY);
     // This is the damage rect that we're going to pass up to our parent.
     nsRect parentDamage;
-    parentDamage.IntersectRect(damage, mInner.mScrollPort);
+    nsIPresShell* presShell = PresContext()->PresShell();
+    // If we're using a displayport, we might be displaying an area
+    // different than our scroll port and the damage needs to be
+    // clipped to that instead.
+    if (mInner.mIsRoot && presShell->UsingDisplayPort()) {
+      parentDamage.IntersectRect(damage, presShell->GetDisplayPort());
+    } else {
+      parentDamage.IntersectRect(damage, mInner.mScrollPort);
+    }
 
     if (IsScrollingActive()) {
       // This is the damage rect that we're going to pass up and
       // only request invalidation of ThebesLayers for.
       // damage is now in our coordinate system, which means it was
       // translated using the current scroll position. Adjust it to
       // reflect the scroll position at last paint, since that's what
       // the ThebesLayers are currently set up for.
--- a/view/public/nsIView.h
+++ b/view/public/nsIView.h
@@ -169,16 +169,27 @@ public:
    * are in appunits of this.
    * The view's bounds (x,y) might not be the same as the view's position,
    * if the view has content above or to the left of its origin.
    * @param aBounds out parameter for bounds
    */
   nsRect GetBounds() const { return mDimBounds; }
 
   /**
+   * Set the dimensions at which invalidations are clipped, which can
+   * be different than |GetDimensions()|.  |aRect| is relative to
+   * |this|.  It can be null, in which case invalidations return to
+   * being clipped to the view dimensions.
+   *
+   * The caller is responsible for invalidating the area that may lie
+   * outside the view dimensions but inside |aRect| after this call.
+   */
+  void SetInvalidationDimensions(const nsRect* aRect);
+
+  /**
    * Get the offset between the coordinate systems of |this| and aOther.
    * Adding the return value to a point in the coordinate system of |this|
    * will transform the point to the coordinate system of aOther.
    *
    * The offset is expressed in appunits of |this|. So if you are getting the
    * offset between views in different documents that might have different
    * appunits per devpixel ratios you need to be careful how you use the
    * result.
--- a/view/src/nsView.cpp
+++ b/view/src/nsView.cpp
@@ -204,16 +204,17 @@ nsView::nsView(nsViewManager* aViewManag
   // Views should be transparent by default. Not being transparent is
   // a promise that the view will paint all its pixels opaquely. Views
   // should make this promise explicitly by calling
   // SetViewContentTransparency.
   mVFlags = 0;
   mViewManager = aViewManager;
   mDirtyRegion = nsnull;
   mDeletionObserver = nsnull;
+  mHaveInvalidationDimensions = PR_FALSE;
   mWidgetIsTopLevel = PR_FALSE;
 }
 
 void nsView::DropMouseGrabbing()
 {
   nsCOMPtr<nsIViewObserver> viewObserver = mViewManager->GetViewObserver();
   if (viewObserver) {
     viewObserver->ClearMouseCapture(this);
@@ -345,16 +346,21 @@ void nsView::SetPosition(nscoord aX, nsc
   mPosY = aY;
 
   NS_ASSERTION(GetParent() || (aX == 0 && aY == 0),
                "Don't try to move the root widget to something non-zero");
 
   ResetWidgetBounds(PR_TRUE, PR_TRUE, PR_FALSE);
 }
 
+void nsIView::SetInvalidationDimensions(const nsRect* aRect)
+{
+  return Impl()->SetInvalidationDimensions(aRect);
+}
+
 void nsView::SetPositionIgnoringChildWidgets(nscoord aX, nscoord aY)
 {
   mDimBounds.x += aX - mPosX;
   mDimBounds.y += aY - mPosY;
   mPosX = aX;
   mPosY = aY;
 
   ResetWidgetBounds(PR_FALSE, PR_TRUE, PR_FALSE);
@@ -490,16 +496,23 @@ void nsView::SetDimensions(const nsRect&
 
   mDimBounds = dims;
 
   if (aResizeWidget) {
     ResetWidgetBounds(PR_FALSE, PR_FALSE, aPaint);
   }
 }
 
+void nsView::SetInvalidationDimensions(const nsRect* aRect)
+{
+  if ((mHaveInvalidationDimensions = !!aRect)) {
+    mInvalidationDimensions = *aRect;
+  }
+}
+
 void nsView::NotifyEffectiveVisibilityChanged(PRBool aEffectivelyVisible)
 {
   if (!aEffectivelyVisible)
   {
     DropMouseGrabbing();
   }
 
   if (nsnull != mWindow)
--- a/view/src/nsView.h
+++ b/view/src/nsView.h
@@ -71,16 +71,17 @@ public:
   /**
    * Called to indicate that the dimensions of the view have been changed.
    * The x and y coordinates may be < 0, indicating that the view extends above
    * or to the left of its origin position. The term 'dimensions' indicates it
    * is relative to this view.
    */
   virtual void SetDimensions(const nsRect &aRect, PRBool aPaint = PR_TRUE,
                              PRBool aResizeWidget = PR_TRUE);
+  void SetInvalidationDimensions(const nsRect* aRect);
   void GetDimensions(nsRect &aRect) const { aRect = mDimBounds; aRect.x -= mPosX; aRect.y -= mPosY; }
   void GetDimensions(nsSize &aSize) const { aSize.width = mDimBounds.width; aSize.height = mDimBounds.height; }
 
   /**
    * Called to indicate that the visibility of a view has been
    * changed.
    * @param visibility new visibility state
    */
@@ -143,16 +144,21 @@ public:
   nsViewManager* GetViewManager() const { return mViewManager; }
   // These are superseded by a better interface in nsIView
   PRInt32 GetZIndex() const { return mZIndex; }
   PRBool GetZIndexIsAuto() const { return (mVFlags & NS_VIEW_FLAG_AUTO_ZINDEX) != 0; }
   // This is a better interface than GetDimensions(nsRect&) above
   nsRect GetDimensions() const { nsRect r = mDimBounds; r.MoveBy(-mPosX, -mPosY); return r; }
   // Same as GetBounds but converts to parent appunits if they are different.
   nsRect GetBoundsInParentUnits() const;
+
+  nsRect GetInvalidationDimensions() const {
+    return mHaveInvalidationDimensions ? mInvalidationDimensions : GetDimensions();
+  }
+
   // These are defined exactly the same in nsIView, but for now they have to be redeclared
   // here because of stupid C++ method hiding rules
 
   PRBool HasNonEmptyDirtyRegion() {
     return mDirtyRegion && !mDirtyRegion->IsEmpty();
   }
   nsRegion* GetDirtyRegion() {
     if (!mDirtyRegion) {
@@ -197,14 +203,21 @@ public:
   nsIWidget* GetNearestWidget(nsPoint* aOffset, const PRInt32 aAPD) const;
 
 protected:
   // Do the actual work of ResetWidgetBounds, unconditionally.  Don't
   // call this method if we have no widget.
   void DoResetWidgetBounds(PRBool aMoveOnly, PRBool aInvalidateChangedSize);
 
   nsRegion*    mDirtyRegion;
+  // invalidations are clipped to mInvalidationDimensions, not
+  // GetDimensions(), when mHaveInvalidationDimensions is true.  This
+  // is used to support persistent "displayport" rendering; see
+  // nsPresShell.cpp.  The coordinates of mInvalidationDimensions are
+  // relative to |this|.
+  nsRect       mInvalidationDimensions;
+  PRPackedBool mHaveInvalidationDimensions;
 
 private:
   void InitializeWindow(PRBool aEnableDragDrop, PRBool aResetVisibility);
 };
 
 #endif
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -566,17 +566,17 @@ nsViewManager::UpdateWidgetArea(nsView *
     return;
     // this should only happen at the top level, and this result
     // should not be consumed by top-level callers, so it doesn't
     // really matter what we return
   }
 
   // If the bounds don't overlap at all, there's nothing to do
   nsRegion intersection;
-  intersection.And(aWidgetView->GetDimensions(), aDamagedRegion);
+  intersection.And(aWidgetView->GetInvalidationDimensions(), aDamagedRegion);
   if (intersection.IsEmpty()) {
     return;
   }
 
   // If the widget is hidden, it don't cover nothing
   if (aWidget) {
     PRBool visible;
     aWidget->IsVisible(visible);
@@ -1618,17 +1618,17 @@ NS_IMETHODIMP nsViewManager::ForceUpdate
   return NS_OK;
 }
 
 nsIntRect nsViewManager::ViewToWidget(nsView *aView, const nsRect &aRect) const
 {
   NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
 
   // intersect aRect with bounds of aView, to prevent generating any illegal rectangles.
-  nsRect bounds = aView->GetDimensions();
+  nsRect bounds = aView->GetInvalidationDimensions();
   nsRect rect;
   rect.IntersectRect(aRect, bounds);
 
   // account for the view's origin not lining up with the widget's
   rect += aView->ViewToWidgetOffset();
 
   // finally, convert to device coordinates.
   return rect.ToOutsidePixels(AppUnitsPerDevPixel());