Bug 690187 - Clip or suppress a marker if it makes the line empty. r=roc
authorMats Palmgren <matspal@gmail.com>
Mon, 19 Dec 2011 15:48:31 +0100
changeset 84678 32682110d4bf2e0da2ce34dd7e541bf829148360
parent 84677 6489afe1cd34076c5f1535937367372712016dea
child 84679 a1a8c14cc2a49af311478a87f4bd5fde1afa0277
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs690187
milestone11.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 690187 - Clip or suppress a marker if it makes the line empty. r=roc
layout/generic/TextOverflow.cpp
layout/generic/TextOverflow.h
layout/reftests/text-overflow/atomic-under-marker-ref.html
layout/reftests/text-overflow/atomic-under-marker.html
layout/reftests/text-overflow/clipped-elements-ref.html
layout/reftests/text-overflow/ellipsis-font-fallback-ref.html
layout/reftests/text-overflow/marker-basic-ref.html
layout/reftests/text-overflow/marker-string-ref.html
layout/reftests/text-overflow/quirks-decorations-ref.html
layout/reftests/text-overflow/reftest.list
layout/reftests/text-overflow/standards-decorations-ref.html
layout/reftests/text-overflow/theme-overflow-ref.html
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -282,17 +282,18 @@ TextOverflow::WillProcessLines(nsDisplay
 }
 
 void
 TextOverflow::ExamineFrameSubtree(nsIFrame*       aFrame,
                                   const nsRect&   aContentArea,
                                   const nsRect&   aInsideMarkersArea,
                                   FrameHashtable* aFramesToHide,
                                   AlignmentEdges* aAlignmentEdges,
-                                  bool*           aFoundVisibleTextOrAtomic)
+                                  bool*           aFoundVisibleTextOrAtomic,
+                                  InnerClipEdges* aClippedMarkerEdges)
 {
   const nsIAtom* frameType = aFrame->GetType();
   if (frameType == nsGkAtoms::brFrame ||
       frameType == nsGkAtoms::placeholderFrame) {
     return;
   }
   const bool isAtomic = IsAtomicElement(aFrame, frameType);
   if (aFrame->GetStyleVisibility()->IsVisible()) {
@@ -307,85 +308,88 @@ TextOverflow::ExamineFrameSubtree(nsIFra
       mRight.mHasOverflow = true;
     }
     if (isAtomic && ((mLeft.mActive && overflowLeft) ||
                      (mRight.mActive && overflowRight))) {
       aFramesToHide->PutEntry(aFrame);
     } else if (isAtomic || frameType == nsGkAtoms::textFrame) {
       AnalyzeMarkerEdges(aFrame, frameType, aInsideMarkersArea,
                          aFramesToHide, aAlignmentEdges,
-                         aFoundVisibleTextOrAtomic);
+                         aFoundVisibleTextOrAtomic,
+                         aClippedMarkerEdges);
     }
   }
   if (isAtomic) {
     return;
   }
 
   nsIFrame* child = aFrame->GetFirstPrincipalChild();
   while (child) {
     ExamineFrameSubtree(child, aContentArea, aInsideMarkersArea,
                         aFramesToHide, aAlignmentEdges,
-                        aFoundVisibleTextOrAtomic);
+                        aFoundVisibleTextOrAtomic,
+                        aClippedMarkerEdges);
     child = child->GetNextSibling();
   }
 }
 
 void
 TextOverflow::AnalyzeMarkerEdges(nsIFrame*       aFrame,
                                  const nsIAtom*  aFrameType,
                                  const nsRect&   aInsideMarkersArea,
                                  FrameHashtable* aFramesToHide,
                                  AlignmentEdges* aAlignmentEdges,
-                                 bool*           aFoundVisibleTextOrAtomic)
+                                 bool*           aFoundVisibleTextOrAtomic,
+                                 InnerClipEdges* aClippedMarkerEdges)
 {
   nsRect borderRect(aFrame->GetOffsetTo(mBlock), aFrame->GetSize());
   nscoord leftOverlap =
     NS_MAX(aInsideMarkersArea.x - borderRect.x, 0);
   nscoord rightOverlap =
     NS_MAX(borderRect.XMost() - aInsideMarkersArea.XMost(), 0);
   bool insideLeftEdge = aInsideMarkersArea.x <= borderRect.XMost();
   bool insideRightEdge = borderRect.x <= aInsideMarkersArea.XMost();
 
+  if (leftOverlap > 0) {
+    aClippedMarkerEdges->AccumulateLeft(borderRect);
+    if (!mLeft.mActive) {
+      leftOverlap = 0;
+    }
+  }
+  if (rightOverlap > 0) {
+    aClippedMarkerEdges->AccumulateRight(borderRect);
+    if (!mRight.mActive) {
+      rightOverlap = 0;
+    }
+  }
+
   if ((leftOverlap > 0 && insideLeftEdge) ||
       (rightOverlap > 0 && insideRightEdge)) {
-    if (aFrameType == nsGkAtoms::textFrame &&
-        aInsideMarkersArea.x < aInsideMarkersArea.XMost()) {
-      if (!mLeft.mActive) {
-        leftOverlap = 0;
-      }
-      if (!mRight.mActive) {
-        rightOverlap = 0;
-      }
-      if (leftOverlap == 0 && rightOverlap == 0) {
-        return;
+    if (aFrameType == nsGkAtoms::textFrame) {
+      if (aInsideMarkersArea.x < aInsideMarkersArea.XMost()) {
+        // a clipped text frame and there is some room between the markers
+        nscoord snappedLeft, snappedRight;
+        bool isFullyClipped =
+          IsFullyClipped(static_cast<nsTextFrame*>(aFrame),
+                         leftOverlap, rightOverlap, &snappedLeft, &snappedRight);
+        if (!isFullyClipped) {
+          nsRect snappedRect = borderRect;
+          if (leftOverlap > 0) {
+            snappedRect.x += snappedLeft;
+            snappedRect.width -= snappedLeft;
+          }
+          if (rightOverlap > 0) {
+            snappedRect.width -= snappedRight;
+          }
+          aAlignmentEdges->Accumulate(snappedRect);
+          *aFoundVisibleTextOrAtomic = true;
+        }
       }
-      // a clipped text frame and there is some room between the markers
-      nscoord snappedLeft, snappedRight;
-      bool isFullyClipped =
-        IsFullyClipped(static_cast<nsTextFrame*>(aFrame),
-                       leftOverlap, rightOverlap, &snappedLeft, &snappedRight);
-      if (!isFullyClipped) {
-        nsRect snappedRect = borderRect;
-        if (leftOverlap > 0) {
-          snappedRect.x += snappedLeft;
-          snappedRect.width -= snappedLeft;
-        }
-        if (rightOverlap > 0) {
-          snappedRect.width -= snappedRight;
-        }
-        aAlignmentEdges->Accumulate(snappedRect);
-        *aFoundVisibleTextOrAtomic = true;
-      }
-    } else if (IsAtomicElement(aFrame, aFrameType)) {
-      if ((leftOverlap > 0 && insideLeftEdge && mLeft.mActive) ||
-          (rightOverlap > 0 && insideRightEdge && mRight.mActive)) {
-        aFramesToHide->PutEntry(aFrame);
-      } else {
-        *aFoundVisibleTextOrAtomic = true;
-      }
+    } else {
+      aFramesToHide->PutEntry(aFrame);
     }
   } else if (!insideLeftEdge || !insideRightEdge) {
     // frame is outside
     if (IsAtomicElement(aFrame, aFrameType)) {
       aFramesToHide->PutEntry(aFrame);
     }
   } else {
     // frame is inside
@@ -429,35 +433,38 @@ TextOverflow::ExamineLineFrames(nsLineBo
     !suppressLeft && lineRect.x < contentArea.x;
   const bool rightOverflow =
     !suppressRight && lineRect.XMost() > contentArea.XMost();
   if (!leftOverflow && !rightOverflow) {
     // The line does not overflow on a side we should ellipsize.
     return;
   }
 
-  PRUint32 pass = 0;
+  int pass = 0;
+  bool retryEmptyLine = true;
   bool guessLeft = leftOverflow;
   bool guessRight = rightOverflow;
   mLeft.mActive = leftOverflow;
   mRight.mActive = rightOverflow;
+  bool clippedLeftMarker = false;
+  bool clippedRightMarker = false;
   do {
     // Setup marker strings as needed.
     if (guessLeft) {
       mLeft.SetupString(mBlock);
     }
     if (guessRight) {
       mRight.SetupString(mBlock);
     }
     
     // If there is insufficient space for both markers then keep the one on the
     // end side per the block's 'direction'.
-    nscoord rightMarkerWidth = mRight.mWidth;
-    nscoord leftMarkerWidth = mLeft.mWidth;
-    if (leftOverflow && rightOverflow &&
+    nscoord rightMarkerWidth = mRight.mActive ? mRight.mWidth : 0;
+    nscoord leftMarkerWidth = mLeft.mActive ? mLeft.mWidth : 0;
+    if (leftMarkerWidth && rightMarkerWidth &&
         leftMarkerWidth + rightMarkerWidth > contentArea.width) {
       if (mBlockIsRTL) {
         rightMarkerWidth = 0;
       } else {
         leftMarkerWidth = 0;
       }
     }
 
@@ -471,36 +478,76 @@ TextOverflow::ExamineLineFrames(nsLineBo
       InflateRight(&insideMarkersArea, -rightMarkerWidth);
     }
 
     // Analyze the frames on aLine for the overflow situation at the content
     // edges and at the edges of the area between the markers.
     bool foundVisibleTextOrAtomic = false;
     PRInt32 n = aLine->GetChildCount();
     nsIFrame* child = aLine->mFirstChild;
+    InnerClipEdges clippedMarkerEdges;
     for (; n-- > 0; child = child->GetNextSibling()) {
       ExamineFrameSubtree(child, contentArea, insideMarkersArea,
                           aFramesToHide, aAlignmentEdges,
-                          &foundVisibleTextOrAtomic);
+                          &foundVisibleTextOrAtomic,
+                          &clippedMarkerEdges);
     }
-    if (guessLeft == mLeft.IsNeeded() && guessRight == mRight.IsNeeded()) {
+    if (!foundVisibleTextOrAtomic && retryEmptyLine) {
+      aAlignmentEdges->mAssigned = false;
+      aFramesToHide->Clear();
+      pass = -1;
+      if (mLeft.IsNeeded() && mLeft.mActive && !clippedLeftMarker) {
+        if (clippedMarkerEdges.mAssignedLeft &&
+            clippedMarkerEdges.mLeft - mContentArea.X() > 0) {
+          mLeft.mWidth = clippedMarkerEdges.mLeft - mContentArea.X();
+          NS_ASSERTION(mLeft.mWidth < mLeft.mIntrinsicWidth,
+                       "clipping a marker should make it strictly smaller");
+          clippedLeftMarker = true;
+        } else {
+          mLeft.mActive = guessLeft = false;
+        }
+        continue;
+      }
+      if (mRight.IsNeeded() && mRight.mActive && !clippedRightMarker) {
+        if (clippedMarkerEdges.mAssignedRight &&
+            mContentArea.XMost() - clippedMarkerEdges.mRight > 0) {
+          mRight.mWidth = mContentArea.XMost() - clippedMarkerEdges.mRight;
+          NS_ASSERTION(mRight.mWidth < mRight.mIntrinsicWidth,
+                       "clipping a marker should make it strictly smaller");
+          clippedRightMarker = true;
+        } else {
+          mRight.mActive = guessRight = false;
+        }
+        continue;
+      }
+      // The line simply has no visible content even without markers,
+      // so examine the line again without suppressing markers.
+      retryEmptyLine = false;
+      mLeft.mWidth = mLeft.mIntrinsicWidth;
+      mLeft.mActive = guessLeft = leftOverflow;
+      mRight.mWidth = mRight.mIntrinsicWidth;
+      mRight.mActive = guessRight = rightOverflow;
+      continue;
+    }
+    if (guessLeft == (mLeft.mActive && mLeft.IsNeeded()) &&
+        guessRight == (mRight.mActive && mRight.IsNeeded())) {
       break;
     } else {
       guessLeft = mLeft.IsNeeded();
       guessRight = mRight.IsNeeded();
       mLeft.Reset();
       mRight.Reset();
       aFramesToHide->Clear();
     }
     NS_ASSERTION(pass == 0, "2nd pass should never guess wrong");
   } while (++pass != 2);
-  if (!leftOverflow) {
+  if (!leftOverflow || !mLeft.mActive) {
     mLeft.Reset();
   }
-  if (!rightOverflow) {
+  if (!rightOverflow || !mRight.mActive) {
     mRight.Reset();
   }
 }
 
 void
 TextOverflow::ProcessLine(const nsDisplayListSet& aLists,
                           nsLineBox*              aLine)
 {
@@ -642,35 +689,35 @@ TextOverflow::CanHaveTextOverflow(nsDisp
 
 void
 TextOverflow::CreateMarkers(const nsLineBox* aLine,
                             bool             aCreateLeft,
                             bool             aCreateRight,
                             const nsRect&    aInsideMarkersArea) const
 {
   if (aCreateLeft) {
-    nsRect markerRect = nsRect(aInsideMarkersArea.x - mLeft.mWidth,
+    nsRect markerRect = nsRect(aInsideMarkersArea.x - mLeft.mIntrinsicWidth,
                                aLine->mBounds.y,
-                               mLeft.mWidth, aLine->mBounds.height);
+                               mLeft.mIntrinsicWidth, aLine->mBounds.height);
     markerRect += mBuilder->ToReferenceFrame(mBlock);
     nsDisplayItem* marker = new (mBuilder)
       nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
                                   aLine->GetAscent(), mLeft.mMarkerString);
     if (marker) {
       marker = ClipMarker(mBuilder, mBlock, marker,
                           mContentArea + mBuilder->ToReferenceFrame(mBlock),
                           &markerRect);
     }
     mMarkerList->AppendNewToTop(marker);
   }
 
   if (aCreateRight) {
     nsRect markerRect = nsRect(aInsideMarkersArea.XMost(),
                                aLine->mBounds.y,
-                               mRight.mWidth, aLine->mBounds.height);
+                               mRight.mIntrinsicWidth, aLine->mBounds.height);
     markerRect += mBuilder->ToReferenceFrame(mBlock);
     nsDisplayItem* marker = new (mBuilder)
       nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect,
                                   aLine->GetAscent(), mRight.mMarkerString);
     if (marker) {
       marker = ClipMarker(mBuilder, mBlock, marker,
                           mContentArea + mBuilder->ToReferenceFrame(mBlock),
                           &markerRect);
@@ -691,13 +738,14 @@ TextOverflow::Marker::SetupString(nsIFra
   nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm),
     nsLayoutUtils::FontSizeInflationFor(aFrame));
   rc->SetFont(fm);
 
   mMarkerString = mStyle->mType == NS_STYLE_TEXT_OVERFLOW_ELLIPSIS ?
                     GetEllipsis(fm) : mStyle->mString;
   mWidth = nsLayoutUtils::GetStringWidth(aFrame, rc, mMarkerString.get(),
                                          mMarkerString.Length());
+  mIntrinsicWidth = mWidth;
   mInitialized = true;
 }
 
 }  // namespace css
 }  // namespace mozilla
--- a/layout/generic/TextOverflow.h
+++ b/layout/generic/TextOverflow.h
@@ -93,16 +93,40 @@ class TextOverflow {
       }
     }
     nscoord Width() { return xmost - x; }
     nscoord x;
     nscoord xmost;
     bool mAssigned;
   };
 
+  struct InnerClipEdges {
+    InnerClipEdges() : mAssignedLeft(false), mAssignedRight(false) {}
+    void AccumulateLeft(const nsRect& aRect) {
+      if (NS_LIKELY(mAssignedLeft)) {
+        mLeft = NS_MAX(mLeft, aRect.X());
+      } else {
+        mLeft = aRect.X();
+        mAssignedLeft = true;
+      }
+    }
+    void AccumulateRight(const nsRect& aRect) {
+      if (NS_LIKELY(mAssignedRight)) {
+        mRight = NS_MIN(mRight, aRect.XMost());
+      } else {
+        mRight = aRect.XMost();
+        mAssignedRight = true;
+      }
+    }
+    nscoord mLeft;
+    nscoord mRight;
+    bool mAssignedLeft;
+    bool mAssignedRight;
+  };
+
   /**
    * Examines frames on the line to determine whether we should draw a left
    * and/or right marker, and if so, which frames should be completely hidden
    * and the bounds of what will be displayed between the markers.
    * @param aLine the line we're processing
    * @param aFramesToHide frames that should have their display items removed
    * @param aAlignmentEdges the outermost edges of all text and atomic
    *   inline-level frames that are inside the area between the markers
@@ -117,23 +141,26 @@ class TextOverflow {
    * @param aFrame the descendant frame of mBlock that we're analyzing
    * @param aContentArea the block's content area
    * @param aInsideMarkersArea the rectangle between the markers
    * @param aFramesToHide frames that should have their display items removed
    * @param aAlignmentEdges the outermost edges of all text and atomic
    *   inline-level frames that are inside the area between the markers
    * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic
    *   inline-level frame is visible between the marker edges
+   * @param aClippedMarkerEdges the innermost edges of all text and atomic
+   *   inline-level frames that are clipped by the current marker width
    */
   void ExamineFrameSubtree(nsIFrame*       aFrame,
                            const nsRect&   aContentArea,
                            const nsRect&   aInsideMarkersArea,
                            FrameHashtable* aFramesToHide,
                            AlignmentEdges* aAlignmentEdges,
-                           bool*           aFoundVisibleTextOrAtomic);
+                           bool*           aFoundVisibleTextOrAtomic,
+                           InnerClipEdges* aClippedMarkerEdges);
 
   /**
    * ExamineFrameSubtree calls this to analyze a frame against the hypothetical
    * marker edges (aInsideMarkersArea) for text frames and atomic inline-level
    * elements.  A text frame adds its extent inside aInsideMarkersArea where
    * grapheme clusters are fully visible.  An atomic adds its border box if
    * it's fully inside aInsideMarkersArea, otherwise the frame is added to
    * aFramesToHide.
@@ -141,23 +168,26 @@ class TextOverflow {
    * @param aFrameType aFrame's frame type
    * @param aInsideMarkersArea the rectangle between the markers
    * @param aFramesToHide frames that should have their display items removed
    * @param aAlignmentEdges the outermost edges of all text and atomic
    *   inline-level frames that are inside the area between the markers
    *                       inside aInsideMarkersArea
    * @param aFoundVisibleTextOrAtomic is set to true if a text or atomic
    *   inline-level frame is visible between the marker edges
+   * @param aClippedMarkerEdges the innermost edges of all text and atomic
+   *   inline-level frames that are clipped by the current marker width
    */
   void AnalyzeMarkerEdges(nsIFrame*       aFrame,
                           const nsIAtom*  aFrameType,
                           const nsRect&   aInsideMarkersArea,
                           FrameHashtable* aFramesToHide,
                           AlignmentEdges* aAlignmentEdges,
-                          bool*           aFoundVisibleTextOrAtomic);
+                          bool*           aFoundVisibleTextOrAtomic,
+                          InnerClipEdges* aClippedMarkerEdges);
 
   /**
    * Clip or remove items given the final marker edges. ("clip" here just means
    * assigning mLeftEdge/mRightEdge for any nsCharClipDisplayItem that needs it,
    * see nsDisplayList.h for a description of that item).
    * @param aFramesToHide remove display items for these frames
    * @param aInsideMarkersArea is the area inside the markers
    */
@@ -200,27 +230,30 @@ class TextOverflow {
 
     bool IsNeeded() const {
       return mHasOverflow;
     }
     void Reset() {
       mHasOverflow = false;
     }
 
+    // The current width of the marker, the range is [0 .. mIntrinsicWidth].
+    nscoord                        mWidth;
     // The intrinsic width of the marker string.
-    nscoord                        mWidth;
+    nscoord                        mIntrinsicWidth;
     // The marker text.
     nsString                       mMarkerString;
     // The style for this side.
     const nsStyleTextOverflowSide* mStyle;
     // True if there is visible overflowing inline content on this side.
     bool                           mHasOverflow;
     // True if mMarkerString and mWidth have been setup from style.
     bool                           mInitialized;
-    // True if the style is text-overflow:clip on this side.
+    // True if the style is text-overflow:clip on this side and the marker
+    // won't cause the line to become empty.
     bool                           mActive;
   };
 
   Marker mLeft;  // the horizontal left marker
   Marker mRight; // the horizontal right marker
 };
 
 } // namespace css
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/atomic-under-marker-ref.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<!--
+    Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/licenses/publicdomain/
+
+    Test: text-overflow with overflow where introducing a marker would cause
+          the line to have no visible text or atomic inline-level content --
+          then we should either suppress or clip the marker
+-->
+<html><head>
+<title>text-overflow: suppress or clip the marker when it hides all content</title>
+<style type="text/css">
+@font-face {
+  font-family: DejaVuSansMono;
+  src: url(../fonts/DejaVuSansMono.woff),url(DejaVuSansMono.woff);
+}
+html,body {
+    color:black; background-color:white; font-size:16px; padding:0; margin:0; font-family:DejaVuSansMono;
+}
+
+.test {
+  overflow:hidden;
+  width:100px;
+  white-space:nowrap;
+  margin:0 100px;
+}
+span {
+ width:97px;
+ display:inline-block;
+}
+s {
+ width:3px;
+ height:10px;
+ margin-left:-2px;
+ display:inline-block;
+ background:blue;
+}
+.rlo {
+  unicode-bidi: bidi-override; direction:rtl;
+}
+.lro {
+  unicode-bidi: bidi-override;
+}
+.rtl {
+  direction:rtl;
+}
+.ltr {
+  direction:ltr;
+}
+
+.t1 { }
+.t2 { }
+.t3 { margin-left:98px; padding-left:3px; }
+
+i {
+  display:inline-block;
+  width:2px; 
+  height:10px;
+  background:blue;
+}
+
+</style>
+
+</head><body>
+
+<div style="float:left;">
+<div class="test t1"><span>!</span>&#x2026;</div> <!-- atomic under marker -->
+<div class="test t1"><span>!</span>&#x2026;</div> <!-- atomic in padding -->
+<div class="test t1"><span>!</span>&#x2026;</div> <!-- atomic under marker and in padding -->
+<div class="test t2"><span>!</span>&#x2026;</div> <!-- atomic under marker -->
+<div class="test t2"><span>!</span>&#x2026;</div> <!-- atomic in padding -->
+<div class="test t2"><span>!</span>&#x2026;</div> <!-- atomic under marker and in padding -->
+
+<div class="test rtl t1"><span>!</span>&#x2026;</div> <!-- atomic under marker -->
+<div class="test rtl t1"><span>!</span>&#x2026;</div> <!-- atomic in padding -->
+<div class="test rtl t1"><span>!</span>&#x2026;</div> <!-- atomic under marker and in padding -->
+<div class="test t2"><x style="margin-left:3px">.|</style></div> <!-- atomic under marker -->
+<div class="test t3">g<i></i></div> <!-- atomic in padding -->
+<div class="test t2"><s style="margin-left:1px;"></s><i style="width:17px"></i></div> <!-- atomic under marker and in padding -->
+
+</div>
+
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text-overflow/atomic-under-marker.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<!--
+    Any copyright is dedicated to the Public Domain.
+    http://creativecommons.org/licenses/publicdomain/
+
+    Test: text-overflow with overflow where introducing a marker would cause
+          the line to have no visible text or atomic inline-level content --
+          then we should either suppress or clip the marker
+-->
+<html><head>
+<title>text-overflow: suppress or clip the marker when it hides all content</title>
+<style type="text/css">
+@font-face {
+  font-family: DejaVuSansMono;
+  src: url(../fonts/DejaVuSansMono.woff),url(DejaVuSansMono.woff);
+}
+html,body {
+    color:black; background-color:white; font-size:16px; padding:0; margin:0; font-family:DejaVuSansMono;
+}
+
+.test {
+  overflow:hidden;
+  width:100px;
+  white-space:nowrap;
+  padding:0 100px;
+}
+span {
+ width:97px;
+ display:inline-block;
+}
+s {
+ width:3px;
+ height:10px;
+ margin-left:-2px;
+ display:inline-block;
+ background:blue;
+}
+.rlo {
+  unicode-bidi: bidi-override; direction:rtl;
+}
+.lro {
+  unicode-bidi: bidi-override;
+}
+.rtl {
+  direction:rtl;
+}
+.ltr {
+  direction:ltr;
+}
+
+.t1 { text-overflow:ellipsis; }
+.t2 { text-overflow:"." ellipsis; }
+.t3 { text-overflow:"long" ellipsis; }
+
+i {
+  display:inline-block;
+  width:2px; 
+  height:10px;
+  background:blue;
+}
+
+</style>
+
+</head><body>
+
+<div style="float:left;">
+<div class="test t1"><span>!</span><i></i>||</div> <!-- atomic under marker -->
+<div class="test t1"><span>!</span>||<i></i></div> <!-- atomic in padding -->
+<div class="test t1"><span>!</span><i style="width:20px"></i></div> <!-- atomic under marker and in padding -->
+<div class="test t2"><span>!</span><i></i>||</div> <!-- atomic under marker -->
+<div class="test t2"><span>!</span>||<i></i></div> <!-- atomic in padding -->
+<div class="test t2"><span>!</span><i style="width:20px"></i></div> <!-- atomic under marker and in padding -->
+
+<div class="test rtl t1"><span>!</span><i></i>||</div> <!-- atomic under marker -->
+<div class="test rtl t1"><span>!</span>||<i></i></div> <!-- atomic in padding -->
+<div class="test rtl t1"><span>!</span><i style="width:20px"></i></div> <!-- atomic under marker and in padding -->
+<div class="test t2"><s></s><i></i>||</div> <!-- atomic under marker -->
+<div class="test t3"><s></s>|<i></i></div> <!-- atomic in padding -->
+<div class="test t2"><s></s><i style="width:20px"></i></div> <!-- atomic under marker and in padding -->
+
+</div>
+
+
+</body>
+</html>
--- a/layout/reftests/text-overflow/clipped-elements-ref.html
+++ b/layout/reftests/text-overflow/clipped-elements-ref.html
@@ -20,55 +20,55 @@ input,select,fieldset,textarea,hr,span {
  padding:0 40px;
 }
 iframe,img { width: 80px; height:10px; }
 select[size="4"],iframe,textarea,hr { height:10px; margin:0; }
 </style></head>
 <body>
 
 <div style="position:absolute; top:0; left:0;">
-  <div class="test">&#x2026;</div>
-  <div class="test">&#x2026;</div>
+  <div class="test"><fieldset style="display:inline;"></fieldset></div>
+  <div class="test"><fieldset style="display:inline-block;"></fieldset></div>
   <div class="test"><fieldset style="display:block;"></fieldset></div>
 
-  <div class="test">&#x2026;</div>
-  <div class="test">&#x2026;</div>
+  <div class="test"><input type="button"></div>
+  <div class="test"><input type="button" style="display:inline-block;"></div>
   <div class="test"><input type="button" style="display:list-item;"></div>
   <div class="test"><input type="button" style="display:block;"></div>
 
-  <div class="test">&#x2026;</div>
-  <div class="test">&#x2026;</div>
+  <div class="test"><input type="text"></div>
+  <div class="test"><input type="text" style="display:inline-block;"></div>
   <div class="test"><input type="text" style="display:block;"></div>
 
-  <div class="test">&#x2026;</div>
-  <div class="test">&#x2026;</div>
+  <div class="test"><textarea></textarea></div>
+  <div class="test"><textarea style="display:inline-block;"></textarea></div>
   <div class="test"><textarea style="display:block;"></textarea></div>
 
-  <div class="test">&#x2026;</div>
+  <div class="test"><select size="1" style=""><option>&nbsp;<option>&nbsp;</select></div>
   <div class="test"><select size="1" style="display:block;"><option>&nbsp;<option>&nbsp;</select></div>
 
-  <div class="test">&#x2026;</div>
+  <div class="test"><select size="4" style=""><option>&nbsp;<option>&nbsp;</select></div>
   <div class="test"><select size="4" style="display:block;"><option>&nbsp;<option>&nbsp;</select></div>
 </div>
 
 <div style="position:absolute; top:0; left:100px;">
-  <div class="test">&#x2026;</div>
+  <div class="test"><iframe src="about:blank" style=""></iframe></div>
   <div class="test"><iframe src="about:blank" style="display:block"></iframe></div>
 
   <div class="test" style="text-align:left">&nbsp;&#x2026;</div>
   <div class="test" style="text-align:left">&nbsp;&#x2026;</div>
   <div class="test"><img style="display:block;" src="../image/big.png"></div>
 
   <div class="test"><x style="display:inline-block; height:100px; width:100%; background:lime">&#x2026;</x></div>
-  <div class="test">&#x2026;</div>
+  <div class="test"><hr style="display:inline-block;vertical-align:top"></div>
   <div class="test"><hr style="display:block;"></div>
 
   <div class="test">&#x2026;</div>
-  <div class="test">&#x2026;</div>
-  <div class="test">&#x2026;</div>
+  <div class="test"><span style="display:inline-block;"></span></div>
+  <div class="test"></div>
   <div class="test"><span style="display:list-item;"></span></div>
   <div class="test"><span style="display:block;"></span></div>
   <div class="test rel">1<span style="position:relative;"></span></div>
   <div class="test rel">2<span style="position:absolute;"></span></div>
   <div class="test rel">3</div>
   <div class="test rel">4</div>
   <div class="test"><span style="float:left"></span></div>
   <div class="test"><span style="float:left; margin-left:-10px"></span></div>
--- a/layout/reftests/text-overflow/ellipsis-font-fallback-ref.html
+++ b/layout/reftests/text-overflow/ellipsis-font-fallback-ref.html
@@ -127,23 +127,23 @@ m {
 
 <!-- start marker -->
 <div id="test3a"><div class="s3 ltr"><div class="p o"><span class="c lro">&nbsp;X<m>...</m></span></div></div></div>
 <div id="test3b"><div class="s3 rtl"><div class="p o"><span class="c lro"><m>...</m>X&nbsp;</span></div></div></div>
 <div id="test3c"><div class="s3 ltr"><div class="p o"><span class="c rlo"><m>...</m>X&nbsp;</span></div></div></div>
 <div id="test3d"><div class="s3 rtl"><div class="p o"><span class="c rlo">&nbsp;X<m>...</m></span></div></div></div>
 
 <!-- start + end marker, no characters fit, marker is clipped -->
-<div id="test6a"><div class="s6 ltr"><div class="p o"><span class=" lro"><m>...</m></span></div></div></div>
-<div id="test6b"><div class="s6 ltr"><div class="p o"><span class=" lro"><m>...</m></span></div></div></div>
-<div id="test6c"><div class="s6 ltr"><div class="p o"><span class=" lro"><m>...</m></span></div></div></div>
-<div id="test6d"><div class="s6 ltr"><div class="p o"><span class=" lro"><m>...</m></span></div></div></div>
+<div id="test6a"><div class="s6 ltr"><div class="p o"><span class="c lro">XXXXXXXXXXXX</span></div></div></div>
+<div id="test6b"><div class="s6 rtl"><div class="p o"><span class="c lro">XXXXXXXXXXXX</span></div></div></div>
+<div id="test6c"><div class="s6 ltr"><div class="p o"><span class="c rlo">XXXXXXXXXXXX</span></div></div></div>
+<div id="test6d"><div class="s6 rtl"><div class="p o"><span class="c rlo">XXXXXXXXXXXX</span></div></div></div>
 
 <!-- start marker, all characters overlapped by marker -->
-<div id="test7a"><div class="s7 ltr"><div class="p o"><span class=" lro"><m>...</m>&nbsp;</span></div></div></div>
-<div id="test7b"><div class="s7 rtl"><div class="p o"><span class=" lro">&nbsp;<m>...</m></span></div></div></div>
-<div id="test7c"><div class="s7 ltr"><div class="p o"><span class=" rlo">&nbsp;<m>...</m></span></div></div></div>
-<div id="test7d"><div class="s7 rtl"><div class="p o"><span class=" rlo"><m>...</m>&nbsp;</span></div></div></div>
+<div id="test7a"><div class="s7 ltr"><div class="p o"><span class="c lro">XXX</span></div></div></div>
+<div id="test7b"><div class="s7 rtl"><div class="p o"><span class="c lro">XXX</span></div></div></div>
+<div id="test7c"><div class="s7 ltr"><div class="p o"><span class="c rlo">XXX</span></div></div></div>
+<div id="test7d"><div class="s7 rtl"><div class="p o"><span class="c rlo">XXX</span></div></div></div>
 
 </div>
 
 </body>
 </html>
--- a/layout/reftests/text-overflow/marker-basic-ref.html
+++ b/layout/reftests/text-overflow/marker-basic-ref.html
@@ -204,61 +204,27 @@ x1 m { position:absolute; right:0; font-
 </div></div>
 </div>
 <div id="test3d">
 <div class="s a"><div class="p rel">
 <div class="abs0" style="text-align:right; left:auto; right:0;"><span class="cr a">&nbsp;&nbsp;&nbsp;</span><m>&#x2026;</m></div>
 </div></div>
 </div>
 
-<div id="test4a">
-<div class="s a"><div class="p rel">
-<div class="abs0" style="text-align:left"><m>&#x2026;</m><span class="cl a">&nbsp;</span></div>
-</div></div>
-</div>
-<div id="test4b">
-<div class="s a"><div class="p rel">
-<div class="abs0" style="text-align:left"><m>&#x2026;</m><span class="cl a">&nbsp;</span></div>
-</div></div>
-</div>
-<div id="test4c">
-<div class="s a"><div class="p rel">
-<div class="abs0" style="text-align:right; left:auto; right:0;"><span class="cr a">&nbsp;&nbsp;&nbsp;</span><m>&#x2026;</m></div>
-</div></div>
-</div>
-<div id="test4d">
-<div class="s a"><div class="p rel">
-<div class="abs0" style="text-align:right; left:auto; right:0;"><span class="cr a">&nbsp;&nbsp;&nbsp;</span><m>&#x2026;</m></div>
-</div></div>
-</div>
+<!-- start marker, marker partly overlaps image, nothing to align with -->
+<div id="test4a"><div class="s a"><div class="p o ltr"><span class="cl a"><img class="overlap1" src="../image/big.png"></span></div></div></div>
+<div id="test4b"><div class="s a"><div class="p o rtl l"><span class="cl a"><img class="overlap1" src="../image/big.png"></span></div></div></div>
+<div id="test4c"><div class="s a"><div class="p o ltr r"><span class="cr a"><img class="overlap1" src="../image/big.png"></span></div></div></div>
+<div id="test4d"><div class="s a"><div class="p o rtl"><span class="cr a"><img class="overlap1" src="../image/big.png"></span></div></div></div>
 
-<div id="test5a">
-<div class="s a"><div class="p rel">
-<div class="abs0" style="text-align:right; left:auto; right:0;"><span class="cr a">&nbsp;</span><m>&#x2026;</m></div>
-<div class="abs0" style="text-align:left"><m>&#x2026;</m><span class="cl a">&nbsp;</span></div>
-</div></div>
-</div>
-<div id="test5b">
-<div class="s a"><div class="p rel">
-<div class="abs0" style="text-align:right; left:auto; right:0;"><span class="cr a">&nbsp;</span><m>&#x2026;</m></div>
-<div class="abs0" style="text-align:left"><m>&#x2026;</m><span class="cl a">&nbsp;</span></div>
-</div></div>
-</div>
-<div id="test5c">
-<div class="s a"><div class="p rel">
-<div class="abs0" style="text-align:right; left:auto; right:0;"><span class="cr a">&nbsp;</span><m>&#x2026;</m></div>
-<div class="abs0" style="text-align:left"><m>&#x2026;</m><span class="cl a">&nbsp;</span></div>
-</div></div>
-</div>
-<div id="test5d">
-<div class="s a"><div class="p rel">
-<div class="abs0" style="text-align:right; left:auto; right:0;"><span class="cr a">&nbsp;</span><m>&#x2026;</m></div>
-<div class="abs0" style="text-align:left"><m>&#x2026;</m><span class="cl a">&nbsp;</span></div>
-</div></div>
-</div>
+<!-- start marker + end, marker partly overlaps image, nothing to align with -->
+<div id="test5a"><div class="s a"><div class="p o ltr"><span class="cl a"><img class="overlap2" src="../image/big.png"></span></div></div></div>
+<div id="test5b"><div class="s a"><div class="p o rtl l"><span class="cl a"><img class="overlap2" src="../image/big.png"></span></div></div></div>
+<div id="test5c"><div class="s a"><div class="p o ltr r"><span class="cr a"><img class="overlap2" src="../image/big.png"></span></div></div></div>
+<div id="test5d"><div class="s a"><div class="p o rtl"><span class="cr a"><img class="overlap2" src="../image/big.png"></span></div></div></div>
 
 <div id="test6a">
 <div class="s a"><div class="p rel">
 <div class="abs0"><span class="cr a">&nbsp;&nbsp;&nbsp;</span><img class="a overlap1" src="../image/big.png"></div>
 <div class="abs0"><span class="cr a">&nbsp;&nbsp;&nbsp;<m style="position:absolute; right:0; bottom:0;"><m0 style="font-size:16px">&#x2026;</m0></m></span></span></div>
 </div></div>
 </div>
 <div id="test6b">
--- a/layout/reftests/text-overflow/marker-string-ref.html
+++ b/layout/reftests/text-overflow/marker-string-ref.html
@@ -19,16 +19,19 @@ html,body {
 .test {
   overflow:hidden;
   width:20ch;
   height:3em;
   white-space:nowrap;
   margin-left:2em;
   position:relative;
 }
+span {
+  margin: 0 -0.5ch;
+}
 m {
   margin: 0;
   position:relative;
 }
 mr {
   position:absolute;
   right:0;
 }
@@ -49,18 +52,18 @@ mr {
 
 </style>
 
 </head><body>
 
 
 <div class="test t1"><m>&nbsp;x</m></div>
 <div class="test rtl t1"><m>&nbsp;x</m></div>
-<div class="test t2"><m>Hello World</m></div>
-<div class="test rtl t2"><m>Hello World</m></div>
+<div class="test t2"><span>xx</span></div>
+<div class="test rtl t2"><span>xx</span></div>
 
-<div class="test t2" style="width:3ch"><m>Hel</m></div>
-<div class="test rtl t2" style="width:3ch"><m>Hel</m></div>
+<div class="test t2" style="width:3ch"><span>xx</span></div>
+<div class="test rtl t2" style="width:3ch"><span>xx</span></div>
 <div class="test"><m>X</m><mr>X</mr></div>
 <div class="test"><m>X</m><mr>X</mr></div>
 
 </body>
 </html>
--- a/layout/reftests/text-overflow/quirks-decorations-ref.html
+++ b/layout/reftests/text-overflow/quirks-decorations-ref.html
@@ -59,15 +59,15 @@ span {
 m { font-size:20px; color:blue; }
 
 </style>
 
 </head><body>
 
 <div class="test t1"><span><span class="xspan">0123&nbsp;56789012</span><m>&#x2026;</m></span></div>
 <div class="test rtl t1"><span><span class="xspan">1&nbsp;56789012345</span><m>&#x2026;</m></span></div>
-<div class="test rtl t2"><span><m>&#x2026;</m><span style="visibility:hidden">&nbsp;</span></span></div>
-<div class="test rtl t3"><span><m>&#x2026;</m><span style="visibility:hidden">&nbsp;</span></span></div>
-<div class="test t4"><span><m>&#x2026;</m><span style="visibility:hidden">&nbsp;</span></span></div>
+<div class="test t2"><span class="xspan">xxxx<m>x</m></span></div>
+<div class="test t3"><span class="xspan">x<m>x</m></span></div>
+<div class="test t4"><span class="xspan">x<m>x</m></span></div>
 
 
 </body>
 </html>
--- a/layout/reftests/text-overflow/reftest.list
+++ b/layout/reftests/text-overflow/reftest.list
@@ -11,12 +11,13 @@ fails-if(Android&&layersOpenGL) HTTP(..)
 HTTP(..) == quirks-decorations.html quirks-decorations-ref.html
 HTTP(..) == quirks-line-height.html quirks-line-height-ref.html
 HTTP(..) == standards-decorations.html standards-decorations-ref.html
 HTTP(..) == standards-line-height.html standards-line-height-ref.html
 HTTP(..) == selection.html selection-ref.html
 HTTP(..) == marker-shadow.html marker-shadow-ref.html
 == aligned-baseline.html aligned-baseline-ref.html
 skip-if(Android) == clipped-elements.html clipped-elements-ref.html
-== theme-overflow.html theme-overflow-ref.html
+HTTP(..) == theme-overflow.html theme-overflow-ref.html
 HTTP(..) == table-cell.html table-cell-ref.html
 HTTP(..) == two-value-syntax.html two-value-syntax-ref.html
 HTTP(..) == single-value.html single-value-ref.html 
+HTTP(..) == atomic-under-marker.html atomic-under-marker-ref.html
--- a/layout/reftests/text-overflow/standards-decorations-ref.html
+++ b/layout/reftests/text-overflow/standards-decorations-ref.html
@@ -58,15 +58,15 @@ span {
 m { font-size:20px; color:blue; }
 
 </style>
 
 </head><body>
 
 <div class="test t1"><span><span class="xspan">0123&nbsp;56789012</span><m>&#x2026;</m></span></div>
 <div class="test rtl t1"><span><span class="xspan">1&nbsp;56789012345</span><m>&#x2026;</m></span></div>
-<div class="test rtl t2" style="color:black">&#x2026;&nbsp;</div>
-<div class="test rtl t3"><span><m>&#x2026;</m><span style="visibility:hidden">&nbsp;</span></span></div>
-<div class="test t4"><span><m>&#x2026;</m><span style="visibility:hidden">&nbsp;</span></span></div>
+<div class="test t2" style="color:black"><span class="xspan">xxxx</span></div>
+<div class="test t3"><span class="xspan">x</span></div>
+<div class="test t4"><span class="xspan">x</span></div>
 
 
 </body>
 </html>
--- a/layout/reftests/text-overflow/theme-overflow-ref.html
+++ b/layout/reftests/text-overflow/theme-overflow-ref.html
@@ -12,50 +12,50 @@
     .r {
       direction:rtl;
     }
 
 .x0 { margin:0px;}
 .x1 { margin:1px;}
 .x2 { margin:2px;}
 .x3 { margin:3px;}
-.x4 { margin-left:-1px; visibility:hidden;}
+.x4 { margin-left:-1px; }
 .r .x4 { margin-right:-1px;}
 
 f { float:left; width:1px; height:1px; margin-left:-100px; }
 .r f { float:right; width:1px; height:1px; margin-right:-100px; }
   </style>
 </head>
 <body>
 <p>
     <input type="checkbox" class="x0"> 0<f></f><br>
     <input type="checkbox" class="x1"> 1<f></f><br>
     <input type="checkbox" class="x2"> 2<f></f><br>
     <input type="checkbox" class="x3"> 3<f></f><br>
-    &#x2026;<input type="checkbox" class="x4"><f></f><br>
+    <input type="checkbox" class="x4"><f></f><br>
 </p>
 
 <p>
     <input type="radio" class="x0"> 0<f></f><br>
     <input type="radio" class="x1"> 1<f></f><br>
     <input type="radio" class="x2"> 2<f></f><br>
     <input type="radio" class="x3"> 3<f></f><br>
-    &#x2026;<input type="radio" class="x4"><f></f><br>
+    <input type="radio" class="x4"><f></f><br>
 </p>
 
 <p class="r">
     <input type="checkbox" class="x0"> 0<f></f><br>
     <input type="checkbox" class="x1"> 1<f></f><br>
     <input type="checkbox" class="x2"> 2<f></f><br>
     <input type="checkbox" class="x3"> 3<f></f><br>
-    &#x2026;<input type="radio" class="x4"><f></f><br>
+    <input type="checkbox" class="x4"><f></f><br>
 </p>
 
 <p class="r">
     <input type="radio" class="x0"> 0<f></f><br>
     <input type="radio" class="x1"> 1<f></f><br>
     <input type="radio" class="x2"> 2<f></f><br>
     <input type="radio" class="x3"> 3<f></f><br>
-    &#x2026;<input type="radio" class="x4"><f></f><br>
+    <input type="radio" class="x4"><f></f><br>
 </p>
 
 </body>
 </html>