forked from mirrors/gecko-dev
This reduces memory used by the cache, since the same font family and language strings often occur many times in the same document (and likely even across documents). Differential Revision: https://phabricator.services.mozilla.com/D162276
819 lines
28 KiB
C++
819 lines
28 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, hyperTextElm, offsetNode);
|
|
|
|
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) {
|
|
RefPtr<nsAtom> lang = NS_Atomize(aValue);
|
|
aAttributes->SetAttribute(nsGkAtoms::language, lang);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// 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 = nsAccUtils::FindARIAAttrValueIn(
|
|
elm->AsElement(), 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) {
|
|
RefPtr<nsAtom> family = NS_Atomize(aValue);
|
|
aAttributes->SetAttribute(nsGkAtoms::font_family, family);
|
|
}
|
|
|
|
bool TextAttrsMgr::FontFamilyTextAttr::GetFontFamily(nsIFrame* aFrame,
|
|
nsString& aFamily) {
|
|
RefPtr<nsFontMetrics> fm =
|
|
nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
|
|
|
|
gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
|
|
RefPtr<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();
|
|
RefPtr<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,
|
|
nsIContent* aRootElm,
|
|
nsIContent* aElm)
|
|
: TTextAttr<Maybe<TextPosValue>>(!aFrame && !aElm), mRootElm(aRootElm) {
|
|
// Get the text-position values for the roots and children.
|
|
// If we find an ARIA text-position value on a DOM element - searching up
|
|
// from the supplied root DOM element - use the associated frame as the root
|
|
// frame. This ensures that we're using the proper root frame for comparison.
|
|
nsIFrame* ariaFrame = nullptr;
|
|
Maybe<TextPosValue> rootAria = GetAriaTextPosValue(aRootElm, ariaFrame);
|
|
if (rootAria && ariaFrame) {
|
|
aRootFrame = ariaFrame;
|
|
}
|
|
Maybe<TextPosValue> rootLayout = GetLayoutTextPosValue(aRootFrame);
|
|
Maybe<TextPosValue> childLayout;
|
|
Maybe<TextPosValue> childAria;
|
|
if (aFrame) {
|
|
childLayout = GetLayoutTextPosValue(aFrame);
|
|
}
|
|
if (aElm) {
|
|
childAria = GetAriaTextPosValue(aElm);
|
|
}
|
|
|
|
// Aria values take precedence over layout values.
|
|
mIsRootDefined = rootAria || rootLayout;
|
|
mRootNativeValue = rootAria ? rootAria : rootLayout;
|
|
mIsDefined = childAria || childLayout;
|
|
mNativeValue = childAria ? childAria : childLayout;
|
|
|
|
// If there's no child text-position information from ARIA, and the child
|
|
// layout info is equivalent to the root layout info (i.e., it's inherited),
|
|
// then we should prefer the root information.
|
|
if (!childAria && childLayout == rootLayout) {
|
|
mIsDefined = false;
|
|
}
|
|
}
|
|
|
|
bool TextAttrsMgr::TextPosTextAttr::GetValueFor(LocalAccessible* aAccessible,
|
|
Maybe<TextPosValue>* aValue) {
|
|
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
|
|
if (elm) {
|
|
nsIFrame* frame = elm->GetPrimaryFrame();
|
|
if (frame) {
|
|
Maybe<TextPosValue> layoutValue = GetLayoutTextPosValue(frame);
|
|
Maybe<TextPosValue> ariaValue = GetAriaTextPosValue(elm);
|
|
|
|
*aValue = ariaValue ? ariaValue : layoutValue;
|
|
return aValue->isSome();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void TextAttrsMgr::TextPosTextAttr::ExposeValue(
|
|
AccAttributes* aAttributes, const Maybe<TextPosValue>& aValue) {
|
|
if (aValue.isNothing()) {
|
|
return;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (atom) {
|
|
aAttributes->SetAttribute(nsGkAtoms::textPosition, atom);
|
|
}
|
|
}
|
|
|
|
Maybe<TextAttrsMgr::TextPosValue>
|
|
TextAttrsMgr::TextPosTextAttr::GetAriaTextPosValue(nsIContent* aElm) const {
|
|
nsIFrame* ariaFrame = nullptr;
|
|
return GetAriaTextPosValue(aElm, ariaFrame);
|
|
}
|
|
|
|
Maybe<TextAttrsMgr::TextPosValue>
|
|
TextAttrsMgr::TextPosTextAttr::GetAriaTextPosValue(nsIContent* aElm,
|
|
nsIFrame*& ariaFrame) const {
|
|
// Search for the superscript and subscript roles that imply text-position.
|
|
const nsIContent* elm = aElm;
|
|
do {
|
|
if (elm->IsElement()) {
|
|
const mozilla::dom::Element* domElm = elm->AsElement();
|
|
static const dom::Element::AttrValuesArray tokens[] = {
|
|
nsGkAtoms::subscript, nsGkAtoms::superscript, nullptr};
|
|
const int32_t valueIdx = domElm->FindAttrValueIn(
|
|
kNameSpaceID_None, nsGkAtoms::role, tokens, eCaseMatters);
|
|
ariaFrame = domElm->GetPrimaryFrame();
|
|
if (valueIdx == 0) {
|
|
return Some(eTextPosSub);
|
|
}
|
|
if (valueIdx == 1) {
|
|
return Some(eTextPosSuper);
|
|
}
|
|
}
|
|
} while ((elm = elm->GetParent()) && elm != mRootElm);
|
|
|
|
ariaFrame = nullptr;
|
|
return Nothing{};
|
|
}
|
|
|
|
Maybe<TextAttrsMgr::TextPosValue>
|
|
TextAttrsMgr::TextPosTextAttr::GetLayoutTextPosValue(nsIFrame* aFrame) const {
|
|
const auto& verticalAlign = aFrame->StyleDisplay()->mVerticalAlign;
|
|
if (verticalAlign.IsKeyword()) {
|
|
switch (verticalAlign.AsKeyword()) {
|
|
case StyleVerticalAlignKeyword::Baseline:
|
|
return Some(eTextPosBaseline);
|
|
case StyleVerticalAlignKeyword::Sub:
|
|
return Some(eTextPosSub);
|
|
case StyleVerticalAlignKeyword::Super:
|
|
return Some(eTextPosSuper);
|
|
// No good guess for the rest, so do not expose value of text-position
|
|
// attribute.
|
|
default:
|
|
return Nothing{};
|
|
}
|
|
}
|
|
|
|
const auto& length = verticalAlign.AsLength();
|
|
if (length.ConvertsToPercentage()) {
|
|
const float percentValue = length.ToPercentage();
|
|
return percentValue > 0 ? Some(eTextPosSuper)
|
|
: (percentValue < 0 ? Some(eTextPosSub)
|
|
: Some(eTextPosBaseline));
|
|
}
|
|
|
|
if (length.ConvertsToLength()) {
|
|
const nscoord coordValue = length.ToLength();
|
|
return coordValue > 0
|
|
? Some(eTextPosSuper)
|
|
: (coordValue < 0 ? Some(eTextPosSub) : Some(eTextPosBaseline));
|
|
}
|
|
|
|
if (const nsIContent* content = aFrame->GetContent()) {
|
|
if (content->IsHTMLElement(nsGkAtoms::sup)) return Some(eTextPosSuper);
|
|
if (content->IsHTMLElement(nsGkAtoms::sub)) return Some(eTextPosSub);
|
|
}
|
|
|
|
return Nothing{};
|
|
}
|