/* -*- 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 AccAttributes_h_
#define AccAttributes_h_
#include "mozilla/ServoStyleConsts.h"
#include "mozilla/a11y/AccGroupInfo.h"
#include "mozilla/Variant.h"
#include "nsTHashMap.h"
#include "nsStringFwd.h"
#include "mozilla/gfx/Matrix.h"
class nsVariant;
namespace IPC {
template <
typename T>
struct ParamTraits;
}
// namespace IPC
namespace mozilla {
namespace dom {
class Element;
}
namespace a11y {
struct FontSize {
int32_t mValue;
bool operator==(
const FontSize& aOther)
const {
return mValue == aOther.mValue;
}
bool operator!=(
const FontSize& aOther)
const {
return mValue != aOther.mValue;
}
};
struct Color {
nscolor mValue;
bool operator==(
const Color& aOther)
const {
return mValue == aOther.mValue; }
bool operator!=(
const Color& aOther)
const {
return mValue != aOther.mValue; }
};
// A special type. If an entry has a value of this type, it instructs the
// target instance of an Update to remove the entry with the same key value.
struct DeleteEntry {
DeleteEntry() : mValue(
true) {}
bool mValue;
bool operator==(
const DeleteEntry& aOther)
const {
return true; }
bool operator!=(
const DeleteEntry& aOther)
const {
return false; }
};
/**
* An attribute that applies to an offset range in a text leaf. This allows it
* to span only part of a text leaf. This is used for spelling errors,
* highlights, etc. which are mapped to DOM selections. This is in contrast to
* most other attributes which can only apply to an entire text leaf and so just
* reside on the leaf itself, rather than requiring offsets.
*/
struct TextOffsetAttribute {
// The offset in the text leaf where the attribute starts. If this is -1, the
// attribute begins before this leaf, crossing Accessibles.
int32_t mStartOffset;
// The offset in the text leaf where the attribute ends (exclusive). If this
// is -1, the attribute ends after this leaf, crossing Accessibles.
int32_t mEndOffset;
// The attribute:
// nsGkAtoms::mark: Semantic highlights such as text fragments.
// nsGkAtoms::spelling: spelling errors
RefPtr<nsAtom> mAttribute;
bool operator==(
const TextOffsetAttribute& aOther)
const {
return mStartOffset == aOther.mStartOffset &&
mEndOffset == aOther.mEndOffset && mAttribute == aOther.mAttribute;
}
bool operator!=(
const TextOffsetAttribute& aOther)
const {
return !(*
this == aOther);
}
bool operator<(
const TextOffsetAttribute& aOther)
const {
return mStartOffset < aOther.mStartOffset;
}
};
class AccAttributes {
// Warning! An AccAttributes can contain another AccAttributes. This is
// intended for object and text attributes. However, the nested
// AccAttributes should never itself contain another AccAttributes, nor
// should it create a cycle. We don't do cycle collection here for
// performance reasons, so violating this rule will cause leaks!
using AttrValueType =
Variant<
bool,
float,
double, int32_t, RefPtr<nsAtom>, nsTArray<int32_t>,
CSSCoord, FontSize, Color, DeleteEntry, UniquePtr<nsString>,
RefPtr<AccAttributes>, uint64_t, UniquePtr<AccGroupInfo>,
UniquePtr<gfx::Matrix4x4>, nsTArray<uint64_t>,
nsTArray<TextOffsetAttribute>>;
static_assert(
sizeof(AttrValueType) <= 16);
using AtomVariantMap = nsTHashMap<RefPtr<nsAtom>, AttrValueType>;
protected:
~AccAttributes() =
default;
public:
AccAttributes() =
default;
AccAttributes(
const AccAttributes&) =
delete;
AccAttributes&
operator=(
const AccAttributes&) =
delete;
NS_INLINE_DECL_REFCOUNTING(mozilla::a11y::AccAttributes)
template <
typename T>
void SetAttribute(nsAtom* aAttrName, T&& aAttrValue) {
using ValType =
std::remove_const_t<std::remove_reference_t<decltype(aAttrValue)>>;
if constexpr (std::is_convertible_v<ValType, nsString>) {
static_assert(std::is_rvalue_reference_v<decltype(aAttrValue)>,
"Please only move strings into this function. To make a "
"copy, use SetAttributeStringCopy.");
UniquePtr<nsString> value = MakeUnique<nsString>(std::move(aAttrValue));
mData.InsertOrUpdate(aAttrName, AsVariant(std::move(value)));
}
else if constexpr (std::is_same_v<ValType, gfx::Matrix4x4>) {
UniquePtr<gfx::Matrix4x4> value = MakeUnique<gfx::Matrix4x4>(aAttrValue);
mData.InsertOrUpdate(aAttrName, AsVariant(std::move(value)));
}
else if constexpr (std::is_same_v<ValType, AccGroupInfo*>) {
UniquePtr<AccGroupInfo> value(aAttrValue);
mData.InsertOrUpdate(aAttrName, AsVariant(std::move(value)));
}
else if constexpr (std::is_convertible_v<ValType, nsAtom*>) {
mData.InsertOrUpdate(aAttrName, AsVariant(RefPtr<nsAtom>(aAttrValue)));
}
else {
mData.InsertOrUpdate(aAttrName, AsVariant(std::forward<T>(aAttrValue)));
}
}
void SetAttributeStringCopy(nsAtom* aAttrName, nsString aAttrValue) {
SetAttribute(aAttrName, std::move(aAttrValue));
}
template <
typename T>
Maybe<
const T&> GetAttribute(nsAtom* aAttrName)
const {
if (
auto value = mData.Lookup(aAttrName)) {
if constexpr (std::is_same_v<nsString, T>) {
if (value->is<UniquePtr<nsString>>()) {
const T& val = *(value->as<UniquePtr<nsString>>().get());
return SomeRef(val);
}
}
else if constexpr (std::is_same_v<gfx::Matrix4x4, T>) {
if (value->is<UniquePtr<gfx::Matrix4x4>>()) {
const T& val = *(value->as<UniquePtr<gfx::Matrix4x4>>());
return SomeRef(val);
}
}
else {
if (value->is<T>()) {
const T& val = value->as<T>();
return SomeRef(val);
}
}
}
return Nothing();
}
template <
typename T>
RefPtr<
const T> GetAttributeRefPtr(nsAtom* aAttrName)
const {
if (
auto value = mData.Lookup(aAttrName)) {
if (value->is<RefPtr<T>>()) {
RefPtr<
const T> ref = value->as<RefPtr<T>>();
return ref;
}
}
return nullptr;
}
template <
typename T>
Maybe<T&> GetMutableAttribute(nsAtom* aAttrName)
const {
static_assert(std::is_same_v<nsTArray<int32_t>, T> ||
std::is_same_v<nsTArray<uint64_t>, T>,
"Only arrays should be mutable attributes");
if (
auto value = mData.Lookup(aAttrName)) {
if (value->is<T>()) {
T& val = value->as<T>();
return SomeRef(val);
}
}
return Nothing();
}
// Get stringified value
bool GetAttribute(nsAtom* aAttrName, nsAString& aAttrValue)
const;
bool HasAttribute(nsAtom* aAttrName)
const {
return mData.Contains(aAttrName);
}
bool Remove(nsAtom* aAttrName) {
return mData.Remove(aAttrName); }
uint32_t Count()
const {
return mData.Count(); }
// Update one instance with the entries in another. The supplied AccAttributes
// will be emptied.
void Update(AccAttributes* aOther);
/**
* Return true if all the attributes in this instance are equal to all the
* attributes in another instance.
*/
bool Equal(
const AccAttributes* aOther)
const;
/**
* Copy attributes from this instance to another instance.
* This should only be used in very specific cases; e.g. merging two sets of
* cached attributes without modifying the cache. It can only copy simple
* value types; e.g. it can't copy array values. Attempting to copy an
* AccAttributes with uncopyable values will cause an assertion.
*/
void CopyTo(AccAttributes* aDest)
const;
// An entry class for our iterator.
class Entry {
public:
Entry(nsAtom* aAttrName,
const AttrValueType* aAttrValue)
: mName(aAttrName), mValue(aAttrValue) {}
nsAtom* Name()
const {
return mName; }
template <
typename T>
Maybe<
const T&> Value()
const {
if constexpr (std::is_same_v<nsString, T>) {
if (mValue->is<UniquePtr<nsString>>()) {
const T& val = *(mValue->as<UniquePtr<nsString>>().get());
return SomeRef(val);
}
}
else if constexpr (std::is_same_v<gfx::Matrix4x4, T>) {
if (mValue->is<UniquePtr<gfx::Matrix4x4>>()) {
const T& val = *(mValue->as<UniquePtr<gfx::Matrix4x4>>());
return SomeRef(val);
}
}
else {
if (mValue->is<T>()) {
const T& val = mValue->as<T>();
return SomeRef(val);
}
}
return Nothing();
}
void NameAsString(nsString& aName)
const {
mName->ToString(aName);
if (StringBeginsWith(aName, u
"aria-"_ns)) {
// Found 'aria-'
aName.ReplaceLiteral(0, 5, u
"");
}
}
void ValueAsString(nsAString& aValueString)
const {
StringFromValueAndName(mName, *mValue, aValueString);
}
// Size of the pair in the hash table.
size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf);
private:
nsAtom* mName;
const AttrValueType* mValue;
friend class AccAttributes;
};
class Iterator {
public:
explicit Iterator(AtomVariantMap::const_iterator aIter)
: mHashIterator(aIter) {}
Iterator() =
delete;
Iterator(
const Iterator&) =
delete;
Iterator&
operator=(
const Iterator&) =
delete;
bool operator!=(
const Iterator& aOther)
const {
return mHashIterator != aOther.mHashIterator;
}
Iterator&
operator++() {
mHashIterator++;
return *
this;
}
Entry
operator*()
const {
auto& entry = *mHashIterator;
return Entry(entry.GetKey(), &entry.GetData());
}
private:
AtomVariantMap::const_iterator mHashIterator;
};
friend class Iterator;
Iterator begin()
const {
return Iterator(mData.begin()); }
Iterator end()
const {
return Iterator(mData.end()); }
#ifdef A11Y_LOG
static void DebugPrint(
const char* aPrefix,
const AccAttributes& aAttributes);
#endif
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf);
private:
static void StringFromValueAndName(nsAtom* aAttrName,
const AttrValueType& aValue,
nsAString& aValueString);
// Opts AccAttributes into the common ToString function.
friend std::ostream&
operator<<(std::ostream& aStream,
const AccAttributes& aAttributes);
AtomVariantMap mData;
friend struct IPC::ParamTraits<AccAttributes*>;
};
}
// namespace a11y
}
// namespace mozilla
#endif