Bug 1631735 Part 1: Make nsCocoaWindow animated transitions asynchronous and atomic...
[gecko.git] / accessible / base / TextAttrs.cpp
blobd8f555b1ab92c55f90c18c28ea85878da05bb675
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "TextAttrs.h"
8 #include "LocalAccessible-inl.h"
9 #include "AccAttributes.h"
10 #include "nsAccUtils.h"
11 #include "nsCoreUtils.h"
12 #include "StyleInfo.h"
14 #include "gfxTextRun.h"
15 #include "nsFontMetrics.h"
16 #include "nsLayoutUtils.h"
17 #include "nsContainerFrame.h"
18 #include "nsStyleUtil.h"
19 #include "HyperTextAccessible.h"
20 #include "mozilla/AppUnits.h"
21 #include "mozilla/gfx/2D.h"
23 using namespace mozilla;
24 using namespace mozilla::a11y;
26 ////////////////////////////////////////////////////////////////////////////////
27 // TextAttrsMgr
28 ////////////////////////////////////////////////////////////////////////////////
30 void TextAttrsMgr::GetAttributes(AccAttributes* aAttributes,
31 uint32_t* aStartOffset, uint32_t* aEndOffset) {
32 // 1. Hyper text accessible must be specified always.
33 // 2. Offset accessible must be specified in
34 // the case of text attributes. Result hyper text offsets are optional if you
35 // just want the attributes for a single text Accessible.
36 // 3. Offset accessible and result hyper text offsets must not be specified
37 // but include default text attributes flag and attributes list must be
38 // specified in the case of default text attributes.
39 MOZ_ASSERT(
40 mHyperTextAcc && ((mOffsetAcc && mOffsetAccIdx != -1) ||
41 (!mOffsetAcc && mOffsetAccIdx == -1 && !aStartOffset &&
42 !aEndOffset && mIncludeDefAttrs && aAttributes)),
43 "Wrong usage of TextAttrsMgr!");
45 // Embedded objects are combined into own range with empty attributes set.
46 if (mOffsetAcc && !mOffsetAcc->IsText()) {
47 if (!aStartOffset) {
48 return;
50 for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
51 LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
52 if (currAcc->IsText()) break;
54 (*aStartOffset)--;
57 uint32_t childCount = mHyperTextAcc->ChildCount();
58 for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childCount;
59 childIdx++) {
60 LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
61 if (currAcc->IsText()) break;
63 (*aEndOffset)++;
66 return;
69 // Get the content and frame of the accessible. In the case of document
70 // accessible it's role content and root frame.
71 nsIContent* hyperTextElm = mHyperTextAcc->GetContent();
72 if (!hyperTextElm) {
73 return; // XXX: we don't support text attrs on document with no body
76 nsIFrame* rootFrame = mHyperTextAcc->GetFrame();
77 if (!rootFrame) {
78 return;
81 nsIContent *offsetNode = nullptr, *offsetElm = nullptr;
82 nsIFrame* frame = nullptr;
83 if (mOffsetAcc) {
84 offsetNode = mOffsetAcc->GetContent();
85 offsetElm = nsCoreUtils::GetDOMElementFor(offsetNode);
86 MOZ_ASSERT(offsetElm, "No element for offset accessible!");
87 if (!offsetElm) return;
89 frame = offsetElm->GetPrimaryFrame();
92 // "language" text attribute
93 LangTextAttr langTextAttr(mHyperTextAcc, hyperTextElm, offsetNode);
95 // "aria-invalid" text attribute
96 InvalidTextAttr invalidTextAttr(hyperTextElm, offsetNode);
98 // "background-color" text attribute
99 BGColorTextAttr bgColorTextAttr(rootFrame, frame);
101 // "color" text attribute
102 ColorTextAttr colorTextAttr(rootFrame, frame);
104 // "font-family" text attribute
105 FontFamilyTextAttr fontFamilyTextAttr(rootFrame, frame);
107 // "font-size" text attribute
108 FontSizeTextAttr fontSizeTextAttr(rootFrame, frame);
110 // "font-style" text attribute
111 FontStyleTextAttr fontStyleTextAttr(rootFrame, frame);
113 // "font-weight" text attribute
114 FontWeightTextAttr fontWeightTextAttr(rootFrame, frame);
116 // "auto-generated" text attribute
117 AutoGeneratedTextAttr autoGenTextAttr(mHyperTextAcc, mOffsetAcc);
119 // "text-underline(line-through)-style(color)" text attributes
120 TextDecorTextAttr textDecorTextAttr(rootFrame, frame);
122 // "text-position" text attribute
123 TextPosTextAttr textPosTextAttr(rootFrame, frame, hyperTextElm, offsetNode);
125 TextAttr* attrArray[] = {
126 &langTextAttr, &invalidTextAttr, &bgColorTextAttr,
127 &colorTextAttr, &fontFamilyTextAttr, &fontSizeTextAttr,
128 &fontStyleTextAttr, &fontWeightTextAttr, &autoGenTextAttr,
129 &textDecorTextAttr, &textPosTextAttr};
131 // Expose text attributes if applicable.
132 if (aAttributes) {
133 for (uint32_t idx = 0; idx < ArrayLength(attrArray); idx++) {
134 attrArray[idx]->Expose(aAttributes, mIncludeDefAttrs);
138 // Expose text attributes range where they are applied if applicable.
139 if (aStartOffset) {
140 GetRange(attrArray, ArrayLength(attrArray), aStartOffset, aEndOffset);
144 void TextAttrsMgr::GetRange(TextAttr* aAttrArray[], uint32_t aAttrArrayLen,
145 uint32_t* aStartOffset, uint32_t* aEndOffset) {
146 // Navigate backward from anchor accessible to find start offset.
147 for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
148 LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
150 // Stop on embedded accessible since embedded accessibles are combined into
151 // own range.
152 if (!currAcc->IsText()) break;
154 MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()),
155 "Text accessible has to have an associated DOM element");
157 bool offsetFound = false;
158 for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
159 TextAttr* textAttr = aAttrArray[attrIdx];
160 if (!textAttr->Equal(currAcc)) {
161 offsetFound = true;
162 break;
166 if (offsetFound) break;
168 *(aStartOffset) -= nsAccUtils::TextLength(currAcc);
171 // Navigate forward from anchor accessible to find end offset.
172 uint32_t childLen = mHyperTextAcc->ChildCount();
173 for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childLen; childIdx++) {
174 LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
175 if (!currAcc->IsText()) break;
177 MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()),
178 "Text accessible has to have an associated DOM element");
180 bool offsetFound = false;
181 for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
182 TextAttr* textAttr = aAttrArray[attrIdx];
184 // Alter the end offset when text attribute changes its value and stop
185 // the search.
186 if (!textAttr->Equal(currAcc)) {
187 offsetFound = true;
188 break;
192 if (offsetFound) break;
194 (*aEndOffset) += nsAccUtils::TextLength(currAcc);
198 ////////////////////////////////////////////////////////////////////////////////
199 // LangTextAttr
200 ////////////////////////////////////////////////////////////////////////////////
202 TextAttrsMgr::LangTextAttr::LangTextAttr(HyperTextAccessible* aRoot,
203 nsIContent* aRootElm, nsIContent* aElm)
204 : TTextAttr<nsString>(!aElm), mRootContent(aRootElm) {
205 aRoot->Language(mRootNativeValue);
206 mIsRootDefined = !mRootNativeValue.IsEmpty();
208 if (aElm) {
209 nsCoreUtils::GetLanguageFor(aElm, mRootContent, mNativeValue);
210 mIsDefined = !mNativeValue.IsEmpty();
214 TextAttrsMgr::LangTextAttr::~LangTextAttr() {}
216 bool TextAttrsMgr::LangTextAttr::GetValueFor(LocalAccessible* aAccessible,
217 nsString* aValue) {
218 nsCoreUtils::GetLanguageFor(aAccessible->GetContent(), mRootContent, *aValue);
219 return !aValue->IsEmpty();
222 void TextAttrsMgr::LangTextAttr::ExposeValue(AccAttributes* aAttributes,
223 const nsString& aValue) {
224 RefPtr<nsAtom> lang = NS_Atomize(aValue);
225 aAttributes->SetAttribute(nsGkAtoms::language, lang);
228 ////////////////////////////////////////////////////////////////////////////////
229 // InvalidTextAttr
230 ////////////////////////////////////////////////////////////////////////////////
232 TextAttrsMgr::InvalidTextAttr::InvalidTextAttr(nsIContent* aRootElm,
233 nsIContent* aElm)
234 : TTextAttr<uint32_t>(!aElm), mRootElm(aRootElm) {
235 mIsRootDefined = GetValue(mRootElm, &mRootNativeValue);
236 if (aElm) mIsDefined = GetValue(aElm, &mNativeValue);
239 bool TextAttrsMgr::InvalidTextAttr::GetValueFor(LocalAccessible* aAccessible,
240 uint32_t* aValue) {
241 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
242 return elm ? GetValue(elm, aValue) : false;
245 void TextAttrsMgr::InvalidTextAttr::ExposeValue(AccAttributes* aAttributes,
246 const uint32_t& aValue) {
247 switch (aValue) {
248 case eFalse:
249 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::_false);
250 break;
252 case eGrammar:
253 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::grammar);
254 break;
256 case eSpelling:
257 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::spelling);
258 break;
260 case eTrue:
261 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::_true);
262 break;
266 bool TextAttrsMgr::InvalidTextAttr::GetValue(nsIContent* aElm,
267 uint32_t* aValue) {
268 nsIContent* elm = aElm;
269 do {
270 if (nsAccUtils::HasDefinedARIAToken(elm, nsGkAtoms::aria_invalid)) {
271 static dom::Element::AttrValuesArray tokens[] = {
272 nsGkAtoms::_false, nsGkAtoms::grammar, nsGkAtoms::spelling, nullptr};
274 int32_t idx = nsAccUtils::FindARIAAttrValueIn(
275 elm->AsElement(), nsGkAtoms::aria_invalid, tokens, eCaseMatters);
276 switch (idx) {
277 case 0:
278 *aValue = eFalse;
279 return true;
280 case 1:
281 *aValue = eGrammar;
282 return true;
283 case 2:
284 *aValue = eSpelling;
285 return true;
286 default:
287 *aValue = eTrue;
288 return true;
291 } while ((elm = elm->GetParent()) && elm != mRootElm);
293 return false;
296 ////////////////////////////////////////////////////////////////////////////////
297 // BGColorTextAttr
298 ////////////////////////////////////////////////////////////////////////////////
300 TextAttrsMgr::BGColorTextAttr::BGColorTextAttr(nsIFrame* aRootFrame,
301 nsIFrame* aFrame)
302 : TTextAttr<nscolor>(!aFrame), mRootFrame(aRootFrame) {
303 mIsRootDefined = GetColor(mRootFrame, &mRootNativeValue);
304 if (aFrame) mIsDefined = GetColor(aFrame, &mNativeValue);
307 bool TextAttrsMgr::BGColorTextAttr::GetValueFor(LocalAccessible* aAccessible,
308 nscolor* aValue) {
309 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
310 if (elm) {
311 nsIFrame* frame = elm->GetPrimaryFrame();
312 if (frame) {
313 return GetColor(frame, aValue);
316 return false;
319 void TextAttrsMgr::BGColorTextAttr::ExposeValue(AccAttributes* aAttributes,
320 const nscolor& aValue) {
321 aAttributes->SetAttribute(nsGkAtoms::backgroundColor, Color{aValue});
324 bool TextAttrsMgr::BGColorTextAttr::GetColor(nsIFrame* aFrame,
325 nscolor* aColor) {
326 nscolor backgroundColor = aFrame->StyleBackground()->BackgroundColor(aFrame);
327 if (NS_GET_A(backgroundColor) > 0) {
328 *aColor = backgroundColor;
329 return true;
332 nsContainerFrame* parentFrame = aFrame->GetParent();
333 if (!parentFrame) {
334 *aColor = aFrame->PresContext()->DefaultBackgroundColor();
335 return true;
338 // Each frame of parents chain for the initially passed 'aFrame' has
339 // transparent background color. So background color isn't changed from
340 // 'mRootFrame' to initially passed 'aFrame'.
341 if (parentFrame == mRootFrame) return false;
343 return GetColor(parentFrame, aColor);
346 ////////////////////////////////////////////////////////////////////////////////
347 // ColorTextAttr
348 ////////////////////////////////////////////////////////////////////////////////
350 TextAttrsMgr::ColorTextAttr::ColorTextAttr(nsIFrame* aRootFrame,
351 nsIFrame* aFrame)
352 : TTextAttr<nscolor>(!aFrame) {
353 mRootNativeValue = aRootFrame->StyleText()->mColor.ToColor();
354 mIsRootDefined = true;
356 if (aFrame) {
357 mNativeValue = aFrame->StyleText()->mColor.ToColor();
358 mIsDefined = true;
362 bool TextAttrsMgr::ColorTextAttr::GetValueFor(LocalAccessible* aAccessible,
363 nscolor* aValue) {
364 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
365 if (elm) {
366 if (nsIFrame* frame = elm->GetPrimaryFrame()) {
367 *aValue = frame->StyleText()->mColor.ToColor();
368 return true;
371 return false;
374 void TextAttrsMgr::ColorTextAttr::ExposeValue(AccAttributes* aAttributes,
375 const nscolor& aValue) {
376 aAttributes->SetAttribute(nsGkAtoms::color, Color{aValue});
379 ////////////////////////////////////////////////////////////////////////////////
380 // FontFamilyTextAttr
381 ////////////////////////////////////////////////////////////////////////////////
383 TextAttrsMgr::FontFamilyTextAttr::FontFamilyTextAttr(nsIFrame* aRootFrame,
384 nsIFrame* aFrame)
385 : TTextAttr<nsString>(!aFrame) {
386 mIsRootDefined = GetFontFamily(aRootFrame, mRootNativeValue);
388 if (aFrame) mIsDefined = GetFontFamily(aFrame, mNativeValue);
391 bool TextAttrsMgr::FontFamilyTextAttr::GetValueFor(LocalAccessible* aAccessible,
392 nsString* aValue) {
393 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
394 if (elm) {
395 nsIFrame* frame = elm->GetPrimaryFrame();
396 if (frame) {
397 return GetFontFamily(frame, *aValue);
400 return false;
403 void TextAttrsMgr::FontFamilyTextAttr::ExposeValue(AccAttributes* aAttributes,
404 const nsString& aValue) {
405 RefPtr<nsAtom> family = NS_Atomize(aValue);
406 aAttributes->SetAttribute(nsGkAtoms::font_family, family);
409 bool TextAttrsMgr::FontFamilyTextAttr::GetFontFamily(nsIFrame* aFrame,
410 nsString& aFamily) {
411 RefPtr<nsFontMetrics> fm =
412 nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
414 gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
415 RefPtr<gfxFont> font = fontGroup->GetFirstValidFont();
416 gfxFontEntry* fontEntry = font->GetFontEntry();
417 aFamily.Append(NS_ConvertUTF8toUTF16(fontEntry->FamilyName()));
418 return true;
421 ////////////////////////////////////////////////////////////////////////////////
422 // FontSizeTextAttr
423 ////////////////////////////////////////////////////////////////////////////////
425 TextAttrsMgr::FontSizeTextAttr::FontSizeTextAttr(nsIFrame* aRootFrame,
426 nsIFrame* aFrame)
427 : TTextAttr<nscoord>(!aFrame) {
428 mDC = aRootFrame->PresContext()->DeviceContext();
430 mRootNativeValue = aRootFrame->StyleFont()->mSize.ToAppUnits();
431 mIsRootDefined = true;
433 if (aFrame) {
434 mNativeValue = aFrame->StyleFont()->mSize.ToAppUnits();
435 mIsDefined = true;
439 bool TextAttrsMgr::FontSizeTextAttr::GetValueFor(LocalAccessible* aAccessible,
440 nscoord* aValue) {
441 nsIContent* el = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
442 if (el) {
443 nsIFrame* frame = el->GetPrimaryFrame();
444 if (frame) {
445 *aValue = frame->StyleFont()->mSize.ToAppUnits();
446 return true;
449 return false;
452 void TextAttrsMgr::FontSizeTextAttr::ExposeValue(AccAttributes* aAttributes,
453 const nscoord& aValue) {
454 // Convert from nscoord to pt.
456 // Note: according to IA2, "The conversion doesn't have to be exact.
457 // The intent is to give the user a feel for the size of the text."
459 // ATK does not specify a unit and will likely follow IA2 here.
461 // XXX todo: consider sharing this code with layout module? (bug 474621)
462 float px = NSAppUnitsToFloatPixels(aValue, mozilla::AppUnitsPerCSSPixel());
463 // Each pt is 4/3 of a CSS pixel.
464 FontSize fontSize{NS_lround(px * 3 / 4)};
466 aAttributes->SetAttribute(nsGkAtoms::font_size, fontSize);
469 ////////////////////////////////////////////////////////////////////////////////
470 // FontStyleTextAttr
471 ////////////////////////////////////////////////////////////////////////////////
473 TextAttrsMgr::FontStyleTextAttr::FontStyleTextAttr(nsIFrame* aRootFrame,
474 nsIFrame* aFrame)
475 : TTextAttr<FontSlantStyle>(!aFrame) {
476 mRootNativeValue = aRootFrame->StyleFont()->mFont.style;
477 mIsRootDefined = true;
479 if (aFrame) {
480 mNativeValue = aFrame->StyleFont()->mFont.style;
481 mIsDefined = true;
485 bool TextAttrsMgr::FontStyleTextAttr::GetValueFor(LocalAccessible* aAccessible,
486 FontSlantStyle* aValue) {
487 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
488 if (elm) {
489 nsIFrame* frame = elm->GetPrimaryFrame();
490 if (frame) {
491 *aValue = frame->StyleFont()->mFont.style;
492 return true;
495 return false;
498 void TextAttrsMgr::FontStyleTextAttr::ExposeValue(
499 AccAttributes* aAttributes, const FontSlantStyle& aValue) {
500 if (aValue.IsNormal()) {
501 aAttributes->SetAttribute(nsGkAtoms::font_style, nsGkAtoms::normal);
502 } else if (aValue.IsItalic()) {
503 RefPtr<nsAtom> atom = NS_Atomize("italic");
504 aAttributes->SetAttribute(nsGkAtoms::font_style, atom);
505 } else {
506 nsAutoCString s;
507 aValue.ToString(s);
508 nsString wide;
509 CopyUTF8toUTF16(s, wide);
510 aAttributes->SetAttribute(nsGkAtoms::font_style, std::move(wide));
514 ////////////////////////////////////////////////////////////////////////////////
515 // FontWeightTextAttr
516 ////////////////////////////////////////////////////////////////////////////////
518 TextAttrsMgr::FontWeightTextAttr::FontWeightTextAttr(nsIFrame* aRootFrame,
519 nsIFrame* aFrame)
520 : TTextAttr<FontWeight>(!aFrame) {
521 mRootNativeValue = GetFontWeight(aRootFrame);
522 mIsRootDefined = true;
524 if (aFrame) {
525 mNativeValue = GetFontWeight(aFrame);
526 mIsDefined = true;
530 bool TextAttrsMgr::FontWeightTextAttr::GetValueFor(LocalAccessible* aAccessible,
531 FontWeight* aValue) {
532 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
533 if (elm) {
534 nsIFrame* frame = elm->GetPrimaryFrame();
535 if (frame) {
536 *aValue = GetFontWeight(frame);
537 return true;
540 return false;
543 void TextAttrsMgr::FontWeightTextAttr::ExposeValue(AccAttributes* aAttributes,
544 const FontWeight& aValue) {
545 int value = aValue.ToIntRounded();
546 aAttributes->SetAttribute(nsGkAtoms::fontWeight, value);
549 FontWeight TextAttrsMgr::FontWeightTextAttr::GetFontWeight(nsIFrame* aFrame) {
550 // nsFont::width isn't suitable here because it's necessary to expose real
551 // value of font weight (used font might not have some font weight values).
552 RefPtr<nsFontMetrics> fm =
553 nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
555 gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
556 RefPtr<gfxFont> font = fontGroup->GetFirstValidFont();
558 // When there doesn't exist a bold font in the family and so the rendering of
559 // a non-bold font face is changed so that the user sees what looks like a
560 // bold font, i.e. synthetic bolding is used. (Simply returns false on any
561 // platforms that don't use the multi-strike synthetic bolding.)
562 if (font->ApplySyntheticBold()) {
563 return FontWeight::BOLD;
566 // On Windows, font->GetStyle()->weight will give the same weight as
567 // fontEntry->Weight(), the weight of the first font in the font group,
568 // which may not be the weight of the font face used to render the
569 // characters. On Mac, font->GetStyle()->weight will just give the same
570 // number as getComputedStyle(). fontEntry->Weight() will give the weight
571 // range supported by the font face used, so we clamp the weight that was
572 // requested by style to what is actually supported by the font.
573 gfxFontEntry* fontEntry = font->GetFontEntry();
574 return fontEntry->Weight().Clamp(font->GetStyle()->weight);
577 ////////////////////////////////////////////////////////////////////////////////
578 // AutoGeneratedTextAttr
579 ////////////////////////////////////////////////////////////////////////////////
580 TextAttrsMgr::AutoGeneratedTextAttr::AutoGeneratedTextAttr(
581 HyperTextAccessible* aHyperTextAcc, LocalAccessible* aAccessible)
582 : TTextAttr<bool>(!aAccessible) {
583 mRootNativeValue = false;
584 mIsRootDefined = false;
586 if (aAccessible) {
587 mIsDefined = mNativeValue =
588 ((aAccessible->NativeRole() == roles::STATICTEXT) ||
589 (aAccessible->NativeRole() == roles::LISTITEM_MARKER));
593 bool TextAttrsMgr::AutoGeneratedTextAttr::GetValueFor(
594 LocalAccessible* aAccessible, bool* aValue) {
595 return *aValue = (aAccessible->NativeRole() == roles::STATICTEXT);
598 void TextAttrsMgr::AutoGeneratedTextAttr::ExposeValue(
599 AccAttributes* aAttributes, const bool& aValue) {
600 aAttributes->SetAttribute(nsGkAtoms::auto_generated, aValue);
603 ////////////////////////////////////////////////////////////////////////////////
604 // TextDecorTextAttr
605 ////////////////////////////////////////////////////////////////////////////////
607 TextAttrsMgr::TextDecorValue::TextDecorValue(nsIFrame* aFrame) {
608 const nsStyleTextReset* textReset = aFrame->StyleTextReset();
609 mStyle = textReset->mTextDecorationStyle;
610 mColor = textReset->mTextDecorationColor.CalcColor(aFrame);
611 mLine =
612 textReset->mTextDecorationLine & (StyleTextDecorationLine::UNDERLINE |
613 StyleTextDecorationLine::LINE_THROUGH);
616 TextAttrsMgr::TextDecorTextAttr::TextDecorTextAttr(nsIFrame* aRootFrame,
617 nsIFrame* aFrame)
618 : TTextAttr<TextDecorValue>(!aFrame) {
619 mRootNativeValue = TextDecorValue(aRootFrame);
620 mIsRootDefined = mRootNativeValue.IsDefined();
622 if (aFrame) {
623 mNativeValue = TextDecorValue(aFrame);
624 mIsDefined = mNativeValue.IsDefined();
628 bool TextAttrsMgr::TextDecorTextAttr::GetValueFor(LocalAccessible* aAccessible,
629 TextDecorValue* aValue) {
630 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
631 if (elm) {
632 nsIFrame* frame = elm->GetPrimaryFrame();
633 if (frame) {
634 *aValue = TextDecorValue(frame);
635 return aValue->IsDefined();
638 return false;
641 void TextAttrsMgr::TextDecorTextAttr::ExposeValue(
642 AccAttributes* aAttributes, const TextDecorValue& aValue) {
643 if (aValue.IsUnderline()) {
644 RefPtr<nsAtom> underlineStyle =
645 StyleInfo::TextDecorationStyleToAtom(aValue.Style());
646 aAttributes->SetAttribute(nsGkAtoms::textUnderlineStyle, underlineStyle);
648 aAttributes->SetAttribute(nsGkAtoms::textUnderlineColor,
649 Color{aValue.Color()});
650 return;
653 if (aValue.IsLineThrough()) {
654 RefPtr<nsAtom> lineThroughStyle =
655 StyleInfo::TextDecorationStyleToAtom(aValue.Style());
656 aAttributes->SetAttribute(nsGkAtoms::textLineThroughStyle,
657 lineThroughStyle);
659 aAttributes->SetAttribute(nsGkAtoms::textLineThroughColor,
660 Color{aValue.Color()});
664 ////////////////////////////////////////////////////////////////////////////////
665 // TextPosTextAttr
666 ////////////////////////////////////////////////////////////////////////////////
668 TextAttrsMgr::TextPosTextAttr::TextPosTextAttr(nsIFrame* aRootFrame,
669 nsIFrame* aFrame,
670 nsIContent* aRootElm,
671 nsIContent* aElm)
672 : TTextAttr<Maybe<TextPosValue>>(!aFrame && !aElm), mRootElm(aRootElm) {
673 // Get the text-position values for the roots and children.
674 // If we find an ARIA text-position value on a DOM element - searching up
675 // from the supplied root DOM element - use the associated frame as the root
676 // frame. This ensures that we're using the proper root frame for comparison.
677 nsIFrame* ariaFrame = nullptr;
678 Maybe<TextPosValue> rootAria = GetAriaTextPosValue(aRootElm, ariaFrame);
679 if (rootAria && ariaFrame) {
680 aRootFrame = ariaFrame;
682 Maybe<TextPosValue> rootLayout = GetLayoutTextPosValue(aRootFrame);
683 Maybe<TextPosValue> childLayout;
684 Maybe<TextPosValue> childAria;
685 if (aFrame) {
686 childLayout = GetLayoutTextPosValue(aFrame);
688 if (aElm) {
689 childAria = GetAriaTextPosValue(aElm);
692 // Aria values take precedence over layout values.
693 mIsRootDefined = rootAria || rootLayout;
694 mRootNativeValue = rootAria ? rootAria : rootLayout;
695 mIsDefined = childAria || childLayout;
696 mNativeValue = childAria ? childAria : childLayout;
698 // If there's no child text-position information from ARIA, and the child
699 // layout info is equivalent to the root layout info (i.e., it's inherited),
700 // then we should prefer the root information.
701 if (!childAria && childLayout == rootLayout) {
702 mIsDefined = false;
706 bool TextAttrsMgr::TextPosTextAttr::GetValueFor(LocalAccessible* aAccessible,
707 Maybe<TextPosValue>* aValue) {
708 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
709 if (elm) {
710 nsIFrame* frame = elm->GetPrimaryFrame();
711 if (frame) {
712 Maybe<TextPosValue> layoutValue = GetLayoutTextPosValue(frame);
713 Maybe<TextPosValue> ariaValue = GetAriaTextPosValue(elm);
715 *aValue = ariaValue ? ariaValue : layoutValue;
716 return aValue->isSome();
719 return false;
722 void TextAttrsMgr::TextPosTextAttr::ExposeValue(
723 AccAttributes* aAttributes, const Maybe<TextPosValue>& aValue) {
724 if (aValue.isNothing()) {
725 return;
728 RefPtr<nsAtom> atom = nullptr;
729 switch (*aValue) {
730 case eTextPosBaseline:
731 atom = nsGkAtoms::baseline;
732 break;
734 case eTextPosSub:
735 atom = nsGkAtoms::sub;
736 break;
738 case eTextPosSuper:
739 atom = NS_Atomize("super");
740 break;
743 if (atom) {
744 aAttributes->SetAttribute(nsGkAtoms::textPosition, atom);
748 Maybe<TextAttrsMgr::TextPosValue>
749 TextAttrsMgr::TextPosTextAttr::GetAriaTextPosValue(nsIContent* aElm) const {
750 nsIFrame* ariaFrame = nullptr;
751 return GetAriaTextPosValue(aElm, ariaFrame);
754 Maybe<TextAttrsMgr::TextPosValue>
755 TextAttrsMgr::TextPosTextAttr::GetAriaTextPosValue(nsIContent* aElm,
756 nsIFrame*& ariaFrame) const {
757 // Search for the superscript and subscript roles that imply text-position.
758 const nsIContent* elm = aElm;
759 do {
760 if (elm->IsElement()) {
761 const mozilla::dom::Element* domElm = elm->AsElement();
762 static const dom::Element::AttrValuesArray tokens[] = {
763 nsGkAtoms::subscript, nsGkAtoms::superscript, nullptr};
764 const int32_t valueIdx = domElm->FindAttrValueIn(
765 kNameSpaceID_None, nsGkAtoms::role, tokens, eCaseMatters);
766 ariaFrame = domElm->GetPrimaryFrame();
767 if (valueIdx == 0) {
768 return Some(eTextPosSub);
770 if (valueIdx == 1) {
771 return Some(eTextPosSuper);
774 } while ((elm = elm->GetParent()) && elm != mRootElm);
776 ariaFrame = nullptr;
777 return Nothing{};
780 Maybe<TextAttrsMgr::TextPosValue>
781 TextAttrsMgr::TextPosTextAttr::GetLayoutTextPosValue(nsIFrame* aFrame) const {
782 const auto& verticalAlign = aFrame->StyleDisplay()->mVerticalAlign;
783 if (verticalAlign.IsKeyword()) {
784 switch (verticalAlign.AsKeyword()) {
785 case StyleVerticalAlignKeyword::Baseline:
786 return Some(eTextPosBaseline);
787 case StyleVerticalAlignKeyword::Sub:
788 return Some(eTextPosSub);
789 case StyleVerticalAlignKeyword::Super:
790 return Some(eTextPosSuper);
791 // No good guess for the rest, so do not expose value of text-position
792 // attribute.
793 default:
794 return Nothing{};
798 const auto& length = verticalAlign.AsLength();
799 if (length.ConvertsToPercentage()) {
800 const float percentValue = length.ToPercentage();
801 return percentValue > 0 ? Some(eTextPosSuper)
802 : (percentValue < 0 ? Some(eTextPosSub)
803 : Some(eTextPosBaseline));
806 if (length.ConvertsToLength()) {
807 const nscoord coordValue = length.ToLength();
808 return coordValue > 0
809 ? Some(eTextPosSuper)
810 : (coordValue < 0 ? Some(eTextPosSub) : Some(eTextPosBaseline));
813 if (const nsIContent* content = aFrame->GetContent()) {
814 if (content->IsHTMLElement(nsGkAtoms::sup)) return Some(eTextPosSuper);
815 if (content->IsHTMLElement(nsGkAtoms::sub)) return Some(eTextPosSub);
818 return Nothing{};