Clip replaced elements (for now, images, canvases, videos, and windowless plugins) to the border radius. (Bug 485501) r=roc a2.0=blocking:beta6
authorL. David Baron <dbaron@dbaron.org>
Thu, 09 Sep 2010 08:21:47 -0700
changeset 52294 0aa1272e932fee3aa47f6cde08ec602e5c0310bc
parent 52293 082bd0be8bc01e4789e80fb7682942eb8e79e974
child 52295 da15f373d499380dc04e7a2d663bfabe5df359fe
push id15592
push userdbaron@mozilla.com
push dateThu, 09 Sep 2010 15:23:38 +0000
treeherdermozilla-central@8adb2f64c138 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs485501
milestone2.0b6pre
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
Clip replaced elements (for now, images, canvases, videos, and windowless plugins) to the border radius. (Bug 485501) r=roc a2.0=blocking:beta6
layout/generic/nsFrame.cpp
layout/generic/nsHTMLCanvasFrame.cpp
layout/generic/nsIFrame.h
layout/generic/nsImageFrame.cpp
layout/generic/nsObjectFrame.cpp
layout/generic/nsVideoFrame.cpp
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1771,16 +1771,37 @@ nsIFrame::BuildDisplayListForChild(nsDis
   // because in the absence of z-index this is the correct order for them.
   // This doesn't affect correctness because the positioned descendants list
   // is sorted by z-order and content in BuildDisplayListForStackingContext,
   // but it means that sort routine needs to do less work.
   aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
   return NS_OK;
 }
 
+void
+nsIFrame::WrapReplacedContentForBorderRadius(nsDisplayListBuilder* aBuilder,
+                                             nsDisplayList* aFromList,
+                                             const nsDisplayListSet& aToLists)
+{
+  nscoord radii[8];
+  if (GetContentBoxBorderRadii(radii)) {
+    // If we have a border-radius, we have to clip our content to that
+    // radius.
+    nsDisplayListCollection set;
+    set.Content()->AppendToTop(aFromList);
+    nsRect clipRect = GetContentRect() - GetPosition() +
+                      aBuilder->ToReferenceFrame(this);
+    OverflowClip(aBuilder, set, aToLists, clipRect, radii, PR_FALSE, PR_TRUE);
+
+    return;
+  }
+
+  aToLists.Content()->AppendToTop(aFromList);
+}
+
 NS_IMETHODIMP  
 nsFrame::GetContentForEvent(nsPresContext* aPresContext,
                             nsEvent* aEvent,
                             nsIContent** aContent)
 {
   nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
   *aContent = f->GetContent();
   NS_IF_ADDREF(*aContent);
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -278,22 +278,29 @@ nsHTMLCanvasFrame::BuildDisplayList(nsDi
                                     const nsDisplayListSet& aLists)
 {
   if (!IsVisibleForPainting(aBuilder))
     return NS_OK;
 
   nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = aLists.Content()->AppendNewToTop(
+  nsDisplayList replacedContent;
+
+  rv = replacedContent.AppendNewToTop(
       new (aBuilder) nsDisplayCanvas(aBuilder, this));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return DisplaySelectionOverlay(aBuilder, aLists.Content(),
-                                 nsISelectionDisplay::DISPLAY_IMAGES);
+  rv = DisplaySelectionOverlay(aBuilder, &replacedContent,
+                               nsISelectionDisplay::DISPLAY_IMAGES);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  WrapReplacedContentForBorderRadius(aBuilder, &replacedContent, aLists);
+
+  return NS_OK;
 }
 
 nsIAtom*
 nsHTMLCanvasFrame::GetType() const
 {
   return nsGkAtoms::HTMLCanvasFrame;
 }
 
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1124,16 +1124,25 @@ public:
    */
   nsresult BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
                                     nsIFrame*               aChild,
                                     const nsRect&           aDirtyRect,
                                     const nsDisplayListSet& aLists,
                                     PRUint32                aFlags = 0);
 
   /**
+   * A helper for replaced elements that want to clip their content to a
+   * border radius, but only need clipping at all when they have a
+   * border radius.
+   */
+  void WrapReplacedContentForBorderRadius(nsDisplayListBuilder* aBuilder,
+                                          nsDisplayList* aFromList,
+                                          const nsDisplayListSet& aToLists);
+
+  /**
    * Does this frame need a view?
    */
   virtual PRBool NeedsView() { return PR_FALSE; }
 
   /**
    * Returns whether this frame has a transform matrix applied to it.  This is true
    * if we have the -moz-transform property or if we're an SVGForeignObjectFrame.
    */
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1238,17 +1238,18 @@ nsImageFrame::BuildDisplayList(nsDisplay
   // to put the background in ... it goes in aLists.BorderBackground() and
   // then if we have a block parent, it will put our background in the right
   // place.
   nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
   NS_ENSURE_SUCCESS(rv, rv);
   // REVIEW: Checking mRect.IsEmpty() makes no sense to me, so I removed it.
   // It can't have been protecting us against bad situations with zero-size
   // images since adding a border would make the rect non-empty.
-    
+
+  nsDisplayList replacedContent;
   if (mComputedSize.width != 0 && mComputedSize.height != 0) {
     nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent);
     NS_ASSERTION(imageLoader, "Not an image loading content?");
 
     nsCOMPtr<imgIRequest> currentRequest;
     if (imageLoader) {
       imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
                               getter_AddRefs(currentRequest));
@@ -1271,23 +1272,23 @@ nsImageFrame::BuildDisplayList(nsDisplay
       haveSize = PR_TRUE;
 
     // We should never have the size and not have an image container
     NS_ABORT_IF_FALSE(!haveSize || imgCon, "Have size but not container?");
 
     if (!imageOK || !haveSize) {
       // No image yet, or image load failed. Draw the alt-text and an icon
       // indicating the status
-      rv = aLists.Content()->AppendNewToTop(new (aBuilder)
+      rv = replacedContent.AppendNewToTop(new (aBuilder)
           nsDisplayGeneric(aBuilder, this, PaintAltFeedback, "AltFeedback",
                            nsDisplayItem::TYPE_ALT_FEEDBACK));
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else {
-      rv = aLists.Content()->AppendNewToTop(new (aBuilder)
+      rv = replacedContent.AppendNewToTop(new (aBuilder)
           nsDisplayImage(aBuilder, this, imgCon));
       NS_ENSURE_SUCCESS(rv, rv);
 
       // If we were previously displaying an icon, we're not anymore
       if (mDisplayingIcon) {
         gIconLoad->RemoveIconObserver(this);
         mDisplayingIcon = PR_FALSE;
       }
@@ -1352,18 +1353,23 @@ nsImageFrame::BuildDisplayList(nsDisplay
             }
           }
         }
       }
     }
   }
 #endif
   
-  return DisplaySelectionOverlay(aBuilder, aLists.Content(),
-                                 nsISelectionDisplay::DISPLAY_IMAGES);
+  rv = DisplaySelectionOverlay(aBuilder, &replacedContent,
+                               nsISelectionDisplay::DISPLAY_IMAGES);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  WrapReplacedContentForBorderRadius(aBuilder, &replacedContent, aLists);
+
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsImageFrame::GetImageMap(nsPresContext *aPresContext, nsIImageMap **aImageMap)
 {
   nsImageMap *map = GetImageMap(aPresContext);
   return CallQueryInterface(map, aImageMap);
 }
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -1348,40 +1348,48 @@ nsObjectFrame::BuildDisplayList(nsDispla
                                 const nsDisplayListSet& aLists)
 {
   // XXX why are we painting collapsed object frames?
   if (!IsVisibleOrCollapsedForPainting(aBuilder))
     return NS_OK;
     
   nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
   NS_ENSURE_SUCCESS(rv, rv);
-  
+
   nsPresContext::nsPresContextType type = PresContext()->Type();
 
   // If we are painting in Print Preview do nothing....
   if (type == nsPresContext::eContext_PrintPreview)
     return NS_OK;
 
   DO_GLOBAL_REFLOW_COUNT_DSP("nsObjectFrame");
 
 #ifndef XP_MACOSX
   if (mWidget && aBuilder->IsInTransform()) {
     // Windowed plugins should not be rendered inside a transform.
     return NS_OK;
   }
 #endif
 
+  nsDisplayList replacedContent;
+
   // determine if we are printing
-  if (type == nsPresContext::eContext_Print)
-    return aLists.Content()->AppendNewToTop(new (aBuilder)
+  if (type == nsPresContext::eContext_Print) {
+    rv = replacedContent.AppendNewToTop(new (aBuilder)
         nsDisplayGeneric(aBuilder, this, PaintPrintPlugin, "PrintPlugin",
                          nsDisplayItem::TYPE_PRINT_PLUGIN));
-
-  return aLists.Content()->AppendNewToTop(new (aBuilder)
-      nsDisplayPlugin(aBuilder, this));
+  } else {
+    rv = replacedContent.AppendNewToTop(new (aBuilder)
+        nsDisplayPlugin(aBuilder, this));
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  WrapReplacedContentForBorderRadius(aBuilder, &replacedContent, aLists);
+
+  return NS_OK;
 }
 
 void
 nsObjectFrame::PrintPlugin(nsIRenderingContext& aRenderingContext,
                            const nsRect& aDirtyRect)
 {
   nsCOMPtr<nsIObjectLoadingContent> obj(do_QueryInterface(mContent));
   if (!obj)
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -53,16 +53,17 @@
 #include "nsPresContext.h"
 #include "nsTransform2D.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsBoxLayoutState.h"
 #include "nsBoxFrame.h"
 #include "nsImageFrame.h"
 #include "nsIImageLoadingContent.h"
 #include "nsDisplayList.h"
+#include "nsCSSRendering.h"
 
 #ifdef ACCESSIBILITY
 #include "nsIServiceManager.h"
 #include "nsIAccessibilityService.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::layers;
@@ -412,40 +413,44 @@ nsVideoFrame::BuildDisplayList(nsDisplay
   if (!IsVisibleForPainting(aBuilder))
     return NS_OK;
 
   DO_GLOBAL_REFLOW_COUNT_DSP("nsVideoFrame");
 
   nsresult rv = DisplayBorderBackgroundOutline(aBuilder, aLists);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsDisplayList replacedContent;
+
   if (HasVideoElement() && !ShouldDisplayPoster()) {
-    rv = aLists.Content()->AppendNewToTop(
+    rv = replacedContent.AppendNewToTop(
       new (aBuilder) nsDisplayVideo(aBuilder, this));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Add child frames to display list. We expect up to two children, an image
   // frame for the poster, and the box frame for the video controls.
   for (nsIFrame *child = mFrames.FirstChild();
        child;
        child = child->GetNextSibling()) {
     if (child->GetType() == nsGkAtoms::imageFrame && ShouldDisplayPoster()) {
       rv = child->BuildDisplayListForStackingContext(aBuilder,
                                                      aDirtyRect - child->GetOffsetTo(this),
-                                                     aLists.Content());
+                                                     &replacedContent);
       NS_ENSURE_SUCCESS(rv,rv);
     } else if (child->GetType() == nsGkAtoms::boxFrame) {
       rv = child->BuildDisplayListForStackingContext(aBuilder,
                                                      aDirtyRect - child->GetOffsetTo(this),
-                                                     aLists.Content());
+                                                     &replacedContent);
       NS_ENSURE_SUCCESS(rv,rv);
     }
   }
 
+  WrapReplacedContentForBorderRadius(aBuilder, &replacedContent, aLists);
+
   return NS_OK;
 }
 
 nsIAtom*
 nsVideoFrame::GetType() const
 {
   return nsGkAtoms::HTMLVideoFrame;
 }