Bug 421885 - "Google reader search results have strange red line and broken border around them" [p=roc@ocallahan.org (Robert O'Callahan [roc]) r=vlad a=blocking1.9+]
authorreed@reedloden.com
Fri, 28 Mar 2008 01:40:02 -0700
changeset 13662 e315cbc5f199db7d1dab254152445fe1ec7613af
parent 13661 c95fc55affcdbd96d913d7890889269bc465e6ca
child 13663 bd270c6bdcc59f44281329481b23aead9b46432c
push idunknown
push userunknown
push dateunknown
reviewersvlad, blocking1.9
bugs421885
milestone1.9pre
Bug 421885 - "Google reader search results have strange red line and broken border around them" [p=roc@ocallahan.org (Robert O'Callahan [roc]) r=vlad a=blocking1.9+]
gfx/public/nsIRenderingContext.h
gfx/public/nsRect.h
gfx/src/nsRect.cpp
gfx/src/thebes/nsThebesImage.cpp
gfx/src/thebes/nsThebesImage.h
gfx/src/thebes/nsThebesRenderingContext.cpp
gfx/src/thebes/nsThebesRenderingContext.h
gfx/thebes/public/gfxPoint.h
layout/base/nsCSSRendering.cpp
layout/reftests/bugs/421885-1-ref.xml
layout/reftests/bugs/421885-1.xml
layout/reftests/bugs/reftest.list
--- a/gfx/public/nsIRenderingContext.h
+++ b/gfx/public/nsIRenderingContext.h
@@ -593,21 +593,24 @@ public:
   virtual void SetTextRunRTL(PRBool aIsRTL) = 0;
 
   /*
    * Tiles an image over an area
    * @param aImage       Image to tile
    * @param aXImageStart x location where the origin (0,0) of the image starts
    * @param aYImageStart y location where the origin (0,0) of the image starts
    * @param aTargetRect  area to draw to
+   * @param aSubimageRect the subimage (in tile space) which we expect to
+   * sample from; may be null to indicate that the whole image is
+   * OK to sample from
    */
   NS_IMETHOD DrawTile(imgIContainer *aImage,
                       nscoord aXImageStart, nscoord aYImageStart,
-                      const nsRect * aTargetRect) = 0;
-
+                      const nsRect * aTargetRect,
+                      const nsIntRect * aSubimageRect) = 0;
 
   /**
    * Get cluster details for a chunk of text.
    *
    * This will fill in the aClusterStarts array with information about
    * what characters are the start of clusters for display.  The
    * information is just a bitfield that is set to 1 if the character
    * is the start of a cluster.  aClusterStarts must already be
--- a/gfx/public/nsRect.h
+++ b/gfx/public/nsRect.h
@@ -172,16 +172,20 @@ struct NS_GFX nsRect {
                                           y = NSToCoordRound(y * aScale); 
                                           width = NSToCoordRound(width * aScale); 
                                           height = NSToCoordRound(height * aScale); 
                                           return *this;}
 
   // Scale by aScale, converting coordinates to integers so that the result
   // is the smallest integer-coordinate rectangle containing the unrounded result
   nsRect& ScaleRoundOut(float aScale);
+  // Scale by the inverse of aScale, converting coordinates to integers so that the result
+  // is the smallest integer-coordinate rectangle containing the unrounded result.
+  // More accurate than ScaleRoundOut(1.0/aScale).
+  nsRect& ScaleRoundOutInverse(float aScale);
   // Scale by aScale, converting coordinates to integers so that the result
   // is the larges integer-coordinate rectangle contained in the unrounded result
   nsRect& ScaleRoundIn(float aScale);
   // Scale by the inverse of aScale, converting coordinates to integers so that
   // the result contains the same pixel centers as the unrounded result
   nsRect& ScaleRoundPreservingCentersInverse(float aScale);
 
   // Helpers for accessing the vertices
--- a/gfx/src/nsRect.cpp
+++ b/gfx/src/nsRect.cpp
@@ -180,16 +180,27 @@ nsRect& nsRect::ScaleRoundOut(float aSca
   nscoord bottom = NSToCoordCeil(float(YMost()) * aScale);
   x = NSToCoordFloor(float(x) * aScale);
   y = NSToCoordFloor(float(y) * aScale);
   width = (right - x);
   height = (bottom - y);
   return *this;
 }
 
+nsRect& nsRect::ScaleRoundOutInverse(float aScale) 
+{
+  nscoord right = NSToCoordCeil(float(XMost()) / aScale);
+  nscoord bottom = NSToCoordCeil(float(YMost()) / aScale);
+  x = NSToCoordFloor(float(x) / aScale);
+  y = NSToCoordFloor(float(y) / aScale);
+  width = (right - x);
+  height = (bottom - y);
+  return *this;
+}
+
 // scale the rect but round to largest contained rect
 nsRect& nsRect::ScaleRoundIn(float aScale) 
 {
   nscoord right = NSToCoordFloor(float(XMost()) * aScale);
   nscoord bottom = NSToCoordFloor(float(YMost()) * aScale);
   x = NSToCoordCeil(float(x) * aScale);
   y = NSToCoordCeil(float(y) * aScale);
   width = (right - x);
--- a/gfx/src/thebes/nsThebesImage.cpp
+++ b/gfx/src/thebes/nsThebesImage.cpp
@@ -615,32 +615,34 @@ nsThebesImage::Draw(nsIRenderingContext 
     return NS_OK;
 }
 
 nsresult
 nsThebesImage::ThebesDrawTile(gfxContext *thebesContext,
                               nsIDeviceContext* dx,
                               const gfxPoint& offset,
                               const gfxRect& targetRect,
+                              const nsIntRect& aSubimageRect,
                               const PRInt32 xPadding,
                               const PRInt32 yPadding)
 {
     NS_ASSERTION(xPadding >= 0 && yPadding >= 0, "negative padding");
 
     if (targetRect.size.width <= 0.0 || targetRect.size.height <= 0.0)
         return NS_OK;
 
     // don't do anything if we have a transparent pixel source
     if (mSinglePixel && mSinglePixelColor.a == 0.0)
         return NS_OK;
 
     PRBool doSnap = !(thebesContext->CurrentMatrix().HasNonTranslation());
     PRBool hasPadding = ((xPadding != 0) || (yPadding != 0));
-
-    nsRefPtr<gfxASurface> tmpSurfaceGrip;
+    gfxImageSurface::gfxImageFormat format = mFormat;
+    
+    gfxPoint tmpOffset = offset;
 
     if (mSinglePixel && !hasPadding) {
         thebesContext->SetColor(mSinglePixelColor);
     } else {
         nsRefPtr<gfxASurface> surface;
         PRInt32 width, height;
 
         if (hasPadding) {
@@ -648,49 +650,136 @@ nsThebesImage::ThebesDrawTile(gfxContext
              * and render the image into it first.  Then we'll tile that surface. */
             width = mWidth + xPadding;
             height = mHeight + yPadding;
 
             // Reject over-wide or over-tall images.
             if (!AllowedImageSize(width, height))
                 return NS_ERROR_FAILURE;
 
-            surface = new gfxImageSurface(gfxIntSize(width, height),
-                                          gfxASurface::ImageFormatARGB32);
+            format = gfxASurface::ImageFormatARGB32;
+            surface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(
+                    gfxIntSize(width, height), format);
             if (!surface || surface->CairoStatus()) {
                 return NS_ERROR_OUT_OF_MEMORY;
             }
 
-            tmpSurfaceGrip = surface;
-
             gfxContext tmpContext(surface);
             if (mSinglePixel) {
                 tmpContext.SetColor(mSinglePixelColor);
             } else {
                 tmpContext.SetSource(ThebesSurface());
             }
             tmpContext.SetOperator(gfxContext::OPERATOR_SOURCE);
             tmpContext.Rectangle(gfxRect(0, 0, mWidth, mHeight));
             tmpContext.Fill();
         } else {
             width = mWidth;
             height = mHeight;
             surface = ThebesSurface();
         }
-
-        gfxMatrix patMat;
-        gfxPoint p0;
-
-        p0.x = - floor(offset.x + 0.5);
-        p0.y = - floor(offset.y + 0.5);
+        
         // Scale factor to account for CSS pixels; note that the offset (and 
         // therefore p0) is in device pixels, while the width and height are in
         // CSS pixels.
         gfxFloat scale = gfxFloat(dx->AppUnitsPerDevPixel()) /
                          gfxFloat(nsIDeviceContext::AppUnitsPerCSSPixel());
+
+        if ((aSubimageRect.width < width || aSubimageRect.height < height) &&
+            (thebesContext->CurrentMatrix().HasNonTranslation() || scale != 1.0)) {
+            // Some of the source image should not be drawn, and we're going
+            // to be doing more than just translation, so we might accidentally
+            // sample the non-drawn pixels. Avoid that by creating a
+            // temporary image representing the portion that will be drawn,
+            // with built-in padding since we can't use EXTEND_PAD and
+            // EXTEND_REPEAT at the same time for different axes.
+            PRInt32 padX = aSubimageRect.width < width ? 1 : 0;
+            PRInt32 padY = aSubimageRect.height < height ? 1 : 0;
+            PRInt32 tileWidth = PR_MIN(aSubimageRect.width, width);
+            PRInt32 tileHeight = PR_MIN(aSubimageRect.height, height);
+            
+            // This tmpSurface will contain a snapshot of the repeated
+            // tile image at (aSubimageRect.x, aSubimageRect.y,
+            // tileWidth, tileHeight), with padX padding added to the left
+            // and right sides and padY padding added to the top and bottom
+            // sides.
+            nsRefPtr<gfxASurface> tmpSurface;
+            tmpSurface = gfxPlatform::GetPlatform()->CreateOffscreenSurface(
+                    gfxIntSize(tileWidth + 2*padX, tileHeight + 2*padY), format);
+            if (!tmpSurface || tmpSurface->CairoStatus()) {
+                return NS_ERROR_OUT_OF_MEMORY;
+            }
+
+            gfxContext tmpContext(tmpSurface);
+            tmpContext.SetOperator(gfxContext::OPERATOR_SOURCE);
+            gfxPattern pat(surface);
+            pat.SetExtend(gfxPattern::EXTEND_REPEAT);
+            
+            // Copy the needed portion of the source image to the temporary
+            // surface. We also copy over horizontal and/or vertical padding
+            // strips one pixel wide, plus the corner pixels if necessary.
+            // So in the most general case the temporary surface ends up
+            // looking like
+            //     P P P ... P P P
+            //     P X X ... X X P
+            //     P X X ... X X P
+            //     ...............
+            //     P X X ... X X P
+            //     P X X ... X X P
+            //     P P P ... P P P
+            // Where each P pixel has the color of its nearest source X
+            // pixel. We implement this as a loop over all nine possible
+            // areas, [padding, body, padding] x [padding, body, padding].
+            // Note that we will not need padding on both axes unless
+            // we are painting just a single tile, in which case this
+            // will hardly ever get called since nsCSSRendering converts
+            // the single-tile case to nsLayoutUtils::DrawImage. But this
+            // could be called on other paths (XUL trees?) and it's simpler
+            // and clearer to do it the general way.
+            PRInt32 destY = 0;
+            for (PRInt32 y = -1; y <= 1; ++y) {
+                PRInt32 stripHeight = y == 0 ? tileHeight : padY;
+                if (stripHeight == 0)
+                    continue;
+                PRInt32 srcY = y == 1 ? aSubimageRect.YMost() - padY : aSubimageRect.y;
+                
+                PRInt32 destX = 0;
+                for (PRInt32 x = -1; x <= 1; ++x) {
+                    PRInt32 stripWidth = x == 0 ? tileWidth : padX;
+                    if (stripWidth == 0)
+                        continue;
+                    PRInt32 srcX = x == 1 ? aSubimageRect.XMost() - padX : aSubimageRect.x;
+
+                    gfxMatrix patMat;
+                    patMat.Translate(gfxPoint(srcX - destX, srcY - destY));
+                    pat.SetMatrix(patMat);
+                    tmpContext.SetPattern(&pat);
+                    tmpContext.Rectangle(gfxRect(destX, destY, stripWidth, stripHeight));
+                    tmpContext.Fill();
+                    tmpContext.NewPath();
+                    
+                    destX += stripWidth;
+                }
+                destY += stripHeight;
+            }
+
+            // tmpOffset was the top-left of the old tile image. Make it
+            // the top-left of the new tile image. Note that tmpOffset is
+            // in destination coordinate space so we have to scale our
+            // CSS pixels.
+            tmpOffset += gfxPoint(aSubimageRect.x - padX, aSubimageRect.y - padY)/scale;
+            
+            surface = tmpSurface;
+        }
+
+        gfxMatrix patMat;
+        gfxPoint p0;
+
+        p0.x = - floor(tmpOffset.x + 0.5);
+        p0.y = - floor(tmpOffset.y + 0.5);
         patMat.Scale(scale, scale);
         patMat.Translate(p0);
 
         gfxPattern pat(surface);
         pat.SetExtend(gfxPattern::EXTEND_REPEAT);
         pat.SetMatrix(patMat);
 
 #ifndef XP_MACOSX
@@ -700,17 +789,17 @@ nsThebesImage::ThebesDrawTile(gfxContext
             pat.SetFilter(0);
         }
 #endif
 
         thebesContext->SetPattern(&pat);
     }
 
     gfxContext::GraphicsOperator op = thebesContext->CurrentOperator();
-    if (op == gfxContext::OPERATOR_OVER && mFormat == gfxASurface::ImageFormatRGB24)
+    if (op == gfxContext::OPERATOR_OVER && format == gfxASurface::ImageFormatRGB24)
         thebesContext->SetOperator(gfxContext::OPERATOR_SOURCE);
 
     thebesContext->NewPath();
     thebesContext->Rectangle(targetRect, doSnap);
     thebesContext->Fill();
 
     thebesContext->SetOperator(op);
     thebesContext->SetColor(gfxRGBA(0,0,0,0));
--- a/gfx/src/thebes/nsThebesImage.h
+++ b/gfx/src/thebes/nsThebesImage.h
@@ -79,16 +79,17 @@ public:
                     const gfxRect &aSourceRect,
                     const gfxRect &aSubimageRect,
                     const gfxRect &aDestRect);
 
     nsresult ThebesDrawTile(gfxContext *thebesContext,
                             nsIDeviceContext* dx,
                             const gfxPoint& aOffset,
                             const gfxRect& aTileRect,
+                            const nsIntRect& aSubimageRect,
                             const PRInt32 aXPadding,
                             const PRInt32 aYPadding);
 
     virtual PRInt8 GetAlphaDepth();
     virtual void* GetBitInfo();
     NS_IMETHOD LockImagePixels(PRBool aMaskPixels);
     NS_IMETHOD UnlockImagePixels(PRBool aMaskPixels);
 
--- a/gfx/src/thebes/nsThebesRenderingContext.cpp
+++ b/gfx/src/thebes/nsThebesRenderingContext.cpp
@@ -773,17 +773,18 @@ nsThebesRenderingContext::PopFilter()
 
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsThebesRenderingContext::DrawTile(imgIContainer *aImage,
                                    nscoord twXOffset, nscoord twYOffset,
-                                   const nsRect *twTargetRect)
+                                   const nsRect *twTargetRect,
+                                   const nsIntRect *subimageRect)
 {
     PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawTile %p %f %f [%f,%f,%f,%f]\n",
                                          this, aImage, FROM_TWIPS(twXOffset), FROM_TWIPS(twYOffset),
                                          FROM_TWIPS(twTargetRect->x), FROM_TWIPS(twTargetRect->y),
                                          FROM_TWIPS(twTargetRect->width), FROM_TWIPS(twTargetRect->height)));
 
     nscoord containerWidth, containerHeight;
     aImage->GetWidth(&containerWidth);
@@ -808,28 +809,44 @@ nsThebesRenderingContext::DrawTile(imgIC
      * so we need to make sure that there is the right amount of padding
      * in between each tile of the nsIImage.  This problem goes away
      * when we change the way the GIF decoder works to have it store
      * full frames that are ready to be composited.
      */
     PRInt32 xPadding = 0;
     PRInt32 yPadding = 0;
 
+    nsIntRect tmpSubimageRect;
+    if (subimageRect) {
+        tmpSubimageRect = *subimageRect;
+    } else {
+        tmpSubimageRect = nsIntRect(0, 0, containerWidth, containerHeight);
+    }
+
     if (imgFrameRect.width != containerWidth ||
         imgFrameRect.height != containerHeight)
     {
         xPadding = containerWidth - imgFrameRect.width;
         yPadding = containerHeight - imgFrameRect.height;
 
+        // XXXroc shouldn't we be adding to 'phase' here? it's tbe origin
+        // at which the image origin should be drawn, and ThebesDrawTile
+        // just draws the origin of its "frame" there, so we should be
+        // adding imgFrameRect.x/y. so that the imgFrame draws in the
+        // right place.
         phase.x -= imgFrameRect.x;
         phase.y -= imgFrameRect.y;
+
+        tmpSubimageRect.x -= imgFrameRect.x;
+        tmpSubimageRect.y -= imgFrameRect.y;
     }
 
     return thebesImage->ThebesDrawTile (mThebes, mDeviceContext, phase,
                                         GFX_RECT_FROM_TWIPS_RECT(*twTargetRect),
+                                        tmpSubimageRect,
                                         xPadding, yPadding);
 }
 
 //
 // text junk
 //
 NS_IMETHODIMP
 nsThebesRenderingContext::SetRightToLeftText(PRBool aIsRTL)
--- a/gfx/src/thebes/nsThebesRenderingContext.h
+++ b/gfx/src/thebes/nsThebesRenderingContext.h
@@ -180,17 +180,17 @@ public:
 
     virtual void* GetNativeGraphicData(GraphicDataType aType);
 
     NS_IMETHOD PushTranslation(PushedTranslation* aState);
     NS_IMETHOD PopTranslation(PushedTranslation* aState);
     NS_IMETHOD SetTranslation(nscoord aX, nscoord aY);
 
     NS_IMETHOD DrawTile(imgIContainer *aImage, nscoord aXOffset, nscoord aYOffset,
-                        const nsRect * aTargetRect);
+                        const nsRect * aTargetRect, const nsIntRect * aSubimageRect);
     NS_IMETHOD SetRightToLeftText(PRBool aIsRTL);
     NS_IMETHOD GetRightToLeftText(PRBool* aIsRTL);
     virtual void SetTextRunRTL(PRBool aIsRTL);
 
     NS_IMETHOD GetClusterInfo(const PRUnichar *aText,
                               PRUint32 aLength,
                               PRUint8 *aClusterStarts);
     virtual PRInt32 GetPosition(const PRUnichar *aText,
--- a/gfx/thebes/public/gfxPoint.h
+++ b/gfx/thebes/public/gfxPoint.h
@@ -115,16 +115,21 @@ struct THEBES_API gfxPoint {
     void MoveTo(gfxFloat aX, gfxFloat aY) { x = aX; y = aY; }
 
     int operator==(const gfxPoint& p) const {
         return ((x == p.x) && (y == p.y));
     }
     int operator!=(const gfxPoint& p) const {
         return ((x != p.x) || (y != p.y));
     }
+    const gfxPoint& operator+=(const gfxPoint& p) {
+        x += p.x;
+        y += p.y;
+        return *this;
+    }
     gfxPoint operator+(const gfxPoint& p) const {
         return gfxPoint(x + p.x, y + p.y);
     }
     gfxPoint operator+(const gfxSize& s) const {
         return gfxPoint(x + s.width, y + s.height);
     }
     gfxPoint operator-(const gfxPoint& p) const {
         return gfxPoint(x - p.x, y - p.y);
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -3899,29 +3899,33 @@ nsCSSRendering::PaintBackgroundWithSC(ns
     NS_ASSERTION(drawRect.x >= absTileRect.x && drawRect.y >= absTileRect.y,
                  "Bogus intersection");
     NS_ASSERTION(drawRect.x < absTileRect.x + tileWidth,
                  "Bogus x coord for draw rect");
     NS_ASSERTION(drawRect.y < absTileRect.y + tileHeight,
                  "Bogus y coord for draw rect");
     // Figure out whether we can get away with not tiling at all.
     nsRect sourceRect = drawRect - absTileRect.TopLeft();
+    // Compute the subimage rectangle that we expect to be sampled.
+    // This is the tile rectangle, clipped to the bgClipArea, and then
+    // passed in relative to the image top-left.
+    nsRect destRect; // The rectangle we would draw ignoring dirty-rect
+    destRect.IntersectRect(absTileRect, bgClipArea);
+    nsRect subimageRect = destRect - aBorderArea.TopLeft() - tileRect.TopLeft();
     if (sourceRect.XMost() <= tileWidth && sourceRect.YMost() <= tileHeight) {
       // The entire drawRect is contained inside a single tile; just
       // draw the corresponding part of the image once.
-      // Pass in the subimage rectangle that we expect to be sampled.
-      // This is the tile rectangle, clipped to the bgClipArea, and then
-      // passed in relative to the image top-left.
-      nsRect destRect; // The rectangle we would draw ignoring dirty-rect
-      destRect.IntersectRect(absTileRect, bgClipArea);
-      nsRect subimageRect = destRect - aBorderArea.TopLeft() - tileRect.TopLeft();
       nsLayoutUtils::DrawImage(&aRenderingContext, image,
               destRect, drawRect, &subimageRect);
     } else {
-      aRenderingContext.DrawTile(image, absTileRect.x, absTileRect.y, &drawRect);
+      // Note that the subimage is in tile space so it may cover
+      // multiple tiles of the image.
+      subimageRect.ScaleRoundOutInverse(nsIDeviceContext::AppUnitsPerCSSPixel());
+      aRenderingContext.DrawTile(image, absTileRect.x, absTileRect.y,
+              &drawRect, &subimageRect);
     }
   }
 
   ctx->Restore();
 
 }
 
 void
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/421885-1-ref.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<svg xmlns:html="http://www.w3.org/1999/xhtml"
+      xmlns="http://www.w3.org/2000/svg">
+  <foreignObject transform="scale(2)" width="100%" height="100%">
+    <!-- test the two-span case, subimage covers two tiles -->
+    <html:div style="width:2px; height:64px; background:black;
+                   margin-bottom:5px;">
+    </html:div>
+    <html:div style="width:64px; height:2px; background:black;
+                   margin-bottom:5px;">
+    </html:div>
+    <!-- test the one-span case, subimage covers one tile at the end of the tile -->
+    <html:div style="width:1px; height:64px; background:black;
+                   margin-bottom:5px;">
+    </html:div>
+    <html:div style="width:64px; height:1px; background:black;
+                   margin-bottom:5px;">
+    </html:div>
+    <!-- test the one-span case, subimage covers one tile at the start of the tile -->
+    <html:div style="width:1px; height:64px; background:black;
+                   margin-bottom:5px;">
+    </html:div>
+    <html:div style="width:64px; height:1px; background:black;
+                   margin-bottom:5px;">
+    </html:div>
+  </foreignObject>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/421885-1.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<svg xmlns:html="http://www.w3.org/1999/xhtml"
+      xmlns="http://www.w3.org/2000/svg">
+  <foreignObject transform="scale(2)" width="100%" height="100%">
+    <!-- test the two-span case, subimage covers two tiles -->
+    <html:div style="width:2px; height:64px; background-position:-31px 0;
+                   background-image: url(square-left-right-32x32.png);
+                   margin-bottom:5px;">
+    </html:div>
+    <html:div style="width:64px; height:2px; background-position:0 -31px;
+                   background-image: url(square-top-bottom-32x32.png);
+                   margin-bottom:5px;">
+    </html:div>
+    <!-- test the one-span case, subimage covers one tile at the end of the tile -->
+    <html:div style="width:1px; height:64px; background-position:-31px 0;
+                   background-image: url(square-left-right-32x32.png);
+                   margin-bottom:5px;">
+    </html:div>
+    <html:div style="width:64px; height:1px; background-position:0 -31px;
+                   background-image: url(square-top-bottom-32x32.png);
+                   margin-bottom:5px;">
+    </html:div>
+    <!-- test the one-span case, subimage covers one tile at the start of the tile -->
+    <html:div style="width:1px; height:64px; background-position:0 0;
+                   background-image: url(square-left-right-32x32.png);
+                   margin-bottom:5px;">
+    </html:div>
+    <html:div style="width:64px; height:1px; background-position:0 0;
+                   background-image: url(square-top-bottom-32x32.png);
+                   margin-bottom:5px;">
+    </html:div>
+  </foreignObject>
+</svg>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -768,16 +768,17 @@ fails == 413027-3.html 413027-3-ref.html
 == 420069-1.html 420069-1-ref.html
 == 420069-2.html 420069-2-ref.html
 == 420351-1.html 420351-1-ref.html
 == 421069.html 421069-ref.html
 == 421069.html 421069-ref2.html
 == 421069-ref.html 421069-ref2.html
 == 421234-1.html 421234-1-ref.html
 == 421419-1.html 421419-1-ref.html
+== 421885-1.xml 421885-1-ref.xml
 == 421955-1.html 421955-1-ref.html
 == 422394-1.html 422394-1-ref.html
 == 423130-1.html 423130-1-ref.html
 == 423385-1.html 423385-1-ref.html
 == 423599-1.html 423599-1-ref.html
 == 423676-1.html 423676-1-ref.html
 == 424074-1.xul 424074-1-ref.xul
 != 424074-1.xul 424074-1-ref2.xul