Files
tubestation/accessible/base/TextAttrs.cpp
Emilio Cobos Álvarez 42a711655b Bug 1773558 - Move fixed-point font types to Rust. r=layout-reviewers,jfkthame
Now that cbindgen and rust support const generics, it seems more simple.

This centralizes all the relevant font constants etc in rust and avoids
conversions when going from rust to C++ and vice versa.

Differential Revision: https://phabricator.services.mozilla.com/D148847
2022-06-13 00:59:23 +00:00

755 lines
26 KiB
C++

/* -*- 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/. */
#include "TextAttrs.h"
#include "LocalAccessible-inl.h"
#include "AccAttributes.h"
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
#include "StyleInfo.h"
#include "gfxTextRun.h"
#include "nsFontMetrics.h"
#include "nsLayoutUtils.h"
#include "nsContainerFrame.h"
#include "nsStyleUtil.h"
#include "HyperTextAccessible.h"
#include "mozilla/AppUnits.h"
#include "mozilla/gfx/2D.h"
using namespace mozilla;
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// TextAttrsMgr
////////////////////////////////////////////////////////////////////////////////
void TextAttrsMgr::GetAttributes(AccAttributes* aAttributes,
uint32_t* aStartOffset, uint32_t* aEndOffset) {
// 1. Hyper text accessible must be specified always.
// 2. Offset accessible must be specified in
// the case of text attributes. Result hyper text offsets are optional if you
// just want the attributes for a single text Accessible.
// 3. Offset accessible and result hyper text offsets must not be specified
// but include default text attributes flag and attributes list must be
// specified in the case of default text attributes.
MOZ_ASSERT(
mHyperTextAcc && ((mOffsetAcc && mOffsetAccIdx != -1) ||
(!mOffsetAcc && mOffsetAccIdx == -1 && !aStartOffset &&
!aEndOffset && mIncludeDefAttrs && aAttributes)),
"Wrong usage of TextAttrsMgr!");
// Embedded objects are combined into own range with empty attributes set.
if (mOffsetAcc && !mOffsetAcc->IsText()) {
if (!aStartOffset) {
return;
}
for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
if (currAcc->IsText()) break;
(*aStartOffset)--;
}
uint32_t childCount = mHyperTextAcc->ChildCount();
for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childCount;
childIdx++) {
LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
if (currAcc->IsText()) break;
(*aEndOffset)++;
}
return;
}
// Get the content and frame of the accessible. In the case of document
// accessible it's role content and root frame.
nsIContent* hyperTextElm = mHyperTextAcc->GetContent();
if (!hyperTextElm) {
return; // XXX: we don't support text attrs on document with no body
}
nsIFrame* rootFrame = mHyperTextAcc->GetFrame();
if (!rootFrame) {
return;
}
nsIContent *offsetNode = nullptr, *offsetElm = nullptr;
nsIFrame* frame = nullptr;
if (mOffsetAcc) {
offsetNode = mOffsetAcc->GetContent();
offsetElm = nsCoreUtils::GetDOMElementFor(offsetNode);
MOZ_ASSERT(offsetElm, "No element for offset accessible!");
if (!offsetElm) return;
frame = offsetElm->GetPrimaryFrame();
}
// "language" text attribute
LangTextAttr langTextAttr(mHyperTextAcc, hyperTextElm, offsetNode);
// "aria-invalid" text attribute
InvalidTextAttr invalidTextAttr(hyperTextElm, offsetNode);
// "background-color" text attribute
BGColorTextAttr bgColorTextAttr(rootFrame, frame);
// "color" text attribute
ColorTextAttr colorTextAttr(rootFrame, frame);
// "font-family" text attribute
FontFamilyTextAttr fontFamilyTextAttr(rootFrame, frame);
// "font-size" text attribute
FontSizeTextAttr fontSizeTextAttr(rootFrame, frame);
// "font-style" text attribute
FontStyleTextAttr fontStyleTextAttr(rootFrame, frame);
// "font-weight" text attribute
FontWeightTextAttr fontWeightTextAttr(rootFrame, frame);
// "auto-generated" text attribute
AutoGeneratedTextAttr autoGenTextAttr(mHyperTextAcc, mOffsetAcc);
// "text-underline(line-through)-style(color)" text attributes
TextDecorTextAttr textDecorTextAttr(rootFrame, frame);
// "text-position" text attribute
TextPosTextAttr textPosTextAttr(rootFrame, frame);
TextAttr* attrArray[] = {
&langTextAttr, &invalidTextAttr, &bgColorTextAttr,
&colorTextAttr, &fontFamilyTextAttr, &fontSizeTextAttr,
&fontStyleTextAttr, &fontWeightTextAttr, &autoGenTextAttr,
&textDecorTextAttr, &textPosTextAttr};
// Expose text attributes if applicable.
if (aAttributes) {
for (uint32_t idx = 0; idx < ArrayLength(attrArray); idx++) {
attrArray[idx]->Expose(aAttributes, mIncludeDefAttrs);
}
}
// Expose text attributes range where they are applied if applicable.
if (aStartOffset) {
GetRange(attrArray, ArrayLength(attrArray), aStartOffset, aEndOffset);
}
}
void TextAttrsMgr::GetRange(TextAttr* aAttrArray[], uint32_t aAttrArrayLen,
uint32_t* aStartOffset, uint32_t* aEndOffset) {
// Navigate backward from anchor accessible to find start offset.
for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
// Stop on embedded accessible since embedded accessibles are combined into
// own range.
if (!currAcc->IsText()) break;
MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()),
"Text accessible has to have an associated DOM element");
bool offsetFound = false;
for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
TextAttr* textAttr = aAttrArray[attrIdx];
if (!textAttr->Equal(currAcc)) {
offsetFound = true;
break;
}
}
if (offsetFound) break;
*(aStartOffset) -= nsAccUtils::TextLength(currAcc);
}
// Navigate forward from anchor accessible to find end offset.
uint32_t childLen = mHyperTextAcc->ChildCount();
for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childLen; childIdx++) {
LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
if (!currAcc->IsText()) break;
MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()),
"Text accessible has to have an associated DOM element");
bool offsetFound = false;
for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
TextAttr* textAttr = aAttrArray[attrIdx];
// Alter the end offset when text attribute changes its value and stop
// the search.
if (!textAttr->Equal(currAcc)) {
offsetFound = true;
break;
}
}
if (offsetFound) break;
(*aEndOffset) += nsAccUtils::TextLength(currAcc);
}
}
////////////////////////////////////////////////////////////////////////////////
// LangTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::LangTextAttr::LangTextAttr(HyperTextAccessible* aRoot,
nsIContent* aRootElm, nsIContent* aElm)
: TTextAttr<nsString>(!aElm), mRootContent(aRootElm) {
aRoot->Language(mRootNativeValue);
mIsRootDefined = !mRootNativeValue.IsEmpty();
if (aElm) {
nsCoreUtils::GetLanguageFor(aElm, mRootContent, mNativeValue);
mIsDefined = !mNativeValue.IsEmpty();
}
}
TextAttrsMgr::LangTextAttr::~LangTextAttr() {}
bool TextAttrsMgr::LangTextAttr::GetValueFor(LocalAccessible* aAccessible,
nsString* aValue) {
nsCoreUtils::GetLanguageFor(aAccessible->GetContent(), mRootContent, *aValue);
return !aValue->IsEmpty();
}
void TextAttrsMgr::LangTextAttr::ExposeValue(AccAttributes* aAttributes,
const nsString& aValue) {
aAttributes->SetAttributeStringCopy(nsGkAtoms::language, aValue);
}
////////////////////////////////////////////////////////////////////////////////
// InvalidTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::InvalidTextAttr::InvalidTextAttr(nsIContent* aRootElm,
nsIContent* aElm)
: TTextAttr<uint32_t>(!aElm), mRootElm(aRootElm) {
mIsRootDefined = GetValue(mRootElm, &mRootNativeValue);
if (aElm) mIsDefined = GetValue(aElm, &mNativeValue);
}
bool TextAttrsMgr::InvalidTextAttr::GetValueFor(LocalAccessible* aAccessible,
uint32_t* aValue) {
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
return elm ? GetValue(elm, aValue) : false;
}
void TextAttrsMgr::InvalidTextAttr::ExposeValue(AccAttributes* aAttributes,
const uint32_t& aValue) {
switch (aValue) {
case eFalse:
aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::_false);
break;
case eGrammar:
aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::grammar);
break;
case eSpelling:
aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::spelling);
break;
case eTrue:
aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::_true);
break;
}
}
bool TextAttrsMgr::InvalidTextAttr::GetValue(nsIContent* aElm,
uint32_t* aValue) {
nsIContent* elm = aElm;
do {
if (nsAccUtils::HasDefinedARIAToken(elm, nsGkAtoms::aria_invalid)) {
static dom::Element::AttrValuesArray tokens[] = {
nsGkAtoms::_false, nsGkAtoms::grammar, nsGkAtoms::spelling, nullptr};
int32_t idx = elm->AsElement()->FindAttrValueIn(
kNameSpaceID_None, nsGkAtoms::aria_invalid, tokens, eCaseMatters);
switch (idx) {
case 0:
*aValue = eFalse;
return true;
case 1:
*aValue = eGrammar;
return true;
case 2:
*aValue = eSpelling;
return true;
default:
*aValue = eTrue;
return true;
}
}
} while ((elm = elm->GetParent()) && elm != mRootElm);
return false;
}
////////////////////////////////////////////////////////////////////////////////
// BGColorTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::BGColorTextAttr::BGColorTextAttr(nsIFrame* aRootFrame,
nsIFrame* aFrame)
: TTextAttr<nscolor>(!aFrame), mRootFrame(aRootFrame) {
mIsRootDefined = GetColor(mRootFrame, &mRootNativeValue);
if (aFrame) mIsDefined = GetColor(aFrame, &mNativeValue);
}
bool TextAttrsMgr::BGColorTextAttr::GetValueFor(LocalAccessible* aAccessible,
nscolor* aValue) {
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
if (elm) {
nsIFrame* frame = elm->GetPrimaryFrame();
if (frame) {
return GetColor(frame, aValue);
}
}
return false;
}
void TextAttrsMgr::BGColorTextAttr::ExposeValue(AccAttributes* aAttributes,
const nscolor& aValue) {
aAttributes->SetAttribute(nsGkAtoms::backgroundColor, Color{aValue});
}
bool TextAttrsMgr::BGColorTextAttr::GetColor(nsIFrame* aFrame,
nscolor* aColor) {
nscolor backgroundColor = aFrame->StyleBackground()->BackgroundColor(aFrame);
if (NS_GET_A(backgroundColor) > 0) {
*aColor = backgroundColor;
return true;
}
nsContainerFrame* parentFrame = aFrame->GetParent();
if (!parentFrame) {
*aColor = aFrame->PresContext()->DefaultBackgroundColor();
return true;
}
// Each frame of parents chain for the initially passed 'aFrame' has
// transparent background color. So background color isn't changed from
// 'mRootFrame' to initially passed 'aFrame'.
if (parentFrame == mRootFrame) return false;
return GetColor(parentFrame, aColor);
}
////////////////////////////////////////////////////////////////////////////////
// ColorTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::ColorTextAttr::ColorTextAttr(nsIFrame* aRootFrame,
nsIFrame* aFrame)
: TTextAttr<nscolor>(!aFrame) {
mRootNativeValue = aRootFrame->StyleText()->mColor.ToColor();
mIsRootDefined = true;
if (aFrame) {
mNativeValue = aFrame->StyleText()->mColor.ToColor();
mIsDefined = true;
}
}
bool TextAttrsMgr::ColorTextAttr::GetValueFor(LocalAccessible* aAccessible,
nscolor* aValue) {
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
if (elm) {
if (nsIFrame* frame = elm->GetPrimaryFrame()) {
*aValue = frame->StyleText()->mColor.ToColor();
return true;
}
}
return false;
}
void TextAttrsMgr::ColorTextAttr::ExposeValue(AccAttributes* aAttributes,
const nscolor& aValue) {
aAttributes->SetAttribute(nsGkAtoms::color, Color{aValue});
}
////////////////////////////////////////////////////////////////////////////////
// FontFamilyTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::FontFamilyTextAttr::FontFamilyTextAttr(nsIFrame* aRootFrame,
nsIFrame* aFrame)
: TTextAttr<nsString>(!aFrame) {
mIsRootDefined = GetFontFamily(aRootFrame, mRootNativeValue);
if (aFrame) mIsDefined = GetFontFamily(aFrame, mNativeValue);
}
bool TextAttrsMgr::FontFamilyTextAttr::GetValueFor(LocalAccessible* aAccessible,
nsString* aValue) {
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
if (elm) {
nsIFrame* frame = elm->GetPrimaryFrame();
if (frame) {
return GetFontFamily(frame, *aValue);
}
}
return false;
}
void TextAttrsMgr::FontFamilyTextAttr::ExposeValue(AccAttributes* aAttributes,
const nsString& aValue) {
aAttributes->SetAttributeStringCopy(nsGkAtoms::font_family, aValue);
}
bool TextAttrsMgr::FontFamilyTextAttr::GetFontFamily(nsIFrame* aFrame,
nsString& aFamily) {
RefPtr<nsFontMetrics> fm =
nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
gfxFont* font = fontGroup->GetFirstValidFont();
gfxFontEntry* fontEntry = font->GetFontEntry();
aFamily.Append(NS_ConvertUTF8toUTF16(fontEntry->FamilyName()));
return true;
}
////////////////////////////////////////////////////////////////////////////////
// FontSizeTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::FontSizeTextAttr::FontSizeTextAttr(nsIFrame* aRootFrame,
nsIFrame* aFrame)
: TTextAttr<nscoord>(!aFrame) {
mDC = aRootFrame->PresContext()->DeviceContext();
mRootNativeValue = aRootFrame->StyleFont()->mSize.ToAppUnits();
mIsRootDefined = true;
if (aFrame) {
mNativeValue = aFrame->StyleFont()->mSize.ToAppUnits();
mIsDefined = true;
}
}
bool TextAttrsMgr::FontSizeTextAttr::GetValueFor(LocalAccessible* aAccessible,
nscoord* aValue) {
nsIContent* el = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
if (el) {
nsIFrame* frame = el->GetPrimaryFrame();
if (frame) {
*aValue = frame->StyleFont()->mSize.ToAppUnits();
return true;
}
}
return false;
}
void TextAttrsMgr::FontSizeTextAttr::ExposeValue(AccAttributes* aAttributes,
const nscoord& aValue) {
// Convert from nscoord to pt.
//
// Note: according to IA2, "The conversion doesn't have to be exact.
// The intent is to give the user a feel for the size of the text."
//
// ATK does not specify a unit and will likely follow IA2 here.
//
// XXX todo: consider sharing this code with layout module? (bug 474621)
float px = NSAppUnitsToFloatPixels(aValue, mozilla::AppUnitsPerCSSPixel());
// Each pt is 4/3 of a CSS pixel.
FontSize fontSize{NS_lround(px * 3 / 4)};
aAttributes->SetAttribute(nsGkAtoms::font_size, fontSize);
}
////////////////////////////////////////////////////////////////////////////////
// FontStyleTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::FontStyleTextAttr::FontStyleTextAttr(nsIFrame* aRootFrame,
nsIFrame* aFrame)
: TTextAttr<FontSlantStyle>(!aFrame) {
mRootNativeValue = aRootFrame->StyleFont()->mFont.style;
mIsRootDefined = true;
if (aFrame) {
mNativeValue = aFrame->StyleFont()->mFont.style;
mIsDefined = true;
}
}
bool TextAttrsMgr::FontStyleTextAttr::GetValueFor(LocalAccessible* aAccessible,
FontSlantStyle* aValue) {
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
if (elm) {
nsIFrame* frame = elm->GetPrimaryFrame();
if (frame) {
*aValue = frame->StyleFont()->mFont.style;
return true;
}
}
return false;
}
void TextAttrsMgr::FontStyleTextAttr::ExposeValue(
AccAttributes* aAttributes, const FontSlantStyle& aValue) {
if (aValue.IsNormal()) {
aAttributes->SetAttribute(nsGkAtoms::font_style, nsGkAtoms::normal);
} else if (aValue.IsItalic()) {
RefPtr<nsAtom> atom = NS_Atomize("italic");
aAttributes->SetAttribute(nsGkAtoms::font_style, atom);
} else {
nsAutoCString s;
aValue.ToString(s);
nsString wide;
CopyUTF8toUTF16(s, wide);
aAttributes->SetAttribute(nsGkAtoms::font_style, std::move(wide));
}
}
////////////////////////////////////////////////////////////////////////////////
// FontWeightTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::FontWeightTextAttr::FontWeightTextAttr(nsIFrame* aRootFrame,
nsIFrame* aFrame)
: TTextAttr<FontWeight>(!aFrame) {
mRootNativeValue = GetFontWeight(aRootFrame);
mIsRootDefined = true;
if (aFrame) {
mNativeValue = GetFontWeight(aFrame);
mIsDefined = true;
}
}
bool TextAttrsMgr::FontWeightTextAttr::GetValueFor(LocalAccessible* aAccessible,
FontWeight* aValue) {
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
if (elm) {
nsIFrame* frame = elm->GetPrimaryFrame();
if (frame) {
*aValue = GetFontWeight(frame);
return true;
}
}
return false;
}
void TextAttrsMgr::FontWeightTextAttr::ExposeValue(AccAttributes* aAttributes,
const FontWeight& aValue) {
int value = aValue.ToIntRounded();
aAttributes->SetAttribute(nsGkAtoms::fontWeight, value);
}
FontWeight TextAttrsMgr::FontWeightTextAttr::GetFontWeight(nsIFrame* aFrame) {
// nsFont::width isn't suitable here because it's necessary to expose real
// value of font weight (used font might not have some font weight values).
RefPtr<nsFontMetrics> fm =
nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
gfxFont* font = fontGroup->GetFirstValidFont();
// When there doesn't exist a bold font in the family and so the rendering of
// a non-bold font face is changed so that the user sees what looks like a
// bold font, i.e. synthetic bolding is used. (Simply returns false on any
// platforms that don't use the multi-strike synthetic bolding.)
if (font->ApplySyntheticBold()) {
return FontWeight::BOLD;
}
// On Windows, font->GetStyle()->weight will give the same weight as
// fontEntry->Weight(), the weight of the first font in the font group,
// which may not be the weight of the font face used to render the
// characters. On Mac, font->GetStyle()->weight will just give the same
// number as getComputedStyle(). fontEntry->Weight() will give the weight
// range supported by the font face used, so we clamp the weight that was
// requested by style to what is actually supported by the font.
gfxFontEntry* fontEntry = font->GetFontEntry();
return fontEntry->Weight().Clamp(font->GetStyle()->weight);
}
////////////////////////////////////////////////////////////////////////////////
// AutoGeneratedTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::AutoGeneratedTextAttr::AutoGeneratedTextAttr(
HyperTextAccessible* aHyperTextAcc, LocalAccessible* aAccessible)
: TTextAttr<bool>(!aAccessible) {
mRootNativeValue = false;
mIsRootDefined = false;
if (aAccessible) {
mIsDefined = mNativeValue =
((aAccessible->NativeRole() == roles::STATICTEXT) ||
(aAccessible->NativeRole() == roles::LISTITEM_MARKER));
}
}
bool TextAttrsMgr::AutoGeneratedTextAttr::GetValueFor(
LocalAccessible* aAccessible, bool* aValue) {
return *aValue = (aAccessible->NativeRole() == roles::STATICTEXT);
}
void TextAttrsMgr::AutoGeneratedTextAttr::ExposeValue(
AccAttributes* aAttributes, const bool& aValue) {
aAttributes->SetAttribute(nsGkAtoms::auto_generated, aValue);
}
////////////////////////////////////////////////////////////////////////////////
// TextDecorTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::TextDecorValue::TextDecorValue(nsIFrame* aFrame) {
const nsStyleTextReset* textReset = aFrame->StyleTextReset();
mStyle = textReset->mTextDecorationStyle;
mColor = textReset->mTextDecorationColor.CalcColor(aFrame);
mLine =
textReset->mTextDecorationLine & (StyleTextDecorationLine::UNDERLINE |
StyleTextDecorationLine::LINE_THROUGH);
}
TextAttrsMgr::TextDecorTextAttr::TextDecorTextAttr(nsIFrame* aRootFrame,
nsIFrame* aFrame)
: TTextAttr<TextDecorValue>(!aFrame) {
mRootNativeValue = TextDecorValue(aRootFrame);
mIsRootDefined = mRootNativeValue.IsDefined();
if (aFrame) {
mNativeValue = TextDecorValue(aFrame);
mIsDefined = mNativeValue.IsDefined();
}
}
bool TextAttrsMgr::TextDecorTextAttr::GetValueFor(LocalAccessible* aAccessible,
TextDecorValue* aValue) {
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
if (elm) {
nsIFrame* frame = elm->GetPrimaryFrame();
if (frame) {
*aValue = TextDecorValue(frame);
return aValue->IsDefined();
}
}
return false;
}
void TextAttrsMgr::TextDecorTextAttr::ExposeValue(
AccAttributes* aAttributes, const TextDecorValue& aValue) {
if (aValue.IsUnderline()) {
RefPtr<nsAtom> underlineStyle =
StyleInfo::TextDecorationStyleToAtom(aValue.Style());
aAttributes->SetAttribute(nsGkAtoms::textUnderlineStyle, underlineStyle);
aAttributes->SetAttribute(nsGkAtoms::textUnderlineColor,
Color{aValue.Color()});
return;
}
if (aValue.IsLineThrough()) {
RefPtr<nsAtom> lineThroughStyle =
StyleInfo::TextDecorationStyleToAtom(aValue.Style());
aAttributes->SetAttribute(nsGkAtoms::textLineThroughStyle,
lineThroughStyle);
aAttributes->SetAttribute(nsGkAtoms::textLineThroughColor,
Color{aValue.Color()});
}
}
////////////////////////////////////////////////////////////////////////////////
// TextPosTextAttr
////////////////////////////////////////////////////////////////////////////////
TextAttrsMgr::TextPosTextAttr::TextPosTextAttr(nsIFrame* aRootFrame,
nsIFrame* aFrame)
: TTextAttr<TextPosValue>(!aFrame) {
mRootNativeValue = GetTextPosValue(aRootFrame);
mIsRootDefined = mRootNativeValue != eTextPosNone;
if (aFrame) {
mNativeValue = GetTextPosValue(aFrame);
mIsDefined = mNativeValue != eTextPosNone;
}
}
bool TextAttrsMgr::TextPosTextAttr::GetValueFor(LocalAccessible* aAccessible,
TextPosValue* aValue) {
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
if (elm) {
nsIFrame* frame = elm->GetPrimaryFrame();
if (frame) {
*aValue = GetTextPosValue(frame);
return *aValue != eTextPosNone;
}
}
return false;
}
void TextAttrsMgr::TextPosTextAttr::ExposeValue(AccAttributes* aAttributes,
const TextPosValue& aValue) {
RefPtr<nsAtom> atom = nullptr;
switch (aValue) {
case eTextPosBaseline:
atom = nsGkAtoms::baseline;
break;
case eTextPosSub:
atom = nsGkAtoms::sub;
break;
case eTextPosSuper:
atom = NS_Atomize("super");
break;
case eTextPosNone:
break;
}
if (atom) {
aAttributes->SetAttribute(nsGkAtoms::textPosition, atom);
}
}
TextAttrsMgr::TextPosValue TextAttrsMgr::TextPosTextAttr::GetTextPosValue(
nsIFrame* aFrame) const {
const auto& verticalAlign = aFrame->StyleDisplay()->mVerticalAlign;
if (verticalAlign.IsKeyword()) {
switch (verticalAlign.AsKeyword()) {
case StyleVerticalAlignKeyword::Baseline:
return eTextPosBaseline;
case StyleVerticalAlignKeyword::Sub:
return eTextPosSub;
case StyleVerticalAlignKeyword::Super:
return eTextPosSuper;
// No good guess for the rest, so do not expose value of text-position
// attribute.
default:
return eTextPosNone;
}
}
const auto& length = verticalAlign.AsLength();
if (length.ConvertsToPercentage()) {
float percentValue = length.ToPercentage();
return percentValue > 0
? eTextPosSuper
: (percentValue < 0 ? eTextPosSub : eTextPosBaseline);
}
if (length.ConvertsToLength()) {
nscoord coordValue = length.ToLength();
return coordValue > 0 ? eTextPosSuper
: (coordValue < 0 ? eTextPosSub : eTextPosBaseline);
}
if (const nsIContent* content = aFrame->GetContent()) {
if (content->IsHTMLElement(nsGkAtoms::sup)) return eTextPosSuper;
if (content->IsHTMLElement(nsGkAtoms::sub)) return eTextPosSub;
}
return eTextPosNone;
}