Bug 480452. Make {ib} splits behave the same way with regard to margins, borders, and padding as inline frames that just have a line-break happening inside them do. r+sr=dbaron
authorBoris Zbarsky <bzbarsky@mit.edu>
Mon, 11 May 2009 22:04:58 -0400
changeset 28217 de33f2e0b22fac8f1dfa8ba76aa32266735001f2
parent 28216 347ad022cc332025fa03ebd9c66c69c22f9d6420
child 28218 f662f710fead23ad2ade5308acf6e1b3b528020a
push idunknown
push userunknown
push dateunknown
bugs480452
milestone1.9.2a1pre
Bug 480452. Make {ib} splits behave the same way with regard to margins, borders, and padding as inline frames that just have a line-break happening inside them do. r+sr=dbaron
layout/base/nsBidiPresUtils.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsLayoutUtils.h
layout/generic/nsInlineFrame.cpp
layout/generic/nsLineLayout.cpp
layout/reftests/inline-borderpadding/left-ltr-ref.html
layout/reftests/inline-borderpadding/left-rtl-ref.html
layout/reftests/inline-borderpadding/ltr-basic.html
layout/reftests/inline-borderpadding/ltr-ib.html
layout/reftests/inline-borderpadding/ltr-span-only-ib.html
layout/reftests/inline-borderpadding/ltr-span-only.html
layout/reftests/inline-borderpadding/reftest.list
layout/reftests/inline-borderpadding/right-ltr-ref.html
layout/reftests/inline-borderpadding/right-rtl-ref.html
layout/reftests/inline-borderpadding/rtl-basic.html
layout/reftests/inline-borderpadding/rtl-ib.html
layout/reftests/inline-borderpadding/rtl-span-only-ib.html
layout/reftests/inline-borderpadding/rtl-span-only.html
layout/reftests/reftest.list
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -837,16 +837,39 @@ nsBidiPresUtils::IsLeftOrRightMost(nsIFr
     aIsLeftMost = PR_FALSE;
     firstFrameState = aContinuationStates->GetEntry(frameState->mFirstVisualFrame);
   }
 
   aIsRightMost = (firstFrameState->mFrameCount == 1) &&
                  (isLTR ? !firstFrameState->mHasContOnNextLines
                         : !firstFrameState->mHasContOnPrevLines);
 
+  if ((aIsLeftMost || aIsRightMost) &&
+      (aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
+    // For ib splits, don't treat the first part as endmost or the
+    // last part as startmost.
+    if (nsLayoutUtils::FrameIsInFirstPartOfIBSplit(aFrame)) {
+      // We are not endmost
+      if (isLTR) {
+        aIsRightMost = PR_FALSE;
+      } else {
+        aIsLeftMost = PR_FALSE;
+      }
+    } else {
+      NS_ASSERTION(nsLayoutUtils::FrameIsInLastPartOfIBSplit(aFrame),
+                   "How did that happen?");
+      // We are not startmost
+      if (isLTR) {
+        aIsLeftMost = PR_FALSE;
+      } else {
+        aIsRightMost = PR_FALSE;
+      }
+    }
+  }
+
   // Reduce number of remaining frames of the continuation chain on the line.
   firstFrameState->mFrameCount--;
 }
 
 void
 nsBidiPresUtils::RepositionFrame(nsIFrame*              aFrame,
                                  PRBool                 aIsOddLevel,
                                  nscoord&               aLeft,
@@ -959,17 +982,18 @@ nsBidiPresUtils::RepositionInlineFrames(
   const nsStyleVisibility* vis = aFirstChild->GetStyleVisibility();
   PRBool isLTR = (NS_STYLE_DIRECTION_LTR == vis->mDirection);
   nscoord leftSpace = 0;
 
   // This method is called from nsBlockFrame::PlaceLine via the call to
   // bidiUtils->ReorderFrames, so this is guaranteed to be after the inlines
   // have been reflowed, which is required for GetUsedMargin/Border/Padding
   nsMargin margin = aFirstChild->GetUsedMargin();
-  if (!aFirstChild->GetPrevContinuation())
+  if (!aFirstChild->GetPrevContinuation() &&
+      !nsLayoutUtils::FrameIsInLastPartOfIBSplit(aFirstChild))
     leftSpace = isLTR ? margin.left : margin.right;
 
   nscoord left = aFirstChild->GetPosition().x - leftSpace;
   nsIFrame* frame;
   PRInt32 count = mVisualFrames.Length();
   PRInt32 index;
   nsContinuationStates continuationStates;
 
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -494,19 +494,19 @@ SetFrameIsSpecial(nsIFrame* aFrame, nsIF
   NS_PRECONDITION(aFrame, "bad args!");
 
   // Mark the frame and all of its siblings as "special".
   for (nsIFrame* frame = aFrame; frame != nsnull; frame = frame->GetNextContinuation()) {
     frame->AddStateBits(NS_FRAME_IS_SPECIAL);
   }
 
   if (aSpecialSibling) {
-    // We should be the first-in-flow
-    NS_ASSERTION(!aFrame->GetPrevInFlow(),
-                 "assigning special sibling to other than first-in-flow!");
+    // We should be the first continuation
+    NS_ASSERTION(!aFrame->GetPrevContinuation(),
+                 "assigning special sibling to other than first continuation!");
 
     // Store the "special sibling" (if we were given one) with the
     // first frame in the flow.
     aFrame->SetProperty(nsGkAtoms::IBSplitSpecialSibling, aSpecialSibling);
   }
 }
 
 static nsIFrame*
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -999,16 +999,36 @@ public:
    */
   static PRBool sDisableGetUsedXAssertions;
 
   /**
    * Returns the text fragment, which aFrame should use for printing.
    * @param aFrame The nsIFrame object, which uses text fragment data.
    */
   static nsTextFragment* GetTextFragmentForPrinting(const nsIFrame* aFrame);
+
+  /**
+   * Return whether aFrame is an inline frame in the first part of an {ib}
+   * split.
+   */
+  static PRBool FrameIsInFirstPartOfIBSplit(const nsIFrame* aFrame) {
+    return (aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL) &&
+      !aFrame->GetFirstContinuation()->
+        GetProperty(nsGkAtoms::IBSplitSpecialPrevSibling);
+  }
+
+  /**
+   * Return whether aFrame is an inline frame in the last part of an {ib}
+   * split.
+   */
+  static PRBool FrameIsInLastPartOfIBSplit(const nsIFrame* aFrame) {
+    return (aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL) &&
+      !aFrame->GetFirstContinuation()->
+        GetProperty(nsGkAtoms::IBSplitSpecialSibling);
+  }
 };
 
 class nsAutoDisableGetUsedXAssertions
 {
 public:
   nsAutoDisableGetUsedXAssertions()
     : mOldValue(nsLayoutUtils::sDisableGetUsedXAssertions)
   {
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -1,8 +1,9 @@
+
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
@@ -405,17 +406,20 @@ nsInlineFrame::ReflowFrames(nsPresContex
                             nsReflowStatus& aStatus)
 {
   nsresult rv = NS_OK;
   aStatus = NS_FRAME_COMPLETE;
 
   nsLineLayout* lineLayout = aReflowState.mLineLayout;
   PRBool ltr = (NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection);
   nscoord leftEdge = 0;
-  if (nsnull == GetPrevContinuation()) {
+  // Don't offset by our start borderpadding if we have a prev continuation or
+  // if we're in the last part of an {ib} split.
+  if (!GetPrevContinuation() &&
+      !nsLayoutUtils::FrameIsInLastPartOfIBSplit(this)) {
     leftEdge = ltr ? aReflowState.mComputedBorderPadding.left
                    : aReflowState.mComputedBorderPadding.right;
   }
   nscoord availableWidth = aReflowState.availableWidth;
   NS_ASSERTION(availableWidth != NS_UNCONSTRAINEDSIZE,
                "should no longer use available widths");
   // Subtract off left and right border+padding from availableWidth
   availableWidth -= leftEdge;
@@ -552,22 +556,38 @@ nsInlineFrame::ReflowFrames(nsPresContex
   // sure that we don't either.
   //
   // Note: CSS demands that empty inline elements still affect the
   // line-height calculations. However, continuations of an inline
   // that are empty we force to empty so that things like collapsed
   // whitespace in an inline element don't affect the line-height.
   aMetrics.width = lineLayout->EndSpan(this);
 
-  // Compute final width
-  if (nsnull == GetPrevContinuation()) {
+  // Compute final width.
+
+  // Make sure to not include our start border and padding if we have a prev
+  // continuation or if we're in the last part of an {ib} split.
+  if (!GetPrevContinuation() &&
+      !nsLayoutUtils::FrameIsInLastPartOfIBSplit(this)) {
     aMetrics.width += ltr ? aReflowState.mComputedBorderPadding.left
                           : aReflowState.mComputedBorderPadding.right;
   }
-  if (NS_FRAME_IS_COMPLETE(aStatus) && (!GetNextContinuation() || GetNextInFlow())) {
+
+  /*
+   * We want to only apply the end border and padding if we're the last
+   * continuation and not in the first part of an {ib} split.  To be the last
+   * continuation we have to be complete (so that we won't get a next-in-flow)
+   * and have no non-fluid continuations.
+   *
+   * FIXME the check for non-fluid continuations is not quite correct in the
+   * code (though the comment above describes it correctly); see bug 492469.
+   */
+  if (NS_FRAME_IS_COMPLETE(aStatus) &&
+      (!GetNextContinuation() || GetNextInFlow()) &&
+      !nsLayoutUtils::FrameIsInFirstPartOfIBSplit(this)) {
     aMetrics.width += ltr ? aReflowState.mComputedBorderPadding.right
                           : aReflowState.mComputedBorderPadding.left;
   }
 
   nsLayoutUtils::SetFontFromStyle(aReflowState.rendContext, mStyleContext);
   nsCOMPtr<nsIFontMetrics> fm;
   aReflowState.rendContext->GetFontMetrics(*getter_AddRefs(fm));
 
@@ -786,16 +806,38 @@ nsInlineFrame::GetSkipSides() const
       // border edge.
       skip |= 1 << NS_SIDE_RIGHT;
     }
     else {
       // If the next continuation is empty, then go ahead and let our right
       // edge border render.
     }
   }
+
+  if (GetStateBits() & NS_FRAME_IS_SPECIAL) {
+    // The first part of an {ib} split should always skip the "end" side (as
+    // determined by this frame's direction) and the last part of such a split
+    // should alwas skip the "start" side.  But figuring out which part of the
+    // split we are involves getting our first continuation, which might be
+    // expensive.  So don't bother if we already have the relevant bits set.
+    PRBool ltr = (NS_STYLE_DIRECTION_LTR == GetStyleVisibility()->mDirection);
+    PRIntn startBit = (1 << (ltr ? NS_SIDE_LEFT : NS_SIDE_RIGHT));
+    PRIntn endBit = (1 << (ltr ? NS_SIDE_RIGHT : NS_SIDE_LEFT));
+    if (((startBit | endBit) & skip) != (startBit | endBit)) {
+      // We're missing one of the skip bits, so check whether we need to set it.
+      if (nsLayoutUtils::FrameIsInFirstPartOfIBSplit(this)) {
+        skip |= endBit;
+      } else {
+        NS_ASSERTION(nsLayoutUtils::FrameIsInLastPartOfIBSplit(this),
+                     "How did that happen?");
+        skip |= startBit;
+      }
+    }
+  }
+
   return skip;
 }
 
 #ifdef ACCESSIBILITY
 NS_IMETHODIMP nsInlineFrame::GetAccessible(nsIAccessible** aAccessible)
 {
   // Broken image accessibles are created here, because layout
   // replaces the image or image control frame with an inline frame
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -1084,18 +1084,23 @@ nsLineLayout::ApplyStartMargin(PerFrameD
 {
   NS_ASSERTION(aReflowState.mStyleDisplay->mFloats == NS_STYLE_FLOAT_NONE,
                "How'd we get a floated inline frame? "
                "The frame ctor should've dealt with this.");
 
   // XXXwaterson probably not the right way to get this; e.g., embeddings, etc.
   PRBool ltr = (NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection);
 
-  // Only apply start-margin on the first-in flow for inline frames
-  if (pfd->mFrame->GetPrevContinuation()) {
+  // Only apply start-margin on the first-in flow for inline frames,
+  // and make sure to not apply it to the last part of an ib split.
+  // Note that the ib special sibling annotations only live on the
+  // first continuation, but we don't want to apply the start margin
+  // for later continuations anyway.
+  if (pfd->mFrame->GetPrevContinuation() ||
+      nsLayoutUtils::FrameIsInLastPartOfIBSplit(pfd->mFrame)) {
     // Zero this out so that when we compute the max-element-width of
     // the frame we will properly avoid adding in the starting margin.
     if (ltr)
       pfd->mMargin.left = 0;
     else
       pfd->mMargin.right = 0;
   }
   else {
@@ -1151,21 +1156,33 @@ nsLineLayout::CanPlaceFrame(PerFrameData
   if (0 != pfd->mBounds.width) {
     NS_ASSERTION(aReflowState.mStyleDisplay->mFloats == NS_STYLE_FLOAT_NONE,
                  "How'd we get a floated inline frame? "
                  "The frame ctor should've dealt with this.");
 
     // XXXwaterson this is probably not exactly right; e.g., embeddings, etc.
     PRBool ltr = (NS_STYLE_DIRECTION_LTR == aReflowState.mStyleVisibility->mDirection);
 
-    if ((NS_FRAME_IS_NOT_COMPLETE(aStatus) || (pfd->mFrame->GetNextContinuation() && !pfd->mFrame->GetNextInFlow())) 
+    /*
+     * We want to only apply the end margin if we're the last continuation and
+     * not in the first part of an {ib} split.  In all other cases we want to
+     * zero it out.  That means zeroing it out if any of these conditions hold:
+     * 1) The frame is not complete (in this case it will get a next-in-flow)
+     * 2) The frame is complete but has a non-fluid continuation.  Note that if
+     *    it has a fluid continuation, that continuation will get destroyed
+     *    later, so we don't want to drop the end-margin in that case.
+     *    // FIXME: bug 492469
+     * 3) The frame is in the first part of an {ib} split.
+     *
+     * However, none of that applies if this is a letter frame (XXXbz why?)
+     */
+    if ((NS_FRAME_IS_NOT_COMPLETE(aStatus) ||
+         (pfd->mFrame->GetNextContinuation() && !pfd->mFrame->GetNextInFlow()) ||
+         nsLayoutUtils::FrameIsInFirstPartOfIBSplit(pfd->mFrame))
         && !pfd->GetFlag(PFD_ISLETTERFRAME)) {
-      // Only apply end margin for the last-in-flow. Zero this out so
-      // that when we compute the max-element-width of the frame we
-      // will properly avoid adding in the end margin.
       if (ltr)
         pfd->mMargin.right = 0;
       else
         pfd->mMargin.left = 0;
     }
   }
   else {
     // Don't apply margin to empty frames.
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/left-ltr-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div>
+      <span style="border: 2px solid; border-right-style: none; padding-left: 5px; margin-left: 30px;">One</span>
+      <br>
+      <span style="border: 2px solid; border-left-style: none; padding-right: 10px; margin-right: 60px;">Two</span>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/left-rtl-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div>
+      <span style="border: 2px solid; border-left-style: none; padding-right: 10px; margin-right: 60px;">One</span>
+      <br>
+      <span style="border: 2px solid; border-right-style: none; padding-left: 5px; margin-left: 30px;">Two</span>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/ltr-basic.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .r { direction: rtl; }
+      .l { direction: ltr; }
+      span { border: 2px solid; padding: 0 10px 0 5px; margin: 0 60px 0 30px; }
+    </style>
+  </head>
+  <body>
+    <div><span>One<br>Two</span></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/ltr-ib.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .r { direction: rtl; }
+      .l { direction: ltr; }
+      span { border: 2px solid; padding: 0 10px 0 5px; margin: 0 60px 0 30px; }
+    </style>
+  </head>
+  <body>
+    <div><span>One<div></div>Two</span></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/ltr-span-only-ib.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .r { direction: rtl; }
+      .l { direction: ltr; }
+      span { border: 2px solid; padding: 0 10px 0 5px; margin: 0 60px 0 30px; }
+    </style>
+  </head>
+  <body>
+    <div class="r"><span class="l">One<div></div>Two</span></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/ltr-span-only.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .r { direction: rtl; }
+      .l { direction: ltr; }
+      span { border: 2px solid; padding: 0 10px 0 5px; margin: 0 60px 0 30px; }
+    </style>
+  </head>
+  <body>
+    <div class="r"><span class="l">One<br>Two</span></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/reftest.list
@@ -0,0 +1,8 @@
+== ltr-basic.html left-ltr-ref.html
+== rtl-basic.html right-rtl-ref.html
+== rtl-span-only.html left-rtl-ref.html
+== ltr-span-only.html right-ltr-ref.html
+== ltr-ib.html left-ltr-ref.html
+== rtl-ib.html right-rtl-ref.html
+== rtl-span-only-ib.html left-rtl-ref.html
+== ltr-span-only-ib.html right-ltr-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/right-ltr-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="direction: rtl">
+      <span style="border: 2px solid; border-right-style: none; padding-left: 5px; margin-left: 30px;">One</span>
+      <br>
+      <span style="border: 2px solid; border-left-style: none; padding-right: 10px; margin-right: 60px;">Two</span>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/right-rtl-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="direction: rtl">
+      <span style="border: 2px solid; border-left-style: none; padding-right: 10px; margin-right: 60px;">One</span>
+      <br>
+      <span style="border: 2px solid; border-right-style: none; padding-left: 5px; margin-left: 30px;">Two</span>
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/rtl-basic.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .r { direction: rtl; }
+      .l { direction: ltr; }
+      span { border: 2px solid; padding: 0 10px 0 5px; margin: 0 60px 0 30px; }
+    </style>
+  </head>
+  <body>
+    <div class="r"><span>One<br>Two</span></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/rtl-ib.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .r { direction: rtl; }
+      .l { direction: ltr; }
+      span { border: 2px solid; padding: 0 10px 0 5px; margin: 0 60px 0 30px; }
+    </style>
+  </head>
+  <body>
+    <div class="r"><span>One<div></div>Two</span></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/rtl-span-only-ib.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .r { direction: rtl; }
+      .l { direction: ltr; }
+      span { border: 2px solid; padding: 0 10px 0 5px; margin: 0 60px 0 30px; }
+    </style>
+  </head>
+  <body>
+    <div><span class="r">One<div></div>Two</span></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/inline-borderpadding/rtl-span-only.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+      .r { direction: rtl; }
+      .l { direction: ltr; }
+      span { border: 2px solid; padding: 0 10px 0 5px; margin: 0 60px 0 30px; }
+    </style>
+  </head>
+  <body>
+    <div><span class="r">One<br>Two</span></div>
+  </body>
+</html>
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -78,16 +78,19 @@ include forms/reftest.list
 include ib-split/reftest.list
 
 # image/
 include image/reftest.list
 
 # image-region/
 include image-region/reftest.list
 
+# inline borders and padding
+include inline-borderpadding/reftest.list
+
 # line-breaking/
 include line-breaking/reftest.list
 
 # mathml/
 include mathml/reftest.list
 
 # margin-collapsing
 include margin-collapsing/reftest.list