no bug - Correct some typos in the comments. a=typo-fix
[gecko.git] / accessible / base / TextAttrs.cpp
blobe1ca62e549baf73565525755d9d743339cd7c49a
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 "AccAttributes.h"
9 #include "nsAccUtils.h"
10 #include "nsCoreUtils.h"
11 #include "StyleInfo.h"
13 #include "gfxTextRun.h"
14 #include "nsFontMetrics.h"
15 #include "nsLayoutUtils.h"
16 #include "nsContainerFrame.h"
17 #include "HyperTextAccessible.h"
18 #include "mozilla/AppUnits.h"
20 using namespace mozilla;
21 using namespace mozilla::a11y;
23 ////////////////////////////////////////////////////////////////////////////////
24 // TextAttrsMgr
25 ////////////////////////////////////////////////////////////////////////////////
27 void TextAttrsMgr::GetAttributes(AccAttributes* aAttributes,
28 uint32_t* aStartOffset, uint32_t* aEndOffset) {
29 // 1. Hyper text accessible must be specified always.
30 // 2. Offset accessible must be specified in
31 // the case of text attributes. Result hyper text offsets are optional if you
32 // just want the attributes for a single text Accessible.
33 // 3. Offset accessible and result hyper text offsets must not be specified
34 // but include default text attributes flag and attributes list must be
35 // specified in the case of default text attributes.
36 MOZ_ASSERT(
37 mHyperTextAcc && ((mOffsetAcc && mOffsetAccIdx != -1) ||
38 (!mOffsetAcc && mOffsetAccIdx == -1 && !aStartOffset &&
39 !aEndOffset && mIncludeDefAttrs && aAttributes)),
40 "Wrong usage of TextAttrsMgr!");
42 // Embedded objects are combined into own range with empty attributes set.
43 if (mOffsetAcc && !mOffsetAcc->IsText()) {
44 if (!aStartOffset) {
45 return;
47 for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
48 LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
49 if (currAcc->IsText()) break;
51 (*aStartOffset)--;
54 uint32_t childCount = mHyperTextAcc->ChildCount();
55 for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childCount;
56 childIdx++) {
57 LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
58 if (currAcc->IsText()) break;
60 (*aEndOffset)++;
63 return;
66 // Get the content and frame of the accessible. In the case of document
67 // accessible it's role content and root frame.
68 nsIContent* hyperTextElm = mHyperTextAcc->GetContent();
69 if (!hyperTextElm) {
70 return; // XXX: we don't support text attrs on document with no body
73 nsIFrame* rootFrame = mHyperTextAcc->GetFrame();
74 if (!rootFrame) {
75 return;
78 nsIContent *offsetNode = nullptr, *offsetElm = nullptr;
79 nsIFrame* frame = nullptr;
80 if (mOffsetAcc) {
81 offsetNode = mOffsetAcc->GetContent();
82 offsetElm = nsCoreUtils::GetDOMElementFor(offsetNode);
83 MOZ_ASSERT(offsetElm, "No element for offset accessible!");
84 if (!offsetElm) return;
86 frame = offsetElm->GetPrimaryFrame();
89 // "language" text attribute
90 LangTextAttr langTextAttr(mHyperTextAcc, hyperTextElm, offsetNode);
92 // "aria-invalid" text attribute
93 InvalidTextAttr invalidTextAttr(hyperTextElm, offsetNode);
95 // "background-color" text attribute
96 BGColorTextAttr bgColorTextAttr(rootFrame, frame);
98 // "color" text attribute
99 ColorTextAttr colorTextAttr(rootFrame, frame);
101 // "font-family" text attribute
102 FontFamilyTextAttr fontFamilyTextAttr(rootFrame, frame);
104 // "font-size" text attribute
105 FontSizeTextAttr fontSizeTextAttr(rootFrame, frame);
107 // "font-style" text attribute
108 FontStyleTextAttr fontStyleTextAttr(rootFrame, frame);
110 // "font-weight" text attribute
111 FontWeightTextAttr fontWeightTextAttr(rootFrame, frame);
113 // "auto-generated" text attribute
114 AutoGeneratedTextAttr autoGenTextAttr(mHyperTextAcc, mOffsetAcc);
116 // "text-underline(line-through)-style(color)" text attributes
117 TextDecorTextAttr textDecorTextAttr(rootFrame, frame);
119 // "text-position" text attribute
120 TextPosTextAttr textPosTextAttr(rootFrame, frame, hyperTextElm, offsetNode);
122 TextAttr* attrArray[] = {
123 &langTextAttr, &invalidTextAttr, &bgColorTextAttr,
124 &colorTextAttr, &fontFamilyTextAttr, &fontSizeTextAttr,
125 &fontStyleTextAttr, &fontWeightTextAttr, &autoGenTextAttr,
126 &textDecorTextAttr, &textPosTextAttr};
128 // Expose text attributes if applicable.
129 if (aAttributes) {
130 for (uint32_t idx = 0; idx < ArrayLength(attrArray); idx++) {
131 attrArray[idx]->Expose(aAttributes, mIncludeDefAttrs);
135 // Expose text attributes range where they are applied if applicable.
136 if (aStartOffset) {
137 GetRange(attrArray, ArrayLength(attrArray), aStartOffset, aEndOffset);
141 void TextAttrsMgr::GetRange(TextAttr* aAttrArray[], uint32_t aAttrArrayLen,
142 uint32_t* aStartOffset, uint32_t* aEndOffset) {
143 // Navigate backward from anchor accessible to find start offset.
144 for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
145 LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
147 // Stop on embedded accessible since embedded accessibles are combined into
148 // own range.
149 if (!currAcc->IsText()) break;
151 MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()),
152 "Text accessible has to have an associated DOM element");
154 bool offsetFound = false;
155 for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
156 TextAttr* textAttr = aAttrArray[attrIdx];
157 if (!textAttr->Equal(currAcc)) {
158 offsetFound = true;
159 break;
163 if (offsetFound) break;
165 *(aStartOffset) -= nsAccUtils::TextLength(currAcc);
168 // Navigate forward from anchor accessible to find end offset.
169 uint32_t childLen = mHyperTextAcc->ChildCount();
170 for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childLen; childIdx++) {
171 LocalAccessible* currAcc = mHyperTextAcc->LocalChildAt(childIdx);
172 if (!currAcc->IsText()) break;
174 MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()),
175 "Text accessible has to have an associated DOM element");
177 bool offsetFound = false;
178 for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
179 TextAttr* textAttr = aAttrArray[attrIdx];
181 // Alter the end offset when text attribute changes its value and stop
182 // the search.
183 if (!textAttr->Equal(currAcc)) {
184 offsetFound = true;
185 break;
189 if (offsetFound) break;
191 (*aEndOffset) += nsAccUtils::TextLength(currAcc);
195 ////////////////////////////////////////////////////////////////////////////////
196 // LangTextAttr
197 ////////////////////////////////////////////////////////////////////////////////
199 TextAttrsMgr::LangTextAttr::LangTextAttr(HyperTextAccessible* aRoot,
200 nsIContent* aRootElm, nsIContent* aElm)
201 : TTextAttr<nsString>(!aElm), mRootContent(aRootElm) {
202 aRoot->Language(mRootNativeValue);
203 mIsRootDefined = !mRootNativeValue.IsEmpty();
205 if (aElm) {
206 nsCoreUtils::GetLanguageFor(aElm, mRootContent, mNativeValue);
207 mIsDefined = !mNativeValue.IsEmpty();
211 TextAttrsMgr::LangTextAttr::~LangTextAttr() {}
213 bool TextAttrsMgr::LangTextAttr::GetValueFor(LocalAccessible* aAccessible,
214 nsString* aValue) {
215 nsCoreUtils::GetLanguageFor(aAccessible->GetContent(), mRootContent, *aValue);
216 return !aValue->IsEmpty();
219 void TextAttrsMgr::LangTextAttr::ExposeValue(AccAttributes* aAttributes,
220 const nsString& aValue) {
221 RefPtr<nsAtom> lang = NS_Atomize(aValue);
222 aAttributes->SetAttribute(nsGkAtoms::language, lang);
225 ////////////////////////////////////////////////////////////////////////////////
226 // InvalidTextAttr
227 ////////////////////////////////////////////////////////////////////////////////
229 TextAttrsMgr::InvalidTextAttr::InvalidTextAttr(nsIContent* aRootElm,
230 nsIContent* aElm)
231 : TTextAttr<uint32_t>(!aElm), mRootElm(aRootElm) {
232 mIsRootDefined = GetValue(mRootElm, &mRootNativeValue);
233 if (aElm) mIsDefined = GetValue(aElm, &mNativeValue);
236 bool TextAttrsMgr::InvalidTextAttr::GetValueFor(LocalAccessible* aAccessible,
237 uint32_t* aValue) {
238 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
239 return elm ? GetValue(elm, aValue) : false;
242 void TextAttrsMgr::InvalidTextAttr::ExposeValue(AccAttributes* aAttributes,
243 const uint32_t& aValue) {
244 switch (aValue) {
245 case eFalse:
246 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::_false);
247 break;
249 case eGrammar:
250 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::grammar);
251 break;
253 case eSpelling:
254 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::spelling);
255 break;
257 case eTrue:
258 aAttributes->SetAttribute(nsGkAtoms::invalid, nsGkAtoms::_true);
259 break;
263 bool TextAttrsMgr::InvalidTextAttr::GetValue(nsIContent* aElm,
264 uint32_t* aValue) {
265 nsIContent* elm = aElm;
266 do {
267 if (nsAccUtils::HasDefinedARIAToken(elm, nsGkAtoms::aria_invalid)) {
268 static dom::Element::AttrValuesArray tokens[] = {
269 nsGkAtoms::_false, nsGkAtoms::grammar, nsGkAtoms::spelling, nullptr};
271 int32_t idx = nsAccUtils::FindARIAAttrValueIn(
272 elm->AsElement(), nsGkAtoms::aria_invalid, tokens, eCaseMatters);
273 switch (idx) {
274 case 0:
275 *aValue = eFalse;
276 return true;
277 case 1:
278 *aValue = eGrammar;
279 return true;
280 case 2:
281 *aValue = eSpelling;
282 return true;
283 default:
284 *aValue = eTrue;
285 return true;
288 } while ((elm = elm->GetParent()) && elm != mRootElm);
290 return false;
293 ////////////////////////////////////////////////////////////////////////////////
294 // BGColorTextAttr
295 ////////////////////////////////////////////////////////////////////////////////
297 TextAttrsMgr::BGColorTextAttr::BGColorTextAttr(nsIFrame* aRootFrame,
298 nsIFrame* aFrame)
299 : TTextAttr<nscolor>(!aFrame), mRootFrame(aRootFrame) {
300 mIsRootDefined = GetColor(mRootFrame, &mRootNativeValue);
301 if (aFrame) mIsDefined = GetColor(aFrame, &mNativeValue);
304 bool TextAttrsMgr::BGColorTextAttr::GetValueFor(LocalAccessible* aAccessible,
305 nscolor* aValue) {
306 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
307 if (elm) {
308 nsIFrame* frame = elm->GetPrimaryFrame();
309 if (frame) {
310 return GetColor(frame, aValue);
313 return false;
316 void TextAttrsMgr::BGColorTextAttr::ExposeValue(AccAttributes* aAttributes,
317 const nscolor& aValue) {
318 aAttributes->SetAttribute(nsGkAtoms::backgroundColor, Color{aValue});
321 bool TextAttrsMgr::BGColorTextAttr::GetColor(nsIFrame* aFrame,
322 nscolor* aColor) {
323 nscolor backgroundColor = aFrame->StyleBackground()->BackgroundColor(aFrame);
324 if (NS_GET_A(backgroundColor) > 0) {
325 *aColor = backgroundColor;
326 return true;
329 nsContainerFrame* parentFrame = aFrame->GetParent();
330 if (!parentFrame) {
331 *aColor = aFrame->PresContext()->DefaultBackgroundColor();
332 return true;
335 // Each frame of parents chain for the initially passed 'aFrame' has
336 // transparent background color. So background color isn't changed from
337 // 'mRootFrame' to initially passed 'aFrame'.
338 if (parentFrame == mRootFrame) return false;
340 return GetColor(parentFrame, aColor);
343 ////////////////////////////////////////////////////////////////////////////////
344 // ColorTextAttr
345 ////////////////////////////////////////////////////////////////////////////////
347 TextAttrsMgr::ColorTextAttr::ColorTextAttr(nsIFrame* aRootFrame,
348 nsIFrame* aFrame)
349 : TTextAttr<nscolor>(!aFrame) {
350 mRootNativeValue = aRootFrame->StyleText()->mColor.ToColor();
351 mIsRootDefined = true;
353 if (aFrame) {
354 mNativeValue = aFrame->StyleText()->mColor.ToColor();
355 mIsDefined = true;
359 bool TextAttrsMgr::ColorTextAttr::GetValueFor(LocalAccessible* aAccessible,
360 nscolor* aValue) {
361 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
362 if (elm) {
363 if (nsIFrame* frame = elm->GetPrimaryFrame()) {
364 *aValue = frame->StyleText()->mColor.ToColor();
365 return true;
368 return false;
371 void TextAttrsMgr::ColorTextAttr::ExposeValue(AccAttributes* aAttributes,
372 const nscolor& aValue) {
373 aAttributes->SetAttribute(nsGkAtoms::color, Color{aValue});
376 ////////////////////////////////////////////////////////////////////////////////
377 // FontFamilyTextAttr
378 ////////////////////////////////////////////////////////////////////////////////
380 TextAttrsMgr::FontFamilyTextAttr::FontFamilyTextAttr(nsIFrame* aRootFrame,
381 nsIFrame* aFrame)
382 : TTextAttr<nsString>(!aFrame) {
383 mIsRootDefined = GetFontFamily(aRootFrame, mRootNativeValue);
385 if (aFrame) mIsDefined = GetFontFamily(aFrame, mNativeValue);
388 bool TextAttrsMgr::FontFamilyTextAttr::GetValueFor(LocalAccessible* aAccessible,
389 nsString* aValue) {
390 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
391 if (elm) {
392 nsIFrame* frame = elm->GetPrimaryFrame();
393 if (frame) {
394 return GetFontFamily(frame, *aValue);
397 return false;
400 void TextAttrsMgr::FontFamilyTextAttr::ExposeValue(AccAttributes* aAttributes,
401 const nsString& aValue) {
402 RefPtr<nsAtom> family = NS_Atomize(aValue);
403 aAttributes->SetAttribute(nsGkAtoms::font_family, family);
406 bool TextAttrsMgr::FontFamilyTextAttr::GetFontFamily(nsIFrame* aFrame,
407 nsString& aFamily) {
408 RefPtr<nsFontMetrics> fm =
409 nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
411 gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
412 RefPtr<gfxFont> font = fontGroup->GetFirstValidFont();
413 gfxFontEntry* fontEntry = font->GetFontEntry();
414 aFamily.Append(NS_ConvertUTF8toUTF16(fontEntry->FamilyName()));
415 return true;
418 ////////////////////////////////////////////////////////////////////////////////
419 // FontSizeTextAttr
420 ////////////////////////////////////////////////////////////////////////////////
422 TextAttrsMgr::FontSizeTextAttr::FontSizeTextAttr(nsIFrame* aRootFrame,
423 nsIFrame* aFrame)
424 : TTextAttr<nscoord>(!aFrame) {
425 mDC = aRootFrame->PresContext()->DeviceContext();
427 mRootNativeValue = aRootFrame->StyleFont()->mSize.ToAppUnits();
428 mIsRootDefined = true;
430 if (aFrame) {
431 mNativeValue = aFrame->StyleFont()->mSize.ToAppUnits();
432 mIsDefined = true;
436 bool TextAttrsMgr::FontSizeTextAttr::GetValueFor(LocalAccessible* aAccessible,
437 nscoord* aValue) {
438 nsIContent* el = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
439 if (el) {
440 nsIFrame* frame = el->GetPrimaryFrame();
441 if (frame) {
442 *aValue = frame->StyleFont()->mSize.ToAppUnits();
443 return true;
446 return false;
449 void TextAttrsMgr::FontSizeTextAttr::ExposeValue(AccAttributes* aAttributes,
450 const nscoord& aValue) {
451 // Convert from nscoord to pt.
453 // Note: according to IA2, "The conversion doesn't have to be exact.
454 // The intent is to give the user a feel for the size of the text."
456 // ATK does not specify a unit and will likely follow IA2 here.
458 // XXX todo: consider sharing this code with layout module? (bug 474621)
459 float px = NSAppUnitsToFloatPixels(aValue, mozilla::AppUnitsPerCSSPixel());
460 // Each pt is 4/3 of a CSS pixel.
461 FontSize fontSize{NS_lround(px * 3 / 4)};
463 aAttributes->SetAttribute(nsGkAtoms::font_size, fontSize);
466 ////////////////////////////////////////////////////////////////////////////////
467 // FontStyleTextAttr
468 ////////////////////////////////////////////////////////////////////////////////
470 TextAttrsMgr::FontStyleTextAttr::FontStyleTextAttr(nsIFrame* aRootFrame,
471 nsIFrame* aFrame)
472 : TTextAttr<FontSlantStyle>(!aFrame) {
473 mRootNativeValue = aRootFrame->StyleFont()->mFont.style;
474 mIsRootDefined = true;
476 if (aFrame) {
477 mNativeValue = aFrame->StyleFont()->mFont.style;
478 mIsDefined = true;
482 bool TextAttrsMgr::FontStyleTextAttr::GetValueFor(LocalAccessible* aAccessible,
483 FontSlantStyle* aValue) {
484 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
485 if (elm) {
486 nsIFrame* frame = elm->GetPrimaryFrame();
487 if (frame) {
488 *aValue = frame->StyleFont()->mFont.style;
489 return true;
492 return false;
495 void TextAttrsMgr::FontStyleTextAttr::ExposeValue(
496 AccAttributes* aAttributes, const FontSlantStyle& aValue) {
497 if (aValue.IsNormal()) {
498 aAttributes->SetAttribute(nsGkAtoms::font_style, nsGkAtoms::normal);
499 } else if (aValue.IsItalic()) {
500 RefPtr<nsAtom> atom = NS_Atomize("italic");
501 aAttributes->SetAttribute(nsGkAtoms::font_style, atom);
502 } else {
503 nsAutoCString s;
504 aValue.ToString(s);
505 nsString wide;
506 CopyUTF8toUTF16(s, wide);
507 aAttributes->SetAttribute(nsGkAtoms::font_style, std::move(wide));
511 ////////////////////////////////////////////////////////////////////////////////
512 // FontWeightTextAttr
513 ////////////////////////////////////////////////////////////////////////////////
515 TextAttrsMgr::FontWeightTextAttr::FontWeightTextAttr(nsIFrame* aRootFrame,
516 nsIFrame* aFrame)
517 : TTextAttr<FontWeight>(!aFrame) {
518 mRootNativeValue = GetFontWeight(aRootFrame);
519 mIsRootDefined = true;
521 if (aFrame) {
522 mNativeValue = GetFontWeight(aFrame);
523 mIsDefined = true;
527 bool TextAttrsMgr::FontWeightTextAttr::GetValueFor(LocalAccessible* aAccessible,
528 FontWeight* aValue) {
529 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
530 if (elm) {
531 nsIFrame* frame = elm->GetPrimaryFrame();
532 if (frame) {
533 *aValue = GetFontWeight(frame);
534 return true;
537 return false;
540 void TextAttrsMgr::FontWeightTextAttr::ExposeValue(AccAttributes* aAttributes,
541 const FontWeight& aValue) {
542 int value = aValue.ToIntRounded();
543 aAttributes->SetAttribute(nsGkAtoms::fontWeight, value);
546 FontWeight TextAttrsMgr::FontWeightTextAttr::GetFontWeight(nsIFrame* aFrame) {
547 // nsFont::width isn't suitable here because it's necessary to expose real
548 // value of font weight (used font might not have some font weight values).
549 RefPtr<nsFontMetrics> fm =
550 nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
552 gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
553 RefPtr<gfxFont> font = fontGroup->GetFirstValidFont();
555 // When there doesn't exist a bold font in the family and so the rendering of
556 // a non-bold font face is changed so that the user sees what looks like a
557 // bold font, i.e. synthetic bolding is used. (Simply returns false on any
558 // platforms that don't use the multi-strike synthetic bolding.)
559 if (font->ApplySyntheticBold()) {
560 return FontWeight::BOLD;
563 // On Windows, font->GetStyle()->weight will give the same weight as
564 // fontEntry->Weight(), the weight of the first font in the font group,
565 // which may not be the weight of the font face used to render the
566 // characters. On Mac, font->GetStyle()->weight will just give the same
567 // number as getComputedStyle(). fontEntry->Weight() will give the weight
568 // range supported by the font face used, so we clamp the weight that was
569 // requested by style to what is actually supported by the font.
570 gfxFontEntry* fontEntry = font->GetFontEntry();
571 return fontEntry->Weight().Clamp(font->GetStyle()->weight);
574 ////////////////////////////////////////////////////////////////////////////////
575 // AutoGeneratedTextAttr
576 ////////////////////////////////////////////////////////////////////////////////
577 TextAttrsMgr::AutoGeneratedTextAttr::AutoGeneratedTextAttr(
578 HyperTextAccessible* aHyperTextAcc, LocalAccessible* aAccessible)
579 : TTextAttr<bool>(!aAccessible) {
580 mRootNativeValue = false;
581 mIsRootDefined = false;
583 if (aAccessible) {
584 mIsDefined = mNativeValue =
585 ((aAccessible->NativeRole() == roles::STATICTEXT) ||
586 (aAccessible->NativeRole() == roles::LISTITEM_MARKER));
590 bool TextAttrsMgr::AutoGeneratedTextAttr::GetValueFor(
591 LocalAccessible* aAccessible, bool* aValue) {
592 return *aValue = (aAccessible->NativeRole() == roles::STATICTEXT);
595 void TextAttrsMgr::AutoGeneratedTextAttr::ExposeValue(
596 AccAttributes* aAttributes, const bool& aValue) {
597 aAttributes->SetAttribute(nsGkAtoms::auto_generated, aValue);
600 ////////////////////////////////////////////////////////////////////////////////
601 // TextDecorTextAttr
602 ////////////////////////////////////////////////////////////////////////////////
604 TextAttrsMgr::TextDecorValue::TextDecorValue(nsIFrame* aFrame) {
605 const nsStyleTextReset* textReset = aFrame->StyleTextReset();
606 mStyle = textReset->mTextDecorationStyle;
607 mColor = textReset->mTextDecorationColor.CalcColor(aFrame);
608 mLine =
609 textReset->mTextDecorationLine & (StyleTextDecorationLine::UNDERLINE |
610 StyleTextDecorationLine::LINE_THROUGH);
613 TextAttrsMgr::TextDecorTextAttr::TextDecorTextAttr(nsIFrame* aRootFrame,
614 nsIFrame* aFrame)
615 : TTextAttr<TextDecorValue>(!aFrame) {
616 mRootNativeValue = TextDecorValue(aRootFrame);
617 mIsRootDefined = mRootNativeValue.IsDefined();
619 if (aFrame) {
620 mNativeValue = TextDecorValue(aFrame);
621 mIsDefined = mNativeValue.IsDefined();
625 bool TextAttrsMgr::TextDecorTextAttr::GetValueFor(LocalAccessible* aAccessible,
626 TextDecorValue* aValue) {
627 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
628 if (elm) {
629 nsIFrame* frame = elm->GetPrimaryFrame();
630 if (frame) {
631 *aValue = TextDecorValue(frame);
632 return aValue->IsDefined();
635 return false;
638 void TextAttrsMgr::TextDecorTextAttr::ExposeValue(
639 AccAttributes* aAttributes, const TextDecorValue& aValue) {
640 if (aValue.IsUnderline()) {
641 RefPtr<nsAtom> underlineStyle =
642 StyleInfo::TextDecorationStyleToAtom(aValue.Style());
643 aAttributes->SetAttribute(nsGkAtoms::textUnderlineStyle, underlineStyle);
645 aAttributes->SetAttribute(nsGkAtoms::textUnderlineColor,
646 Color{aValue.Color()});
647 return;
650 if (aValue.IsLineThrough()) {
651 RefPtr<nsAtom> lineThroughStyle =
652 StyleInfo::TextDecorationStyleToAtom(aValue.Style());
653 aAttributes->SetAttribute(nsGkAtoms::textLineThroughStyle,
654 lineThroughStyle);
656 aAttributes->SetAttribute(nsGkAtoms::textLineThroughColor,
657 Color{aValue.Color()});
661 ////////////////////////////////////////////////////////////////////////////////
662 // TextPosTextAttr
663 ////////////////////////////////////////////////////////////////////////////////
665 TextAttrsMgr::TextPosTextAttr::TextPosTextAttr(nsIFrame* aRootFrame,
666 nsIFrame* aFrame,
667 nsIContent* aRootElm,
668 nsIContent* aElm)
669 : TTextAttr<Maybe<TextPosValue>>(!aFrame && !aElm), mRootElm(aRootElm) {
670 // Get the text-position values for the roots and children.
671 // If we find an ARIA text-position value on a DOM element - searching up
672 // from the supplied root DOM element - use the associated frame as the root
673 // frame. This ensures that we're using the proper root frame for comparison.
674 nsIFrame* ariaFrame = nullptr;
675 Maybe<TextPosValue> rootAria = GetAriaTextPosValue(aRootElm, ariaFrame);
676 if (rootAria && ariaFrame) {
677 aRootFrame = ariaFrame;
679 Maybe<TextPosValue> rootLayout = GetLayoutTextPosValue(aRootFrame);
680 Maybe<TextPosValue> childLayout;
681 Maybe<TextPosValue> childAria;
682 if (aFrame) {
683 childLayout = GetLayoutTextPosValue(aFrame);
685 if (aElm) {
686 childAria = GetAriaTextPosValue(aElm);
689 // Aria values take precedence over layout values.
690 mIsRootDefined = rootAria || rootLayout;
691 mRootNativeValue = rootAria ? rootAria : rootLayout;
692 mIsDefined = childAria || childLayout;
693 mNativeValue = childAria ? childAria : childLayout;
695 // If there's no child text-position information from ARIA, and the child
696 // layout info is equivalent to the root layout info (i.e., it's inherited),
697 // then we should prefer the root information.
698 if (!childAria && childLayout == rootLayout) {
699 mIsDefined = false;
703 bool TextAttrsMgr::TextPosTextAttr::GetValueFor(LocalAccessible* aAccessible,
704 Maybe<TextPosValue>* aValue) {
705 nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
706 if (elm) {
707 nsIFrame* frame = elm->GetPrimaryFrame();
708 if (frame) {
709 Maybe<TextPosValue> layoutValue = GetLayoutTextPosValue(frame);
710 Maybe<TextPosValue> ariaValue = GetAriaTextPosValue(elm);
712 *aValue = ariaValue ? ariaValue : layoutValue;
713 return aValue->isSome();
716 return false;
719 void TextAttrsMgr::TextPosTextAttr::ExposeValue(
720 AccAttributes* aAttributes, const Maybe<TextPosValue>& aValue) {
721 if (aValue.isNothing()) {
722 return;
725 RefPtr<nsAtom> atom = nullptr;
726 switch (*aValue) {
727 case eTextPosBaseline:
728 atom = nsGkAtoms::baseline;
729 break;
731 case eTextPosSub:
732 atom = nsGkAtoms::sub;
733 break;
735 case eTextPosSuper:
736 atom = NS_Atomize("super");
737 break;
740 if (atom) {
741 aAttributes->SetAttribute(nsGkAtoms::textPosition, atom);
745 Maybe<TextAttrsMgr::TextPosValue>
746 TextAttrsMgr::TextPosTextAttr::GetAriaTextPosValue(nsIContent* aElm) const {
747 nsIFrame* ariaFrame = nullptr;
748 return GetAriaTextPosValue(aElm, ariaFrame);
751 Maybe<TextAttrsMgr::TextPosValue>
752 TextAttrsMgr::TextPosTextAttr::GetAriaTextPosValue(nsIContent* aElm,
753 nsIFrame*& ariaFrame) const {
754 // Search for the superscript and subscript roles that imply text-position.
755 const nsIContent* elm = aElm;
756 do {
757 if (elm->IsElement()) {
758 const mozilla::dom::Element* domElm = elm->AsElement();
759 static const dom::Element::AttrValuesArray tokens[] = {
760 nsGkAtoms::subscript, nsGkAtoms::superscript, nullptr};
761 const int32_t valueIdx = domElm->FindAttrValueIn(
762 kNameSpaceID_None, nsGkAtoms::role, tokens, eCaseMatters);
763 ariaFrame = domElm->GetPrimaryFrame();
764 if (valueIdx == 0) {
765 return Some(eTextPosSub);
767 if (valueIdx == 1) {
768 return Some(eTextPosSuper);
771 } while ((elm = elm->GetParent()) && elm != mRootElm);
773 ariaFrame = nullptr;
774 return Nothing{};
777 Maybe<TextAttrsMgr::TextPosValue>
778 TextAttrsMgr::TextPosTextAttr::GetLayoutTextPosValue(nsIFrame* aFrame) const {
779 const auto& verticalAlign = aFrame->StyleDisplay()->mVerticalAlign;
780 if (verticalAlign.IsKeyword()) {
781 switch (verticalAlign.AsKeyword()) {
782 case StyleVerticalAlignKeyword::Baseline:
783 return Some(eTextPosBaseline);
784 case StyleVerticalAlignKeyword::Sub:
785 return Some(eTextPosSub);
786 case StyleVerticalAlignKeyword::Super:
787 return Some(eTextPosSuper);
788 // No good guess for the rest, so do not expose value of text-position
789 // attribute.
790 default:
791 return Nothing{};
795 const auto& length = verticalAlign.AsLength();
796 if (length.ConvertsToPercentage()) {
797 const float percentValue = length.ToPercentage();
798 return percentValue > 0 ? Some(eTextPosSuper)
799 : (percentValue < 0 ? Some(eTextPosSub)
800 : Some(eTextPosBaseline));
803 if (length.ConvertsToLength()) {
804 const nscoord coordValue = length.ToLength();
805 return coordValue > 0
806 ? Some(eTextPosSuper)
807 : (coordValue < 0 ? Some(eTextPosSub) : Some(eTextPosBaseline));
810 if (const nsIContent* content = aFrame->GetContent()) {
811 if (content->IsHTMLElement(nsGkAtoms::sup)) return Some(eTextPosSuper);
812 if (content->IsHTMLElement(nsGkAtoms::sub)) return Some(eTextPosSub);
815 return Nothing{};