Bug 931460. Part 1: When a <legend>'s frame is reparented to the <fieldset>, update StickyScrollContainers to match if necessary. r=mats
authorRobert O'Callahan <robert@ocallahan.org>
Sat, 23 Nov 2013 22:48:26 +1300
changeset 158176 be8c4d516bdae46d3b89c0f4151c2784ab00efcd
parent 158175 4d75ae037706f5f83f88e69b4935d6496979e982
child 158177 29f004e4fa778a4fdef5f299b32de7ab57c7ad71
push id36943
push userrocallahan@mozilla.com
push dateMon, 02 Dec 2013 01:08:51 +0000
treeherdermozilla-inbound@08fd80f4b2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmats
bugs931460
milestone28.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 931460. Part 1: When a <legend>'s frame is reparented to the <fieldset>, update StickyScrollContainers to match if necessary. r=mats
layout/base/crashtests/931460-1.html
layout/base/crashtests/935765-1.html
layout/base/crashtests/crashtests.list
layout/base/nsCSSFrameConstructor.cpp
layout/generic/StickyScrollContainer.cpp
layout/generic/StickyScrollContainer.h
layout/generic/nsIFrame.h
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/931460-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<meta charset="UTF-8">
+<body><fieldset style="overflow: hidden;"><legend style="position: sticky;"></legend></fieldset></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/935765-1.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+</head>
+<body onload="document.getElementById('a').remove();">
+<fieldset style="overflow: scroll;"><legend><textarea id="a" style="position: sticky;"></textarea></legend></fieldset>
+</body>
+</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -414,8 +414,10 @@ load 836990-1.html
 load 852293.html
 load 860579-1.html
 pref(layers.force-active,true) load 859526-1.html
 pref(layers.force-active,true) load 859630-1.html
 load 866588.html
 load 897852.html
 asserts(4-6) load 898913.html # bug 847368
 load 931464.html
+load 931460-1.html
+load 935765-1.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -62,16 +62,17 @@
 #include "nsIObjectLoadingContent.h"
 #include "nsTArray.h"
 #include "nsGenericDOMDataNode.h"
 #include "mozilla/dom/Element.h"
 #include "nsAutoLayoutPhase.h"
 #include "nsStyleStructInlines.h"
 #include "nsPageContentFrame.h"
 #include "RestyleManager.h"
+#include "StickyScrollContainer.h"
 
 #ifdef MOZ_XUL
 #include "nsIRootBox.h"
 #endif
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
@@ -3092,16 +3093,20 @@ nsCSSFrameConstructor::ConstructFieldSet
       // We want the legend to be the first frame in the fieldset child list.
       // That way the EventStateManager will do the right thing when tabbing
       // from a selection point within the legend (bug 236071), which is
       // used for implementing legend access keys (bug 81481).
       // GetAdjustedParentFrame() below depends on this frame order.
       childItems.RemoveFrame(child);
       // Make sure to reparent the legend so it has the fieldset as the parent.
       fieldsetKids.InsertFrame(fieldsetFrame, nullptr, child);
+      if (scrollFrame) {
+        StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary(
+            child, blockFrame);
+      }
       break;
     }
   }
 
   if (isScrollable) {
     FinishBuildingScrollFrame(scrollFrame, blockFrame);
   }
 
--- a/layout/generic/StickyScrollContainer.cpp
+++ b/layout/generic/StickyScrollContainer.cpp
@@ -59,16 +59,51 @@ StickyScrollContainer::GetStickyScrollCo
   if (!s) {
     s = new StickyScrollContainer(scrollFrame);
     props.Set(StickyScrollContainerProperty(), s);
   }
   return s;
 }
 
 // static
+void
+StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary(nsIFrame* aFrame,
+                                                                      nsIFrame* aOldParent)
+{
+  nsIScrollableFrame* oldScrollFrame =
+    nsLayoutUtils::GetNearestScrollableFrame(aOldParent,
+      nsLayoutUtils::SCROLLABLE_SAME_DOC |
+      nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
+  if (!oldScrollFrame) {
+    // XXX maybe aFrame has sticky descendants that can be sticky now, but
+    // we aren't going to handle that.
+    return;
+  }
+  FrameProperties props = static_cast<nsIFrame*>(do_QueryFrame(oldScrollFrame))->
+    Properties();
+  StickyScrollContainer* oldSSC = static_cast<StickyScrollContainer*>
+    (props.Get(StickyScrollContainerProperty()));
+  if (!oldSSC) {
+    // aOldParent had no sticky descendants, so aFrame doesn't have any sticky
+    // descendants, and we're done here.
+    return;
+  }
+
+  auto i = oldSSC->mFrames.Length();
+  while (i-- > 0) {
+    nsIFrame* f = oldSSC->mFrames[i];
+    StickyScrollContainer* newSSC = GetStickyScrollContainerForFrame(f);
+    if (newSSC != oldSSC) {
+      oldSSC->RemoveFrame(f);
+      newSSC->AddFrame(f);
+    }
+  }
+}
+
+// static
 StickyScrollContainer*
 StickyScrollContainer::GetStickyScrollContainerForScrollFrame(nsIFrame* aFrame)
 {
   FrameProperties props = aFrame->Properties();
   return static_cast<StickyScrollContainer*>
     (props.Get(StickyScrollContainerProperty()));
 }
 
--- a/layout/generic/StickyScrollContainer.h
+++ b/layout/generic/StickyScrollContainer.h
@@ -32,16 +32,22 @@ public:
   static StickyScrollContainer* GetStickyScrollContainerForFrame(nsIFrame* aFrame);
 
   /**
    * Find the StickyScrollContainer associated with the given scroll frame,
    * if it exists.
    */
   static StickyScrollContainer* GetStickyScrollContainerForScrollFrame(nsIFrame* aScrollFrame);
 
+  /**
+   * aFrame may have moved into or out of a scroll frame's frame subtree.
+   */
+  static void NotifyReparentedFrameAcrossScrollFrameBoundary(nsIFrame* aFrame,
+                                                             nsIFrame* aOldParent);
+
   void AddFrame(nsIFrame* aFrame) {
     mFrames.AppendElement(aFrame);
   }
   void RemoveFrame(nsIFrame* aFrame) {
     mFrames.RemoveElement(aFrame);
   }
 
   nsIScrollableFrame* ScrollFrame() const {
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -857,16 +857,22 @@ public:
 
   virtual void SetAdditionalStyleContext(int32_t aIndex,
                                          nsStyleContext* aStyleContext) = 0;
 
   /**
    * Accessor functions for geometric parent
    */
   nsIFrame* GetParent() const { return mParent; }
+  /**
+   * Set this frame's parent to aParent.
+   * If the frame may have moved into or out of a scrollframe's
+   * frame subtree, StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary
+   * must also be called.
+   */
   virtual void SetParent(nsIFrame* aParent) = 0;
 
   /**
    * Bounding rect of the frame. The values are in app units, and the origin is
    * relative to the upper-left of the geometric parent. The size includes the
    * content area, borders, and padding.
    *
    * Note: moving or sizing the frame does not affect the view's size or