Bug 1268749 part 2 - Make pseudo-classes able to present conditionally like properties. r=heycam draft
authorXidorn Quan <quanxunzhen@gmail.com>
Wed, 04 May 2016 15:09:36 +1000
changeset 363182 e059616e5ec853c17614d119d58d3aeb5cb6c7af
parent 363168 b2da0b2063a5e2a2af3bdda5dbe26e3fb6aa1935
child 363183 3fbdf8fcde0064316b821d1ffde98e9e3f8185a1
push id17127
push userxquan@mozilla.com
push dateWed, 04 May 2016 05:11:05 +0000
reviewersheycam
bugs1268749
milestone49.0a1
Bug 1268749 part 2 - Make pseudo-classes able to present conditionally like properties. r=heycam MozReview-Commit-ID: 6D3UPQGrS71
layout/inspector/inDOMUtils.cpp
layout/style/nsCSSParser.cpp
layout/style/nsCSSPseudoClassList.h
layout/style/nsCSSPseudoClasses.cpp
layout/style/nsCSSPseudoClasses.h
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -1206,26 +1206,25 @@ GetStatesForPseudoClass(const nsAString&
     EventStates(),
     EventStates()
   };
   static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates) ==
                 static_cast<size_t>(CSSPseudoClassType::MAX),
                 "Length of PseudoClassStates array is incorrect");
 
   nsCOMPtr<nsIAtom> atom = NS_Atomize(aStatePseudo);
+  CSSPseudoClassType type = nsCSSPseudoClasses::GetPseudoType(atom, true, true);
 
   // Ignore :moz-any-link so we don't give the element simultaneous
   // visited and unvisited style state
-  if (nsCSSPseudoClasses::GetPseudoType(atom) ==
-      CSSPseudoClassType::mozAnyLink) {
+  if (type == CSSPseudoClassType::mozAnyLink) {
     return EventStates();
   }
   // Our array above is long enough that indexing into it with
   // NotPseudo is ok.
-  CSSPseudoClassType type = nsCSSPseudoClasses::GetPseudoType(atom);
   return sPseudoClassStates[static_cast<CSSPseudoClassTypeBase>(type)];
 }
 
 NS_IMETHODIMP
 inDOMUtils::GetCSSPseudoElementNames(uint32_t* aLength, char16_t*** aNames)
 {
   nsTArray<nsIAtom*> array;
 
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -5912,29 +5912,24 @@ CSSParserImpl::ParsePseudoSelector(int32
   nsContentUtils::ASCIIToLower(buffer);
   nsCOMPtr<nsIAtom> pseudo = NS_Atomize(buffer);
 
   // stash away some info about this pseudo so we only have to get it once.
   bool isTreePseudo = false;
   CSSPseudoElementType pseudoElementType =
     nsCSSPseudoElements::GetPseudoType(pseudo);
   CSSPseudoClassType pseudoClassType =
-    nsCSSPseudoClasses::GetPseudoType(pseudo);
+    nsCSSPseudoClasses::GetPseudoType(pseudo, AgentRulesEnabled(),
+                                      ChromeRulesEnabled());
   bool pseudoClassIsUserAction =
     nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType);
 
-  if (!AgentRulesEnabled() &&
-      ((pseudoElementType < CSSPseudoElementType::Count &&
-        nsCSSPseudoElements::PseudoElementIsUASheetOnly(pseudoElementType)) ||
-       (pseudoClassType != CSSPseudoClassType::NotPseudo &&
-        nsCSSPseudoClasses::PseudoClassIsUASheetOnly(pseudoClassType)) ||
-       (!ChromeRulesEnabled() &&
-        (pseudoClassType != CSSPseudoClassType::NotPseudo &&
-         nsCSSPseudoClasses::PseudoClassIsUASheetAndChromeOnly(pseudoClassType))))) {
-    // This pseudo-element or pseudo-class is not exposed to content.
+  if (pseudoElementType < CSSPseudoElementType::Count && !AgentRulesEnabled() &&
+      nsCSSPseudoElements::PseudoElementIsUASheetOnly(pseudoElementType)) {
+    // This pseudo-element is not exposed to content.
     REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
     UngetToken();
     return eSelectorParsingStatus_Error;
   }
 
   if (nsCSSAnonBoxes::IsNonElement(pseudo)) {
     // Non-element anonymous boxes should not match any rule.
     REPORT_UNEXPECTED_TOKEN(PEPseudoSelUnknown);
--- a/layout/style/nsCSSPseudoClassList.h
+++ b/layout/style/nsCSSPseudoClassList.h
@@ -92,17 +92,17 @@ CSS_PSEUDO_CLASS(mozIsHTML, ":-moz-is-ht
 
 // Match all custom elements whose created callback has not yet been invoked
  CSS_STATE_PSEUDO_CLASS(unresolved, ":unresolved", 0, "", NS_EVENT_STATE_UNRESOLVED)
 
 // Matches nodes that are in a native-anonymous subtree (i.e., nodes in
 // a subtree of C++ anonymous content constructed by Gecko for its own
 // purposes).
 CSS_PSEUDO_CLASS(mozNativeAnonymous, ":-moz-native-anonymous",
-                 CSS_PSEUDO_CLASS_UA_SHEET_ONLY, "")
+                 CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "")
 
 // Matches anything when the specified look-and-feel metric is set
 CSS_PSEUDO_CLASS(mozSystemMetric, ":-moz-system-metric", 0, "")
 
 // -moz-locale-dir(ltr) and -moz-locale-dir(rtl) may be used
 // to match based on the locale's chrome direction
 CSS_PSEUDO_CLASS(mozLocaleDir, ":-moz-locale-dir", 0, "")
 
@@ -119,17 +119,17 @@ CSS_PSEUDO_CLASS(mozLWThemeDarkText, ":-
 CSS_PSEUDO_CLASS(mozWindowInactive, ":-moz-window-inactive", 0, "")
 
 // Matches any table elements that have a nonzero border attribute,
 // according to HTML integer attribute parsing rules.
 CSS_PSEUDO_CLASS(mozTableBorderNonzero, ":-moz-table-border-nonzero", 0, "")
 
 // Matches HTML frame/iframe elements which are mozbrowser.
 CSS_PSEUDO_CLASS(mozBrowserFrame, ":-moz-browser-frame",
-                 CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "")
+                 CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "")
 
 // Matches whatever the contextual reference elements are for the
 // matching operation.
 CSS_PSEUDO_CLASS(scope, ":scope", 0, "layout.css.scope-pseudo.enabled")
 
 // :not needs to come at the end of the non-bit pseudo-class list, since
 // it doesn't actually get directly matched on in SelectorMatches.
 CSS_PSEUDO_CLASS(negation, ":not", 0, "")
@@ -171,44 +171,44 @@ CSS_STATE_PSEUDO_CLASS(mozFullScreenAnce
 // Matches if the element is focused and should show a focus ring
 CSS_STATE_PSEUDO_CLASS(mozFocusRing, ":-moz-focusring", 0, "", NS_EVENT_STATE_FOCUSRING)
 
 // Image, object, etc state pseudo-classes
 CSS_STATE_PSEUDO_CLASS(mozBroken, ":-moz-broken", 0, "", NS_EVENT_STATE_BROKEN)
 CSS_STATE_PSEUDO_CLASS(mozLoading, ":-moz-loading", 0, "", NS_EVENT_STATE_LOADING)
 
 CSS_STATE_PSEUDO_CLASS(mozUserDisabled, ":-moz-user-disabled",
-                       CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "",
+                       CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
                        NS_EVENT_STATE_USERDISABLED)
 CSS_STATE_PSEUDO_CLASS(mozSuppressed, ":-moz-suppressed",
-                       CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "",
+                       CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
                        NS_EVENT_STATE_SUPPRESSED)
 CSS_STATE_PSEUDO_CLASS(mozTypeUnsupported, ":-moz-type-unsupported",
-                       CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "",
+                       CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
                        NS_EVENT_STATE_TYPE_UNSUPPORTED)
 CSS_STATE_PSEUDO_CLASS(mozTypeUnsupportedPlatform, ":-moz-type-unsupported-platform",
-                       CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "",
+                       CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
                        NS_EVENT_STATE_TYPE_UNSUPPORTED_PLATFORM)
 CSS_STATE_PSEUDO_CLASS(mozHandlerClickToPlay, ":-moz-handler-clicktoplay",
-                       CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "",
+                       CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
                        NS_EVENT_STATE_TYPE_CLICK_TO_PLAY)
 CSS_STATE_PSEUDO_CLASS(mozHandlerVulnerableUpdatable, ":-moz-handler-vulnerable-updatable",
-                       CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "",
+                       CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
                        NS_EVENT_STATE_VULNERABLE_UPDATABLE)
 CSS_STATE_PSEUDO_CLASS(mozHandlerVulnerableNoUpdate, ":-moz-handler-vulnerable-no-update",
-                       CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "",
+                       CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
                        NS_EVENT_STATE_VULNERABLE_NO_UPDATE)
 CSS_STATE_PSEUDO_CLASS(mozHandlerDisabled, ":-moz-handler-disabled",
-                       CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "",
+                       CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
                        NS_EVENT_STATE_HANDLER_DISABLED)
 CSS_STATE_PSEUDO_CLASS(mozHandlerBlocked, ":-moz-handler-blocked",
-                       CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "",
+                       CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
                        NS_EVENT_STATE_HANDLER_BLOCKED)
 CSS_STATE_PSEUDO_CLASS(mozHandlerCrashed, ":-moz-handler-crashed",
-                       CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME, "",
+                       CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME, "",
                        NS_EVENT_STATE_HANDLER_CRASHED)
 
 CSS_STATE_PSEUDO_CLASS(mozMathIncrementScriptLevel,
                        ":-moz-math-increment-script-level", 0, "",
                        NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL)
 
 // CSS 3 UI
 // http://www.w3.org/TR/2004/CR-css3-ui-20040511/#pseudo-classes
--- a/layout/style/nsCSSPseudoClasses.cpp
+++ b/layout/style/nsCSSPseudoClasses.cpp
@@ -20,16 +20,24 @@ using namespace mozilla;
 #include "nsCSSPseudoClassList.h"
 #undef CSS_PSEUDO_CLASS
 
 #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
   NS_STATIC_ATOM_BUFFER(name_##_pseudo_class_buffer, value_)
 #include "nsCSSPseudoClassList.h"
 #undef CSS_PSEUDO_CLASS
 
+#define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
+  static_assert(!((flags_) & CSS_PSEUDO_CLASS_ENABLED_IN_CHROME) || \
+                ((flags_) & CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), \
+                "Pseudo-class '" #name_ "' is enabled in chrome, so it " \
+                "should also be enabled in UA sheets");
+#include "nsCSSPseudoClassList.h"
+#undef CSS_PSEUDO_CLASS
+
 // Array of nsStaticAtom for each of the pseudo-classes.
 static const nsStaticAtom CSSPseudoClasses_info[] = {
 #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
   NS_STATIC_ATOM(name_##_pseudo_class_buffer, &sPseudoClass_##name_),
 #include "nsCSSPseudoClassList.h"
 #undef CSS_PSEUDO_CLASS
 };
 
@@ -39,20 +47,28 @@ static const nsStaticAtom CSSPseudoClass
 static const uint32_t CSSPseudoClasses_flags[] = {
 #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
   flags_,
 #include "nsCSSPseudoClassList.h"
 #undef CSS_PSEUDO_CLASS
 };
 
 static bool sPseudoClassEnabled[] = {
+// If the pseudo class has any "ENABLED_IN" flag set, it is disabled by
+// default. Note that, if a pseudo class has pref, whatever its default
+// value is, it'll later be changed in nsCSSPseudoClasses::AddRefAtoms()
+// If the pseudo class has "ENABLED_IN" flags but doesn't have a pref,
+// it is an internal pseudo class which is disabled elsewhere.
+#define IS_ENABLED_BY_DEFAULT(flags_) \
+  (!((flags_) & CSS_PSEUDO_CLASS_ENABLED_MASK))
 #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_) \
-  true,
+  IS_ENABLED_BY_DEFAULT(flags_),
 #include "nsCSSPseudoClassList.h"
 #undef CSS_PSEUDO_CLASS
+#undef IS_ENABLED_BY_DEFAULT
 };
 
 void nsCSSPseudoClasses::AddRefAtoms()
 {
   NS_RegisterStaticAtoms(CSSPseudoClasses_info);
 
 #define CSS_PSEUDO_CLASS(name_, value_, flags_, pref_)                        \
   if (pref_[0]) {                                                             \
@@ -85,22 +101,35 @@ nsCSSPseudoClasses::HasNthPairArg(Type a
 void
 nsCSSPseudoClasses::PseudoTypeToString(Type aType, nsAString& aString)
 {
   MOZ_ASSERT(aType < Type::Count, "Unexpected type");
   auto idx = static_cast<CSSPseudoClassTypeBase>(aType);
   (*CSSPseudoClasses_info[idx].mAtom)->ToString(aString);
 }
 
-CSSPseudoClassType
-nsCSSPseudoClasses::GetPseudoType(nsIAtom* aAtom)
+/* static */ CSSPseudoClassType
+nsCSSPseudoClasses::GetPseudoType(nsIAtom* aAtom,
+                                  bool aAgentEnabled, bool aChromeEnabled)
 {
   for (uint32_t i = 0; i < ArrayLength(CSSPseudoClasses_info); ++i) {
     if (*CSSPseudoClasses_info[i].mAtom == aAtom) {
-      return sPseudoClassEnabled[i] ? Type(i) : Type::NotPseudo;
+      Type type = Type(i);
+      if (sPseudoClassEnabled[i]) {
+        return type;
+      } else {
+        auto flags = FlagsForPseudoClass(type);
+        if ((aChromeEnabled &&
+             (flags & CSS_PSEUDO_CLASS_ENABLED_IN_CHROME)) ||
+            (aAgentEnabled &&
+             (flags & CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS))) {
+          return type;
+        }
+      }
+      return Type::NotPseudo;
     }
   }
 
   return Type::NotPseudo;
 }
 
 /* static */ bool
 nsCSSPseudoClasses::IsUserActionPseudoClass(Type aType)
--- a/layout/style/nsCSSPseudoClasses.h
+++ b/layout/style/nsCSSPseudoClasses.h
@@ -5,20 +5,30 @@
 
 /* atom list for CSS pseudo-classes */
 
 #ifndef nsCSSPseudoClasses_h___
 #define nsCSSPseudoClasses_h___
 
 #include "nsStringFwd.h"
 
-// This pseudo-class is accepted only in UA style sheets.
-#define CSS_PSEUDO_CLASS_UA_SHEET_ONLY                 (1<<0)
-// This pseudo-class is accepted only in UA style sheets and chrome.
-#define CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME           (1<<1)
+// The following two flags along with the pref defines where this pseudo
+// class can be used:
+// * If none of the two flags is presented, the pref completely controls
+//   the availability of this pseudo class. And in that case, if it has
+//   no pref, this property is usable everywhere.
+// * If any of the flags is set, this pseudo class is always enabled in
+//   the specific contexts regardless of the value of the pref. If there
+//   is no pref for this pseudo class at all in this case, it is an
+//   internal-only pseudo class, which cannot be used anywhere else.
+#define CSS_PSEUDO_CLASS_ENABLED_MASK                  (3<<0)
+#define CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS          (1<<0)
+#define CSS_PSEUDO_CLASS_ENABLED_IN_CHROME             (1<<1)
+#define CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME \
+  (CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS | CSS_PSEUDO_CLASS_ENABLED_IN_CHROME)
 
 class nsIAtom;
 
 namespace mozilla {
 
 // The total count of CSSPseudoClassType is less than 256,
 // so use uint8_t as its underlying type.
 typedef uint8_t CSSPseudoClassTypeBase;
@@ -37,37 +47,25 @@ enum class CSSPseudoClassType : CSSPseud
 
 class nsCSSPseudoClasses
 {
   typedef mozilla::CSSPseudoClassType Type;
 
 public:
   static void AddRefAtoms();
 
-  static Type GetPseudoType(nsIAtom* aAtom);
+  static Type GetPseudoType(nsIAtom* aAtom,
+                            bool aAgentEnabled, bool aChromeEnabled);
   static bool HasStringArg(Type aType);
   static bool HasNthPairArg(Type aType);
   static bool HasSelectorListArg(Type aType) {
     return aType == Type::any;
   }
   static bool IsUserActionPseudoClass(Type aType);
 
-  static bool PseudoClassIsUASheetOnly(Type aType) {
-    return PseudoClassHasFlags(aType, CSS_PSEUDO_CLASS_UA_SHEET_ONLY);
-  }
-  static bool PseudoClassIsUASheetAndChromeOnly(Type aType) {
-    return PseudoClassHasFlags(aType, CSS_PSEUDO_CLASS_UA_SHEET_AND_CHROME);
-  }
-
   // Should only be used on types other than Count and NotPseudoClass
   static void PseudoTypeToString(Type aType, nsAString& aString);
 
 private:
   static uint32_t FlagsForPseudoClass(const Type aType);
-
-  // Does the given pseudo-class have all of the flags given?
-  static bool PseudoClassHasFlags(const Type aType, uint32_t aFlags)
-  {
-    return (FlagsForPseudoClass(aType) & aFlags) == aFlags;
-  }
 };
 
 #endif /* nsCSSPseudoClasses_h___ */