Bug 886646 - Part 1: Support position:sticky in the CSS parser, enabled by a preference. r=heycam
authorCorey Ford <cford@mozilla.com>
Thu, 05 Sep 2013 15:47:08 -0700
changeset 145939 cc50ede322393576f832a0992b34e46c234432ab
parent 145938 9c34952d655b3a95184f23c0e2dd61f947c0573d
child 145940 d65d4436d3a6de25b5897056d3f2c465ae5b85bb
push id25229
push userryanvm@gmail.com
push dateFri, 06 Sep 2013 20:49:59 +0000
treeherdermozilla-central@bdbdeda69f05 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs886646
milestone26.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 886646 - Part 1: Support position:sticky in the CSS parser, enabled by a preference. r=heycam
layout/base/nsLayoutUtils.cpp
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsStyleConsts.h
layout/style/test/property_database.js
modules/libpref/src/init/all.js
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -88,16 +88,17 @@ using namespace mozilla::layers;
 using namespace mozilla::layout;
 
 using mozilla::image::Angle;
 using mozilla::image::Flip;
 using mozilla::image::ImageOps;
 using mozilla::image::Orientation;
 
 #define FLEXBOX_ENABLED_PREF_NAME "layout.css.flexbox.enabled"
+#define STICKY_ENABLED_PREF_NAME "layout.css.sticky.enabled"
 
 #ifdef DEBUG
 // TODO: remove, see bug 598468.
 bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
 #endif // DEBUG
 
 typedef gfxPattern::GraphicsFilter GraphicsFilter;
 typedef FrameMetrics::ViewID ViewID;
@@ -115,16 +116,21 @@ static ViewID sScrollIdCounter = FrameMe
 
 // These are indices into kDisplayKTable.  They'll be initialized
 // the first time that FlexboxEnabledPrefChangeCallback() is invoked.
 static int32_t sIndexOfFlexInDisplayTable;
 static int32_t sIndexOfInlineFlexInDisplayTable;
 // This tracks whether those ^^ indices have been initialized
 static bool sAreFlexKeywordIndicesInitialized = false;
 
+// This is an index into kPositionKTable. It will be initialized
+// the first time that StickyEnabledPrefChangeCallback() is invoked.
+static int32_t sIndexOfStickyInPositionTable;
+static bool sIsStickyKeywordIndexInitialized = false;
+
 typedef nsDataHashtable<nsUint64HashKey, nsIContent*> ContentMap;
 static ContentMap* sContentMap = nullptr;
 static ContentMap& GetContentMap() {
   if (!sContentMap) {
     sContentMap = new ContentMap();
   }
   return *sContentMap;
 }
@@ -165,16 +171,48 @@ FlexboxEnabledPrefChangeCallback(const c
   if (sIndexOfInlineFlexInDisplayTable >= 0) {
     nsCSSProps::kDisplayKTable[sIndexOfInlineFlexInDisplayTable] =
       isFlexboxEnabled ? eCSSKeyword_inline_flex : eCSSKeyword_UNKNOWN;
   }
 
   return 0;
 }
 
+// When the pref "layout.css.sticky.enabled" changes, this function is invoked
+// to let us update kPositionKTable, to selectively disable or restore the
+// entry for "sticky" in that table.
+static int
+StickyEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
+{
+  MOZ_ASSERT(strncmp(aPrefName, STICKY_ENABLED_PREF_NAME,
+                     NS_ARRAY_LENGTH(STICKY_ENABLED_PREF_NAME)) == 0,
+             "We only registered this callback for a single pref, so it "
+             "should only be called for that pref");
+
+  bool isStickyEnabled =
+    Preferences::GetBool(STICKY_ENABLED_PREF_NAME, false);
+
+  if (!sIsStickyKeywordIndexInitialized) {
+    // First run: find the position of "sticky" in kPositionKTable.
+    sIndexOfStickyInPositionTable =
+      nsCSSProps::FindIndexOfKeyword(eCSSKeyword_sticky,
+                                     nsCSSProps::kPositionKTable);
+    MOZ_ASSERT(sIndexOfStickyInPositionTable >= 0,
+               "Couldn't find sticky in kPositionKTable");
+    sIsStickyKeywordIndexInitialized = true;
+  }
+
+  // OK -- now, stomp on or restore the "sticky" entry in kPositionKTable,
+  // depending on whether the sticky pref is enabled vs. disabled.
+  nsCSSProps::kPositionKTable[sIndexOfStickyInPositionTable] =
+    isStickyEnabled ? eCSSKeyword_sticky : eCSSKeyword_UNKNOWN;
+
+  return 0;
+}
+
 template <class AnimationsOrTransitions>
 static AnimationsOrTransitions*
 HasAnimationOrTransition(nsIContent* aContent,
                          nsIAtom* aAnimationProperty,
                          nsCSSProperty aProperty)
 {
   AnimationsOrTransitions* animations =
     static_cast<AnimationsOrTransitions*>(aContent->GetProperty(aAnimationProperty));
@@ -4997,29 +5035,34 @@ nsLayoutUtils::Initialize()
   Preferences::AddBoolVarCache(&sFontSizeInflationDisabledInMasterProcess,
                                "font.size.inflation.disabledInMasterProcess");
   Preferences::AddBoolVarCache(&sInvalidationDebuggingIsEnabled,
                                "nglayout.debug.invalidation");
 
   Preferences::RegisterCallback(FlexboxEnabledPrefChangeCallback,
                                 FLEXBOX_ENABLED_PREF_NAME);
   FlexboxEnabledPrefChangeCallback(FLEXBOX_ENABLED_PREF_NAME, nullptr);
+  Preferences::RegisterCallback(StickyEnabledPrefChangeCallback,
+                                STICKY_ENABLED_PREF_NAME);
+  StickyEnabledPrefChangeCallback(STICKY_ENABLED_PREF_NAME, nullptr);
 }
 
 /* static */
 void
 nsLayoutUtils::Shutdown()
 {
   if (sContentMap) {
     delete sContentMap;
     sContentMap = nullptr;
   }
 
   Preferences::UnregisterCallback(FlexboxEnabledPrefChangeCallback,
                                   FLEXBOX_ENABLED_PREF_NAME);
+  Preferences::UnregisterCallback(StickyEnabledPrefChangeCallback,
+                                  STICKY_ENABLED_PREF_NAME);
 }
 
 /* static */
 void
 nsLayoutUtils::RegisterImageRequest(nsPresContext* aPresContext,
                                     imgIRequest* aRequest,
                                     bool* aRequestRegistered)
 {
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -480,16 +480,17 @@ CSS_KEY(space-around, space_around)
 CSS_KEY(space-between, space_between)
 CSS_KEY(square, square)
 CSS_KEY(stacked-fractions, stacked_fractions)
 CSS_KEY(start, start)
 CSS_KEY(static, static)
 CSS_KEY(status-bar, status_bar)
 CSS_KEY(step-end, step_end)
 CSS_KEY(step-start, step_start)
+CSS_KEY(sticky, sticky)
 CSS_KEY(stretch, stretch)
 CSS_KEY(stretch-to-fit, stretch_to_fit)
 CSS_KEY(stroke, stroke)
 CSS_KEY(style, style)
 CSS_KEY(styleset, styleset)
 CSS_KEY(stylistic, stylistic)
 CSS_KEY(sub, sub)
 CSS_KEY(super, super)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1367,21 +1367,25 @@ const int32_t nsCSSProps::kPointerEvents
   eCSSKeyword_painted, NS_STYLE_POINTER_EVENTS_PAINTED,
   eCSSKeyword_fill, NS_STYLE_POINTER_EVENTS_FILL,
   eCSSKeyword_stroke, NS_STYLE_POINTER_EVENTS_STROKE,
   eCSSKeyword_all, NS_STYLE_POINTER_EVENTS_ALL,
   eCSSKeyword_auto, NS_STYLE_POINTER_EVENTS_AUTO,
   eCSSKeyword_UNKNOWN, -1
 };
 
-const int32_t nsCSSProps::kPositionKTable[] = {
+int32_t nsCSSProps::kPositionKTable[] = {
   eCSSKeyword_static, NS_STYLE_POSITION_STATIC,
   eCSSKeyword_relative, NS_STYLE_POSITION_RELATIVE,
   eCSSKeyword_absolute, NS_STYLE_POSITION_ABSOLUTE,
   eCSSKeyword_fixed, NS_STYLE_POSITION_FIXED,
+  // NOTE: This currently needs to be the last entry in the table,
+  // because the "layout.css.sticky.enabled" pref that disables
+  // this will disable all the entries after it, too.
+  eCSSKeyword_sticky, NS_STYLE_POSITION_STICKY,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const int32_t nsCSSProps::kRadialGradientShapeKTable[] = {
   eCSSKeyword_circle,  NS_STYLE_GRADIENT_SHAPE_CIRCULAR,
   eCSSKeyword_ellipse, NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL,
   eCSSKeyword_UNKNOWN,-1
 };
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -513,17 +513,19 @@ public:
   static const int32_t kOverflowKTable[];
   static const int32_t kOverflowSubKTable[];
   static const int32_t kPageBreakKTable[];
   static const int32_t kPageBreakInsideKTable[];
   static const int32_t kPageMarksKTable[];
   static const int32_t kPageSizeKTable[];
   static const int32_t kPitchKTable[];
   static const int32_t kPointerEventsKTable[];
-  static const int32_t kPositionKTable[];
+  // Not const because we modify its entries when the pref
+  // "layout.css.sticky.enabled" changes:
+  static int32_t kPositionKTable[];
   static const int32_t kRadialGradientShapeKTable[];
   static const int32_t kRadialGradientSizeKTable[];
   static const int32_t kRadialGradientLegacySizeKTable[];
   static const int32_t kResizeKTable[];
   static const int32_t kSpeakKTable[];
   static const int32_t kSpeakHeaderKTable[];
   static const int32_t kSpeakNumeralKTable[];
   static const int32_t kSpeakPunctuationKTable[];
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -526,16 +526,17 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_WIDTH_FIT_CONTENT              2
 #define NS_STYLE_WIDTH_AVAILABLE                3
 
 // See nsStylePosition.mPosition
 #define NS_STYLE_POSITION_STATIC                0
 #define NS_STYLE_POSITION_RELATIVE              1
 #define NS_STYLE_POSITION_ABSOLUTE              2
 #define NS_STYLE_POSITION_FIXED                 3
+#define NS_STYLE_POSITION_STICKY                4
 
 // See nsStylePosition.mClip
 #define NS_STYLE_CLIP_AUTO                      0x00
 #define NS_STYLE_CLIP_RECT                      0x01
 #define NS_STYLE_CLIP_TYPE_MASK                 0x0F
 #define NS_STYLE_CLIP_LEFT_AUTO                 0x10
 #define NS_STYLE_CLIP_TOP_AUTO                  0x20
 #define NS_STYLE_CLIP_RIGHT_AUTO                0x40
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -4735,16 +4735,20 @@ if (SpecialPowers.getBoolPref("layout.cs
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "auto" ],
 		other_values: [ "grayscale" ],
 		invalid_values: [ "none", "subpixel-antialiased", "antialiased" ]
 	};
 }
 
+if (SpecialPowers.getBoolPref("layout.css.sticky.enabled")) {
+	gCSSProperties["position"].other_values.push("sticky");
+}
+
 if (SpecialPowers.getBoolPref("layout.css.mix-blend-mode.enabled")) {
         gCSSProperties["mix-blend-mode"] = {
         domProp: "mixBlendMode",
         inherited: false,
         type: CSS_TYPE_LONGHAND,
         initial_values: [ "normal" ],
         other_values: ["multiply", "screen", "overlay", "darken", "lighten", "color-dodge", "color-burn",
             "hard-light", "soft-light", "difference", "exclusion", "hue", "saturation", "color", "luminosity"],
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1790,16 +1790,19 @@ pref("layout.css.mix-blend-mode.enabled"
 pref("layout.css.supports-rule.enabled", true);
 
 // Is support for CSS Filters enabled?
 pref("layout.css.filters.enabled", false);
 
 // Is support for CSS Flexbox enabled?
 pref("layout.css.flexbox.enabled", true);
 
+// Is support for CSS sticky positioning enabled?
+pref("layout.css.sticky.enabled", false);
+
 // Is support for the CSS4 image-orientation property enabled?
 pref("layout.css.image-orientation.enabled", true);
 
 // Is support for CSS3 Fonts features enabled?
 // (includes font-variant-*, font-kerning, font-synthesis
 // and the @font-feature-values rule)
 // Note: with this enabled, font-feature-settings is aliased
 // to -moz-font-feature-settings.  When unprefixing, this should