Add start/end attributes support to StackFrame [r=enndeakin,a=blocking-fennec]
authorVivien Nicolas <21@vingtetun.org>
Mon, 28 Feb 2011 19:06:29 +0100
changeset 63170 a1d3010eff92e6f1e43ac123bc370aa183edb339
parent 63168 9aac1cc647940567c2fd2be5d0bad9d6ef6d489d (current diff)
parent 63169 99420cb92ff7f1cd9b4b8bd84ea6498cf0474d07 (diff)
child 63171 0e4b5530151d4881b58cd2ba62821391492e6639
push id19056
push uservnicolas@mozilla.com
push dateMon, 28 Feb 2011 18:09:35 +0000
treeherdermozilla-central@a1d3010eff92 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersenndeakin, blocking-fennec
milestone2.0b13pre
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
Add start/end attributes support to StackFrame [r=enndeakin,a=blocking-fennec]
content/xul/content/src/nsXULElement.cpp
gfx/layers/basic/BasicLayers.cpp
layout/xul/base/src/nsBoxFrame.cpp
layout/xul/base/src/nsStackLayout.cpp
layout/xul/base/test/test_stack.xul
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -1834,17 +1834,18 @@ nsXULElement::GetAttributeChangeHint(con
         // value attribute is being added or removed, then we need to
         // return a hint of frame change.  (See bugzilla bug 95475 for
         // details.)
         retval = NS_STYLE_HINT_FRAMECHANGE;
     } else {
         // if left or top changes we reflow. This will happen in xul
         // containers that manage positioned children such as a stack.
         if (nsGkAtoms::left == aAttribute || nsGkAtoms::top == aAttribute ||
-            nsGkAtoms::right == aAttribute || nsGkAtoms::bottom == aAttribute)
+            nsGkAtoms::right == aAttribute || nsGkAtoms::bottom == aAttribute ||
+            nsGkAtoms::start == aAttribute || nsGkAtoms::end == aAttribute)
             retval = NS_STYLE_HINT_REFLOW;
     }
 
     return retval;
 }
 
 NS_IMETHODIMP_(PRBool)
 nsXULElement::IsAttributeMapped(const nsIAtom* aAttribute) const
--- a/layout/xul/base/src/nsBoxFrame.cpp
+++ b/layout/xul/base/src/nsBoxFrame.cpp
@@ -1126,16 +1126,18 @@ nsBoxFrame::AttributeChanged(PRInt32 aNa
   if (aAttribute == nsGkAtoms::width       ||
       aAttribute == nsGkAtoms::height      ||
       aAttribute == nsGkAtoms::align       ||
       aAttribute == nsGkAtoms::valign      ||
       aAttribute == nsGkAtoms::left        ||
       aAttribute == nsGkAtoms::top         ||
       aAttribute == nsGkAtoms::right        ||
       aAttribute == nsGkAtoms::bottom       ||
+      aAttribute == nsGkAtoms::start        ||
+      aAttribute == nsGkAtoms::end          ||
       aAttribute == nsGkAtoms::minwidth     ||
       aAttribute == nsGkAtoms::maxwidth     ||
       aAttribute == nsGkAtoms::minheight    ||
       aAttribute == nsGkAtoms::maxheight    ||
       aAttribute == nsGkAtoms::flex         ||
       aAttribute == nsGkAtoms::orient       ||
       aAttribute == nsGkAtoms::pack         ||
       aAttribute == nsGkAtoms::dir          ||
@@ -1198,17 +1200,19 @@ nsBoxFrame::AttributeChanged(PRInt32 aNa
       if (autostretch)
         mState |= NS_STATE_AUTO_STRETCH;
       else
         mState &= ~NS_STATE_AUTO_STRETCH;
     }
     else if (aAttribute == nsGkAtoms::left ||
              aAttribute == nsGkAtoms::top ||
              aAttribute == nsGkAtoms::right ||
-             aAttribute == nsGkAtoms::bottom) {
+             aAttribute == nsGkAtoms::bottom ||
+             aAttribute == nsGkAtoms::start ||
+             aAttribute == nsGkAtoms::end) {
       mState &= ~NS_STATE_STACK_NOT_POSITIONED;
     }
     else if (aAttribute == nsGkAtoms::mousethrough) {
       UpdateMouseThrough();
     }
 
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
--- a/layout/xul/base/src/nsStackLayout.cpp
+++ b/layout/xul/base/src/nsStackLayout.cpp
@@ -202,19 +202,48 @@ nsStackLayout::GetOffset(nsBoxLayoutStat
   // wasting time fetching attributes.
   if (aChild->IsBoxFrame() &&
       (aChild->GetStateBits() & NS_STATE_STACK_NOT_POSITIONED))
     return 0;
 
   PRUint8 offsetSpecified = 0;
   nsIContent* content = aChild->GetContent();
   if (content) {
+    PRBool ltr = aChild->GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
     nsAutoString value;
     PRInt32 error;
 
+    content->GetAttr(kNameSpaceID_None, nsGkAtoms::start, value);
+    if (!value.IsEmpty()) {
+      value.Trim("%");
+      if (ltr) {
+        aOffset.left =
+          nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+        offsetSpecified |= SPECIFIED_LEFT;
+      } else {
+        aOffset.right =
+          nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+        offsetSpecified |= SPECIFIED_RIGHT;
+      }
+    }
+
+    content->GetAttr(kNameSpaceID_None, nsGkAtoms::end, value);
+    if (!value.IsEmpty()) {
+      value.Trim("%");
+      if (ltr) {
+        aOffset.right =
+          nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+        offsetSpecified |= SPECIFIED_RIGHT;
+      } else {
+        aOffset.left =
+          nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
+        offsetSpecified |= SPECIFIED_LEFT;
+      }
+    }
+
     content->GetAttr(kNameSpaceID_None, nsGkAtoms::left, value);
     if (!value.IsEmpty()) {
       value.Trim("%");
       aOffset.left =
         nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
       offsetSpecified |= SPECIFIED_LEFT;
     }
 
--- a/layout/xul/base/test/test_stack.xul
+++ b/layout/xul/base/test/test_stack.xul
@@ -7,38 +7,80 @@
   <script type="application/javascript" src="/MochiKit/packed.js" />
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"/>
 
   <!-- a * before an expected value means an offset from the right or bottom edge -->
   <stack id="stack">
     <hbox id="left-top" left="10" top="12" width="20" height="24"
                         expectedleft="10" expectedtop="12" expectedright="30" expectedbottom="36"
                         stackwidth="30" stackheight="36"/>
+    <hbox id="start-top" start="10" top="12" width="20" height="24"
+                         expectedleft="10" expectedtop="12" expectedright="30" expectedbottom="36"
+                         stackwidth="30" stackheight="36"/>
     <hbox id="right-bottom" right="10" bottom="12" width="20" height="24"
                             expectedleft="*30" expectedtop="*36" expectedright="*10" expectedbottom="*12"
                             stackwidth="30" stackheight="36"/>
+    <hbox id="end-bottom" end="10" bottom="12" width="20" height="24"
+                          expectedleft="*30" expectedtop="*36" expectedright="*10" expectedbottom="*12"
+                          stackwidth="30" stackheight="36"/>
     <hbox id="left-bottom" left="18" bottom="15" width="16" height="19"
                            expectedleft="18" expectedtop="*34" expectedright="34" expectedbottom="*15"
                            stackwidth="34" stackheight="34"/>
+    <hbox id="start-bottom" start="18" bottom="15" width="16" height="19"
+                            expectedleft="18" expectedtop="*34" expectedright="34" expectedbottom="*15"
+                            stackwidth="34" stackheight="34"/>
     <hbox id="right-top" right="5" top="8" width="10" height="11"
                          expectedleft="*15" expectedtop="8" expectedright="*5" expectedbottom="19"
                          stackwidth="15" stackheight="19"/>
+    <hbox id="end-top" end="5" top="8" width="10" height="11"
+                       expectedleft="*15" expectedtop="8" expectedright="*5" expectedbottom="19"
+                       stackwidth="15" stackheight="19"/>
     <hbox id="left-right" left="12" right="9" width="15" height="6"
                           expectedleft="12" expectedtop="0" expectedright="*9" expectedbottom="*0"
                           stackwidth="36" stackheight="6"/>
+    <hbox id="start-right" start="12" right="9" width="15" height="6"
+                           expectedleft="12" expectedtop="0" expectedright="*9" expectedbottom="*0"
+                           stackwidth="36" stackheight="6"/>
+    <hbox id="left-end" start="12" end="9" width="15" height="6"
+                        expectedleft="12" expectedtop="0" expectedright="*9" expectedbottom="*0"
+                        stackwidth="36" stackheight="6"/>
+    <hbox id="start-end" start="12" end="9" width="15" height="6"
+                         expectedleft="12" expectedtop="0" expectedright="*9" expectedbottom="*0"
+                         stackwidth="36" stackheight="6"/>
     <hbox id="top-bottom" top="20" bottom="39" width="15" height="6"
                           expectedleft="0" expectedtop="20" expectedright="*0" expectedbottom="*39"
                           stackwidth="15" stackheight="65"/>
     <hbox id="left-right-top-bottom" style="left: 5px; top: 5px; right: 8px; bottom: 8px;"
                                      left="16" top="20" right="20" bottom="35" width="7" height="8"
                                      expectedleft="16" expectedtop="20" expectedright="*20" expectedbottom="*35"
                                      stackwidth="43" stackheight="63"/>
+    <hbox id="start-right-top-bottom" style="left: 5px; top: 5px; right: 8px; bottom: 8px;"
+                                      start="16" top="20" right="20" bottom="35" width="7" height="8"
+                                      expectedleft="16" expectedtop="20" expectedright="*20" expectedbottom="*35"
+                                      stackwidth="43" stackheight="63"/>
+    <hbox id="left-end-top-bottom" style="left: 5px; top: 5px; right: 8px; bottom: 8px;"
+                                   left="16" top="20" end="20" bottom="35" width="7" height="8"
+                                   expectedleft="16" expectedtop="20" expectedright="*20" expectedbottom="*35"
+                                   stackwidth="43" stackheight="63"/>
+    <hbox id="start-end-top-bottom" style="left: 5px; top: 5px; right: 8px; bottom: 8px;"
+                                    start="16" top="20" end="20" bottom="35" width="7" height="8"
+                                    expectedleft="16" expectedtop="20" expectedright="*20" expectedbottom="*35"
+                                    stackwidth="43" stackheight="63"/>
     <hbox id="left-right-top-bottom-nosize" left="16" top="20" right="20" bottom="35"
                                             expectedleft="16" expectedtop="20" expectedright="*20" expectedbottom="*35"
                                             stackwidth="36" stackheight="55"/>
+    <hbox id="start-right-top-bottom-nosize" start="16" top="20" right="20" bottom="35"
+                                             expectedleft="16" expectedtop="20" expectedright="*20" expectedbottom="*35"
+                                             stackwidth="36" stackheight="55"/>
+    <hbox id="left-end-top-bottom-nosize" left="16" top="20" end="20" bottom="35"
+                                          expectedleft="16" expectedtop="20" expectedright="*20" expectedbottom="*35"
+                                          stackwidth="36" stackheight="55"/>
+    <hbox id="start-end-top-bottom-nosize" start="16" top="20" end="20" bottom="35"
+                                           expectedleft="16" expectedtop="20" expectedright="*20" expectedbottom="*35"
+                                           stackwidth="36" stackheight="55"/>
     <hbox id="none" width="10" height="12" expectedleft="0" expectedtop="0" expectedright="*0" expectedbottom="*0"
                     stackwidth="10" stackheight="12"/>
     <hbox id="none-nosize" expectedleft="0" expectedtop="0" expectedright="*0" expectedbottom="*0"
                            stackwidth="0" stackheight="0"/>
     <hbox id="style-left-right-top-bottom"
           style="left: 17px; top: 20px;" right="20" bottom="35" width="7" height="8"
           expectedleft="*27" expectedtop="*43" expectedright="*20" expectedbottom="*35"
           stackwidth="27" stackheight="43"/>
@@ -67,55 +109,79 @@
           stackwidth="6" stackheight="4"/>
   </stack>
 
   <stack id="stack-with-size" width="12" height="14">
     <hbox id="left-top-with-stack-size" left="10" top="12" width="20" height="24"
                                         expectedleft="10" expectedtop="12" expectedright="30" expectedbottom="36"/>
   </stack>
 
+  <stack id="stack-with-start-end" width="30">
+    <hbox id="start-with-start-end" start="10" top="12" width="20" height="24"
+                                    expectedstart="10" expectedend="30"/>
+    <hbox id="end-width-start-end" end="5" top="12" width="20" height="24"
+                                   expectedstart="5" expectedend="25"/>
+    <hbox id="start-end-width-start-end" start="12" end="9" width="20" top="12" height="24"
+                                         expectedstart="12" expectedend="21"/>
+  </stack>
+
   <stack id="stack-with-border"
          style="border-left: 4px solid black; border-top: 2px solid black; border-right: 1px solid black; border-bottom: 3px solid black;">
     <hbox id="left-top-with-border" left="10" top="14" width="20" height="24"
                                     expectedleft="14" expectedtop="16" expectedright="34" expectedbottom="40"/>
+    <hbox id="start-top-with-border" start="10" top="14" width="20" height="24"
+                                     expectedleft="14" expectedtop="16" expectedright="34" expectedbottom="40"/>
     <hbox id="right-bottom-with-border" right="5" bottom="8" width="6" height="10"
                                         expectedleft="*12" expectedtop="*21" expectedright="*6" expectedbottom="*11"/>
+    <hbox id="end-bottom-with-border" end="5" bottom="8" width="6" height="10"
+                                      expectedleft="*12" expectedtop="*21" expectedright="*6" expectedbottom="*11"/>
     <hbox id="left-top-right-bottom-with-border" left="12" right="5" top="18" bottom="8"
           expectedleft="16" expectedtop="20" expectedright="*6" expectedbottom="*11"/>
+    <hbox id="start-top-right-bottom-with-border" start="12" right="5" top="18" bottom="8"
+          expectedleft="16" expectedtop="20" expectedright="*6" expectedbottom="*11"/>
+    <hbox id="left-top-end-bottom-with-border" left="12" end="5" top="18" bottom="8"
+          expectedleft="16" expectedtop="20" expectedright="*6" expectedbottom="*11"/>
+    <hbox id="start-top-end-bottom-with-border" start="12" end="5" top="18" bottom="8"
+          expectedleft="16" expectedtop="20" expectedright="*6" expectedbottom="*11"/>
     <hbox id="none-with-with-border"
           expectedleft="4" expectedtop="2" expectedright="*1" expectedbottom="*3"/>
   </stack>
 
   <stack id="stack-dyn"/>
   <stack id="stack-dyn-sized" width="12" height="14"/>
 
   <body xmlns="http://www.w3.org/1999/xhtml"/>
 
   <script><![CDATA[
     SimpleTest.waitForExplicitFinish();
 
     var stackRect;
     var dynStack;
 
-    function compareSide(child, actual, side, dyn)
+    function compareSide(child, actual, side, dyn, direction)
     {
+      var clientRect = child.getBoundingClientRect();
       var vertical = (side == "top" || side == "bottom");
       var expectedval = child.getAttribute("expected" + side);
       if (expectedval.indexOf("*") == 0)
         expectedval = (vertical ? stackRect.bottom : stackRect.right) - Number(expectedval.substring(1));
+      else if (direction == "rtl")
+        expectedval = (vertical ? stackRect.top : -stackRect.width + clientRect.right + clientRect.left) + Number(expectedval);
       else
         expectedval = (vertical ? stackRect.top : stackRect.left) + Number(expectedval);
 
       is(actual, expectedval, child.id + " " + side + (dyn ? " dynamic" : ""));
     }
 
     function runTest()
     {
       runTestForStack("stack", false);
       runTestForStack("stack-with-size", false);
+      runTestForStartEndAttributes("stack-with-start-end", "ltr");
+      runTestForStartEndAttributes("stack-with-start-end", "rtl");
 
       var stackWithSize = $("stack-with-size");
 
       var sizedStackRect = stackWithSize.getBoundingClientRect();
       is(sizedStackRect.width, 30, "stack size stretched width");
       is(sizedStackRect.height, 36, "stack size stretched height");
 
       // set -moz-stack-sizing: ignore and ensure that the stack does not grow
@@ -137,16 +203,41 @@
       // now test adding stack children dynamically to ensure that
       // the size of the stack adjusts accordingly
       dynStack = $("stack-dyn");
       runTestForStack("stack", true);
 
       SimpleTest.finish();
     }
 
+    function runTestForStartEndAttributes(stackid, aDirection)
+    {
+      // Change the direction of the layout to RTL to ensure start/end are
+      // working as expected
+      var stack = $(stackid);
+      stack.style.direction = aDirection;
+
+      var stackRect = stack.getBoundingClientRect();
+      var children = stack.childNodes;
+      for (var c = children.length - 1; c >= 0; c--) {
+        var child = children[c];
+      
+        // do tests only for elements that have a rtl-enabled mode
+        if (!child.hasAttribute("start") && !child.hasAttribute("end"))
+          continue;
+
+        var childrect = child.getBoundingClientRect();
+        compareSide(child, childrect.right, "end", false, aDirection);
+        compareSide(child, childrect.left, "start", false, aDirection);
+      }
+
+      // Reset the direction
+      stack.style.direction = "ltr";
+    }
+
     function runTestForStack(stackid, dyn)
     {
       var stack = $(stackid);
       if (!dyn)
         stackRect = stack.getBoundingClientRect();
       var children = stack.childNodes;
       for (var c = children.length - 1; c >= 0; c--) {
         var child = children[c];
@@ -168,35 +259,54 @@
           dynStack.removeChild(child);
       }
     }
 
     function testPositionChanges(stack, ignoreStackSizing)
     {
       var add = ignoreStackSizing ? " ignore stack sizing" : "";
 
-      // ensure that changing left/top/right/bottom works
+      // ensure that changing left/top/right/bottom/start/end works
       var stackchild = document.getElementById("left-top-with-stack-size");
       stackchild.left = 18;
       is(stackchild.getBoundingClientRect().left, stack.getBoundingClientRect().left + 18, "left changed" + add);
       is(stack.getBoundingClientRect().width, ignoreStackSizing ? 12 : 38, "left changed stack width" + add);
 
+      stackchild.left = "";
+      stackchild.setAttribute("start", "19");
+      is(stackchild.getBoundingClientRect().left, stack.getBoundingClientRect().left + 19, "left changed" + add);
+      is(stack.getBoundingClientRect().width, ignoreStackSizing ? 12 : 39, "left changed stack width" + add);
+      stackchild.removeAttribute("start");
+      stackchild.left = 18;
+
       stackchild.top = 22;
       is(stackchild.getBoundingClientRect().top, stack.getBoundingClientRect().top + 22, "top changed" + add);
       is(stack.getBoundingClientRect().height, ignoreStackSizing ? 14 : 46, "left changed stack height" + add);
 
       stackchild.setAttribute("right", "6");
       is(stackchild.getBoundingClientRect().right, stack.getBoundingClientRect().left + 18, "right changed" + add);
       // the width is only 12 pixels in ignoreStackSizing mode, so don't check the offset
       // from the right edge in this case
       if (!ignoreStackSizing)
         is(stackchild.getBoundingClientRect().right, stack.getBoundingClientRect().right - 6,
            "right changed from right edge" + add);
       is(stack.getBoundingClientRect().width, ignoreStackSizing ? 12 : 24, "right changed stack width" + add);
 
+      stackchild.removeAttribute("right");
+      stackchild.setAttribute("end", "7");
+      is(stackchild.getBoundingClientRect().right, stack.getBoundingClientRect().left + 18, "right changed" + add);
+      // the width is only 12 pixels in ignoreStackSizing mode, so don't check the offset
+      // from the right edge in this case
+      if (!ignoreStackSizing)
+        is(stackchild.getBoundingClientRect().right, stack.getBoundingClientRect().right - 7,
+           "right changed from right edge" + add);
+      is(stack.getBoundingClientRect().width, ignoreStackSizing ? 12 : 25, "right changed stack width" + add);
+      stackchild.removeAttribute("end");
+      stackchild.setAttribute("right", "6");
+
       stackchild.setAttribute("bottom", "9");
       is(stackchild.getBoundingClientRect().bottom, stack.getBoundingClientRect().top + 22, "bottom changed" + add);
       is(stack.getBoundingClientRect().height, ignoreStackSizing ? 14 : 31, "bottom changed stack height" + add);
       if (!ignoreStackSizing)
         is(stackchild.getBoundingClientRect().bottom, stack.getBoundingClientRect().bottom - 9,
            "right changed from bottom edge" + add);
 
       stackchild.left = "";