/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_StyleContextSource_h #define mozilla_StyleContextSource_h #include "mozilla/ServoBindingTypes.h" #include "nsRuleNode.h" namespace mozilla { // Tagged union between Gecko Rule Nodes and Servo Computed Values. // // The rule node is the node in the lexicographic tree of rule nodes // (the "rule tree") that indicates which style rules are used to // compute the style data, and in what cascading order. The least // specific rule matched is the one whose rule node is a child of the // root of the rule tree, and the most specific rule matched is the // |mRule| member of the rule node. // // In the Servo case, we hold an atomically-refcounted handle to a // Servo ComputedValues struct, which is more or less the Servo equivalent // of an nsStyleContext. // Underlying pointer without any strong ownership semantics. struct NonOwningStyleContextSource { MOZ_IMPLICIT NonOwningStyleContextSource(nsRuleNode* aRuleNode) : mBits(reinterpret_cast(aRuleNode)) {} explicit NonOwningStyleContextSource(const ServoComputedValues* aComputedValues) : mBits(reinterpret_cast(aComputedValues) | 1) {} bool operator==(const NonOwningStyleContextSource& aOther) const { MOZ_ASSERT(IsServoComputedValues() == aOther.IsServoComputedValues(), "Comparing Servo to Gecko - probably a bug"); return mBits == aOther.mBits; } bool operator!=(const NonOwningStyleContextSource& aOther) const { return !(*this == aOther); } // We intentionally avoid exposing IsGeckoRuleNode() here, because that would // encourage callers to do: // // if (source.IsGeckoRuleNode()) { // // Code that we would run unconditionally if it weren't for Servo. // } // // We want these branches to compile away when MOZ_STYLO is disabled, but that // won't happen if there's an implicit null-check. bool IsNull() const { return !mBits; } bool IsGeckoRuleNodeOrNull() const { return !IsServoComputedValues(); } bool IsServoComputedValues() const { #ifdef MOZ_STYLO return mBits & 1; #else return false; #endif } nsRuleNode* AsGeckoRuleNode() const { MOZ_ASSERT(IsGeckoRuleNodeOrNull() && !IsNull()); return reinterpret_cast(mBits); } const ServoComputedValues* AsServoComputedValues() const { MOZ_ASSERT(IsServoComputedValues()); return reinterpret_cast(mBits & ~1); } bool MatchesNoRules() const { if (IsGeckoRuleNodeOrNull()) { return AsGeckoRuleNode()->IsRoot(); } // Just assume a Servo-backed StyleContextSource always matches some rules. // // MatchesNoRules is used to ensure style contexts that match no rules // go into a separate mEmptyChild list on their parent. This is only used // as an optimization so that calling FindChildWithRules for style context // sharing is faster for text nodes (which match no rules, and are common). // Since Servo will handle sharing for us, there's no need to split children // into two lists. return false; } private: uintptr_t mBits; }; // Higher-level struct that owns a strong reference to the source. The source // is never null. struct OwningStyleContextSource { explicit OwningStyleContextSource(already_AddRefed aRuleNode) : mRaw(aRuleNode.take()) { MOZ_COUNT_CTOR(OwningStyleContextSource); MOZ_ASSERT(!mRaw.IsNull()); }; explicit OwningStyleContextSource(already_AddRefed aComputedValues) : mRaw(aComputedValues.take()) { MOZ_COUNT_CTOR(OwningStyleContextSource); MOZ_ASSERT(!mRaw.IsNull()); } OwningStyleContextSource(OwningStyleContextSource&& aOther) : mRaw(aOther.mRaw) { MOZ_COUNT_CTOR(OwningStyleContextSource); aOther.mRaw = nullptr; } OwningStyleContextSource& operator=(OwningStyleContextSource&) = delete; OwningStyleContextSource(OwningStyleContextSource&) = delete; ~OwningStyleContextSource() { MOZ_COUNT_DTOR(OwningStyleContextSource); if (mRaw.IsNull()) { // We must have invoked the move constructor. } else if (IsGeckoRuleNode()) { RefPtr releaseme = dont_AddRef(AsGeckoRuleNode()); } else { MOZ_ASSERT(IsServoComputedValues()); RefPtr releaseme = dont_AddRef(AsServoComputedValues()); } } bool operator==(const OwningStyleContextSource& aOther) const { return mRaw == aOther.mRaw; } bool operator!=(const OwningStyleContextSource& aOther) const { return !(*this == aOther); } bool IsNull() const { return mRaw.IsNull(); } bool IsGeckoRuleNode() const { MOZ_ASSERT(!mRaw.IsNull()); return mRaw.IsGeckoRuleNodeOrNull(); } bool IsServoComputedValues() const { return mRaw.IsServoComputedValues(); } NonOwningStyleContextSource AsRaw() const { return mRaw; } nsRuleNode* AsGeckoRuleNode() const { return mRaw.AsGeckoRuleNode(); } ServoComputedValues* AsServoComputedValues() const { return const_cast(mRaw.AsServoComputedValues()); } bool MatchesNoRules() const { return mRaw.MatchesNoRules(); } private: NonOwningStyleContextSource mRaw; }; } // namespace mozilla #endif // mozilla_StyleContextSource_h