Introduce fxcrt::Mask<E> template.
Because C++ just isn't strict enough to catch everything otherwise.
-- Convert one sample usage.
Change-Id: Ife65ff01f54f3e620c788686798359aa741059a9
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/83957
Commit-Queue: Tom Sepez <tsepez@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index 8509148..0d38ebd 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -59,6 +59,7 @@
"fx_types.h",
"fx_unicode.cpp",
"fx_unicode.h",
+ "mask.h",
"maybe_owned.h",
"observed_ptr.cpp",
"observed_ptr.h",
@@ -158,6 +159,7 @@
"fx_random_unittest.cpp",
"fx_string_unittest.cpp",
"fx_system_unittest.cpp",
+ "mask_unittest.cpp",
"maybe_owned_unittest.cpp",
"observed_ptr_unittest.cpp",
"pdfium_span_unittest.cpp",
diff --git a/core/fxcrt/mask.h b/core/fxcrt/mask.h
new file mode 100644
index 0000000..a5d8129
--- /dev/null
+++ b/core/fxcrt/mask.h
@@ -0,0 +1,118 @@
+// Copyright 2021 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CORE_FXCRT_MASK_H_
+#define CORE_FXCRT_MASK_H_
+
+#include <type_traits>
+
+namespace fxcrt {
+
+// Provides extremely strict type-checking on masks of enum class bitflags,
+// for code where flags may not be passed consistently.
+template <typename E>
+class Mask {
+ public:
+ using UnderlyingType = typename std::underlying_type<E>::type;
+
+ // Escape hatch for when value comes aross an API, say.
+ static Mask FromUnderlyingUnchecked(UnderlyingType val) { return Mask(val); }
+
+ constexpr Mask() = default;
+ constexpr Mask(const Mask& that) = default;
+
+ // NOLINTNEXTLINE(runtime/explicit)
+ constexpr Mask(E val) : val_(static_cast<UnderlyingType>(val)) {}
+
+ // Unfortunately, std::initializer_list<> can't be used in constexpr
+ // methods per C++ standards, and we need constexpr for a zero-cost
+ // abstraction. Hence, expand out constructors of various arity.
+ constexpr Mask(E v1, E v2)
+ : val_(static_cast<UnderlyingType>(v1) |
+ static_cast<UnderlyingType>(v2)) {}
+
+ constexpr Mask(E v1, E v2, E v3)
+ : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+ static_cast<UnderlyingType>(v3)) {}
+
+ constexpr Mask(E v1, E v2, E v3, E v4)
+ : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+ static_cast<UnderlyingType>(v3) |
+ static_cast<UnderlyingType>(v4)) {}
+
+ constexpr Mask(E v1, E v2, E v3, E v4, E v5)
+ : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+ static_cast<UnderlyingType>(v3) | static_cast<UnderlyingType>(v4) |
+ static_cast<UnderlyingType>(v5)) {}
+
+ constexpr Mask(E v1, E v2, E v3, E v4, E v5, E v6)
+ : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+ static_cast<UnderlyingType>(v3) | static_cast<UnderlyingType>(v4) |
+ static_cast<UnderlyingType>(v5) |
+ static_cast<UnderlyingType>(v6)) {}
+
+ constexpr Mask(E v1, E v2, E v3, E v4, E v5, E v6, E v7)
+ : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+ static_cast<UnderlyingType>(v3) | static_cast<UnderlyingType>(v4) |
+ static_cast<UnderlyingType>(v5) | static_cast<UnderlyingType>(v6) |
+ static_cast<UnderlyingType>(v7)) {}
+
+ constexpr Mask(E v1, E v2, E v3, E v4, E v5, E v6, E v7, E v8)
+ : val_(static_cast<UnderlyingType>(v1) | static_cast<UnderlyingType>(v2) |
+ static_cast<UnderlyingType>(v3) | static_cast<UnderlyingType>(v4) |
+ static_cast<UnderlyingType>(v5) | static_cast<UnderlyingType>(v6) |
+ static_cast<UnderlyingType>(v7) |
+ static_cast<UnderlyingType>(v8)) {}
+
+ explicit operator bool() const { return !!val_; }
+ Mask operator~() const { return Mask(~val_); }
+ constexpr Mask operator|(const Mask& that) const {
+ return Mask(val_ | that.val_);
+ }
+ constexpr Mask operator&(const Mask& that) const {
+ return Mask(val_ & that.val_);
+ }
+ constexpr Mask operator^(const Mask& that) const {
+ return Mask(val_ ^ that.val_);
+ }
+ Mask& operator=(const Mask& that) {
+ val_ = that.val_;
+ return *this;
+ }
+ Mask& operator|=(const Mask& that) {
+ val_ |= that.val_;
+ return *this;
+ }
+ Mask& operator&=(const Mask& that) {
+ val_ &= that.val_;
+ return *this;
+ }
+ Mask& operator^=(const Mask& that) {
+ val_ ^= that.val_;
+ return *this;
+ }
+ bool operator==(const Mask& that) const { return val_ == that.val_; }
+ bool operator!=(const Mask& that) const { return val_ != that.val_; }
+
+ bool TestAll(const Mask& that) const {
+ return (val_ & that.val_) == that.val_;
+ }
+
+ // Because ~ can't be applied to enum class without casting.
+ void Clear(const Mask& that) { val_ &= ~that.val_; }
+
+ // Escape hatch, usage should be minimized.
+ UnderlyingType UncheckedValue() const { return val_; }
+
+ private:
+ explicit constexpr Mask(UnderlyingType val) : val_(val) {}
+
+ UnderlyingType val_ = 0;
+};
+
+} // namespace fxcrt
+
+using fxcrt::Mask;
+
+#endif // CORE_FXCRT_MASK_H_
diff --git a/core/fxcrt/mask_unittest.cpp b/core/fxcrt/mask_unittest.cpp
new file mode 100644
index 0000000..c0d442f
--- /dev/null
+++ b/core/fxcrt/mask_unittest.cpp
@@ -0,0 +1,170 @@
+// Copyright 2021 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/fxcrt/mask.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace fxcrt {
+namespace {
+
+enum class Privilege : uint8_t {
+ kPriv1 = 1 << 0,
+ kPriv2 = 1 << 1,
+ kPriv4 = 1 << 2,
+ kPriv8 = 1 << 3,
+ kPriv16 = 1 << 4,
+ kPriv32 = 1 << 5,
+ kPriv64 = 1 << 6,
+ kPriv128 = 1 << 7,
+};
+
+constexpr Mask<Privilege> kAllMask = {
+ Privilege::kPriv1, Privilege::kPriv2, Privilege::kPriv4,
+ Privilege::kPriv8, Privilege::kPriv16, Privilege::kPriv32,
+ Privilege::kPriv64, Privilege::kPriv128,
+};
+
+} // namespace
+
+static_assert(sizeof(Mask<Privilege>) == sizeof(Privilege),
+ "Mask size must be the same as enum");
+
+TEST(Mask, Empty) {
+ constexpr Mask<Privilege> privs;
+ EXPECT_EQ(0u, privs.UncheckedValue());
+ EXPECT_FALSE(privs & Privilege::kPriv1);
+ EXPECT_FALSE(privs & Privilege::kPriv4);
+ EXPECT_FALSE(privs & Privilege::kPriv8);
+ EXPECT_FALSE(privs & kAllMask);
+}
+
+TEST(Mask, FromOne) {
+ Mask<Privilege> privs = Privilege::kPriv1;
+ EXPECT_EQ(1u, privs.UncheckedValue());
+ EXPECT_TRUE(privs & Privilege::kPriv1);
+ EXPECT_FALSE(privs & Privilege::kPriv4);
+ EXPECT_FALSE(privs & Privilege::kPriv8);
+ EXPECT_TRUE(privs & kAllMask);
+}
+
+TEST(Mask, FromTwo) {
+ // Not adjacent bits, just because.
+ Mask<Privilege> privs = {Privilege::kPriv1, Privilege::kPriv8};
+ EXPECT_EQ(9u, privs.UncheckedValue());
+ EXPECT_TRUE(privs & Privilege::kPriv1);
+ EXPECT_FALSE(privs & Privilege::kPriv4);
+ EXPECT_TRUE(privs & Privilege::kPriv8);
+ EXPECT_TRUE(privs & kAllMask);
+}
+
+TEST(Mask, FromThree) {
+ Mask<Privilege> privs = {
+ Privilege::kPriv1,
+ Privilege::kPriv2,
+ Privilege::kPriv4,
+ };
+ EXPECT_EQ(7u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromFour) {
+ Mask<Privilege> privs = {
+ Privilege::kPriv1,
+ Privilege::kPriv2,
+ Privilege::kPriv4,
+ Privilege::kPriv8,
+ };
+ EXPECT_EQ(15u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromFive) {
+ Mask<Privilege> privs = {
+ Privilege::kPriv1, Privilege::kPriv2, Privilege::kPriv4,
+ Privilege::kPriv8, Privilege::kPriv16,
+ };
+ EXPECT_EQ(31u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromSix) {
+ Mask<Privilege> privs = {
+ Privilege::kPriv1, Privilege::kPriv2, Privilege::kPriv4,
+ Privilege::kPriv8, Privilege::kPriv16, Privilege::kPriv32,
+ };
+ EXPECT_EQ(63u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromSeven) {
+ Mask<Privilege> privs = {
+ Privilege::kPriv1, Privilege::kPriv2, Privilege::kPriv4,
+ Privilege::kPriv8, Privilege::kPriv16, Privilege::kPriv32,
+ Privilege::kPriv64,
+ };
+ EXPECT_EQ(127u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromEight) {
+ Mask<Privilege> privs = {
+ Privilege::kPriv1, Privilege::kPriv2, Privilege::kPriv4,
+ Privilege::kPriv8, Privilege::kPriv16, Privilege::kPriv32,
+ Privilege::kPriv64, Privilege::kPriv128,
+ };
+ EXPECT_EQ(255u, privs.UncheckedValue());
+}
+
+TEST(Mask, FromUnderlying) {
+ auto privs = Mask<Privilege>::FromUnderlyingUnchecked(5);
+ EXPECT_EQ(5u, privs.UncheckedValue());
+ EXPECT_TRUE(privs & Privilege::kPriv1);
+ EXPECT_TRUE(privs & Privilege::kPriv4);
+ EXPECT_FALSE(privs & Privilege::kPriv8);
+}
+
+TEST(Mask, AssignAndEQ) {
+ Mask<Privilege> source = {Privilege::kPriv1, Privilege::kPriv8};
+ Mask<Privilege> other = Privilege::kPriv1;
+ Mask<Privilege> dest;
+ dest = source;
+ EXPECT_EQ(9u, dest.UncheckedValue());
+ EXPECT_EQ(source, dest);
+ EXPECT_NE(other, dest);
+}
+
+TEST(Mask, OrAndAnd) {
+ Mask<Privilege> source = {Privilege::kPriv1, Privilege::kPriv8};
+ Mask<Privilege> or_result =
+ source | Mask<Privilege>{Privilege::kPriv1, Privilege::kPriv4};
+ Mask<Privilege> and_result =
+ source & Mask<Privilege>{Privilege::kPriv1, Privilege::kPriv4};
+ EXPECT_EQ(13u, or_result.UncheckedValue());
+ EXPECT_EQ(1u, and_result.UncheckedValue());
+}
+
+TEST(Mask, OrEqualsAndAndEquals) {
+ Mask<Privilege> source_or = {Privilege::kPriv1, Privilege::kPriv8};
+ Mask<Privilege> source_and = {Privilege::kPriv1, Privilege::kPriv8};
+ source_or |= {Privilege::kPriv1, Privilege::kPriv4};
+ source_and &= {Privilege::kPriv1, Privilege::kPriv4};
+ EXPECT_EQ(13u, source_or.UncheckedValue());
+ EXPECT_EQ(1u, source_and.UncheckedValue());
+}
+
+TEST(Mask, Clear) {
+ Mask<Privilege> source = kAllMask;
+ source.Clear({Privilege::kPriv1, Privilege::kPriv4});
+ EXPECT_EQ(250u, source.UncheckedValue());
+}
+
+TEST(Mask, TestAll) {
+ Mask<Privilege> source = {
+ Privilege::kPriv1,
+ Privilege::kPriv8,
+ Privilege::kPriv64,
+ };
+ Mask<Privilege> passes = {Privilege::kPriv1, Privilege::kPriv64};
+ Mask<Privilege> fails = {Privilege::kPriv1, Privilege::kPriv32};
+ EXPECT_TRUE(source.TestAll(passes));
+ EXPECT_FALSE(source.TestAll(fails));
+}
+
+} // namespace fxcrt
diff --git a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp b/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp
index 27ef26a..525f139 100644
--- a/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp
+++ b/fpdfsdk/fpdfxfa/cpdfxfa_widgethandler.cpp
@@ -651,7 +651,7 @@
}
FWL_KeyFlagMask CPDFXFA_WidgetHandler::GetKeyFlags(FWL_EventFlagMask dwFlag) {
- FWL_KeyFlagMask dwFWLFlag = 0;
+ FWL_KeyFlagMask dwFWLFlag;
if (dwFlag & FWL_EVENTFLAG_ControlKey)
dwFWLFlag |= FWL_KEYFLAG_Ctrl;
diff --git a/xfa/fwl/cfwl_edit.cpp b/xfa/fwl/cfwl_edit.cpp
index 1c3fb9e..a43f9f8 100644
--- a/xfa/fwl/cfwl_edit.cpp
+++ b/xfa/fwl/cfwl_edit.cpp
@@ -40,9 +40,9 @@
constexpr int kEditMargin = 3;
#if defined(OS_APPLE)
-constexpr FWL_KeyFlagMask kEditingModifier = FWL_KEYFLAG_Command;
+constexpr FWL_KeyFlag kEditingModifier = FWL_KEYFLAG_Command;
#else
-constexpr FWL_KeyFlagMask kEditingModifier = FWL_KEYFLAG_Ctrl;
+constexpr FWL_KeyFlag kEditingModifier = FWL_KEYFLAG_Ctrl;
#endif
} // namespace
diff --git a/xfa/fwl/cfwl_message.h b/xfa/fwl/cfwl_message.h
index 0e4c65d..3027c45 100644
--- a/xfa/fwl/cfwl_message.h
+++ b/xfa/fwl/cfwl_message.h
@@ -9,6 +9,7 @@
#include <type_traits>
+#include "core/fxcrt/mask.h"
#include "core/fxcrt/unowned_ptr.h"
#include "v8/include/cppgc/macros.h"
@@ -23,7 +24,7 @@
FWL_KEYFLAG_RButton = 1 << 5,
FWL_KEYFLAG_MButton = 1 << 6
};
-using FWL_KeyFlagMask = std::underlying_type<FWL_KeyFlag>::type;
+using FWL_KeyFlagMask = Mask<FWL_KeyFlag>;
class CFWL_Message {
CPPGC_STACK_ALLOCATED(); // Allow Raw/Unowned pointers.
diff --git a/xfa/fwl/cfwl_messagemouse.h b/xfa/fwl/cfwl_messagemouse.h
index cfeb5b4..05e4e49 100644
--- a/xfa/fwl/cfwl_messagemouse.h
+++ b/xfa/fwl/cfwl_messagemouse.h
@@ -32,7 +32,7 @@
~CFWL_MessageMouse() override;
const MouseCommand m_dwCmd;
- FWL_KeyFlagMask m_dwFlags = 0;
+ FWL_KeyFlagMask m_dwFlags;
CFX_PointF m_pos;
};
diff --git a/xfa/fwl/cfwl_notedriver.cpp b/xfa/fwl/cfwl_notedriver.cpp
index d2a2059..f39fb37 100644
--- a/xfa/fwl/cfwl_notedriver.cpp
+++ b/xfa/fwl/cfwl_notedriver.cpp
@@ -234,8 +234,8 @@
CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
if (m_pHover) {
CFWL_MessageMouse msLeave(
- m_pHover.Get(), CFWL_MessageMouse::MouseCommand::kLeave, 0,
- pTarget->TransformTo(m_pHover.Get(), pMsg->m_pos));
+ m_pHover.Get(), CFWL_MessageMouse::MouseCommand::kLeave,
+ FWL_KeyFlagMask(), pTarget->TransformTo(m_pHover.Get(), pMsg->m_pos));
DispatchMessage(&msLeave, nullptr);
}
if (pTarget->GetClassID() == FWL_Type::Form) {
@@ -244,8 +244,8 @@
}
m_pHover = pTarget;
- CFWL_MessageMouse msHover(pTarget, CFWL_MessageMouse::MouseCommand::kHover, 0,
- pMsg->m_pos);
+ CFWL_MessageMouse msHover(pTarget, CFWL_MessageMouse::MouseCommand::kHover,
+ FWL_KeyFlagMask(), pMsg->m_pos);
DispatchMessage(&msHover, nullptr);
}
diff --git a/xfa/fxfa/cxfa_fffield.cpp b/xfa/fxfa/cxfa_fffield.cpp
index e8cc8c8..bd540e8 100644
--- a/xfa/fxfa/cxfa_fffield.cpp
+++ b/xfa/fxfa/cxfa_fffield.cpp
@@ -372,8 +372,8 @@
return false;
CFWL_MessageMouse msg(GetNormalWidget(),
- CFWL_MessageMouse::MouseCommand::kEnter, 0,
- CFX_PointF());
+ CFWL_MessageMouse::MouseCommand::kEnter,
+ FWL_KeyFlagMask(), CFX_PointF());
SendMessageToFWLWidget(&msg);
return true;
}
@@ -383,8 +383,8 @@
return false;
CFWL_MessageMouse msg(GetNormalWidget(),
- CFWL_MessageMouse::MouseCommand::kLeave, 0,
- CFX_PointF());
+ CFWL_MessageMouse::MouseCommand::kLeave,
+ FWL_KeyFlagMask(), CFX_PointF());
SendMessageToFWLWidget(&msg);
return true;
}