mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			382 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			382 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* 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_CounterStyleManager_h_
 | 
						|
#define mozilla_CounterStyleManager_h_
 | 
						|
 | 
						|
#include "nsAtom.h"
 | 
						|
#include "nsGkAtoms.h"
 | 
						|
#include "nsStringFwd.h"
 | 
						|
#include "nsTHashMap.h"
 | 
						|
#include "nsHashKeys.h"
 | 
						|
 | 
						|
#include "nsStyleConsts.h"
 | 
						|
 | 
						|
#include "mozilla/Attributes.h"
 | 
						|
 | 
						|
class nsPresContext;
 | 
						|
 | 
						|
namespace mozilla {
 | 
						|
 | 
						|
enum class SpeakAs : uint8_t {
 | 
						|
  Bullets = 0,
 | 
						|
  Numbers = 1,
 | 
						|
  Words = 2,
 | 
						|
  Spellout = 3,
 | 
						|
  Other = 255
 | 
						|
};
 | 
						|
 | 
						|
class WritingMode;
 | 
						|
 | 
						|
typedef int32_t CounterValue;
 | 
						|
 | 
						|
class CounterStyleManager;
 | 
						|
class AnonymousCounterStyle;
 | 
						|
 | 
						|
struct NegativeType;
 | 
						|
struct PadType;
 | 
						|
 | 
						|
class CounterStyle {
 | 
						|
 protected:
 | 
						|
  explicit constexpr CounterStyle(ListStyle aStyle) : mStyle(aStyle) {}
 | 
						|
 | 
						|
 private:
 | 
						|
  CounterStyle(const CounterStyle& aOther) = delete;
 | 
						|
  void operator=(const CounterStyle& other) = delete;
 | 
						|
 | 
						|
 public:
 | 
						|
  constexpr ListStyle GetStyle() const { return mStyle; }
 | 
						|
  bool IsNone() const { return mStyle == ListStyle::None; }
 | 
						|
  bool IsCustomStyle() const { return mStyle == ListStyle::Custom; }
 | 
						|
  // A style is dependent if it depends on the counter style manager.
 | 
						|
  // Custom styles are certainly dependent. In addition, some builtin
 | 
						|
  // styles are dependent for fallback.
 | 
						|
  bool IsDependentStyle() const;
 | 
						|
 | 
						|
  // Note: caller is responsible to handle range limits and fallback, so that
 | 
						|
  // the ordinal value passed to GetPrefix or GetSuffix is within the style's
 | 
						|
  // supported range.
 | 
						|
  void GetPrefix(CounterValue aOrdinal, nsAString& aResult) {
 | 
						|
    MOZ_ASSERT(IsOrdinalInRange(aOrdinal),
 | 
						|
               "caller should have handled range fallback");
 | 
						|
    GetPrefixInternal(aResult);
 | 
						|
  }
 | 
						|
  void GetSuffix(CounterValue aOrdinal, nsAString& aResult) {
 | 
						|
    MOZ_ASSERT(IsOrdinalInRange(aOrdinal),
 | 
						|
               "caller should have handled range fallback");
 | 
						|
    GetSuffixInternal(aResult);
 | 
						|
  }
 | 
						|
 | 
						|
  void GetCounterText(CounterValue aOrdinal, WritingMode aWritingMode,
 | 
						|
                      nsAString& aResult, bool& aIsRTL);
 | 
						|
  virtual void GetSpokenCounterText(CounterValue aOrdinal,
 | 
						|
                                    WritingMode aWritingMode,
 | 
						|
                                    nsAString& aResult, bool& aIsBullet);
 | 
						|
 | 
						|
  // XXX This method could be removed once ::-moz-list-bullet and
 | 
						|
  //     ::-moz-list-number are completely merged into ::marker.
 | 
						|
  virtual bool IsBullet() = 0;
 | 
						|
 | 
						|
  virtual void GetNegative(NegativeType& aResult) = 0;
 | 
						|
  /**
 | 
						|
   * This method returns whether an ordinal is in the range of this
 | 
						|
   * counter style. Note that, it is possible that an ordinal in range
 | 
						|
   * is rejected by the generating algorithm.
 | 
						|
   */
 | 
						|
  virtual bool IsOrdinalInRange(CounterValue aOrdinal) = 0;
 | 
						|
  /**
 | 
						|
   * This method returns whether an ordinal is in the default range of
 | 
						|
   * this counter style. This is the effective range when no 'range'
 | 
						|
   * descriptor is specified.
 | 
						|
   */
 | 
						|
  virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) = 0;
 | 
						|
  virtual void GetPad(PadType& aResult) = 0;
 | 
						|
  virtual CounterStyle* GetFallback() = 0;
 | 
						|
  virtual SpeakAs GetSpeakAs() = 0;
 | 
						|
  virtual bool UseNegativeSign() = 0;
 | 
						|
 | 
						|
  virtual void CallFallbackStyle(CounterValue aOrdinal,
 | 
						|
                                 WritingMode aWritingMode, nsAString& aResult,
 | 
						|
                                 bool& aIsRTL);
 | 
						|
  virtual bool GetInitialCounterText(CounterValue aOrdinal,
 | 
						|
                                     WritingMode aWritingMode,
 | 
						|
                                     nsAString& aResult, bool& aIsRTL) = 0;
 | 
						|
 | 
						|
  virtual AnonymousCounterStyle* AsAnonymous() { return nullptr; }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This returns the counter-style that should handle the given ordinal value,
 | 
						|
   * following the fallback chain if necessary.
 | 
						|
   */
 | 
						|
  virtual CounterStyle* ResolveFallbackFor(CounterValue aOrdinal);
 | 
						|
 | 
						|
 protected:
 | 
						|
  friend class CustomCounterStyle;
 | 
						|
  // Get the style's prefix or suffix text, without checking range coverage or
 | 
						|
  // handling fallback (but following 'extends' relations as needed).
 | 
						|
  virtual void GetPrefixInternal(nsAString& aResult) = 0;
 | 
						|
  virtual void GetSuffixInternal(nsAString& aResult) = 0;
 | 
						|
 | 
						|
  const ListStyle mStyle;
 | 
						|
};
 | 
						|
 | 
						|
class AnonymousCounterStyle final : public CounterStyle {
 | 
						|
 public:
 | 
						|
  explicit AnonymousCounterStyle(const nsAString& aContent);
 | 
						|
  AnonymousCounterStyle(StyleSymbolsType, nsTArray<nsString> aSymbols);
 | 
						|
 | 
						|
  virtual bool IsBullet() override;
 | 
						|
 | 
						|
  virtual void GetNegative(NegativeType& aResult) override;
 | 
						|
  virtual bool IsOrdinalInRange(CounterValue aOrdinal) override;
 | 
						|
  virtual bool IsOrdinalInAutoRange(CounterValue aOrdinal) override;
 | 
						|
  virtual void GetPad(PadType& aResult) override;
 | 
						|
  virtual CounterStyle* GetFallback() override;
 | 
						|
  virtual SpeakAs GetSpeakAs() override;
 | 
						|
  virtual bool UseNegativeSign() override;
 | 
						|
 | 
						|
  virtual bool GetInitialCounterText(CounterValue aOrdinal,
 | 
						|
                                     WritingMode aWritingMode,
 | 
						|
                                     nsAString& aResult, bool& aIsRTL) override;
 | 
						|
 | 
						|
  virtual AnonymousCounterStyle* AsAnonymous() override { return this; }
 | 
						|
 | 
						|
  bool IsSingleString() const { return mSingleString; }
 | 
						|
  auto GetSymbols() const { return Span<const nsString>{mSymbols}; }
 | 
						|
 | 
						|
  StyleCounterSystem GetSystem() const;
 | 
						|
 | 
						|
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AnonymousCounterStyle)
 | 
						|
 | 
						|
 protected:
 | 
						|
  virtual void GetPrefixInternal(nsAString& aResult) override;
 | 
						|
  virtual void GetSuffixInternal(nsAString& aResult) override;
 | 
						|
 | 
						|
 private:
 | 
						|
  ~AnonymousCounterStyle() = default;
 | 
						|
 | 
						|
  bool mSingleString;
 | 
						|
  StyleSymbolsType mSymbolsType;
 | 
						|
  nsTArray<nsString> mSymbols;
 | 
						|
};
 | 
						|
 | 
						|
// A smart pointer to CounterStyle. It either owns a reference to an
 | 
						|
// anonymous counter style, or weakly refers to a named counter style
 | 
						|
// managed by counter style manager.
 | 
						|
class CounterStylePtr {
 | 
						|
 public:
 | 
						|
  CounterStylePtr() : mRaw(0) {}
 | 
						|
  CounterStylePtr(const CounterStylePtr& aOther) : mRaw(aOther.mRaw) {
 | 
						|
    if (!mRaw) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    switch (GetType()) {
 | 
						|
      case eAnonymousCounterStyle:
 | 
						|
        AsAnonymous()->AddRef();
 | 
						|
        break;
 | 
						|
      case eAtom:
 | 
						|
        AsAtom()->AddRef();
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        MOZ_ASSERT_UNREACHABLE("Unknown type");
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  CounterStylePtr(CounterStylePtr&& aOther) : mRaw(aOther.mRaw) {
 | 
						|
    aOther.mRaw = 0;
 | 
						|
  }
 | 
						|
  ~CounterStylePtr() { Reset(); }
 | 
						|
 | 
						|
  CounterStylePtr& operator=(const CounterStylePtr& aOther) {
 | 
						|
    if (this != &aOther) {
 | 
						|
      Reset();
 | 
						|
      new (this) CounterStylePtr(aOther);
 | 
						|
    }
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  CounterStylePtr& operator=(CounterStylePtr&& aOther) {
 | 
						|
    if (this != &aOther) {
 | 
						|
      Reset();
 | 
						|
      mRaw = aOther.mRaw;
 | 
						|
      aOther.mRaw = 0;
 | 
						|
    }
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  CounterStylePtr& operator=(decltype(nullptr)) {
 | 
						|
    Reset();
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  CounterStylePtr& operator=(nsStaticAtom* aStaticAtom) {
 | 
						|
    Reset();
 | 
						|
    mRaw = reinterpret_cast<uintptr_t>(aStaticAtom) | eAtom;
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  CounterStylePtr& operator=(already_AddRefed<nsAtom> aAtom) {
 | 
						|
    Reset();
 | 
						|
    mRaw = reinterpret_cast<uintptr_t>(aAtom.take()) | eAtom;
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
  CounterStylePtr& operator=(AnonymousCounterStyle* aCounterStyle) {
 | 
						|
    Reset();
 | 
						|
    if (aCounterStyle) {
 | 
						|
      CounterStyle* raw = do_AddRef(aCounterStyle).take();
 | 
						|
      mRaw = reinterpret_cast<uintptr_t>(raw) | eAnonymousCounterStyle;
 | 
						|
    }
 | 
						|
    return *this;
 | 
						|
  }
 | 
						|
 | 
						|
  // TODO(emilio): Make CounterStyle have a single representation, either by
 | 
						|
  // removing CounterStylePtr or by moving this representation to Rust.
 | 
						|
  static CounterStylePtr FromStyle(const StyleCounterStyle& aStyle) {
 | 
						|
    CounterStylePtr ret;
 | 
						|
    if (aStyle.IsName()) {
 | 
						|
      ret = do_AddRef(aStyle.AsName().AsAtom());
 | 
						|
    } else {
 | 
						|
      StyleSymbolsType type = aStyle.AsSymbols()._0;
 | 
						|
      Span<const StyleSymbol> symbols = aStyle.AsSymbols()._1._0.AsSpan();
 | 
						|
      nsTArray<nsString> transcoded(symbols.Length());
 | 
						|
      for (const auto& symbol : symbols) {
 | 
						|
        MOZ_ASSERT(symbol.IsString(), "Should not have <ident> in symbols()");
 | 
						|
        transcoded.AppendElement(
 | 
						|
            NS_ConvertUTF8toUTF16(symbol.AsString().AsString()));
 | 
						|
      }
 | 
						|
      ret = new AnonymousCounterStyle(type, std::move(transcoded));
 | 
						|
    }
 | 
						|
    return ret;
 | 
						|
  }
 | 
						|
 | 
						|
  explicit operator bool() const { return !!mRaw; }
 | 
						|
  bool operator!() const { return !mRaw; }
 | 
						|
  bool operator==(const CounterStylePtr& aOther) const {
 | 
						|
    // FIXME(emilio): For atoms this is all right, but for symbols doesn't this
 | 
						|
    // cause us to compare as unequal all the time, even if the specified
 | 
						|
    // symbols didn't change?
 | 
						|
    return mRaw == aOther.mRaw;
 | 
						|
  }
 | 
						|
  bool operator!=(const CounterStylePtr& aOther) const {
 | 
						|
    return mRaw != aOther.mRaw;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAtom* AsAtom() const {
 | 
						|
    MOZ_ASSERT(IsAtom());
 | 
						|
    return reinterpret_cast<nsAtom*>(mRaw & ~eMask);
 | 
						|
  }
 | 
						|
  AnonymousCounterStyle* AsAnonymous() const {
 | 
						|
    MOZ_ASSERT(IsAnonymous());
 | 
						|
    return static_cast<AnonymousCounterStyle*>(
 | 
						|
        reinterpret_cast<CounterStyle*>(mRaw & ~eMask));
 | 
						|
  }
 | 
						|
 | 
						|
  bool IsAtom() const { return GetType() == eAtom; }
 | 
						|
  bool IsAnonymous() const { return GetType() == eAnonymousCounterStyle; }
 | 
						|
 | 
						|
  bool IsNone() const { return IsAtom() && AsAtom() == nsGkAtoms::none; }
 | 
						|
 | 
						|
 private:
 | 
						|
  enum Type : uintptr_t {
 | 
						|
    eAnonymousCounterStyle = 0,
 | 
						|
    eAtom = 1,
 | 
						|
    eMask = 1,
 | 
						|
  };
 | 
						|
 | 
						|
  static_assert(alignof(CounterStyle) >= 1 << eMask,
 | 
						|
                "We're gonna tag the pointer, so it better fit");
 | 
						|
  static_assert(alignof(nsAtom) >= 1 << eMask,
 | 
						|
                "We're gonna tag the pointer, so it better fit");
 | 
						|
 | 
						|
  Type GetType() const { return static_cast<Type>(mRaw & eMask); }
 | 
						|
 | 
						|
  void Reset() {
 | 
						|
    if (!mRaw) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    switch (GetType()) {
 | 
						|
      case eAnonymousCounterStyle:
 | 
						|
        AsAnonymous()->Release();
 | 
						|
        break;
 | 
						|
      case eAtom:
 | 
						|
        AsAtom()->Release();
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        MOZ_ASSERT_UNREACHABLE("Unknown type");
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    mRaw = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  // mRaw contains the pointer, and its last bit is used to store the type of
 | 
						|
  // the pointer.
 | 
						|
  // If the type is eAtom, the pointer owns a reference to an nsAtom
 | 
						|
  // (potentially null).
 | 
						|
  // If the type is eAnonymousCounterStyle, it owns a reference to an
 | 
						|
  // anonymous counter style (never null).
 | 
						|
  uintptr_t mRaw;
 | 
						|
};
 | 
						|
 | 
						|
class CounterStyleManager final {
 | 
						|
 private:
 | 
						|
  ~CounterStyleManager();
 | 
						|
 | 
						|
 public:
 | 
						|
  explicit CounterStyleManager(nsPresContext* aPresContext);
 | 
						|
 | 
						|
  void Disconnect();
 | 
						|
 | 
						|
  bool IsInitial() const {
 | 
						|
    // only 'none', 'decimal', and 'disc'
 | 
						|
    return mStyles.Count() == 3;
 | 
						|
  }
 | 
						|
 | 
						|
  // Returns the counter style object for the given name from the style
 | 
						|
  // table if it is already built, and nullptr otherwise.
 | 
						|
  CounterStyle* GetCounterStyle(nsAtom* aName) const {
 | 
						|
    return mStyles.Get(aName);
 | 
						|
  }
 | 
						|
  // Same as GetCounterStyle but try to build the counter style object
 | 
						|
  // rather than returning nullptr if that hasn't been built.
 | 
						|
  CounterStyle* ResolveCounterStyle(nsAtom* aName);
 | 
						|
  CounterStyle* ResolveCounterStyle(const CounterStylePtr& aPtr) {
 | 
						|
    if (aPtr.IsAtom()) {
 | 
						|
      return ResolveCounterStyle(aPtr.AsAtom());
 | 
						|
    }
 | 
						|
    return aPtr.AsAnonymous();
 | 
						|
  }
 | 
						|
 | 
						|
  static CounterStyle* GetBuiltinStyle(ListStyle aStyle);
 | 
						|
  static CounterStyle* GetNoneStyle() {
 | 
						|
    return GetBuiltinStyle(ListStyle::None);
 | 
						|
  }
 | 
						|
  static CounterStyle* GetDecimalStyle() {
 | 
						|
    return GetBuiltinStyle(ListStyle::Decimal);
 | 
						|
  }
 | 
						|
  static CounterStyle* GetDiscStyle() {
 | 
						|
    return GetBuiltinStyle(ListStyle::Disc);
 | 
						|
  }
 | 
						|
 | 
						|
  // This method will scan all existing counter styles generated by this
 | 
						|
  // manager, and remove or mark data dirty accordingly. It returns true
 | 
						|
  // if any counter style is changed, false elsewise. This method should
 | 
						|
  // be called when any counter style may be affected.
 | 
						|
  bool NotifyRuleChanged();
 | 
						|
  // NotifyRuleChanged will evict no longer needed counter styles into
 | 
						|
  // mRetiredStyles, and this function destroys all objects listed there.
 | 
						|
  // It should be called only after no one may ever use those objects.
 | 
						|
  void CleanRetiredStyles();
 | 
						|
 | 
						|
  nsPresContext* PresContext() const { return mPresContext; }
 | 
						|
 | 
						|
  NS_INLINE_DECL_REFCOUNTING(CounterStyleManager)
 | 
						|
 | 
						|
 private:
 | 
						|
  void DestroyCounterStyle(CounterStyle* aCounterStyle);
 | 
						|
 | 
						|
  nsPresContext* mPresContext;
 | 
						|
  nsTHashMap<nsRefPtrHashKey<nsAtom>, CounterStyle*> mStyles;
 | 
						|
  nsTArray<CounterStyle*> mRetiredStyles;
 | 
						|
};
 | 
						|
 | 
						|
}  // namespace mozilla
 | 
						|
 | 
						|
#endif /* !defined(mozilla_CounterStyleManager_h_) */
 |