Bug 1224918 part 1 - Make SetDiscrete more type-safe and easy to use with enum classes. r=dbaron draft
authorXidorn Quan <quanxunzhen@gmail.com>
Tue, 24 Nov 2015 11:44:40 +1100
changeset 374936 4590fab0f366403a0e74f5105b4e4cbadb18eb7f
parent 374935 0a2de45be726b291911f6b07ab160a06193d0ce0
child 374937 37e53646cb493614aea59687fedce7e658c3dc42
push id20115
push userxquan@mozilla.com
push dateFri, 03 Jun 2016 01:16:18 +0000
reviewersdbaron
bugs1224918
milestone49.0a1
Bug 1224918 part 1 - Make SetDiscrete more type-safe and easy to use with enum classes. r=dbaron MozReview-Commit-ID: 591zyIlPxKh
layout/style/nsRuleNode.cpp
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -12,16 +12,17 @@
 #include <algorithm>
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for PlaybackDirection
 #include "mozilla/Likely.h"
 #include "mozilla/LookAndFeel.h"
+#include "mozilla/unused.h"
 
 #include "mozilla/css/Declaration.h"
 
 #include "nsAlgorithm.h" // for clamped()
 #include "nsRuleNode.h"
 #include "nscore.h"
 #include "nsIWidget.h"
 #include "nsIPresShell.h"
@@ -1275,120 +1276,187 @@ static void SetStyleImage(nsStyleContext
       break;
     }
     default:
       MOZ_ASSERT_UNREACHABLE("Unexpected Unit type.");
       break;
   }
 }
 
+struct SetEnumValueHelper
+{
+  template<typename FieldT>
+  static void SetIntegerValue(FieldT&, const nsCSSValue&)
+  {
+    // FIXME Is it possible to turn this assertion into a compilation error?
+    MOZ_ASSERT_UNREACHABLE("inappropriate unit");
+  }
+
+#define DEFINE_ENUM_CLASS_SETTER(type_, min_, max_) \
+  static void SetEnumeratedValue(type_& aField, const nsCSSValue& aValue) \
+  { \
+    auto value = aValue.GetIntValue(); \
+    MOZ_ASSERT(value >= static_cast<decltype(value)>(type_::min_) && \
+               value <= static_cast<decltype(value)>(type_::max_), \
+               "inappropriate value"); \
+    aField = static_cast<type_>(value); \
+  }
+
+  DEFINE_ENUM_CLASS_SETTER(StyleBoxSizing, Content, Border)
+
+#undef DEF_SET_ENUMERATED_VALUE
+};
+
+template<typename FieldT>
+struct SetIntegerValueHelper
+{
+  static void SetIntegerValue(FieldT& aField, const nsCSSValue& aValue)
+  {
+    aField = aValue.GetIntValue();
+  }
+  static void SetEnumeratedValue(FieldT& aField, const nsCSSValue& aValue)
+  {
+    aField = aValue.GetIntValue();
+  }
+};
+
+template<typename FieldT>
+struct SetValueHelper : Conditional<IsEnum<FieldT>::value,
+                                    SetEnumValueHelper,
+                                    SetIntegerValueHelper<FieldT>>::Type
+{
+  template<typename ValueT>
+  static void SetValue(FieldT& aField, const ValueT& aValue)
+  {
+    aField = aValue;
+  }
+  static void SetValue(FieldT&, unused_t)
+  {
+    // FIXME Is it possible to turn this assertion into a compilation error?
+    MOZ_ASSERT_UNREACHABLE("inappropriate unit");
+  }
+};
+
+
 // flags for SetValue - align values with SETCOORD_* constants
 // where possible
 
 #define SETVAL_NORMAL                 0x01   // N
 #define SETVAL_AUTO                   0x02   // A
 #define SETVAL_INTEGER                0x40   // I
 #define SETVAL_ENUMERATED             0x80   // E
 #define SETVAL_NONE                   0x100  // O
 #define SETVAL_SYSTEM_FONT            0x2000
 #define SETVAL_UNSET_INHERIT          0x00400000
 #define SETVAL_UNSET_INITIAL          0x00800000
 
 // no caller cares whether aField was changed or not
-template <typename FieldT,
-          typename T1, typename T2, typename T3, typename T4, typename T5>
+template<typename FieldT, typename InitialT,
+         typename AutoT, typename NoneT, typename NormalT, typename SysFontT>
 static void
-SetValue(const nsCSSValue& aValue, FieldT & aField,
+SetValue(const nsCSSValue& aValue, FieldT& aField,
          RuleNodeCacheConditions& aConditions, uint32_t aMask,
          FieldT aParentValue,
-         T1 aInitialValue,
-         T2 aAutoValue,
-         T3 aNoneValue,
-         T4 aNormalValue,
-         T5 aSystemFontValue)
-{
+         InitialT aInitialValue,
+         AutoT aAutoValue,
+         NoneT aNoneValue,
+         NormalT aNormalValue,
+         SysFontT aSystemFontValue)
+{
+  typedef SetValueHelper<FieldT> Helper;
+
   switch (aValue.GetUnit()) {
   case eCSSUnit_Null:
     return;
 
     // every caller of SetValue provides inherit and initial
     // alternatives, so we don't require them to say so in the mask
   case eCSSUnit_Inherit:
     aConditions.SetUncacheable();
     aField = aParentValue;
     return;
 
   case eCSSUnit_Initial:
-    aField = aInitialValue;
+    Helper::SetValue(aField, aInitialValue);
     return;
 
     // every caller provides one or other of these alternatives,
     // but they have to say which
   case eCSSUnit_Enumerated:
     if (aMask & SETVAL_ENUMERATED) {
-      aField = FieldT(aValue.GetIntValue());
+      Helper::SetEnumeratedValue(aField, aValue);
       return;
     }
     break;
 
   case eCSSUnit_Integer:
     if (aMask & SETVAL_INTEGER) {
-      aField = FieldT(aValue.GetIntValue());
+      Helper::SetIntegerValue(aField, aValue);
       return;
     }
     break;
 
     // remaining possibilities in descending order of frequency of use
   case eCSSUnit_Auto:
     if (aMask & SETVAL_AUTO) {
-      aField = aAutoValue;
+      Helper::SetValue(aField, aAutoValue);
       return;
     }
     break;
 
   case eCSSUnit_None:
     if (aMask & SETVAL_NONE) {
-      aField = aNoneValue;
+      Helper::SetValue(aField, aNoneValue);
       return;
     }
     break;
 
   case eCSSUnit_Normal:
     if (aMask & SETVAL_NORMAL) {
-      aField = aNormalValue;
+      Helper::SetValue(aField, aNormalValue);
       return;
     }
     break;
 
   case eCSSUnit_System_Font:
     if (aMask & SETVAL_SYSTEM_FONT) {
-      aField = aSystemFontValue;
+      Helper::SetValue(aField, aSystemFontValue);
       return;
     }
     break;
 
   case eCSSUnit_Unset:
     if (aMask & SETVAL_UNSET_INHERIT) {
       aConditions.SetUncacheable();
       aField = aParentValue;
       return;
     }
     if (aMask & SETVAL_UNSET_INITIAL) {
-      aField = aInitialValue;
+      Helper::SetValue(aField, aInitialValue);
       return;
     }
     break;
 
   default:
     break;
   }
 
   NS_NOTREACHED("SetValue: inappropriate unit");
 }
 
+template <typename FieldT, typename T1>
+static void
+SetValue(const nsCSSValue& aValue, FieldT& aField,
+         RuleNodeCacheConditions& aConditions, uint32_t aMask,
+         FieldT aParentValue, T1 aInitialValue)
+{
+  SetValue(aValue, aField, aConditions, aMask, aParentValue,
+           aInitialValue, Unused, Unused, Unused, Unused);
+}
+
 // flags for SetFactor
 #define SETFCT_POSITIVE 0x01        // assert value is >= 0.0f
 #define SETFCT_OPACITY  0x02        // clamp value to [0.0f .. 1.0f]
 #define SETFCT_NONE     0x04        // allow _None (uses aInitialValue).
 #define SETFCT_UNSET_INHERIT  0x00400000
 #define SETFCT_UNSET_INITIAL  0x00800000
 
 static void
@@ -8367,21 +8435,17 @@ nsRuleNode::ComputePositionData(void* aS
              SETCOORD_UNSET_INITIAL,
            aContext, mPresContext, conditions);
 
   // box-sizing: enum, inherit, initial
   SetValue(*aRuleData->ValueForBoxSizing(),
            pos->mBoxSizing, conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentPos->mBoxSizing,
-           StyleBoxSizing::Content,
-           StyleBoxSizing::Content /* ignored */,
-           StyleBoxSizing::Content /* ignored */,
-           StyleBoxSizing::Content /* ignored */,
-           StyleBoxSizing::Content /* ignored */);
+           StyleBoxSizing::Content);
 
   // align-content: enum, inherit, initial
   SetValue(*aRuleData->ValueForAlignContent(),
            pos->mAlignContent, conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentPos->mAlignContent,
            NS_STYLE_ALIGN_NORMAL, 0, 0, 0, 0);